@paulduvall/claude-dev-toolkit 0.0.1-alpha.2 → 0.0.1-alpha.21
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/LICENSE +21 -0
- package/README.md +88 -37
- package/bin/claude-commands +307 -65
- package/commands/active/xarchitecture.md +393 -0
- package/commands/active/xconfig.md +127 -0
- package/commands/active/xcontinue.md +92 -0
- package/commands/active/xdebug.md +130 -0
- package/commands/active/xdocs.md +178 -0
- package/commands/active/xexplore.md +94 -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/active/xverify.md +80 -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/xdevcontainer.md +238 -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 +903 -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/.smellrc.example.json +19 -0
- package/hooks/README.md +263 -0
- package/hooks/check-commit-signing.py +127 -0
- package/hooks/check-complexity.py +38 -0
- package/hooks/check-security.py +37 -0
- package/hooks/claude-wrapper.sh +29 -0
- package/hooks/config.py +110 -0
- package/hooks/file-logger.sh +100 -0
- package/hooks/lib/argument-parser.sh +427 -0
- package/hooks/lib/config-constants.sh +230 -0
- package/hooks/lib/context-manager.sh +560 -0
- package/hooks/lib/error-handler.sh +423 -0
- package/hooks/lib/execution-engine.sh +444 -0
- package/hooks/lib/execution-results.sh +113 -0
- package/hooks/lib/execution-simulation.sh +114 -0
- package/hooks/lib/field-validators.sh +104 -0
- package/hooks/lib/file-utils.sh +398 -0
- package/hooks/lib/subagent-discovery.sh +468 -0
- package/hooks/lib/subagent-validator.sh +407 -0
- package/hooks/lib/validation-reporter.sh +134 -0
- package/hooks/on-error-debug.sh +226 -0
- package/hooks/pre-commit-quality.sh +204 -0
- package/hooks/pre-commit-test-runner.sh +132 -0
- package/hooks/pre-write-security.sh +115 -0
- package/hooks/prevent-credential-exposure.sh +279 -0
- package/hooks/security_bandit.py +177 -0
- package/hooks/security_checks.py +97 -0
- package/hooks/security_secrets.py +81 -0
- package/hooks/security_trojan.py +61 -0
- package/hooks/settings.example.json +52 -0
- package/hooks/smell_checks.py +238 -0
- package/hooks/smell_javascript.py +231 -0
- package/hooks/smell_python.py +110 -0
- package/hooks/smell_ruff.py +70 -0
- package/hooks/smell_types.py +72 -0
- package/hooks/subagent-trigger-simple.sh +202 -0
- package/hooks/subagent-trigger.sh +253 -0
- package/hooks/suppression.py +82 -0
- package/hooks/tab-color.sh +70 -0
- package/hooks/verify-before-edit.sh +135 -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/hook-installer-core.js +2 -2
- package/lib/installation-instruction-generator.js +213 -495
- package/lib/installer.js +134 -56
- package/lib/oidc-command.js +740 -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 +359 -0
- package/lib/setup-wizard.js +155 -262
- package/lib/uninstall-command.js +100 -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 +7 -3
- package/scripts/postinstall.js +172 -157
- package/subagents/debug-specialist.md +7 -0
- package/templates/README.md +115 -0
- package/templates/basic-settings.json +30 -0
- package/templates/comprehensive-settings.json +57 -0
- package/templates/global-claude.md +344 -0
- package/templates/hybrid-hook-config.yaml +132 -0
- package/templates/security-focused-settings.json +62 -0
- package/templates/subagent-hooks.yaml +188 -0
- package/lib/package-manager-service.js +0 -270
- package/subagents/debug-context.md +0 -197
|
@@ -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;
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Setup Command Implementation
|
|
5
|
+
* Replaces setup.sh functionality with npm package equivalent
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const os = require('os');
|
|
11
|
+
const { execSync } = require('child_process');
|
|
12
|
+
|
|
13
|
+
class SetupCommand {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.homeDir = process.env.TEST_HOME || os.homedir();
|
|
16
|
+
this.claudeDir = path.join(this.homeDir, '.claude');
|
|
17
|
+
this.commandsDir = path.join(this.claudeDir, 'commands');
|
|
18
|
+
this.settingsFile = path.join(this.claudeDir, 'settings.json');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Execute setup with options
|
|
23
|
+
*/
|
|
24
|
+
async execute(options = {}) {
|
|
25
|
+
console.log('🚀 Claude Dev Toolkit Setup\n');
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
type = 'basic',
|
|
29
|
+
commands = 'active',
|
|
30
|
+
skipConfigure = false,
|
|
31
|
+
skipHooks = false,
|
|
32
|
+
force = false,
|
|
33
|
+
dryRun = false
|
|
34
|
+
} = options;
|
|
35
|
+
|
|
36
|
+
if (dryRun) {
|
|
37
|
+
return this.showDryRun(options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// 1. Verify Claude Code availability (optional check)
|
|
42
|
+
this.checkClaudeCode();
|
|
43
|
+
|
|
44
|
+
// 2. Create directory structure
|
|
45
|
+
await this.createDirectoryStructure(force);
|
|
46
|
+
|
|
47
|
+
// 3. Install commands
|
|
48
|
+
if (commands !== 'none') {
|
|
49
|
+
await this.installCommands(commands);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 4. Apply configuration template
|
|
53
|
+
if (!skipConfigure) {
|
|
54
|
+
await this.applyConfigurationTemplate(type);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 5. Install hooks (if requested)
|
|
58
|
+
if (!skipHooks) {
|
|
59
|
+
await this.installHooks();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 6. Verify installation
|
|
63
|
+
await this.verifySetup();
|
|
64
|
+
|
|
65
|
+
console.log('\n✅ Setup completed successfully!');
|
|
66
|
+
console.log('\n💡 Next steps:');
|
|
67
|
+
console.log(' • Run: claude-commands verify');
|
|
68
|
+
console.log(' • Try: /xhelp in Claude Code to see all commands');
|
|
69
|
+
|
|
70
|
+
return { success: true, message: 'Setup completed successfully' };
|
|
71
|
+
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error(`\n❌ Setup failed: ${error.message}`);
|
|
74
|
+
return { success: false, error: error.message };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Show dry run preview
|
|
80
|
+
*/
|
|
81
|
+
showDryRun(options) {
|
|
82
|
+
console.log('🔍 Dry Run - Preview of setup actions:\n');
|
|
83
|
+
|
|
84
|
+
console.log('📁 Directory Structure:');
|
|
85
|
+
console.log(` • Create: ${this.claudeDir}`);
|
|
86
|
+
console.log(` • Create: ${this.commandsDir}`);
|
|
87
|
+
|
|
88
|
+
if (!options.skipConfigure) {
|
|
89
|
+
console.log('\n⚙️ Configuration:');
|
|
90
|
+
console.log(` • Apply template: ${options.type || 'basic'}`);
|
|
91
|
+
console.log(` • Create: ${this.settingsFile}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
console.log('\n📦 Commands Installation:');
|
|
95
|
+
const commandSet = options.commands || 'active';
|
|
96
|
+
console.log(` • Install: ${commandSet} command set`);
|
|
97
|
+
|
|
98
|
+
if (!options.skipHooks) {
|
|
99
|
+
console.log('\n🎣 Hooks:');
|
|
100
|
+
console.log(' • Install security hooks');
|
|
101
|
+
console.log(' • Configure file logging');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log('\n🔍 Verification:');
|
|
105
|
+
console.log(' • Check installation completeness');
|
|
106
|
+
console.log(' • Validate configuration');
|
|
107
|
+
|
|
108
|
+
console.log('\n💡 This was a dry run - no changes were made');
|
|
109
|
+
console.log(' Run without --dry-run to execute setup');
|
|
110
|
+
|
|
111
|
+
return { success: true, dryRun: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check Claude Code availability
|
|
116
|
+
*/
|
|
117
|
+
checkClaudeCode() {
|
|
118
|
+
console.log('🔍 Checking Claude Code availability...');
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
execSync('claude --version', { stdio: 'pipe' });
|
|
122
|
+
console.log(' ✅ Claude Code detected');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.log(' ⚠️ Claude Code not detected (optional)');
|
|
125
|
+
console.log(' 💡 Install with: npm install -g @anthropic-ai/claude-code');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Create directory structure
|
|
131
|
+
*/
|
|
132
|
+
async createDirectoryStructure(force) {
|
|
133
|
+
console.log('📁 Creating directory structure...');
|
|
134
|
+
|
|
135
|
+
// Check if directories already exist
|
|
136
|
+
if (fs.existsSync(this.claudeDir) && !force) {
|
|
137
|
+
console.log(' ✅ ~/.claude directory already exists');
|
|
138
|
+
} else {
|
|
139
|
+
fs.mkdirSync(this.claudeDir, { recursive: true });
|
|
140
|
+
console.log(` ✅ Created: ${this.claudeDir}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!fs.existsSync(this.commandsDir)) {
|
|
144
|
+
fs.mkdirSync(this.commandsDir, { recursive: true });
|
|
145
|
+
console.log(` ✅ Created: ${this.commandsDir}`);
|
|
146
|
+
} else {
|
|
147
|
+
console.log(' ✅ Commands directory already exists');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Install commands
|
|
153
|
+
*/
|
|
154
|
+
async installCommands(commandSet) {
|
|
155
|
+
console.log(`📦 Installing ${commandSet} commands...`);
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
const installer = require('./installer');
|
|
159
|
+
const options = {};
|
|
160
|
+
|
|
161
|
+
switch (commandSet) {
|
|
162
|
+
case 'active':
|
|
163
|
+
options.active = true;
|
|
164
|
+
break;
|
|
165
|
+
case 'experiments':
|
|
166
|
+
options.experiments = true;
|
|
167
|
+
break;
|
|
168
|
+
case 'all':
|
|
169
|
+
options.all = true;
|
|
170
|
+
break;
|
|
171
|
+
default:
|
|
172
|
+
options.active = true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await installer.install(options);
|
|
176
|
+
console.log(' ✅ Commands installed successfully');
|
|
177
|
+
} catch (error) {
|
|
178
|
+
throw new Error(`Command installation failed: ${error.message}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Apply configuration template
|
|
184
|
+
*/
|
|
185
|
+
async applyConfigurationTemplate(templateName) {
|
|
186
|
+
console.log(`⚙️ Applying ${templateName} configuration template...`);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
const config = require('./config');
|
|
190
|
+
await config.applyTemplate(templateName);
|
|
191
|
+
console.log(' ✅ Configuration template applied');
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.log(` ⚠️ Configuration template application failed: ${error.message}`);
|
|
194
|
+
// Don't fail setup for configuration issues
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Install hooks and their lib/ dependencies to ~/.claude/hooks/
|
|
200
|
+
*/
|
|
201
|
+
async installHooks() {
|
|
202
|
+
console.log('🎣 Installing hooks...');
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const hooksInstaller = require('./hook-installer');
|
|
206
|
+
const targetHooksDir = path.join(this.claudeDir, 'hooks');
|
|
207
|
+
const availableHooks = hooksInstaller.getAvailableHooks();
|
|
208
|
+
const hookNames = availableHooks.map(h => h.name);
|
|
209
|
+
|
|
210
|
+
const result = hooksInstaller.installSecurityHooks(
|
|
211
|
+
targetHooksDir,
|
|
212
|
+
hookNames,
|
|
213
|
+
{ force: true, backup: true }
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
if (result.success) {
|
|
217
|
+
console.log(` ✅ ${result.installed.length} hooks installed`);
|
|
218
|
+
} else {
|
|
219
|
+
console.log(` ⚠️ Hook installation had issues: ${result.errors.join(', ')}`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Copy lib/ directory (shared modules required by most hooks)
|
|
223
|
+
this._installHookLibs(targetHooksDir);
|
|
224
|
+
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.log(` ⚠️ Hooks installation skipped: ${error.message}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Copy hooks/lib/ modules to target directory
|
|
232
|
+
*/
|
|
233
|
+
_installHookLibs(targetHooksDir) {
|
|
234
|
+
const sourceLibDir = path.join(__dirname, '..', 'hooks', 'lib');
|
|
235
|
+
const targetLibDir = path.join(targetHooksDir, 'lib');
|
|
236
|
+
|
|
237
|
+
if (!fs.existsSync(sourceLibDir)) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fs.mkdirSync(targetLibDir, { recursive: true });
|
|
242
|
+
|
|
243
|
+
const libFiles = fs.readdirSync(sourceLibDir).filter(f => f.endsWith('.sh'));
|
|
244
|
+
for (const file of libFiles) {
|
|
245
|
+
const content = fs.readFileSync(path.join(sourceLibDir, file), 'utf8');
|
|
246
|
+
fs.writeFileSync(path.join(targetLibDir, file), content, { mode: 0o755 });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log(` ✅ ${libFiles.length} lib modules installed`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Verify setup completion
|
|
254
|
+
*/
|
|
255
|
+
async verifySetup() {
|
|
256
|
+
console.log('🔍 Verifying setup...');
|
|
257
|
+
|
|
258
|
+
const issues = [];
|
|
259
|
+
|
|
260
|
+
// Check directory structure
|
|
261
|
+
if (!fs.existsSync(this.claudeDir)) {
|
|
262
|
+
issues.push('Claude directory not found');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (!fs.existsSync(this.commandsDir)) {
|
|
266
|
+
issues.push('Commands directory not found');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Check command installation
|
|
270
|
+
try {
|
|
271
|
+
const commands = fs.readdirSync(this.commandsDir).filter(f => f.endsWith('.md'));
|
|
272
|
+
if (commands.length === 0) {
|
|
273
|
+
issues.push('No commands installed');
|
|
274
|
+
} else {
|
|
275
|
+
console.log(` ✅ ${commands.length} commands installed`);
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
issues.push('Cannot read commands directory');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Check hooks installation
|
|
282
|
+
const hooksDir = path.join(this.claudeDir, 'hooks');
|
|
283
|
+
if (fs.existsSync(hooksDir)) {
|
|
284
|
+
const hooks = fs.readdirSync(hooksDir).filter(f => f.endsWith('.sh'));
|
|
285
|
+
if (hooks.length > 0) {
|
|
286
|
+
console.log(` ✅ ${hooks.length} hooks installed`);
|
|
287
|
+
} else {
|
|
288
|
+
issues.push('Hooks directory exists but no hooks found');
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
issues.push('Hooks directory not found');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Check configuration
|
|
295
|
+
if (fs.existsSync(this.settingsFile)) {
|
|
296
|
+
console.log(' ✅ Configuration file present');
|
|
297
|
+
} else {
|
|
298
|
+
console.log(' ⚠️ No configuration file (will use defaults)');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (issues.length > 0) {
|
|
302
|
+
console.log(' ⚠️ Issues detected:');
|
|
303
|
+
issues.forEach(issue => console.log(` • ${issue}`));
|
|
304
|
+
throw new Error(`Setup verification failed: ${issues.join(', ')}`);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
console.log(' ✅ Setup verification passed');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get available templates
|
|
312
|
+
*/
|
|
313
|
+
getAvailableTemplates() {
|
|
314
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
315
|
+
try {
|
|
316
|
+
return fs.readdirSync(templatesDir)
|
|
317
|
+
.filter(f => f.endsWith('.json'))
|
|
318
|
+
.map(f => f.replace('.json', ''));
|
|
319
|
+
} catch (error) {
|
|
320
|
+
return ['basic', 'comprehensive', 'security-focused'];
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get help text for setup command
|
|
326
|
+
*/
|
|
327
|
+
getHelpText() {
|
|
328
|
+
return `
|
|
329
|
+
Setup the Claude Dev Toolkit with custom commands and configuration.
|
|
330
|
+
|
|
331
|
+
This command replaces the functionality of setup.sh script, providing
|
|
332
|
+
a complete installation and configuration of the Claude Code toolkit.
|
|
333
|
+
|
|
334
|
+
Usage:
|
|
335
|
+
claude-commands setup [options]
|
|
336
|
+
|
|
337
|
+
Options:
|
|
338
|
+
--type <template> Configuration template to apply
|
|
339
|
+
(basic, comprehensive, security-focused)
|
|
340
|
+
--commands <set> Command set to install
|
|
341
|
+
(active, experiments, all, none)
|
|
342
|
+
--skip-configure Skip configuration step
|
|
343
|
+
--skip-hooks Skip hooks installation
|
|
344
|
+
--force Overwrite existing installation
|
|
345
|
+
--dry-run Preview actions without executing
|
|
346
|
+
|
|
347
|
+
Examples:
|
|
348
|
+
claude-commands setup
|
|
349
|
+
claude-commands setup --type comprehensive --commands all
|
|
350
|
+
claude-commands setup --dry-run
|
|
351
|
+
claude-commands setup --type security-focused --skip-hooks
|
|
352
|
+
|
|
353
|
+
This command performs the equivalent of running setup.sh with intelligent
|
|
354
|
+
defaults and enhanced error handling.
|
|
355
|
+
`.trim();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
module.exports = SetupCommand;
|