@paulduvall/claude-dev-toolkit 0.0.1-alpha.6 → 0.0.1-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/bin/claude-commands +185 -64
- package/hooks/lib/argument-parser.sh +0 -0
- package/hooks/lib/config-constants.sh +0 -0
- package/hooks/lib/context-manager.sh +0 -0
- package/hooks/lib/error-handler.sh +0 -0
- package/hooks/lib/execution-engine.sh +0 -0
- package/hooks/lib/file-utils.sh +0 -0
- package/hooks/lib/subagent-discovery.sh +0 -0
- package/hooks/lib/subagent-validator.sh +0 -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 +48 -6
- package/lib/configure-command.js +428 -0
- package/lib/dependency-validator.js +64 -5
- 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/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/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 +1 -1
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ npm install -g @paulduvall/claude-dev-toolkit
|
|
|
41
41
|
```bash
|
|
42
42
|
# Install specific command sets
|
|
43
43
|
claude-commands install --active # Install 13 production commands
|
|
44
|
-
claude-commands install --
|
|
44
|
+
claude-commands install --experiments # Install 45 experimental commands
|
|
45
45
|
claude-commands install --all # Install all 58 commands
|
|
46
46
|
```
|
|
47
47
|
|
|
@@ -89,7 +89,7 @@ Advanced commands for specialized workflows:
|
|
|
89
89
|
claude-commands list # List all available commands
|
|
90
90
|
claude-commands status # Show installation status
|
|
91
91
|
claude-commands install --active # Install production commands
|
|
92
|
-
claude-commands install --
|
|
92
|
+
claude-commands install --experiments # Install experimental commands
|
|
93
93
|
claude-commands install --all # Install all commands
|
|
94
94
|
|
|
95
95
|
# Configuration Management
|
|
@@ -174,8 +174,8 @@ npm run lint # Code linting
|
|
|
174
174
|
- **Configuration Tests**: Template and setup validation
|
|
175
175
|
|
|
176
176
|
### Architecture
|
|
177
|
-
- **
|
|
178
|
-
- **JavaScript-Based**:
|
|
177
|
+
- **Self-Contained Package**: No dependencies on repository cloning
|
|
178
|
+
- **JavaScript-Based**: Native Node.js integration with comprehensive testing
|
|
179
179
|
- **Modular Design**: Separate installer, config, and validation modules
|
|
180
180
|
- **Cross-Platform**: Works on macOS, Linux, and Windows
|
|
181
181
|
|
|
@@ -203,7 +203,7 @@ claude-commands install --active # Reinstall commands
|
|
|
203
203
|
chmod 755 ~/.claude/commands/*.md # Fix permissions
|
|
204
204
|
|
|
205
205
|
# Missing experimental commands?
|
|
206
|
-
claude-commands install --
|
|
206
|
+
claude-commands install --experiments # Install experimental set
|
|
207
207
|
|
|
208
208
|
# Test failures?
|
|
209
209
|
npm test # Run full test suite
|
|
@@ -212,13 +212,13 @@ npm run validate # Validate package
|
|
|
212
212
|
|
|
213
213
|
### Validation Commands
|
|
214
214
|
```bash
|
|
215
|
-
# Repository validation (from main repo)
|
|
216
|
-
./validate-commands.sh # JavaScript-based validation
|
|
217
|
-
./verify-setup.sh # Complete setup verification
|
|
218
|
-
|
|
219
215
|
# Package validation
|
|
220
216
|
npm run validate # Package structure validation
|
|
221
217
|
npm test # Comprehensive test suite
|
|
218
|
+
|
|
219
|
+
# CLI validation
|
|
220
|
+
claude-commands verify # Complete setup verification
|
|
221
|
+
claude-commands list # Check installed commands
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
## 📚 Documentation
|
|
@@ -252,8 +252,8 @@ npm test
|
|
|
252
252
|
```
|
|
253
253
|
|
|
254
254
|
### Adding Commands
|
|
255
|
-
1. Create command files in
|
|
256
|
-
2. Commands
|
|
255
|
+
1. Create command files in repository `slash-commands/active/` or `slash-commands/experiments/`
|
|
256
|
+
2. Commands are included in NPM package through build process
|
|
257
257
|
3. Validate with `npm run test:commands`
|
|
258
258
|
4. Follow existing patterns and security guidelines
|
|
259
259
|
|
|
@@ -264,7 +264,7 @@ npm test
|
|
|
264
264
|
|
|
265
265
|
## 🔄 Recent Updates
|
|
266
266
|
|
|
267
|
-
### Version 0.0.1-alpha.
|
|
267
|
+
### Version 0.0.1-alpha.7
|
|
268
268
|
- ✅ **NPM Scoped Package**: Published as `@paulduvall/claude-dev-toolkit`
|
|
269
269
|
- ✅ **Configuration Command**: Built-in `config` command for template management
|
|
270
270
|
- ✅ **Workflow Reporting**: Comprehensive GitHub Actions reporting
|
package/bin/claude-commands
CHANGED
|
@@ -20,64 +20,53 @@ program
|
|
|
20
20
|
.command('list')
|
|
21
21
|
.description('List all available commands')
|
|
22
22
|
.option('-a, --active', 'Show only active commands')
|
|
23
|
-
.option('-e, --
|
|
23
|
+
.option('-e, --experiments', 'Show only experimental commands')
|
|
24
24
|
.action((options) => {
|
|
25
25
|
const claudeDir = path.join(os.homedir(), '.claude', 'commands');
|
|
26
|
-
const activeDir = path.join(claudeDir, 'active');
|
|
27
|
-
const experimentalDir = path.join(claudeDir, 'experiments');
|
|
28
26
|
|
|
29
27
|
console.log('📦 Claude Custom Commands\n');
|
|
30
28
|
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} else {
|
|
42
|
-
console.log(' No active commands found');
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
console.log(' Commands directory not found');
|
|
46
|
-
}
|
|
47
|
-
console.log('');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!options.active) {
|
|
51
|
-
console.log('🧪 Experimental Commands:');
|
|
52
|
-
if (fs.existsSync(experimentalDir)) {
|
|
53
|
-
const expCommands = fs.readdirSync(experimentalDir)
|
|
54
|
-
.filter(f => f.endsWith('.md'))
|
|
55
|
-
.map(f => f.replace('.md', ''))
|
|
56
|
-
.sort();
|
|
57
|
-
|
|
58
|
-
if (expCommands.length > 0) {
|
|
59
|
-
expCommands.forEach(cmd => console.log(` /${cmd}`));
|
|
60
|
-
} else {
|
|
61
|
-
console.log(' No experimental commands found');
|
|
62
|
-
}
|
|
29
|
+
if (fs.existsSync(claudeDir)) {
|
|
30
|
+
const allCommands = fs.readdirSync(claudeDir)
|
|
31
|
+
.filter(f => f.endsWith('.md'))
|
|
32
|
+
.map(f => f.replace('.md', ''))
|
|
33
|
+
.sort();
|
|
34
|
+
|
|
35
|
+
if (allCommands.length > 0) {
|
|
36
|
+
console.log('🚀 Available Commands:');
|
|
37
|
+
allCommands.forEach(cmd => console.log(` /${cmd}`));
|
|
38
|
+
console.log(`\n📊 Total: ${allCommands.length} commands`);
|
|
63
39
|
} else {
|
|
64
|
-
console.log('
|
|
40
|
+
console.log(' No commands found');
|
|
65
41
|
}
|
|
66
|
-
|
|
42
|
+
} else {
|
|
43
|
+
console.log(' Commands directory not found');
|
|
67
44
|
}
|
|
68
45
|
|
|
69
|
-
console.log('💡 Usage: Try /xhelp in Claude Code to see all commands');
|
|
46
|
+
console.log('\n💡 Usage: Try /xhelp in Claude Code to see all commands');
|
|
70
47
|
});
|
|
71
48
|
|
|
72
49
|
program
|
|
73
50
|
.command('install')
|
|
74
|
-
.description('Install command sets')
|
|
75
|
-
.option('--active', 'Install
|
|
76
|
-
.option('--
|
|
77
|
-
.option('--all', 'Install
|
|
78
|
-
.
|
|
51
|
+
.description('Install command sets to ~/.claude/commands/')
|
|
52
|
+
.option('--active', 'Install production-ready commands (default)')
|
|
53
|
+
.option('--experiments', 'Install experimental commands only')
|
|
54
|
+
.option('--all', 'Install both active and experimental')
|
|
55
|
+
.option('--include <pattern>', 'Include specific commands matching pattern')
|
|
56
|
+
.option('--exclude <pattern>', 'Exclude commands matching pattern')
|
|
57
|
+
.option('--dry-run', 'Show what would be installed without making changes')
|
|
58
|
+
.option('--backup', 'Create backup before installation')
|
|
59
|
+
.action(async (options) => {
|
|
79
60
|
const installer = require('../lib/installer');
|
|
80
|
-
|
|
61
|
+
try {
|
|
62
|
+
const result = await installer.install(options);
|
|
63
|
+
if (!result.success && !result.dryRun) {
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error(`Installation failed: ${error.message}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
81
70
|
});
|
|
82
71
|
|
|
83
72
|
program
|
|
@@ -85,33 +74,22 @@ program
|
|
|
85
74
|
.description('Show installation status')
|
|
86
75
|
.action(() => {
|
|
87
76
|
const claudeDir = path.join(os.homedir(), '.claude', 'commands');
|
|
88
|
-
const activeDir = path.join(claudeDir, 'active');
|
|
89
|
-
const experimentalDir = path.join(claudeDir, 'experiments');
|
|
90
77
|
|
|
91
78
|
console.log('📊 Claude Dev Toolkit Status\n');
|
|
92
79
|
|
|
93
80
|
// Check Claude directory
|
|
94
|
-
console.log('📁 Installation
|
|
95
|
-
console.log(`
|
|
96
|
-
console.log(` Active commands: ${fs.existsSync(activeDir) ? '✅' : '❌'} ${activeDir}`);
|
|
97
|
-
console.log(` Experimental: ${fs.existsSync(experimentalDir) ? '✅' : '❌'} ${experimentalDir}\n`);
|
|
81
|
+
console.log('📁 Installation Path:');
|
|
82
|
+
console.log(` Commands directory: ${fs.existsSync(claudeDir) ? '✅' : '❌'} ${claudeDir}\n`);
|
|
98
83
|
|
|
99
84
|
// Count commands
|
|
100
|
-
let
|
|
101
|
-
let expCount = 0;
|
|
102
|
-
|
|
103
|
-
if (fs.existsSync(activeDir)) {
|
|
104
|
-
activeCount = fs.readdirSync(activeDir).filter(f => f.endsWith('.md')).length;
|
|
105
|
-
}
|
|
85
|
+
let totalCount = 0;
|
|
106
86
|
|
|
107
|
-
if (fs.existsSync(
|
|
108
|
-
|
|
87
|
+
if (fs.existsSync(claudeDir)) {
|
|
88
|
+
totalCount = fs.readdirSync(claudeDir).filter(f => f.endsWith('.md')).length;
|
|
109
89
|
}
|
|
110
90
|
|
|
111
91
|
console.log('📦 Command Inventory:');
|
|
112
|
-
console.log(`
|
|
113
|
-
console.log(` Experimental commands: ${expCount}`);
|
|
114
|
-
console.log(` Total commands: ${activeCount + expCount}\n`);
|
|
92
|
+
console.log(` Total commands: ${totalCount}\n`);
|
|
115
93
|
|
|
116
94
|
// Package info
|
|
117
95
|
console.log('📋 Package Information:');
|
|
@@ -119,13 +97,13 @@ program
|
|
|
119
97
|
console.log(` CLI Location: ${process.argv[1]}\n`);
|
|
120
98
|
|
|
121
99
|
// Quick health check
|
|
122
|
-
const isHealthy = fs.existsSync(claudeDir) &&
|
|
100
|
+
const isHealthy = fs.existsSync(claudeDir) && totalCount > 0;
|
|
123
101
|
console.log(`🔍 Overall Status: ${isHealthy ? '✅ Healthy' : '⚠️ Issues detected'}`);
|
|
124
102
|
|
|
125
103
|
if (!isHealthy) {
|
|
126
104
|
console.log('\n💡 Troubleshooting:');
|
|
127
|
-
console.log(' • Try: npm install -g claude-dev-toolkit');
|
|
128
|
-
console.log(' • Or reinstall
|
|
105
|
+
console.log(' • Try: npm install -g @paulduvall/claude-dev-toolkit');
|
|
106
|
+
console.log(' • Or reinstall: claude-commands install --all');
|
|
129
107
|
}
|
|
130
108
|
});
|
|
131
109
|
|
|
@@ -149,4 +127,147 @@ program
|
|
|
149
127
|
config.handleCommand(options);
|
|
150
128
|
});
|
|
151
129
|
|
|
130
|
+
program
|
|
131
|
+
.command('configure')
|
|
132
|
+
.description('Configure Claude Code settings (replaces configure-claude-code.sh)')
|
|
133
|
+
.option('--template <name>', 'Apply named template')
|
|
134
|
+
.option('--interactive', 'Launch interactive configuration wizard')
|
|
135
|
+
.option('--validate', 'Validate current configuration')
|
|
136
|
+
.option('--reset', 'Reset to default configuration')
|
|
137
|
+
.option('--backup', 'Create backup before changes (default: true)')
|
|
138
|
+
.option('--no-backup', 'Skip backup creation')
|
|
139
|
+
.action(async (options) => {
|
|
140
|
+
const ConfigureCommand = require('../lib/configure-command');
|
|
141
|
+
const configureCmd = new ConfigureCommand();
|
|
142
|
+
try {
|
|
143
|
+
const result = await configureCmd.execute(options);
|
|
144
|
+
if (!result.success) {
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error(`Configuration failed: ${error.message}`);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
program
|
|
154
|
+
.command('setup')
|
|
155
|
+
.description('Setup the Claude Dev Toolkit with custom commands and configuration')
|
|
156
|
+
.option('--type <template>', 'Configuration template to apply (basic, comprehensive, security-focused)')
|
|
157
|
+
.option('--commands <set>', 'Command set to install (active, experiments, all, none)')
|
|
158
|
+
.option('--skip-configure', 'Skip configuration step')
|
|
159
|
+
.option('--skip-hooks', 'Skip hooks installation')
|
|
160
|
+
.option('--force', 'Overwrite existing installation')
|
|
161
|
+
.option('--dry-run', 'Preview actions without executing')
|
|
162
|
+
.action(async (options) => {
|
|
163
|
+
const SetupCommand = require('../lib/setup-command');
|
|
164
|
+
const setupCmd = new SetupCommand();
|
|
165
|
+
try {
|
|
166
|
+
const result = await setupCmd.execute(options);
|
|
167
|
+
if (!result.success && !result.dryRun) {
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(`Setup failed: ${error.message}`);
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
program
|
|
177
|
+
.command('verify')
|
|
178
|
+
.description('Verify the Claude Dev Toolkit installation status and health')
|
|
179
|
+
.option('--verbose', 'Show detailed verification information')
|
|
180
|
+
.option('--fix', 'Attempt to fix detected issues automatically')
|
|
181
|
+
.action(async (options) => {
|
|
182
|
+
const VerifyCommand = require('../lib/verify-command');
|
|
183
|
+
const verifyCmd = new VerifyCommand();
|
|
184
|
+
try {
|
|
185
|
+
const result = await verifyCmd.execute(options);
|
|
186
|
+
// Set exit code based on health status
|
|
187
|
+
if (result.overall === 'critical') {
|
|
188
|
+
process.exit(2);
|
|
189
|
+
} else if (result.overall === 'warning') {
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
// Exit 0 for healthy
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(`Verification failed: ${error.message}`);
|
|
195
|
+
process.exit(2);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
program
|
|
200
|
+
.command('backup [name]')
|
|
201
|
+
.description('Create named backup of Claude Code configuration')
|
|
202
|
+
.action(async (name) => {
|
|
203
|
+
const BackupRestoreCommand = require('../lib/backup-restore-command');
|
|
204
|
+
const backupCmd = new BackupRestoreCommand();
|
|
205
|
+
try {
|
|
206
|
+
const result = await backupCmd.backup(name);
|
|
207
|
+
if (!result.success) {
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
console.error(`Backup failed: ${error.message}`);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
program
|
|
217
|
+
.command('restore <name>')
|
|
218
|
+
.description('Restore from a named backup')
|
|
219
|
+
.action(async (name) => {
|
|
220
|
+
const BackupRestoreCommand = require('../lib/backup-restore-command');
|
|
221
|
+
const restoreCmd = new BackupRestoreCommand();
|
|
222
|
+
try {
|
|
223
|
+
const result = await restoreCmd.restore(name);
|
|
224
|
+
if (!result.success) {
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.error(`Restore failed: ${error.message}`);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
program
|
|
234
|
+
.command('update')
|
|
235
|
+
.description('Check for package updates')
|
|
236
|
+
.action(async () => {
|
|
237
|
+
const { version } = require('../package.json');
|
|
238
|
+
console.log('🔍 Checking for updates...\n');
|
|
239
|
+
console.log(`Current version: ${version}`);
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const { execSync } = require('child_process');
|
|
243
|
+
const output = execSync('npm view @paulduvall/claude-dev-toolkit version', {
|
|
244
|
+
encoding: 'utf8',
|
|
245
|
+
stdio: 'pipe'
|
|
246
|
+
}).trim();
|
|
247
|
+
|
|
248
|
+
console.log(`Latest version: ${output}`);
|
|
249
|
+
|
|
250
|
+
if (output !== version) {
|
|
251
|
+
console.log('\n🆕 Update available!');
|
|
252
|
+
console.log('\nTo update, run:');
|
|
253
|
+
console.log(' npm update -g @paulduvall/claude-dev-toolkit');
|
|
254
|
+
|
|
255
|
+
// Check for breaking changes in major version
|
|
256
|
+
const currentMajor = parseInt(version.split('.')[0]);
|
|
257
|
+
const latestMajor = parseInt(output.split('.')[0]);
|
|
258
|
+
|
|
259
|
+
if (latestMajor > currentMajor) {
|
|
260
|
+
console.log('\n⚠️ Major version update - may contain breaking changes');
|
|
261
|
+
console.log(' Review release notes before updating');
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
console.log('\n✅ You are using the latest version');
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('❌ Could not check for updates');
|
|
268
|
+
console.log(' Please check your internet connection');
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
152
273
|
program.parse(process.argv);
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/hooks/lib/file-utils.sh
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup and Restore Commands Implementation - Refactored
|
|
3
|
+
* Orchestrator for backup and restore operations using focused services
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const BackupService = require('./services/backup-service');
|
|
7
|
+
const RestoreService = require('./services/restore-service');
|
|
8
|
+
const BackupListService = require('./services/backup-list-service');
|
|
9
|
+
const BaseCommand = require('./base/base-command');
|
|
10
|
+
const FileSystemUtils = require('./utils/file-system-utils');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
class BackupRestoreCommand extends BaseCommand {
|
|
14
|
+
constructor(config = null) {
|
|
15
|
+
super(config);
|
|
16
|
+
this.backupService = new BackupService(this.config);
|
|
17
|
+
this.restoreService = new RestoreService(this.config);
|
|
18
|
+
this.listService = new BackupListService(this.config);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a backup of the entire .claude directory
|
|
23
|
+
*/
|
|
24
|
+
async backup(name = null) {
|
|
25
|
+
this.logger.step('Creating backup of Claude Code configuration', { backupName: name });
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const result = await this.backupService.create(name);
|
|
29
|
+
|
|
30
|
+
// Try to compress the backup
|
|
31
|
+
const compressed = await this.compressBackup(result.path);
|
|
32
|
+
|
|
33
|
+
this.logger.complete(`Backup '${result.name}' created successfully`, {
|
|
34
|
+
files: result.totalFiles,
|
|
35
|
+
size: FileSystemUtils.formatSize(result.totalSize),
|
|
36
|
+
compressed: !!compressed
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
if (compressed) {
|
|
40
|
+
this.logger.info(`Backup compressed and stored at: ${compressed.path}`, {
|
|
41
|
+
compressionRatio: compressed.size / result.totalSize
|
|
42
|
+
});
|
|
43
|
+
} else {
|
|
44
|
+
this.logger.info(`Backup stored at: ${result.path}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
name: result.name,
|
|
50
|
+
path: compressed ? compressed.path : result.path,
|
|
51
|
+
metadata: result.metadata
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return this.handleError(error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Try to compress a backup using tar
|
|
61
|
+
*/
|
|
62
|
+
async compressBackup(backupPath) {
|
|
63
|
+
try {
|
|
64
|
+
const backupName = require('path').basename(backupPath);
|
|
65
|
+
const tarPath = `${backupPath}.tar.gz`;
|
|
66
|
+
|
|
67
|
+
execSync(`tar -czf "${tarPath}" -C "${this.config.backupsDir}" "${backupName}"`, {
|
|
68
|
+
encoding: 'utf8',
|
|
69
|
+
stdio: 'pipe'
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Remove uncompressed backup
|
|
73
|
+
FileSystemUtils.remove(backupPath);
|
|
74
|
+
|
|
75
|
+
const compressedSize = FileSystemUtils.getStats(tarPath).size;
|
|
76
|
+
return {
|
|
77
|
+
path: tarPath,
|
|
78
|
+
size: compressedSize
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Compression failed, keep uncompressed backup
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Restore from a backup
|
|
88
|
+
*/
|
|
89
|
+
async restore(backupName) {
|
|
90
|
+
this.logger.step(`Restoring from backup: ${backupName}`, { backupName });
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
// Create undo backup first
|
|
94
|
+
this.logger.info('Creating undo backup for safety');
|
|
95
|
+
const undoBackup = await this.backup('undo-before-restore');
|
|
96
|
+
|
|
97
|
+
if (!undoBackup.success) {
|
|
98
|
+
this.logger.warn('Could not create undo backup, continuing anyway', {
|
|
99
|
+
risk: 'restore cannot be undone'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Perform restore using service
|
|
104
|
+
const result = await this.restoreService.restore(backupName);
|
|
105
|
+
|
|
106
|
+
this.logger.complete(`Restore completed successfully`, {
|
|
107
|
+
restoredCount: result.restoredCount,
|
|
108
|
+
hasUndo: undoBackup.success
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (undoBackup.success) {
|
|
112
|
+
this.logger.info(`To undo this restore, run: claude-commands restore ${undoBackup.name}`, {
|
|
113
|
+
undoCommand: `claude-commands restore ${undoBackup.name}`
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
success: true,
|
|
119
|
+
restoredCount: result.restoredCount,
|
|
120
|
+
undoBackup: undoBackup.success ? undoBackup.name : null
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
} catch (error) {
|
|
124
|
+
return this.handleError(error);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* List available backups
|
|
130
|
+
*/
|
|
131
|
+
async listBackups() {
|
|
132
|
+
try {
|
|
133
|
+
return await this.listService.display();
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return this.handleError(error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = BackupRestoreCommand;
|