@paulduvall/claude-dev-toolkit 0.0.1-alpha.1
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 +254 -0
- package/bin/claude-commands +132 -0
- package/lib/claude-code-compatibility.js +545 -0
- package/lib/command-selector.js +245 -0
- package/lib/config.js +182 -0
- package/lib/context-utils.js +80 -0
- package/lib/dependency-validator.js +354 -0
- package/lib/error-factory.js +394 -0
- package/lib/error-handler-utils.js +432 -0
- package/lib/error-recovery-system.js +563 -0
- package/lib/failure-recovery-installer.js +370 -0
- package/lib/hook-installer-core.js +330 -0
- package/lib/hook-installer.js +187 -0
- package/lib/hook-metadata-service.js +352 -0
- package/lib/hook-validator.js +358 -0
- package/lib/installation-configuration.js +380 -0
- package/lib/installation-instruction-generator.js +564 -0
- package/lib/installer.js +68 -0
- package/lib/package-manager-service.js +270 -0
- package/lib/permission-error-handler.js +543 -0
- package/lib/platform-utils.js +491 -0
- package/lib/setup-wizard-ui.js +245 -0
- package/lib/setup-wizard.js +355 -0
- package/lib/system-requirements-checker.js +558 -0
- package/lib/utils.js +15 -0
- package/lib/validation-utils.js +320 -0
- package/lib/version-validator-service.js +326 -0
- package/package.json +73 -0
- package/scripts/postinstall.js +182 -0
- package/scripts/validate.js +94 -0
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@paulduvall/claude-dev-toolkit",
|
|
3
|
+
"version": "0.0.1-alpha.1",
|
|
4
|
+
"description": "Custom commands toolkit for Claude Code - streamline your development workflow",
|
|
5
|
+
"author": "Paul Duvall",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"claude-code",
|
|
9
|
+
"claude",
|
|
10
|
+
"ai",
|
|
11
|
+
"development",
|
|
12
|
+
"automation",
|
|
13
|
+
"commands"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/PaulDuvall/claude-code.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/PaulDuvall/claude-code/issues"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/PaulDuvall/claude-code#readme",
|
|
23
|
+
"bin": {
|
|
24
|
+
"claude-commands": "bin/claude-commands"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"postinstall": "node scripts/postinstall.js",
|
|
28
|
+
"test": "node tests/test_all_suites.js",
|
|
29
|
+
"test:req007": "node tests/test_req_007_interactive_setup_wizard.js",
|
|
30
|
+
"test:req009": "node tests/test_req_009_configuration_template_application.js",
|
|
31
|
+
"test:req018": "node tests/test_req_018_security_hook_installation.js",
|
|
32
|
+
"test:req020": "node tests/test_req_020_installation_failure_recovery.js",
|
|
33
|
+
"test:commands": "node tests/test_command_validation.js",
|
|
34
|
+
"test:workflow": "node tests/test_core_workflow_commands.js",
|
|
35
|
+
"test:security": "node tests/test_security_commands.js",
|
|
36
|
+
"test:quality": "node tests/test_quality_commands.js",
|
|
37
|
+
"test:git": "node tests/test_git_commands.js",
|
|
38
|
+
"test:ux": "node tests/test_user_experience.js",
|
|
39
|
+
"test:validation": "node tests/test_validation_system.js",
|
|
40
|
+
"test:install": "scripts/publishing/test-package-install.sh",
|
|
41
|
+
"test:manual": "scripts/publishing/manual-test-suite.sh",
|
|
42
|
+
"publish:local": "scripts/publishing/setup-local-registry.sh",
|
|
43
|
+
"publish:private": "scripts/publishing/publish-private.sh",
|
|
44
|
+
"lint": "eslint lib/**/*.js bin/**/*.js",
|
|
45
|
+
"validate": "node scripts/validate.js"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"commander": "^9.0.0",
|
|
49
|
+
"inquirer": "^8.2.7",
|
|
50
|
+
"js-yaml": "^4.1.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"eslint": "^8.0.0",
|
|
54
|
+
"jest": "^29.0.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=16.0.0"
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"bin/",
|
|
61
|
+
"lib/",
|
|
62
|
+
"commands/",
|
|
63
|
+
"templates/",
|
|
64
|
+
"hooks/",
|
|
65
|
+
"scripts/postinstall.js",
|
|
66
|
+
"scripts/validate.js",
|
|
67
|
+
"README.md",
|
|
68
|
+
"LICENSE"
|
|
69
|
+
],
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const { execSync } = require('child_process');
|
|
7
|
+
|
|
8
|
+
// Check for skip flag
|
|
9
|
+
const skipSetup = process.env.CLAUDE_SKIP_SETUP === 'true' ||
|
|
10
|
+
process.argv.includes('--skip-setup');
|
|
11
|
+
|
|
12
|
+
console.log('🚀 Setting up Claude Custom Commands...');
|
|
13
|
+
|
|
14
|
+
async function runSetup() {
|
|
15
|
+
try {
|
|
16
|
+
// Get Claude Code directory
|
|
17
|
+
const homeDir = os.homedir();
|
|
18
|
+
const claudeDir = path.join(homeDir, '.claude');
|
|
19
|
+
const commandsDir = path.join(claudeDir, 'commands');
|
|
20
|
+
const hooksDir = path.join(claudeDir, 'hooks');
|
|
21
|
+
|
|
22
|
+
// Ensure Claude directories exist
|
|
23
|
+
if (!fs.existsSync(claudeDir)) {
|
|
24
|
+
fs.mkdirSync(claudeDir, { recursive: true });
|
|
25
|
+
console.log('✅ Created .claude directory');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!fs.existsSync(commandsDir)) {
|
|
29
|
+
fs.mkdirSync(commandsDir, { recursive: true });
|
|
30
|
+
console.log('✅ Created .claude/commands directory');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(hooksDir)) {
|
|
34
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
35
|
+
console.log('✅ Created .claude/hooks directory');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get package installation directory
|
|
39
|
+
const packageDir = __dirname.replace('/scripts', '');
|
|
40
|
+
|
|
41
|
+
// Check if we should run interactive setup
|
|
42
|
+
if (!skipSetup && process.stdin.isTTY) {
|
|
43
|
+
console.log('\n📋 Starting Interactive Setup Wizard...');
|
|
44
|
+
console.log('(Use --skip-setup or set CLAUDE_SKIP_SETUP=true to skip)\n');
|
|
45
|
+
|
|
46
|
+
const InteractiveSetupWizard = require('../lib/setup-wizard');
|
|
47
|
+
const wizard = new InteractiveSetupWizard(claudeDir);
|
|
48
|
+
|
|
49
|
+
// Validate environment first (REQ-006)
|
|
50
|
+
const envCheck = wizard.validateEnvironment();
|
|
51
|
+
if (!envCheck.valid) {
|
|
52
|
+
console.error('❌ Environment validation failed:', envCheck.message);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Run interactive setup (REQ-007)
|
|
57
|
+
const setupResult = await wizard.runInteractiveSetup();
|
|
58
|
+
|
|
59
|
+
if (setupResult.completed) {
|
|
60
|
+
const config = setupResult.configuration;
|
|
61
|
+
|
|
62
|
+
// Install commands based on selection
|
|
63
|
+
const sourceCommandsDir = path.join(packageDir, 'commands');
|
|
64
|
+
if (fs.existsSync(sourceCommandsDir)) {
|
|
65
|
+
copySelectedCommands(sourceCommandsDir, commandsDir, config);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Install security hooks if selected
|
|
69
|
+
if (config.securityHooks) {
|
|
70
|
+
const sourceHooksDir = path.join(packageDir, 'hooks');
|
|
71
|
+
if (fs.existsSync(sourceHooksDir)) {
|
|
72
|
+
copySelectedHooks(sourceHooksDir, hooksDir, config.selectedHooks || []);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Apply configuration template
|
|
77
|
+
if (config.template) {
|
|
78
|
+
const templateFile = path.join(packageDir, 'templates', `${config.template}-settings.json`);
|
|
79
|
+
const targetFile = path.join(claudeDir, 'settings.json');
|
|
80
|
+
if (fs.existsSync(templateFile)) {
|
|
81
|
+
fs.copyFileSync(templateFile, targetFile);
|
|
82
|
+
console.log(`✅ Applied ${config.template} configuration template`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// Non-interactive installation - install all commands by default
|
|
88
|
+
console.log('Running non-interactive installation...');
|
|
89
|
+
|
|
90
|
+
const sourceCommandsDir = path.join(packageDir, 'commands');
|
|
91
|
+
if (fs.existsSync(sourceCommandsDir)) {
|
|
92
|
+
copyAllCommands(sourceCommandsDir, commandsDir);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Count installed commands (now in flat structure)
|
|
97
|
+
if (fs.existsSync(commandsDir)) {
|
|
98
|
+
const installedCommands = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md')).length;
|
|
99
|
+
console.log(`\n📦 Installed ${installedCommands} commands`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('\n🎉 Installation complete!');
|
|
103
|
+
console.log('\nNext steps:');
|
|
104
|
+
console.log('1. Run: claude-commands list');
|
|
105
|
+
console.log('2. Try: claude-commands --help');
|
|
106
|
+
console.log('3. Configure: claude-commands config');
|
|
107
|
+
console.log('4. Explore commands in Claude Code using /xhelp\n');
|
|
108
|
+
|
|
109
|
+
} catch (error) {
|
|
110
|
+
console.error('❌ Installation failed:', error.message);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function copyAllCommands(sourceDir, targetDir) {
|
|
116
|
+
const items = fs.readdirSync(sourceDir);
|
|
117
|
+
for (const item of items) {
|
|
118
|
+
const sourcePath = path.join(sourceDir, item);
|
|
119
|
+
const targetPath = path.join(targetDir, item);
|
|
120
|
+
|
|
121
|
+
if (fs.statSync(sourcePath).isDirectory()) {
|
|
122
|
+
if (!fs.existsSync(targetPath)) {
|
|
123
|
+
fs.mkdirSync(targetPath, { recursive: true });
|
|
124
|
+
}
|
|
125
|
+
copyAllCommands(sourcePath, targetPath);
|
|
126
|
+
} else if (item.endsWith('.md')) {
|
|
127
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function copySelectedCommands(sourceDir, targetDir, config) {
|
|
133
|
+
// Based on installation type, copy appropriate commands
|
|
134
|
+
const installationType = config.installationType || 'standard';
|
|
135
|
+
|
|
136
|
+
if (installationType === 'full' || !config.commandSets) {
|
|
137
|
+
// Copy all commands
|
|
138
|
+
copyAllCommands(sourceDir, targetDir);
|
|
139
|
+
} else {
|
|
140
|
+
// Copy selected command sets
|
|
141
|
+
const commandSets = config.commandSets || [];
|
|
142
|
+
|
|
143
|
+
// Always copy active commands for standard installation (flat structure)
|
|
144
|
+
if (installationType === 'standard' || commandSets.includes('development')) {
|
|
145
|
+
const activeSource = path.join(sourceDir, 'active');
|
|
146
|
+
if (fs.existsSync(activeSource)) {
|
|
147
|
+
copyAllCommands(activeSource, targetDir); // Copy directly to targetDir, no subdirectory
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Copy experimental if selected (flat structure to avoid namespace)
|
|
152
|
+
if (commandSets.includes('experimental') || installationType === 'full') {
|
|
153
|
+
const expSource = path.join(sourceDir, 'experiments');
|
|
154
|
+
if (fs.existsSync(expSource)) {
|
|
155
|
+
copyAllCommands(expSource, targetDir); // Copy directly to targetDir, no subdirectory
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function copySelectedHooks(sourceDir, targetDir, selectedHooks) {
|
|
162
|
+
const items = fs.readdirSync(sourceDir);
|
|
163
|
+
for (const item of items) {
|
|
164
|
+
const sourcePath = path.join(sourceDir, item);
|
|
165
|
+
|
|
166
|
+
// Copy hook if it's selected or if no specific selection (copy all)
|
|
167
|
+
if (selectedHooks.length === 0 || selectedHooks.some(h => item.includes(h))) {
|
|
168
|
+
const targetPath = path.join(targetDir, item);
|
|
169
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
170
|
+
|
|
171
|
+
// Make shell scripts executable
|
|
172
|
+
if (item.endsWith('.sh')) {
|
|
173
|
+
fs.chmodSync(targetPath, '755');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log(`✅ Installed hook: ${item}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Run the setup
|
|
182
|
+
runSetup();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
console.log('🔍 Validating Claude Custom Commands package...');
|
|
7
|
+
|
|
8
|
+
let errors = 0;
|
|
9
|
+
let warnings = 0;
|
|
10
|
+
|
|
11
|
+
const log = (level, message) => {
|
|
12
|
+
const prefix = level === 'error' ? '❌' : level === 'warn' ? '⚠️' : '✅';
|
|
13
|
+
console.log(`${prefix} ${message}`);
|
|
14
|
+
if (level === 'error') errors++;
|
|
15
|
+
if (level === 'warn') warnings++;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const packageDir = __dirname.replace('/scripts', '');
|
|
20
|
+
|
|
21
|
+
// Check required directories
|
|
22
|
+
const requiredDirs = ['bin', 'lib', 'commands', 'commands/active', 'commands/experiments'];
|
|
23
|
+
requiredDirs.forEach(dir => {
|
|
24
|
+
const dirPath = path.join(packageDir, dir);
|
|
25
|
+
if (fs.existsSync(dirPath)) {
|
|
26
|
+
log('info', `Directory exists: ${dir}/`);
|
|
27
|
+
} else {
|
|
28
|
+
log('error', `Missing required directory: ${dir}/`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Check required files
|
|
33
|
+
const requiredFiles = [
|
|
34
|
+
'package.json',
|
|
35
|
+
'README.md',
|
|
36
|
+
'bin/claude-commands',
|
|
37
|
+
'lib/config.js',
|
|
38
|
+
'lib/installer.js',
|
|
39
|
+
'lib/utils.js'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
requiredFiles.forEach(file => {
|
|
43
|
+
const filePath = path.join(packageDir, file);
|
|
44
|
+
if (fs.existsSync(filePath)) {
|
|
45
|
+
log('info', `File exists: ${file}`);
|
|
46
|
+
} else {
|
|
47
|
+
log('error', `Missing required file: ${file}`);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Check bin file permissions
|
|
52
|
+
const binFile = path.join(packageDir, 'bin/claude-commands');
|
|
53
|
+
if (fs.existsSync(binFile)) {
|
|
54
|
+
const stats = fs.statSync(binFile);
|
|
55
|
+
const mode = (stats.mode & parseInt('777', 8)).toString(8);
|
|
56
|
+
if (mode.includes('7') || mode.includes('5')) {
|
|
57
|
+
log('info', `Binary is executable (${mode})`);
|
|
58
|
+
} else {
|
|
59
|
+
log('warn', `Binary may not be executable (${mode})`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Count commands
|
|
64
|
+
const activeDir = path.join(packageDir, 'commands/active');
|
|
65
|
+
const experimentalDir = path.join(packageDir, 'commands/experiments');
|
|
66
|
+
|
|
67
|
+
if (fs.existsSync(activeDir)) {
|
|
68
|
+
const activeCount = fs.readdirSync(activeDir).filter(f => f.endsWith('.md')).length;
|
|
69
|
+
log('info', `Found ${activeCount} active commands`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(experimentalDir)) {
|
|
73
|
+
const expCount = fs.readdirSync(experimentalDir).filter(f => f.endsWith('.md')).length;
|
|
74
|
+
log('info', `Found ${expCount} experimental commands`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log('');
|
|
78
|
+
console.log(`📊 Validation Summary:`);
|
|
79
|
+
console.log(` Errors: ${errors}`);
|
|
80
|
+
console.log(` Warnings: ${warnings}`);
|
|
81
|
+
|
|
82
|
+
if (errors > 0) {
|
|
83
|
+
console.log('');
|
|
84
|
+
console.log('❌ Package validation failed');
|
|
85
|
+
process.exit(1);
|
|
86
|
+
} else {
|
|
87
|
+
console.log('');
|
|
88
|
+
console.log('✅ Package validation passed');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error('❌ Validation failed:', error.message);
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|