claude-code-templates 1.0.2 → 1.1.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-templates",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "CLI tool to setup Claude Code configurations for different programming languages",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -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
+ };
@@ -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
- await fs.copy(sourcePath, destPath, { overwrite: true });
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
- return `Setup Claude Code for ${chalk.cyan(language)}${framework !== 'none' ? ` with ${chalk.green(framework)}` : ''}?`;
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
  }
@@ -0,0 +1,239 @@
1
+ # Git Workflow Helper
2
+
3
+ Manage Git workflows with best practices and common operations.
4
+
5
+ ## Purpose
6
+
7
+ This command helps you perform common Git operations following best practices for collaborative development.
8
+
9
+ ## Usage
10
+
11
+ ```
12
+ /git-workflow
13
+ ```
14
+
15
+ ## What this command does
16
+
17
+ 1. **Guides through Git operations** with proper workflow
18
+ 2. **Suggests best practices** for commits and branches
19
+ 3. **Helps with conflict resolution** and merging
20
+ 4. **Provides templates** for commit messages
21
+ 5. **Manages branching strategies** (Git Flow, GitHub Flow)
22
+
23
+ ## Common Workflows
24
+
25
+ ### Feature Development
26
+ ```bash
27
+ # Create and switch to feature branch
28
+ git checkout -b feature/user-authentication
29
+
30
+ # Work on your feature
31
+ # ... make changes ...
32
+
33
+ # Stage and commit changes
34
+ git add .
35
+ git commit -m "feat: add user authentication system
36
+
37
+ - Implement login/logout functionality
38
+ - Add password validation
39
+ - Create user session management
40
+ - Add authentication middleware"
41
+
42
+ # Push feature branch
43
+ git push -u origin feature/user-authentication
44
+
45
+ # Create pull request (via GitHub/GitLab interface)
46
+ ```
47
+
48
+ ### Hotfix Workflow
49
+ ```bash
50
+ # Create hotfix branch from main
51
+ git checkout main
52
+ git checkout -b hotfix/security-patch
53
+
54
+ # Fix the issue
55
+ # ... make changes ...
56
+
57
+ # Commit the fix
58
+ git commit -m "fix: resolve security vulnerability in auth module
59
+
60
+ - Patch XSS vulnerability in login form
61
+ - Update input validation
62
+ - Add CSRF protection"
63
+
64
+ # Push and create urgent PR
65
+ git push -u origin hotfix/security-patch
66
+ ```
67
+
68
+ ### Sync with Remote
69
+ ```bash
70
+ # Update main branch
71
+ git checkout main
72
+ git pull origin main
73
+
74
+ # Update feature branch with latest main
75
+ git checkout feature/your-feature
76
+ git rebase main
77
+ # OR
78
+ git merge main
79
+ ```
80
+
81
+ ## Commit Message Conventions
82
+
83
+ ### Conventional Commits Format
84
+ ```
85
+ <type>[optional scope]: <description>
86
+
87
+ [optional body]
88
+
89
+ [optional footer(s)]
90
+ ```
91
+
92
+ ### Common Types
93
+ - **feat**: New feature
94
+ - **fix**: Bug fix
95
+ - **docs**: Documentation changes
96
+ - **style**: Code style changes (formatting, etc.)
97
+ - **refactor**: Code refactoring
98
+ - **test**: Adding or updating tests
99
+ - **chore**: Maintenance tasks
100
+
101
+ ### Examples
102
+ ```bash
103
+ # Feature
104
+ git commit -m "feat(auth): add OAuth2 integration"
105
+
106
+ # Bug fix
107
+ git commit -m "fix(api): handle null response in user endpoint"
108
+
109
+ # Documentation
110
+ git commit -m "docs: update API documentation for v2.0"
111
+
112
+ # Breaking change
113
+ git commit -m "feat!: change API response format
114
+
115
+ BREAKING CHANGE: API responses now use 'data' wrapper"
116
+ ```
117
+
118
+ ## Branch Management
119
+
120
+ ### Git Flow Strategy
121
+ ```bash
122
+ # Main branches
123
+ main # Production-ready code
124
+ develop # Integration branch
125
+
126
+ # Supporting branches
127
+ feature/* # New features
128
+ release/* # Release preparation
129
+ hotfix/* # Quick fixes to production
130
+ ```
131
+
132
+ ### GitHub Flow (Simplified)
133
+ ```bash
134
+ # Only main branch + feature branches
135
+ main # Production-ready code
136
+ feature/* # All new work
137
+ ```
138
+
139
+ ## Conflict Resolution
140
+
141
+ ### When Conflicts Occur
142
+ ```bash
143
+ # Start merge/rebase
144
+ git merge feature-branch
145
+ # OR
146
+ git rebase main
147
+
148
+ # If conflicts occur, Git will list conflicted files
149
+ # Edit each file to resolve conflicts
150
+
151
+ # Mark conflicts as resolved
152
+ git add conflicted-file.js
153
+
154
+ # Continue the merge/rebase
155
+ git merge --continue
156
+ # OR
157
+ git rebase --continue
158
+ ```
159
+
160
+ ### Conflict Markers
161
+ ```javascript
162
+ <<<<<<< HEAD
163
+ // Your current branch code
164
+ const user = getCurrentUser();
165
+ =======
166
+ // Incoming branch code
167
+ const user = getAuthenticatedUser();
168
+ >>>>>>> feature-branch
169
+ ```
170
+
171
+ ## Useful Git Commands
172
+
173
+ ### Status and Information
174
+ ```bash
175
+ git status # Check working directory status
176
+ git log --oneline # View commit history
177
+ git branch -a # List all branches
178
+ git remote -v # List remote repositories
179
+ ```
180
+
181
+ ### Undoing Changes
182
+ ```bash
183
+ git checkout -- file.js # Discard changes to file
184
+ git reset HEAD file.js # Unstage file
185
+ git reset --soft HEAD~1 # Undo last commit (keep changes)
186
+ git reset --hard HEAD~1 # Undo last commit (discard changes)
187
+ ```
188
+
189
+ ### Stashing Work
190
+ ```bash
191
+ git stash # Save current work
192
+ git stash pop # Apply and remove latest stash
193
+ git stash list # List all stashes
194
+ git stash apply stash@{1} # Apply specific stash
195
+ ```
196
+
197
+ ## Best Practices
198
+
199
+ 1. **Commit Often** - Make small, focused commits
200
+ 2. **Write Clear Messages** - Use conventional commit format
201
+ 3. **Test Before Committing** - Ensure code works
202
+ 4. **Pull Before Push** - Keep history clean
203
+ 5. **Use Branches** - Don't work directly on main
204
+ 6. **Review Code** - Use pull requests for collaboration
205
+ 7. **Keep History Clean** - Rebase feature branches when appropriate
206
+
207
+ ## Git Hooks (Optional)
208
+
209
+ ### Pre-commit Hook
210
+ ```bash
211
+ #!/bin/sh
212
+ # .git/hooks/pre-commit
213
+
214
+ # Run linter
215
+ npm run lint
216
+ if [ $? -ne 0 ]; then
217
+ echo "Linting failed. Please fix errors before committing."
218
+ exit 1
219
+ fi
220
+
221
+ # Run tests
222
+ npm test
223
+ if [ $? -ne 0 ]; then
224
+ echo "Tests failed. Please fix tests before committing."
225
+ exit 1
226
+ fi
227
+ ```
228
+
229
+ ### Commit Message Hook
230
+ ```bash
231
+ #!/bin/sh
232
+ # .git/hooks/commit-msg
233
+
234
+ # Check commit message format
235
+ if ! grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}" "$1"; then
236
+ echo "Invalid commit message format. Use conventional commits."
237
+ exit 1
238
+ fi
239
+ ```