claude-init 1.0.31 → 1.0.33

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-init",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "Initialize Claude development environment with configurations and templates",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  handleSelectiveFileCopy,
9
9
  handleSingleFile
10
10
  } from './fileManager.js';
11
- import { promptYesNo } from './prompt.js';
11
+ import { promptYesNo, promptChoice } from './prompt.js';
12
12
  import { getTemplatesPath, getFilesInDirectory } from './utils.js';
13
13
 
14
14
  /**
@@ -89,15 +89,30 @@ export async function cli() {
89
89
  false
90
90
  );
91
91
 
92
+ // Determine which devcontainer configuration to install
93
+ const devcontainerVariant = await promptChoice(
94
+ 'Which devcontainer configuration to install?',
95
+ [
96
+ { value: 'js', label: 'JavaScript/TypeScript' },
97
+ { value: 'go', label: 'Go' },
98
+ { value: 'rust', label: 'Rust' },
99
+ { value: 'none', label: 'Skip devcontainer installation' }
100
+ ],
101
+ 'js'
102
+ );
103
+
104
+ // Map devcontainer variant to source directory name in templates
105
+ const devcontainerSourceMap = {
106
+ 'js': '.devcontainer',
107
+ 'go': '.devcontainer-go',
108
+ 'rust': '.devcontainer-rust'
109
+ };
110
+
92
111
  const tasks = [
93
112
  {
94
113
  name: 'CLAUDE.md',
95
114
  handler: () => handleClaudeMarkdown(targetDir)
96
115
  },
97
- {
98
- name: '.devcontainer',
99
- handler: () => handleDirectoryMirror(targetDir, '.devcontainer')
100
- },
101
116
  {
102
117
  name: '.claude/settings.json',
103
118
  handler: () => handleSingleFile(targetDir, '.claude/settings.json', { overwrite: overwriteSettings })
@@ -111,6 +126,35 @@ export async function cli() {
111
126
  }
112
127
  ];
113
128
 
129
+ // Conditionally add devcontainer task
130
+ if (devcontainerVariant !== 'none') {
131
+ const sourceDir = devcontainerSourceMap[devcontainerVariant];
132
+ tasks.splice(1, 0, {
133
+ name: '.devcontainer',
134
+ handler: async () => {
135
+ const targetPath = path.join(targetDir, '.devcontainer');
136
+ const templatePath = path.join(getTemplatesPath(), sourceDir);
137
+
138
+ const exists = await fs.pathExists(targetPath);
139
+
140
+ // Remove existing .devcontainer if it exists
141
+ if (exists) {
142
+ await fs.remove(targetPath);
143
+ }
144
+
145
+ // Copy from selected source to .devcontainer
146
+ await fs.copy(templatePath, targetPath);
147
+
148
+ return {
149
+ action: exists ? 'updated' : 'created',
150
+ details: exists
151
+ ? `Overwrote .devcontainer with ${devcontainerVariant} configuration`
152
+ : `Created .devcontainer with ${devcontainerVariant} configuration`
153
+ };
154
+ }
155
+ });
156
+ }
157
+
114
158
  // Conditionally add .claude/agents task
115
159
  if (includeAgents) {
116
160
  tasks.push({
@@ -160,6 +204,10 @@ export async function cli() {
160
204
  console.log(chalk.cyan(` Files added: ${totalActions.filesAdded}`));
161
205
  }
162
206
 
207
+ if (devcontainerVariant === 'none') {
208
+ console.log(chalk.gray(` - devcontainer skipped by user`));
209
+ }
210
+
163
211
  if (!includeAgents) {
164
212
  console.log(chalk.gray(` - .claude/agents skipped by user`));
165
213
  }
package/src/prompt.js CHANGED
@@ -42,3 +42,62 @@ export async function promptYesNo(question, defaultAnswer) {
42
42
  });
43
43
  });
44
44
  }
45
+
46
+ /**
47
+ * Prompts the user to select from multiple choices.
48
+ * @param {string} question - The question to ask
49
+ * @param {Array<{value: string, label: string}>} choices - Array of choices with value and label
50
+ * @param {string} defaultValue - The default value if user presses Enter or if non-TTY
51
+ * @returns {Promise<string>} - The selected value
52
+ */
53
+ export async function promptChoice(question, choices, defaultValue) {
54
+ // If not TTY, return default immediately
55
+ if (!process.stdin.isTTY) {
56
+ return defaultValue;
57
+ }
58
+
59
+ const rl = readline.createInterface({
60
+ input: process.stdin,
61
+ output: process.stdout
62
+ });
63
+
64
+ return new Promise((resolve) => {
65
+ // Build the prompt with choices
66
+ const choicesText = choices.map((choice, index) =>
67
+ `${index + 1}. ${choice.label}${choice.value === defaultValue ? ' (default)' : ''}`
68
+ ).join('\n ');
69
+
70
+ const promptText = `${question}\n ${choicesText}\nSelect (1-${choices.length}): `;
71
+
72
+ rl.question(promptText, (answer) => {
73
+ rl.close();
74
+
75
+ const normalized = answer.trim();
76
+
77
+ // Empty answer uses default
78
+ if (normalized === '') {
79
+ resolve(defaultValue);
80
+ return;
81
+ }
82
+
83
+ // Try to parse as number
84
+ const choiceIndex = parseInt(normalized, 10) - 1;
85
+
86
+ // Check if valid number choice
87
+ if (!isNaN(choiceIndex) && choiceIndex >= 0 && choiceIndex < choices.length) {
88
+ resolve(choices[choiceIndex].value);
89
+ return;
90
+ }
91
+
92
+ // Try to match by value directly
93
+ const matchedChoice = choices.find(c => c.value.toLowerCase() === normalized.toLowerCase());
94
+ if (matchedChoice) {
95
+ resolve(matchedChoice.value);
96
+ return;
97
+ }
98
+
99
+ // Invalid input, use default
100
+ resolve(defaultValue);
101
+ });
102
+ });
103
+ }