claude-init 1.0.30 → 1.0.32

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.30",
3
+ "version": "1.0.32",
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
  /**
@@ -58,7 +58,7 @@ export async function cli() {
58
58
  if (await fs.pathExists(settingsPath)) {
59
59
  overwriteSettings = await promptYesNo(
60
60
  'Overwrite existing .claude/settings.json with template version?',
61
- false
61
+ true
62
62
  );
63
63
  }
64
64
 
@@ -78,7 +78,7 @@ export async function cli() {
78
78
  if (hasOverlappingMd) {
79
79
  overwriteCommandsMd = await promptYesNo(
80
80
  'Overwrite existing .claude/commands/*.md with template versions?',
81
- false
81
+ true
82
82
  );
83
83
  }
84
84
  }
@@ -86,18 +86,33 @@ export async function cli() {
86
86
  // Determine if we should install .claude/agents
87
87
  const includeAgents = await promptYesNo(
88
88
  'Install .claude/agents files?',
89
- true
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 directory name
105
+ const devcontainerMap = {
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,15 @@ export async function cli() {
111
126
  }
112
127
  ];
113
128
 
129
+ // Conditionally add devcontainer task
130
+ if (devcontainerVariant !== 'none') {
131
+ const devcontainerDir = devcontainerMap[devcontainerVariant];
132
+ tasks.splice(1, 0, {
133
+ name: devcontainerDir,
134
+ handler: () => handleDirectoryMirror(targetDir, devcontainerDir)
135
+ });
136
+ }
137
+
114
138
  // Conditionally add .claude/agents task
115
139
  if (includeAgents) {
116
140
  tasks.push({
@@ -160,6 +184,10 @@ export async function cli() {
160
184
  console.log(chalk.cyan(` Files added: ${totalActions.filesAdded}`));
161
185
  }
162
186
 
187
+ if (devcontainerVariant === 'none') {
188
+ console.log(chalk.gray(` - devcontainer skipped by user`));
189
+ }
190
+
163
191
  if (!includeAgents) {
164
192
  console.log(chalk.gray(` - .claude/agents skipped by user`));
165
193
  }
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
+ }