claude-code-templates 1.1.1 → 1.1.3
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/bin/create-claude-config.js +3 -4
- package/package.json +3 -3
- package/scripts/sync-templates.js +28 -28
- package/src/command-scanner.js +103 -3
- package/src/file-operations.js +56 -13
- package/src/index.js +3 -6
- package/src/prompts.js +164 -30
- package/src/templates.js +12 -20
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/components.md +63 -0
- package/templates/javascript-typescript/examples/angular-app/.claude/commands/services.md +62 -0
- package/templates/javascript-typescript/examples/node-api/.claude/commands/api-endpoint.md +46 -0
- package/templates/javascript-typescript/examples/node-api/.claude/commands/database.md +56 -0
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +61 -0
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +57 -0
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +102 -0
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +29 -0
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +44 -0
- package/templates/javascript-typescript/examples/react-app/.claude/commands/state-management.md +45 -0
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +81 -0
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/components.md +46 -0
- package/templates/javascript-typescript/examples/vue-app/.claude/commands/composables.md +51 -0
- package/templates/javascript-typescript/.claude/commands/react-component.md +0 -54
- package/templates/javascript-typescript/.claude/commands/route.md +0 -193
|
@@ -6,7 +6,7 @@ const boxen = require('boxen');
|
|
|
6
6
|
const createClaudeConfig = require('../src/index');
|
|
7
7
|
|
|
8
8
|
// ASCII Art for Claude Code Templates
|
|
9
|
-
const banner = chalk.
|
|
9
|
+
const banner = chalk.hex('#D97706')(`
|
|
10
10
|
██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗
|
|
11
11
|
██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝
|
|
12
12
|
██║ ██║ ███████║██║ ██║██║ ██║█████╗
|
|
@@ -27,9 +27,8 @@ const banner = chalk.cyan(`
|
|
|
27
27
|
██║ ██╔══╝ ██║╚██╔╝██║██╔═══╝ ██║ ██╔══██║ ██║ ██╔══╝ ╚════██║
|
|
28
28
|
██║ ███████╗██║ ╚═╝ ██║██║ ███████╗██║ ██║ ██║ ███████╗███████║
|
|
29
29
|
╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚══════╝
|
|
30
|
-
`) +
|
|
31
|
-
|
|
32
|
-
`);
|
|
30
|
+
`) +
|
|
31
|
+
chalk.yellow('\n 🚀 Setup Claude Code for any project language 🚀');
|
|
33
32
|
|
|
34
33
|
console.log(banner);
|
|
35
34
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "CLI tool to setup Claude Code configurations for different programming languages",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"start": "node bin/create-claude-config.js",
|
|
14
14
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
15
|
"sync": "node scripts/sync-templates.js",
|
|
16
|
-
"presync": "echo \"🔄
|
|
17
|
-
"postsync": "echo \"✅
|
|
16
|
+
"presync": "echo \"🔄 Starting template synchronization...\"",
|
|
17
|
+
"postsync": "echo \"✅ Synchronization completed. Ready to publish!\"",
|
|
18
18
|
"prepublishOnly": "npm run sync"
|
|
19
19
|
},
|
|
20
20
|
"keywords": [
|
|
@@ -5,17 +5,17 @@ const path = require('path');
|
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Script
|
|
9
|
-
*
|
|
8
|
+
* Script to synchronize templates from root folders
|
|
9
|
+
* to cli-tool/templates/
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
async function syncTemplates() {
|
|
13
|
-
console.log(chalk.blue('🔄
|
|
13
|
+
console.log(chalk.blue('🔄 Synchronizing templates...'));
|
|
14
14
|
|
|
15
15
|
const rootDir = path.join(__dirname, '..', '..');
|
|
16
16
|
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// Languages to synchronize
|
|
19
19
|
const languages = ['common', 'javascript-typescript', 'python', 'rust', 'go'];
|
|
20
20
|
|
|
21
21
|
let totalCopied = 0;
|
|
@@ -26,26 +26,26 @@ async function syncTemplates() {
|
|
|
26
26
|
const targetDir = path.join(templatesDir, language);
|
|
27
27
|
|
|
28
28
|
if (!await fs.pathExists(sourceDir)) {
|
|
29
|
-
console.log(chalk.yellow(`⚠️
|
|
29
|
+
console.log(chalk.yellow(`⚠️ Source folder does not exist: ${language}`));
|
|
30
30
|
continue;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
console.log(chalk.cyan(`\n📂
|
|
33
|
+
console.log(chalk.cyan(`\n📂 Synchronizing ${language}...`));
|
|
34
34
|
|
|
35
|
-
//
|
|
35
|
+
// Clean destination directory
|
|
36
36
|
if (await fs.pathExists(targetDir)) {
|
|
37
37
|
await fs.remove(targetDir);
|
|
38
|
-
console.log(chalk.gray(` 🗑️
|
|
38
|
+
console.log(chalk.gray(` 🗑️ Previous directory removed`));
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
//
|
|
41
|
+
// Copy everything from source
|
|
42
42
|
try {
|
|
43
43
|
await fs.copy(sourceDir, targetDir, {
|
|
44
44
|
filter: (src, dest) => {
|
|
45
|
-
//
|
|
45
|
+
// Filter files we don't want to copy
|
|
46
46
|
const relativePath = path.relative(sourceDir, src);
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// Exclude specific directories and files
|
|
49
49
|
if (relativePath.includes('node_modules')) return false;
|
|
50
50
|
if (relativePath.includes('.git')) return false;
|
|
51
51
|
if (relativePath.includes('package-lock.json')) return false;
|
|
@@ -55,26 +55,26 @@ async function syncTemplates() {
|
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
//
|
|
58
|
+
// Count copied files
|
|
59
59
|
const stats = await getDirectoryStats(targetDir);
|
|
60
60
|
totalCopied += stats.files;
|
|
61
61
|
|
|
62
|
-
console.log(chalk.green(` ✅ ${stats.files}
|
|
62
|
+
console.log(chalk.green(` ✅ ${stats.files} files copied`));
|
|
63
63
|
|
|
64
|
-
//
|
|
64
|
+
// Show copied structure
|
|
65
65
|
if (stats.files > 0) {
|
|
66
66
|
await showDirectoryStructure(targetDir, ' ');
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
} catch (error) {
|
|
70
|
-
console.error(chalk.red(` ❌ Error
|
|
70
|
+
console.error(chalk.red(` ❌ Error copying ${language}:`), error.message);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
console.log(chalk.green(`\n🎉
|
|
75
|
-
console.log(chalk.white(`📊 Total
|
|
74
|
+
console.log(chalk.green(`\n🎉 Synchronization completed!`));
|
|
75
|
+
console.log(chalk.white(`📊 Total synchronized files: ${totalCopied}`));
|
|
76
76
|
|
|
77
|
-
//
|
|
77
|
+
// Verify that no hook files exist
|
|
78
78
|
await cleanupOldReferences();
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -118,7 +118,7 @@ async function showDirectoryStructure(dir, prefix = '') {
|
|
|
118
118
|
if (stat.isDirectory()) {
|
|
119
119
|
console.log(chalk.blue(`${prefix}${connector}${item}/`));
|
|
120
120
|
if (item === '.claude' || item === 'commands') {
|
|
121
|
-
//
|
|
121
|
+
// Show only one more level for .claude and commands
|
|
122
122
|
const subItems = await fs.readdir(itemPath);
|
|
123
123
|
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
124
124
|
for (let j = 0; j < Math.min(subItems.length, 3); j++) {
|
|
@@ -127,7 +127,7 @@ async function showDirectoryStructure(dir, prefix = '') {
|
|
|
127
127
|
console.log(chalk.gray(`${newPrefix}${subConnector}${subItem}`));
|
|
128
128
|
}
|
|
129
129
|
if (subItems.length > 3) {
|
|
130
|
-
console.log(chalk.gray(`${newPrefix}└── ...
|
|
130
|
+
console.log(chalk.gray(`${newPrefix}└── ... and ${subItems.length - 3} more`));
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
} else {
|
|
@@ -137,22 +137,22 @@ async function showDirectoryStructure(dir, prefix = '') {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
async function cleanupOldReferences() {
|
|
140
|
-
console.log(chalk.yellow('\n🧹
|
|
140
|
+
console.log(chalk.yellow('\n🧹 Cleaning up obsolete references...'));
|
|
141
141
|
|
|
142
142
|
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// Search and remove hooks directories
|
|
145
145
|
const languages = ['javascript-typescript', 'python', 'common'];
|
|
146
146
|
|
|
147
147
|
for (const language of languages) {
|
|
148
148
|
const hooksDir = path.join(templatesDir, language, '.claude', 'hooks');
|
|
149
149
|
if (await fs.pathExists(hooksDir)) {
|
|
150
150
|
await fs.remove(hooksDir);
|
|
151
|
-
console.log(chalk.yellow(` 🗑️
|
|
151
|
+
console.log(chalk.yellow(` 🗑️ Removed: ${language}/.claude/hooks/`));
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
//
|
|
155
|
+
// Check for empty files in commands
|
|
156
156
|
for (const language of languages) {
|
|
157
157
|
const commandsDir = path.join(templatesDir, language, '.claude', 'commands');
|
|
158
158
|
if (await fs.pathExists(commandsDir)) {
|
|
@@ -160,10 +160,10 @@ async function cleanupOldReferences() {
|
|
|
160
160
|
for (const file of files) {
|
|
161
161
|
const filePath = path.join(commandsDir, file);
|
|
162
162
|
const stat = await fs.stat(filePath);
|
|
163
|
-
if (stat.size < 50) { //
|
|
163
|
+
if (stat.size < 50) { // Very small files are probably empty
|
|
164
164
|
const content = await fs.readFile(filePath, 'utf8');
|
|
165
165
|
if (content.trim().length < 10) {
|
|
166
|
-
console.log(chalk.yellow(` ⚠️
|
|
166
|
+
console.log(chalk.yellow(` ⚠️ Possibly empty file: ${language}/.claude/commands/${file} (${stat.size} bytes)`));
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -171,10 +171,10 @@ async function cleanupOldReferences() {
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
//
|
|
174
|
+
// Function to execute synchronization
|
|
175
175
|
if (require.main === module) {
|
|
176
176
|
syncTemplates().catch(error => {
|
|
177
|
-
console.error(chalk.red('❌ Error
|
|
177
|
+
console.error(chalk.red('❌ Error during synchronization:'), error);
|
|
178
178
|
process.exit(1);
|
|
179
179
|
});
|
|
180
180
|
}
|
package/src/command-scanner.js
CHANGED
|
@@ -87,8 +87,8 @@ function scanCommandsInDirectory(commandsDir, category) {
|
|
|
87
87
|
|
|
88
88
|
commands.push({
|
|
89
89
|
name: commandName,
|
|
90
|
-
displayName: metadata.title
|
|
91
|
-
description: metadata.description
|
|
90
|
+
displayName: createShortDisplayName(commandName, metadata.title),
|
|
91
|
+
description: createShortDescription(metadata.description, commandName),
|
|
92
92
|
category: category,
|
|
93
93
|
filePath: filePath,
|
|
94
94
|
checked: metadata.defaultChecked || false
|
|
@@ -145,6 +145,104 @@ function parseCommandMetadata(content, commandName) {
|
|
|
145
145
|
return metadata;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Creates a short display name from command name for better console display
|
|
150
|
+
* @param {string} commandName - The command name
|
|
151
|
+
* @param {string} title - The full title from markdown
|
|
152
|
+
* @returns {string} Short display name
|
|
153
|
+
*/
|
|
154
|
+
function createShortDisplayName(commandName, title) {
|
|
155
|
+
// Define mapping for common command names to short display names
|
|
156
|
+
const shortNames = {
|
|
157
|
+
'api-endpoint': 'API Endpoint',
|
|
158
|
+
'debug': 'Debug',
|
|
159
|
+
'lint': 'Lint',
|
|
160
|
+
'test': 'Test',
|
|
161
|
+
'refactor': 'Refactor',
|
|
162
|
+
'typescript-migrate': 'TS Migration',
|
|
163
|
+
'npm-scripts': 'NPM Scripts',
|
|
164
|
+
'component': 'Component',
|
|
165
|
+
'hooks': 'Hooks',
|
|
166
|
+
'state-management': 'State Mgmt',
|
|
167
|
+
'middleware': 'Middleware',
|
|
168
|
+
'route': 'Route',
|
|
169
|
+
'database': 'Database',
|
|
170
|
+
'components': 'Components',
|
|
171
|
+
'services': 'Services',
|
|
172
|
+
'composables': 'Composables',
|
|
173
|
+
'django-model': 'Django Model',
|
|
174
|
+
'flask-route': 'Flask Route',
|
|
175
|
+
'git-workflow': 'Git Workflow',
|
|
176
|
+
'project-setup': 'Project Setup'
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Return predefined short name if available
|
|
180
|
+
if (shortNames[commandName]) {
|
|
181
|
+
return shortNames[commandName];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// If title is short enough, use it
|
|
185
|
+
if (title && title.length <= 15) {
|
|
186
|
+
return title;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Create short name from command name
|
|
190
|
+
return commandName
|
|
191
|
+
.split('-')
|
|
192
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
193
|
+
.join(' ');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Creates a short description for better console display
|
|
198
|
+
* @param {string} description - The full description
|
|
199
|
+
* @param {string} commandName - The command name
|
|
200
|
+
* @returns {string} Short description
|
|
201
|
+
*/
|
|
202
|
+
function createShortDescription(description, commandName) {
|
|
203
|
+
// Define short descriptions for common commands
|
|
204
|
+
const shortDescriptions = {
|
|
205
|
+
'api-endpoint': 'Generate API endpoint',
|
|
206
|
+
'debug': 'Debug issues',
|
|
207
|
+
'lint': 'Fix linting issues',
|
|
208
|
+
'test': 'Run tests',
|
|
209
|
+
'refactor': 'Refactor code',
|
|
210
|
+
'typescript-migrate': 'Migrate to TypeScript',
|
|
211
|
+
'npm-scripts': 'Manage NPM scripts',
|
|
212
|
+
'component': 'Create component',
|
|
213
|
+
'hooks': 'React hooks helper',
|
|
214
|
+
'state-management': 'Manage state',
|
|
215
|
+
'middleware': 'Create middleware',
|
|
216
|
+
'route': 'Create route',
|
|
217
|
+
'database': 'Database operations',
|
|
218
|
+
'components': 'Create components',
|
|
219
|
+
'services': 'Create services',
|
|
220
|
+
'composables': 'Create composables',
|
|
221
|
+
'django-model': 'Create Django model',
|
|
222
|
+
'flask-route': 'Create Flask route',
|
|
223
|
+
'git-workflow': 'Git workflow helper',
|
|
224
|
+
'project-setup': 'Setup project'
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Return predefined short description if available
|
|
228
|
+
if (shortDescriptions[commandName]) {
|
|
229
|
+
return shortDescriptions[commandName];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// If description exists and is short enough, use it
|
|
233
|
+
if (description && description.length <= 40) {
|
|
234
|
+
return description;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Truncate long descriptions
|
|
238
|
+
if (description && description.length > 40) {
|
|
239
|
+
return description.substring(0, 37) + '...';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Fallback to command name
|
|
243
|
+
return `${commandName.replace('-', ' ')} command`;
|
|
244
|
+
}
|
|
245
|
+
|
|
148
246
|
/**
|
|
149
247
|
* Get commands available for a specific language and framework combination
|
|
150
248
|
* @param {string} language - The language template
|
|
@@ -171,5 +269,7 @@ module.exports = {
|
|
|
171
269
|
getAvailableCommands,
|
|
172
270
|
getCommandsForLanguageAndFramework,
|
|
173
271
|
scanCommandsInDirectory,
|
|
174
|
-
parseCommandMetadata
|
|
272
|
+
parseCommandMetadata,
|
|
273
|
+
createShortDisplayName,
|
|
274
|
+
createShortDescription
|
|
175
275
|
};
|
package/src/file-operations.js
CHANGED
|
@@ -23,25 +23,68 @@ async function copyTemplateFiles(templateConfig, targetDir) {
|
|
|
23
23
|
console.log(chalk.yellow(`📁 Existing .claude directory backed up to .claude.backup`));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
// Copy base files
|
|
26
|
+
// Copy base files and framework-specific files
|
|
27
27
|
for (const file of templateConfig.files) {
|
|
28
28
|
const sourcePath = path.join(templateDir, file.source);
|
|
29
29
|
const destPath = path.join(targetDir, file.destination);
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
|
-
//
|
|
33
|
-
if (file.source.includes('.claude/commands')) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
// Handle framework-specific command files specially
|
|
33
|
+
if (file.source.includes('.claude/commands') && file.source.includes('examples/')) {
|
|
34
|
+
// This is a framework-specific commands directory - merge with existing commands
|
|
35
|
+
await fs.ensureDir(destPath);
|
|
36
|
+
|
|
37
|
+
// Copy framework-specific commands to the commands directory
|
|
38
|
+
const frameworkFiles = await fs.readdir(sourcePath);
|
|
39
|
+
for (const frameworkFile of frameworkFiles) {
|
|
40
|
+
const srcFile = path.join(sourcePath, frameworkFile);
|
|
41
|
+
const destFile = path.join(destPath, frameworkFile);
|
|
42
|
+
await fs.copy(srcFile, destFile, { overwrite: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(chalk.green(`✓ Copied framework commands ${file.source} → ${file.destination}`));
|
|
46
|
+
} else if (file.source.includes('.claude') && !file.source.includes('examples/')) {
|
|
47
|
+
// This is base .claude directory - copy it but handle commands specially
|
|
48
|
+
await fs.copy(sourcePath, destPath, {
|
|
49
|
+
overwrite: true,
|
|
50
|
+
filter: (src) => {
|
|
51
|
+
// Skip the commands directory itself - we'll handle it separately
|
|
52
|
+
return !src.endsWith('.claude/commands');
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Now handle base commands specifically
|
|
57
|
+
const baseCommandsPath = path.join(sourcePath, 'commands');
|
|
58
|
+
const destCommandsPath = path.join(destPath, 'commands');
|
|
59
|
+
|
|
60
|
+
if (await fs.pathExists(baseCommandsPath)) {
|
|
61
|
+
await fs.ensureDir(destCommandsPath);
|
|
62
|
+
|
|
63
|
+
// Copy base commands, but exclude framework-specific ones that were moved
|
|
64
|
+
const baseCommands = await fs.readdir(baseCommandsPath);
|
|
65
|
+
const excludeCommands = ['react-component.md', 'route.md', 'api-endpoint.md']; // Commands moved to framework dirs
|
|
66
|
+
|
|
67
|
+
for (const baseCommand of baseCommands) {
|
|
68
|
+
if (!excludeCommands.includes(baseCommand)) {
|
|
69
|
+
const srcFile = path.join(baseCommandsPath, baseCommand);
|
|
70
|
+
const destFile = path.join(destCommandsPath, baseCommand);
|
|
71
|
+
await fs.copy(srcFile, destFile, { overwrite: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
42
74
|
}
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
|
|
76
|
+
console.log(chalk.green(`✓ Copied base configuration and commands ${file.source} → ${file.destination}`));
|
|
77
|
+
} else {
|
|
78
|
+
// Copy regular files (CLAUDE.md, settings.json, etc.)
|
|
79
|
+
await fs.copy(sourcePath, destPath, {
|
|
80
|
+
overwrite: true,
|
|
81
|
+
filter: (src) => {
|
|
82
|
+
// Skip commands directory during regular copy - we handle them above
|
|
83
|
+
return !src.includes('.claude/commands');
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination}`));
|
|
87
|
+
}
|
|
45
88
|
} catch (error) {
|
|
46
89
|
console.error(chalk.red(`✗ Failed to copy ${file.source}:`), error.message);
|
|
47
90
|
throw error;
|
package/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ const path = require('path');
|
|
|
5
5
|
const ora = require('ora');
|
|
6
6
|
const { detectProject } = require('./utils');
|
|
7
7
|
const { getTemplateConfig } = require('./templates');
|
|
8
|
-
const { createPrompts } = require('./prompts');
|
|
8
|
+
const { createPrompts, interactivePrompts } = require('./prompts');
|
|
9
9
|
const { copyTemplateFiles } = require('./file-operations');
|
|
10
10
|
|
|
11
11
|
async function createClaudeConfig(options = {}) {
|
|
@@ -19,9 +19,6 @@ async function createClaudeConfig(options = {}) {
|
|
|
19
19
|
const projectInfo = await detectProject(targetDir);
|
|
20
20
|
spinner.succeed('Project detection complete');
|
|
21
21
|
|
|
22
|
-
// Create interactive prompts
|
|
23
|
-
const prompts = createPrompts(projectInfo, options);
|
|
24
|
-
|
|
25
22
|
let config;
|
|
26
23
|
if (options.yes) {
|
|
27
24
|
// Use defaults
|
|
@@ -31,8 +28,8 @@ async function createClaudeConfig(options = {}) {
|
|
|
31
28
|
features: []
|
|
32
29
|
};
|
|
33
30
|
} else {
|
|
34
|
-
// Interactive prompts
|
|
35
|
-
config = await
|
|
31
|
+
// Interactive prompts with back navigation
|
|
32
|
+
config = await interactivePrompts(projectInfo, options);
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
// Check if user confirmed the setup
|
package/src/prompts.js
CHANGED
|
@@ -1,7 +1,169 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
2
3
|
const { getAvailableLanguages, getFrameworksForLanguage } = require('./templates');
|
|
3
4
|
const { getCommandsForLanguageAndFramework } = require('./command-scanner');
|
|
4
5
|
|
|
6
|
+
async function interactivePrompts(projectInfo, options = {}) {
|
|
7
|
+
const state = {
|
|
8
|
+
currentStep: 0,
|
|
9
|
+
answers: {},
|
|
10
|
+
steps: []
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// Build steps array based on options
|
|
14
|
+
if (!options.language) state.steps.push('language');
|
|
15
|
+
if (!options.framework) state.steps.push('framework');
|
|
16
|
+
state.steps.push('commands', 'confirm');
|
|
17
|
+
|
|
18
|
+
while (state.currentStep < state.steps.length) {
|
|
19
|
+
const stepName = state.steps[state.currentStep];
|
|
20
|
+
const result = await showStep(stepName, state.answers, projectInfo, options);
|
|
21
|
+
|
|
22
|
+
if (result.action === 'back') {
|
|
23
|
+
if (state.currentStep > 0) {
|
|
24
|
+
state.currentStep--;
|
|
25
|
+
// Clear the answer for the step we're going back from
|
|
26
|
+
delete state.answers[stepName];
|
|
27
|
+
}
|
|
28
|
+
} else if (result.action === 'next') {
|
|
29
|
+
state.answers[stepName] = result.value;
|
|
30
|
+
state.currentStep++;
|
|
31
|
+
} else if (result.action === 'exit') {
|
|
32
|
+
return { confirm: false };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return state.answers;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function showStep(stepName, currentAnswers, projectInfo, options) {
|
|
40
|
+
const stepConfig = getStepConfig(stepName, currentAnswers, projectInfo, options);
|
|
41
|
+
|
|
42
|
+
if (!stepConfig) {
|
|
43
|
+
return { action: 'next', value: null };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add back option if not first step
|
|
47
|
+
const isFirstStep = stepName === 'language' || (options.language && stepName === 'framework') ||
|
|
48
|
+
(options.language && options.framework && stepName === 'commands');
|
|
49
|
+
|
|
50
|
+
if (!isFirstStep && stepConfig.type === 'list') {
|
|
51
|
+
stepConfig.choices = [
|
|
52
|
+
{ value: '__back__', name: chalk.gray('← Back') },
|
|
53
|
+
new inquirer.Separator(),
|
|
54
|
+
...stepConfig.choices
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const answer = await inquirer.prompt([stepConfig]);
|
|
59
|
+
const value = answer[stepName];
|
|
60
|
+
|
|
61
|
+
if (value === '__back__') {
|
|
62
|
+
return { action: 'back' };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { action: 'next', value };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getStepConfig(stepName, currentAnswers, projectInfo, options) {
|
|
69
|
+
switch (stepName) {
|
|
70
|
+
case 'language':
|
|
71
|
+
return {
|
|
72
|
+
type: 'list',
|
|
73
|
+
name: 'language',
|
|
74
|
+
message: 'Select your programming language:',
|
|
75
|
+
choices: getAvailableLanguages(),
|
|
76
|
+
default: projectInfo.detectedLanguage || 'common',
|
|
77
|
+
prefix: chalk.blue('🔤')
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
case 'framework':
|
|
81
|
+
const selectedLanguage = currentAnswers.language || options.language;
|
|
82
|
+
const frameworks = getFrameworksForLanguage(selectedLanguage);
|
|
83
|
+
|
|
84
|
+
if (frameworks.length === 0) {
|
|
85
|
+
return null; // Skip this step
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
type: 'list',
|
|
90
|
+
name: 'framework',
|
|
91
|
+
message: 'Select your framework (optional):',
|
|
92
|
+
choices: [
|
|
93
|
+
{ value: 'none', name: 'None / Generic' },
|
|
94
|
+
...frameworks
|
|
95
|
+
],
|
|
96
|
+
default: projectInfo.detectedFramework || 'none',
|
|
97
|
+
prefix: chalk.green('🎯')
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
case 'commands':
|
|
101
|
+
const commandLanguage = currentAnswers.language || options.language;
|
|
102
|
+
const commandFramework = currentAnswers.framework || options.framework;
|
|
103
|
+
|
|
104
|
+
if (!commandLanguage || commandLanguage === 'common') {
|
|
105
|
+
return {
|
|
106
|
+
type: 'checkbox',
|
|
107
|
+
name: 'commands',
|
|
108
|
+
message: 'Select commands to include (use space to select):',
|
|
109
|
+
choices: [
|
|
110
|
+
{
|
|
111
|
+
value: 'basic-commands',
|
|
112
|
+
name: 'Basic development commands',
|
|
113
|
+
checked: true
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
prefix: chalk.cyan('📋')
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const availableCommands = getCommandsForLanguageAndFramework(commandLanguage, commandFramework);
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
type: 'checkbox',
|
|
124
|
+
name: 'commands',
|
|
125
|
+
message: 'Select commands to include (use space to select, enter to continue):',
|
|
126
|
+
choices: availableCommands.map(cmd => ({
|
|
127
|
+
value: cmd.name,
|
|
128
|
+
name: `${cmd.displayName} - ${cmd.description}`,
|
|
129
|
+
checked: cmd.checked
|
|
130
|
+
})),
|
|
131
|
+
prefix: chalk.cyan('📋')
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
case 'confirm':
|
|
135
|
+
const confirmLanguage = currentAnswers.language || options.language || 'common';
|
|
136
|
+
const confirmFramework = currentAnswers.framework || options.framework || 'none';
|
|
137
|
+
const commandCount = currentAnswers.commands ? currentAnswers.commands.length : 0;
|
|
138
|
+
|
|
139
|
+
let message = `Setup Claude Code for ${chalk.cyan(confirmLanguage)}`;
|
|
140
|
+
if (confirmFramework !== 'none') {
|
|
141
|
+
message += ` with ${chalk.green(confirmFramework)}`;
|
|
142
|
+
}
|
|
143
|
+
if (commandCount > 0) {
|
|
144
|
+
message += ` (${chalk.yellow(commandCount)} commands)`;
|
|
145
|
+
}
|
|
146
|
+
message += '?';
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
type: 'list',
|
|
150
|
+
name: 'confirm',
|
|
151
|
+
message,
|
|
152
|
+
choices: [
|
|
153
|
+
{ value: '__back__', name: chalk.gray('← Back to modify settings') },
|
|
154
|
+
new inquirer.Separator(),
|
|
155
|
+
{ value: true, name: chalk.green('✅ Yes, proceed with setup') },
|
|
156
|
+
{ value: false, name: chalk.red('❌ No, cancel setup') }
|
|
157
|
+
],
|
|
158
|
+
default: true,
|
|
159
|
+
prefix: chalk.red('🚀')
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
default:
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
5
167
|
function createPrompts(projectInfo, options = {}) {
|
|
6
168
|
const prompts = [];
|
|
7
169
|
|
|
@@ -76,35 +238,6 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
76
238
|
}
|
|
77
239
|
});
|
|
78
240
|
|
|
79
|
-
// Features selection
|
|
80
|
-
prompts.push({
|
|
81
|
-
type: 'checkbox',
|
|
82
|
-
name: 'features',
|
|
83
|
-
message: 'Select additional features:',
|
|
84
|
-
choices: [
|
|
85
|
-
{
|
|
86
|
-
value: 'git-hooks',
|
|
87
|
-
name: 'Git hooks integration',
|
|
88
|
-
checked: false
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
value: 'testing',
|
|
92
|
-
name: 'Enhanced testing commands',
|
|
93
|
-
checked: true
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
value: 'linting',
|
|
97
|
-
name: 'Code linting and formatting',
|
|
98
|
-
checked: true
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
value: 'debugging',
|
|
102
|
-
name: 'Debugging helpers',
|
|
103
|
-
checked: false
|
|
104
|
-
}
|
|
105
|
-
],
|
|
106
|
-
prefix: chalk.yellow('⚙️')
|
|
107
|
-
});
|
|
108
241
|
|
|
109
242
|
// Confirmation
|
|
110
243
|
prompts.push({
|
|
@@ -152,5 +285,6 @@ function createProjectTypePrompt(detectedTypes) {
|
|
|
152
285
|
|
|
153
286
|
module.exports = {
|
|
154
287
|
createPrompts,
|
|
155
|
-
createProjectTypePrompt
|
|
288
|
+
createProjectTypePrompt,
|
|
289
|
+
interactivePrompts
|
|
156
290
|
};
|