claude-code-templates 1.0.2 → 1.1.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/package.json +7 -2
- package/scripts/sync-templates.js +182 -0
- package/src/command-scanner.js +175 -0
- package/src/file-operations.js +34 -2
- package/src/prompts.js +47 -1
- package/src/templates.js +10 -1
- package/templates/common/.claude/commands/git-workflow.md +239 -0
- package/templates/common/.claude/commands/project-setup.md +316 -0
- package/templates/go/README.md +25 -0
- package/templates/javascript-typescript/.claude/commands/api-endpoint.md +51 -1
- package/templates/javascript-typescript/.claude/commands/debug.md +52 -1
- package/templates/javascript-typescript/.claude/commands/lint.md +48 -1
- package/templates/javascript-typescript/.claude/commands/npm-scripts.md +48 -1
- package/templates/javascript-typescript/.claude/commands/react-component.md +54 -1
- package/templates/javascript-typescript/.claude/commands/refactor.md +55 -1
- package/templates/javascript-typescript/.claude/commands/route.md +193 -0
- package/templates/javascript-typescript/.claude/commands/test.md +61 -1
- package/templates/javascript-typescript/.claude/commands/typescript-migrate.md +51 -1
- package/templates/javascript-typescript/.claude/settings.json +41 -2
- package/templates/javascript-typescript/.mcp.json +13 -0
- package/templates/javascript-typescript/README.md +213 -187
- package/templates/python/.claude/commands/django-model.md +124 -0
- package/templates/python/.claude/commands/flask-route.md +217 -0
- package/templates/python/.claude/commands/lint.md +111 -0
- package/templates/python/.claude/commands/test.md +73 -0
- package/templates/rust/README.md +26 -0
- package/templates/javascript-typescript/.claude/hooks/format-on-save.json +0 -1
- package/templates/javascript-typescript/.claude/hooks/lint-on-save.json +0 -1
- package/templates/javascript-typescript/.claude/hooks/typescript-check.json +0 -1
- package/templates/javascript-typescript/examples/node-api/.claude/commands/middleware.md +0 -1
- package/templates/javascript-typescript/examples/node-api/.claude/commands/route.md +0 -1
- package/templates/javascript-typescript/examples/node-api/CLAUDE.md +0 -1
- package/templates/javascript-typescript/examples/react-app/.claude/commands/component.md +0 -1
- package/templates/javascript-typescript/examples/react-app/.claude/commands/hooks.md +0 -1
- package/templates/javascript-typescript/examples/react-app/CLAUDE.md +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "CLI tool to setup Claude Code configurations for different programming languages",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
13
|
"start": "node bin/create-claude-config.js",
|
|
14
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
15
|
+
"sync": "node scripts/sync-templates.js",
|
|
16
|
+
"presync": "echo \"🔄 Iniciando sincronización de plantillas...\"",
|
|
17
|
+
"postsync": "echo \"✅ Sincronización completada. Listo para publicar!\"",
|
|
18
|
+
"prepublishOnly": "npm run sync"
|
|
15
19
|
},
|
|
16
20
|
"keywords": [
|
|
17
21
|
"claude",
|
|
@@ -46,6 +50,7 @@
|
|
|
46
50
|
"files": [
|
|
47
51
|
"bin/",
|
|
48
52
|
"src/",
|
|
53
|
+
"scripts/",
|
|
49
54
|
"templates/",
|
|
50
55
|
"README.md"
|
|
51
56
|
]
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Script para sincronizar las plantillas desde las carpetas root
|
|
9
|
+
* hacia cli-tool/templates/
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
async function syncTemplates() {
|
|
13
|
+
console.log(chalk.blue('🔄 Sincronizando plantillas...'));
|
|
14
|
+
|
|
15
|
+
const rootDir = path.join(__dirname, '..', '..');
|
|
16
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
17
|
+
|
|
18
|
+
// Lenguajes a sincronizar
|
|
19
|
+
const languages = ['common', 'javascript-typescript', 'python', 'rust', 'go'];
|
|
20
|
+
|
|
21
|
+
let totalCopied = 0;
|
|
22
|
+
let totalSkipped = 0;
|
|
23
|
+
|
|
24
|
+
for (const language of languages) {
|
|
25
|
+
const sourceDir = path.join(rootDir, language);
|
|
26
|
+
const targetDir = path.join(templatesDir, language);
|
|
27
|
+
|
|
28
|
+
if (!await fs.pathExists(sourceDir)) {
|
|
29
|
+
console.log(chalk.yellow(`⚠️ Carpeta source no existe: ${language}`));
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(chalk.cyan(`\n📂 Sincronizando ${language}...`));
|
|
34
|
+
|
|
35
|
+
// Limpiar directorio destino
|
|
36
|
+
if (await fs.pathExists(targetDir)) {
|
|
37
|
+
await fs.remove(targetDir);
|
|
38
|
+
console.log(chalk.gray(` 🗑️ Directorio anterior eliminado`));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Copiar todo desde source
|
|
42
|
+
try {
|
|
43
|
+
await fs.copy(sourceDir, targetDir, {
|
|
44
|
+
filter: (src, dest) => {
|
|
45
|
+
// Filtrar archivos que no queremos copiar
|
|
46
|
+
const relativePath = path.relative(sourceDir, src);
|
|
47
|
+
|
|
48
|
+
// Excluir directorios y archivos específicos
|
|
49
|
+
if (relativePath.includes('node_modules')) return false;
|
|
50
|
+
if (relativePath.includes('.git')) return false;
|
|
51
|
+
if (relativePath.includes('package-lock.json')) return false;
|
|
52
|
+
if (relativePath.endsWith('.log')) return false;
|
|
53
|
+
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Contar archivos copiados
|
|
59
|
+
const stats = await getDirectoryStats(targetDir);
|
|
60
|
+
totalCopied += stats.files;
|
|
61
|
+
|
|
62
|
+
console.log(chalk.green(` ✅ ${stats.files} archivos copiados`));
|
|
63
|
+
|
|
64
|
+
// Mostrar estructura copiada
|
|
65
|
+
if (stats.files > 0) {
|
|
66
|
+
await showDirectoryStructure(targetDir, ' ');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} catch (error) {
|
|
70
|
+
console.error(chalk.red(` ❌ Error copiando ${language}:`), error.message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.green(`\n🎉 Sincronización completada!`));
|
|
75
|
+
console.log(chalk.white(`📊 Total de archivos sincronizados: ${totalCopied}`));
|
|
76
|
+
|
|
77
|
+
// Verificar que no existan archivos hooks
|
|
78
|
+
await cleanupOldReferences();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function getDirectoryStats(dir) {
|
|
82
|
+
let files = 0;
|
|
83
|
+
let dirs = 0;
|
|
84
|
+
|
|
85
|
+
if (!await fs.pathExists(dir)) {
|
|
86
|
+
return { files: 0, dirs: 0 };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const items = await fs.readdir(dir);
|
|
90
|
+
|
|
91
|
+
for (const item of items) {
|
|
92
|
+
const itemPath = path.join(dir, item);
|
|
93
|
+
const stat = await fs.stat(itemPath);
|
|
94
|
+
|
|
95
|
+
if (stat.isDirectory()) {
|
|
96
|
+
dirs++;
|
|
97
|
+
const subStats = await getDirectoryStats(itemPath);
|
|
98
|
+
files += subStats.files;
|
|
99
|
+
dirs += subStats.dirs;
|
|
100
|
+
} else {
|
|
101
|
+
files++;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return { files, dirs };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function showDirectoryStructure(dir, prefix = '') {
|
|
109
|
+
const items = await fs.readdir(dir);
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < items.length; i++) {
|
|
112
|
+
const item = items[i];
|
|
113
|
+
const itemPath = path.join(dir, item);
|
|
114
|
+
const stat = await fs.stat(itemPath);
|
|
115
|
+
const isLast = i === items.length - 1;
|
|
116
|
+
const connector = isLast ? '└── ' : '├── ';
|
|
117
|
+
|
|
118
|
+
if (stat.isDirectory()) {
|
|
119
|
+
console.log(chalk.blue(`${prefix}${connector}${item}/`));
|
|
120
|
+
if (item === '.claude' || item === 'commands') {
|
|
121
|
+
// Mostrar solo un nivel más para .claude y commands
|
|
122
|
+
const subItems = await fs.readdir(itemPath);
|
|
123
|
+
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
124
|
+
for (let j = 0; j < Math.min(subItems.length, 3); j++) {
|
|
125
|
+
const subItem = subItems[j];
|
|
126
|
+
const subConnector = j === Math.min(subItems.length, 3) - 1 ? '└── ' : '├── ';
|
|
127
|
+
console.log(chalk.gray(`${newPrefix}${subConnector}${subItem}`));
|
|
128
|
+
}
|
|
129
|
+
if (subItems.length > 3) {
|
|
130
|
+
console.log(chalk.gray(`${newPrefix}└── ... y ${subItems.length - 3} más`));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
console.log(chalk.gray(`${prefix}${connector}${item}`));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function cleanupOldReferences() {
|
|
140
|
+
console.log(chalk.yellow('\n🧹 Limpiando referencias obsoletas...'));
|
|
141
|
+
|
|
142
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
143
|
+
|
|
144
|
+
// Buscar y eliminar directorios hooks
|
|
145
|
+
const languages = ['javascript-typescript', 'python', 'common'];
|
|
146
|
+
|
|
147
|
+
for (const language of languages) {
|
|
148
|
+
const hooksDir = path.join(templatesDir, language, '.claude', 'hooks');
|
|
149
|
+
if (await fs.pathExists(hooksDir)) {
|
|
150
|
+
await fs.remove(hooksDir);
|
|
151
|
+
console.log(chalk.yellow(` 🗑️ Eliminado: ${language}/.claude/hooks/`));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Verificar archivos vacíos en commands
|
|
156
|
+
for (const language of languages) {
|
|
157
|
+
const commandsDir = path.join(templatesDir, language, '.claude', 'commands');
|
|
158
|
+
if (await fs.pathExists(commandsDir)) {
|
|
159
|
+
const files = await fs.readdir(commandsDir);
|
|
160
|
+
for (const file of files) {
|
|
161
|
+
const filePath = path.join(commandsDir, file);
|
|
162
|
+
const stat = await fs.stat(filePath);
|
|
163
|
+
if (stat.size < 50) { // Archivos muy pequeños probablemente estén vacíos
|
|
164
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
165
|
+
if (content.trim().length < 10) {
|
|
166
|
+
console.log(chalk.yellow(` ⚠️ Archivo posiblemente vacío: ${language}/.claude/commands/${file} (${stat.size} bytes)`));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Función para ejecutar la sincronización
|
|
175
|
+
if (require.main === module) {
|
|
176
|
+
syncTemplates().catch(error => {
|
|
177
|
+
console.error(chalk.red('❌ Error durante la sincronización:'), error);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = { syncTemplates };
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scans and returns available commands for a given language
|
|
6
|
+
* @param {string} language - The language template to scan
|
|
7
|
+
* @returns {Array} Array of available commands with metadata
|
|
8
|
+
*/
|
|
9
|
+
function getAvailableCommands(language) {
|
|
10
|
+
const templatesDir = path.join(__dirname, '..', 'templates');
|
|
11
|
+
const languageDir = path.join(templatesDir, language);
|
|
12
|
+
|
|
13
|
+
// Check if language directory exists
|
|
14
|
+
if (!fs.existsSync(languageDir)) {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const commands = [];
|
|
19
|
+
|
|
20
|
+
// Scan main .claude/commands directory
|
|
21
|
+
const mainCommandsDir = path.join(languageDir, '.claude', 'commands');
|
|
22
|
+
if (fs.existsSync(mainCommandsDir)) {
|
|
23
|
+
const mainCommands = scanCommandsInDirectory(mainCommandsDir, 'core');
|
|
24
|
+
commands.push(...mainCommands);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Scan framework-specific commands in examples
|
|
28
|
+
const examplesDir = path.join(languageDir, 'examples');
|
|
29
|
+
if (fs.existsSync(examplesDir)) {
|
|
30
|
+
const frameworkDirs = fs.readdirSync(examplesDir).filter(dir => {
|
|
31
|
+
return fs.statSync(path.join(examplesDir, dir)).isDirectory();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
frameworkDirs.forEach(framework => {
|
|
35
|
+
const frameworkCommandsDir = path.join(examplesDir, framework, '.claude', 'commands');
|
|
36
|
+
if (fs.existsSync(frameworkCommandsDir)) {
|
|
37
|
+
const frameworkCommands = scanCommandsInDirectory(frameworkCommandsDir, framework);
|
|
38
|
+
commands.push(...frameworkCommands);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Remove duplicates based on command name
|
|
44
|
+
const uniqueCommands = commands.reduce((acc, command) => {
|
|
45
|
+
const existing = acc.find(c => c.name === command.name);
|
|
46
|
+
if (!existing) {
|
|
47
|
+
acc.push(command);
|
|
48
|
+
} else {
|
|
49
|
+
// If duplicate, prefer core commands over framework-specific ones
|
|
50
|
+
if (command.category === 'core' && existing.category !== 'core') {
|
|
51
|
+
const index = acc.findIndex(c => c.name === command.name);
|
|
52
|
+
acc[index] = command;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return acc;
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
return uniqueCommands.sort((a, b) => {
|
|
59
|
+
// Sort by category first (core first), then by name
|
|
60
|
+
if (a.category !== b.category) {
|
|
61
|
+
return a.category === 'core' ? -1 : 1;
|
|
62
|
+
}
|
|
63
|
+
return a.name.localeCompare(b.name);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Scans a directory for command files and returns command metadata
|
|
69
|
+
* @param {string} commandsDir - Directory to scan
|
|
70
|
+
* @param {string} category - Category of commands (core, react-app, node-api, etc.)
|
|
71
|
+
* @returns {Array} Array of command objects
|
|
72
|
+
*/
|
|
73
|
+
function scanCommandsInDirectory(commandsDir, category) {
|
|
74
|
+
const commands = [];
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const files = fs.readdirSync(commandsDir);
|
|
78
|
+
|
|
79
|
+
files.forEach(file => {
|
|
80
|
+
if (path.extname(file) === '.md') {
|
|
81
|
+
const commandName = path.basename(file, '.md');
|
|
82
|
+
const filePath = path.join(commandsDir, file);
|
|
83
|
+
|
|
84
|
+
// Read the command file to extract metadata
|
|
85
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
86
|
+
const metadata = parseCommandMetadata(content, commandName);
|
|
87
|
+
|
|
88
|
+
commands.push({
|
|
89
|
+
name: commandName,
|
|
90
|
+
displayName: metadata.title || commandName,
|
|
91
|
+
description: metadata.description || `${commandName} command`,
|
|
92
|
+
category: category,
|
|
93
|
+
filePath: filePath,
|
|
94
|
+
checked: metadata.defaultChecked || false
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.warn(`Warning: Could not scan commands in ${commandsDir}:`, error.message);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return commands;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Parses command metadata from markdown file content
|
|
107
|
+
* @param {string} content - The markdown content
|
|
108
|
+
* @param {string} commandName - The command name
|
|
109
|
+
* @returns {Object} Parsed metadata
|
|
110
|
+
*/
|
|
111
|
+
function parseCommandMetadata(content, commandName) {
|
|
112
|
+
const metadata = {
|
|
113
|
+
title: null,
|
|
114
|
+
description: null,
|
|
115
|
+
defaultChecked: false
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const lines = content.split('\n');
|
|
119
|
+
|
|
120
|
+
// Extract title from first heading
|
|
121
|
+
for (let i = 0; i < Math.min(lines.length, 10); i++) {
|
|
122
|
+
const line = lines[i].trim();
|
|
123
|
+
if (line.startsWith('# ')) {
|
|
124
|
+
metadata.title = line.substring(2).trim();
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Extract description from purpose section or first paragraph
|
|
130
|
+
const purposeMatch = content.match(/## Purpose\s*\n\s*(.+?)(?=\n\n|\n##|$)/s);
|
|
131
|
+
if (purposeMatch) {
|
|
132
|
+
metadata.description = purposeMatch[1].trim().replace(/\n/g, ' ');
|
|
133
|
+
} else {
|
|
134
|
+
// Try to find first paragraph after title
|
|
135
|
+
const paragraphMatch = content.match(/^#[^\n]*\n\s*\n(.+?)(?=\n\n|\n##|$)/s);
|
|
136
|
+
if (paragraphMatch) {
|
|
137
|
+
metadata.description = paragraphMatch[1].trim().replace(/\n/g, ' ');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Determine default checked state (core commands usually checked by default)
|
|
142
|
+
const coreCommands = ['test', 'lint', 'debug', 'refactor'];
|
|
143
|
+
metadata.defaultChecked = coreCommands.includes(commandName);
|
|
144
|
+
|
|
145
|
+
return metadata;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get commands available for a specific language and framework combination
|
|
150
|
+
* @param {string} language - The language template
|
|
151
|
+
* @param {string} framework - The framework (optional)
|
|
152
|
+
* @returns {Array} Array of available commands
|
|
153
|
+
*/
|
|
154
|
+
function getCommandsForLanguageAndFramework(language, framework = null) {
|
|
155
|
+
const allCommands = getAvailableCommands(language);
|
|
156
|
+
|
|
157
|
+
if (!framework || framework === 'none') {
|
|
158
|
+
// Return only core commands
|
|
159
|
+
return allCommands.filter(cmd => cmd.category === 'core');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Return core commands + framework-specific commands
|
|
163
|
+
return allCommands.filter(cmd =>
|
|
164
|
+
cmd.category === 'core' ||
|
|
165
|
+
cmd.category === framework ||
|
|
166
|
+
cmd.category === `${framework}-app`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
getAvailableCommands,
|
|
172
|
+
getCommandsForLanguageAndFramework,
|
|
173
|
+
scanCommandsInDirectory,
|
|
174
|
+
parseCommandMetadata
|
|
175
|
+
};
|
package/src/file-operations.js
CHANGED
|
@@ -23,19 +23,51 @@ 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 files
|
|
26
|
+
// Copy base files (but skip .claude/commands for now)
|
|
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
|
-
|
|
32
|
+
// Skip .claude/commands directories - we'll handle them separately
|
|
33
|
+
if (file.source.includes('.claude/commands')) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await fs.copy(sourcePath, destPath, {
|
|
38
|
+
overwrite: true,
|
|
39
|
+
filter: (src) => {
|
|
40
|
+
// Skip commands directory during base copy
|
|
41
|
+
return !src.includes('.claude/commands');
|
|
42
|
+
}
|
|
43
|
+
});
|
|
33
44
|
console.log(chalk.green(`✓ Copied ${file.source} → ${file.destination}`));
|
|
34
45
|
} catch (error) {
|
|
35
46
|
console.error(chalk.red(`✗ Failed to copy ${file.source}:`), error.message);
|
|
36
47
|
throw error;
|
|
37
48
|
}
|
|
38
49
|
}
|
|
50
|
+
|
|
51
|
+
// Copy selected commands individually
|
|
52
|
+
if (templateConfig.selectedCommands && templateConfig.selectedCommands.length > 0) {
|
|
53
|
+
const commandsDir = path.join(targetDir, '.claude', 'commands');
|
|
54
|
+
await fs.ensureDir(commandsDir);
|
|
55
|
+
|
|
56
|
+
for (const command of templateConfig.selectedCommands) {
|
|
57
|
+
try {
|
|
58
|
+
const commandFileName = `${command.name}.md`;
|
|
59
|
+
const destPath = path.join(commandsDir, commandFileName);
|
|
60
|
+
|
|
61
|
+
await fs.copy(command.filePath, destPath);
|
|
62
|
+
console.log(chalk.green(`✓ Added command: ${command.displayName}`));
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(chalk.red(`✗ Failed to copy command ${command.name}:`), error.message);
|
|
65
|
+
// Don't throw - continue with other commands
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
console.log(chalk.cyan(`📋 Installed ${templateConfig.selectedCommands.length} commands`));
|
|
70
|
+
}
|
|
39
71
|
}
|
|
40
72
|
|
|
41
73
|
async function ensureDirectoryExists(dirPath) {
|
package/src/prompts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const { getAvailableLanguages, getFrameworksForLanguage } = require('./templates');
|
|
3
|
+
const { getCommandsForLanguageAndFramework } = require('./command-scanner');
|
|
3
4
|
|
|
4
5
|
function createPrompts(projectInfo, options = {}) {
|
|
5
6
|
const prompts = [];
|
|
@@ -41,6 +42,40 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
41
42
|
});
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
// Command selection
|
|
46
|
+
prompts.push({
|
|
47
|
+
type: 'checkbox',
|
|
48
|
+
name: 'commands',
|
|
49
|
+
message: 'Select commands to include (use space to select):',
|
|
50
|
+
choices: (answers) => {
|
|
51
|
+
const selectedLanguage = answers.language || options.language;
|
|
52
|
+
const selectedFramework = answers.framework || options.framework;
|
|
53
|
+
|
|
54
|
+
if (!selectedLanguage || selectedLanguage === 'common') {
|
|
55
|
+
return [
|
|
56
|
+
{
|
|
57
|
+
value: 'basic-commands',
|
|
58
|
+
name: 'Basic development commands',
|
|
59
|
+
checked: true
|
|
60
|
+
}
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const availableCommands = getCommandsForLanguageAndFramework(selectedLanguage, selectedFramework);
|
|
65
|
+
|
|
66
|
+
return availableCommands.map(cmd => ({
|
|
67
|
+
value: cmd.name,
|
|
68
|
+
name: `${cmd.displayName} - ${cmd.description}`,
|
|
69
|
+
checked: cmd.checked
|
|
70
|
+
}));
|
|
71
|
+
},
|
|
72
|
+
prefix: chalk.cyan('📋'),
|
|
73
|
+
when: (answers) => {
|
|
74
|
+
const selectedLanguage = answers.language || options.language;
|
|
75
|
+
return selectedLanguage && selectedLanguage !== 'common';
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
44
79
|
// Features selection
|
|
45
80
|
prompts.push({
|
|
46
81
|
type: 'checkbox',
|
|
@@ -78,7 +113,18 @@ function createPrompts(projectInfo, options = {}) {
|
|
|
78
113
|
message: (answers) => {
|
|
79
114
|
const language = answers.language || options.language || 'common';
|
|
80
115
|
const framework = answers.framework || options.framework || 'none';
|
|
81
|
-
|
|
116
|
+
const commandCount = answers.commands ? answers.commands.length : 0;
|
|
117
|
+
|
|
118
|
+
let message = `Setup Claude Code for ${chalk.cyan(language)}`;
|
|
119
|
+
if (framework !== 'none') {
|
|
120
|
+
message += ` with ${chalk.green(framework)}`;
|
|
121
|
+
}
|
|
122
|
+
if (commandCount > 0) {
|
|
123
|
+
message += ` (${chalk.yellow(commandCount)} commands)`;
|
|
124
|
+
}
|
|
125
|
+
message += '?';
|
|
126
|
+
|
|
127
|
+
return message;
|
|
82
128
|
},
|
|
83
129
|
default: true,
|
|
84
130
|
prefix: chalk.red('🚀')
|
package/src/templates.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
+
const { getCommandsForLanguageAndFramework } = require('./command-scanner');
|
|
2
3
|
|
|
3
4
|
const TEMPLATES_CONFIG = {
|
|
4
5
|
'common': {
|
|
@@ -101,7 +102,7 @@ function getFrameworksForLanguage(language) {
|
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
function getTemplateConfig(selections) {
|
|
104
|
-
const { language, framework } = selections;
|
|
105
|
+
const { language, framework, commands = [] } = selections;
|
|
105
106
|
const baseConfig = TEMPLATES_CONFIG[language];
|
|
106
107
|
|
|
107
108
|
if (!baseConfig) {
|
|
@@ -118,10 +119,18 @@ function getTemplateConfig(selections) {
|
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
// Handle command selection
|
|
123
|
+
let selectedCommands = [];
|
|
124
|
+
if (commands && commands.length > 0) {
|
|
125
|
+
const availableCommands = getCommandsForLanguageAndFramework(language, framework);
|
|
126
|
+
selectedCommands = availableCommands.filter(cmd => commands.includes(cmd.name));
|
|
127
|
+
}
|
|
128
|
+
|
|
121
129
|
return {
|
|
122
130
|
language,
|
|
123
131
|
framework,
|
|
124
132
|
files,
|
|
133
|
+
selectedCommands,
|
|
125
134
|
config: baseConfig
|
|
126
135
|
};
|
|
127
136
|
}
|