claude-init 1.0.21 → 1.0.24
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/.claude/settings.json +5 -2
- package/.claude/settings.local.json +0 -1
- package/CLAUDE.md +1 -4
- package/README.md +92 -4
- package/package.json +1 -1
- package/src/cli.js +78 -14
- package/src/fileManager.js +72 -30
- package/src/prompt.js +44 -0
package/.claude/settings.json
CHANGED
package/CLAUDE.md
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
+ During you interaction with the user, if you find anything reusable in this project (e.g. version of a library, model name), especially about a fix to a mistake you made or a correction you received, you should take note in the `Lessons` section in the `CLAUDE.md` file so you will not make the same mistake again.
|
|
2
|
-
+ You should also use the `CLAUDE.md` file as a scratchpad to organize your thoughts. Especially when you receive a new task, you should first review the content of the scratchpad, clear old different task but keep lessons learned, then explain the task, and plan the steps you need to take to complete the task. You can use todo markers to indicate the progress, e.g.
|
|
3
|
-
[X] Task 1
|
|
4
|
-
[ ] Task 2
|
|
1
|
+
+ During you interaction with the user, if you find anything reusable in this project (e.g. version of a library, model name), especially about a fix to a mistake you made or a correction you received, you should take note in the `Lessons` section in the `CLAUDE.md` file so you will not make the same mistake again.
|
package/README.md
CHANGED
|
@@ -5,9 +5,11 @@ A CLI tool to initialize Claude development environment with standardized config
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- **Smart Setup**: Automatically detects existing files and only creates/updates what's needed
|
|
8
|
-
- **
|
|
8
|
+
- **Interactive Prompts**: Asks before overwriting existing command files or installing agents
|
|
9
|
+
- **Non-destructive by Default**: Preserves existing files unless you choose to overwrite
|
|
9
10
|
- **Progress Feedback**: Clear visual indicators of what's being created, updated, or skipped
|
|
10
11
|
- **Cross-platform**: Works on Windows, macOS, and Linux
|
|
12
|
+
- **CI-friendly**: Non-interactive mode with sensible defaults when not running in a TTY
|
|
11
13
|
|
|
12
14
|
## Requirements
|
|
13
15
|
|
|
@@ -55,14 +57,100 @@ Running `npx claude-init` sets up your current directory with:
|
|
|
55
57
|
|
|
56
58
|
#### ⚙️ .claude/settings.json
|
|
57
59
|
- **If missing**: Creates with default Claude settings
|
|
58
|
-
- **If exists**:
|
|
60
|
+
- **If exists**: Prompts whether to overwrite with template version
|
|
61
|
+
- **Interactive prompt**: If the file exists, you'll be asked whether to overwrite it
|
|
62
|
+
- Answer **Yes** to replace with fresh template
|
|
63
|
+
- Answer **No** (default) to preserve your custom settings
|
|
59
64
|
|
|
60
|
-
#### 📋 .claude/commands
|
|
65
|
+
#### 📋 .claude/commands
|
|
61
66
|
- **If missing**: Creates with all template files
|
|
62
|
-
- **If exists**: Only adds missing files
|
|
67
|
+
- **If exists**: Only adds missing files by default
|
|
68
|
+
- **Interactive prompt**: If existing `.md` files are detected, you'll be asked whether to overwrite them with template versions
|
|
69
|
+
- Answer **Yes** to replace your customized `.md` files with fresh templates (while still adding any missing files)
|
|
70
|
+
- Answer **No** (default) to keep your existing `.md` files and only add missing files
|
|
71
|
+
|
|
72
|
+
#### 🤖 .claude/agents
|
|
73
|
+
- **Interactive prompt**: You'll be asked whether to install `.claude/agents` files
|
|
74
|
+
- Answer **Yes** (default) to install/update agent files
|
|
75
|
+
- Answer **No** to skip agents entirely
|
|
76
|
+
|
|
77
|
+
### Interactive Prompts
|
|
78
|
+
|
|
79
|
+
When running interactively (in a terminal), `claude-init` will ask you:
|
|
80
|
+
|
|
81
|
+
1. **Overwrite existing .claude/settings.json?** (only if the file already exists)
|
|
82
|
+
- Prompt: `Overwrite existing .claude/settings.json with template version? (y/N):`
|
|
83
|
+
- Default: **No** (preserves your custom settings)
|
|
84
|
+
- If **Yes**: Replaces your settings file with fresh template
|
|
85
|
+
- If **No**: Keeps your existing settings file
|
|
86
|
+
|
|
87
|
+
2. **Overwrite existing .claude/commands/\*.md?** (only if you have existing `.md` files that match templates)
|
|
88
|
+
- Prompt: `Overwrite existing .claude/commands/*.md with template versions? (y/N):`
|
|
89
|
+
- Default: **No** (preserves your customizations)
|
|
90
|
+
- If **Yes**: Replaces existing `.md` files with templates, still adds missing files
|
|
91
|
+
- If **No**: Keeps your existing files, only adds missing files
|
|
92
|
+
|
|
93
|
+
3. **Install .claude/agents?**
|
|
94
|
+
- Prompt: `Install .claude/agents files? (Y/n):`
|
|
95
|
+
- Default: **Yes** (installs agents)
|
|
96
|
+
- If **Yes**: Installs/updates agents as normal
|
|
97
|
+
- If **No**: Skips agents entirely (shown in summary)
|
|
98
|
+
|
|
99
|
+
**Non-interactive mode (CI/scripts)**: When `process.stdin.isTTY` is false (e.g., in CI pipelines), prompts are skipped and defaults are used:
|
|
100
|
+
- `.claude/settings.json` is **not** overwritten (preserves custom settings)
|
|
101
|
+
- `.claude/commands/*.md` files are **not** overwritten (preserves customizations)
|
|
102
|
+
- `.claude/agents` **is** installed (matches current behavior)
|
|
63
103
|
|
|
64
104
|
## Example Output
|
|
65
105
|
|
|
106
|
+
### Interactive Run (First Time)
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
🚀 Claude Environment Initializer
|
|
110
|
+
Initializing in: /path/to/your/project
|
|
111
|
+
|
|
112
|
+
Install .claude/agents files? (Y/n): y
|
|
113
|
+
|
|
114
|
+
✓ Created new CLAUDE.md
|
|
115
|
+
✓ Created .devcontainer directory
|
|
116
|
+
✓ Created .claude/settings.json
|
|
117
|
+
✓ Created .claude/commands with 5 files
|
|
118
|
+
✓ Created .claude/agents with 3 files
|
|
119
|
+
|
|
120
|
+
📊 Summary:
|
|
121
|
+
Created: 5 items
|
|
122
|
+
Files added: 8
|
|
123
|
+
|
|
124
|
+
✨ Claude environment is ready!
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Interactive Run (With Existing Files)
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
🚀 Claude Environment Initializer
|
|
131
|
+
Initializing in: /path/to/your/project
|
|
132
|
+
|
|
133
|
+
Overwrite existing .claude/commands/*.md with template versions? (y/N): n
|
|
134
|
+
Install .claude/agents files? (Y/n): n
|
|
135
|
+
Overwrite existing .claude/settings.json with template version? (y/N): n
|
|
136
|
+
|
|
137
|
+
- CLAUDE.md already contains template content
|
|
138
|
+
- Directory .devcontainer already exists
|
|
139
|
+
- File .claude/settings.json already exists
|
|
140
|
+
↻ Added 1 missing files to .claude/commands (added: 1)
|
|
141
|
+
- All files in .claude/agents are up to date
|
|
142
|
+
|
|
143
|
+
📊 Summary:
|
|
144
|
+
Updated: 1 items
|
|
145
|
+
Skipped: 3 items (already exist)
|
|
146
|
+
Files added: 1
|
|
147
|
+
- .claude/agents skipped by user
|
|
148
|
+
|
|
149
|
+
✅ Claude environment is already up to date!
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Non-Interactive Run (Original Output)
|
|
153
|
+
|
|
66
154
|
```
|
|
67
155
|
🚀 Claude Environment Initializer
|
|
68
156
|
Initializing in: /path/to/your/project
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -1,23 +1,39 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import ora from 'ora';
|
|
3
|
-
import
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs-extra';
|
|
5
|
+
import {
|
|
4
6
|
handleClaudeMarkdown,
|
|
5
7
|
handleDirectoryMirror,
|
|
6
8
|
handleSelectiveFileCopy,
|
|
7
9
|
handleSingleFile
|
|
8
10
|
} from './fileManager.js';
|
|
11
|
+
import { promptYesNo } from './prompt.js';
|
|
12
|
+
import { getTemplatesPath, getFilesInDirectory } from './utils.js';
|
|
9
13
|
|
|
10
14
|
/**
|
|
11
15
|
* Format action result with appropriate colors and icons
|
|
12
16
|
*/
|
|
13
17
|
function formatResult(result) {
|
|
14
|
-
const { action, details, filesAdded } = result;
|
|
15
|
-
|
|
18
|
+
const { action, details, filesAdded, filesOverwritten } = result;
|
|
19
|
+
|
|
16
20
|
switch (action) {
|
|
17
21
|
case 'created':
|
|
18
22
|
return chalk.green(`✓ ${details}`);
|
|
19
23
|
case 'updated':
|
|
20
|
-
|
|
24
|
+
let fileInfo = '';
|
|
25
|
+
if (filesAdded !== undefined || filesOverwritten !== undefined) {
|
|
26
|
+
const parts = [];
|
|
27
|
+
if (filesAdded !== undefined && filesAdded > 0) {
|
|
28
|
+
parts.push(`added: ${filesAdded}`);
|
|
29
|
+
}
|
|
30
|
+
if (filesOverwritten !== undefined && filesOverwritten > 0) {
|
|
31
|
+
parts.push(`overwritten: ${filesOverwritten}`);
|
|
32
|
+
}
|
|
33
|
+
if (parts.length > 0) {
|
|
34
|
+
fileInfo = ` (${parts.join(', ')})`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
21
37
|
return chalk.yellow(`↻ ${details}${fileInfo}`);
|
|
22
38
|
case 'skipped':
|
|
23
39
|
return chalk.gray(`- ${details}`);
|
|
@@ -31,11 +47,48 @@ function formatResult(result) {
|
|
|
31
47
|
*/
|
|
32
48
|
export async function cli() {
|
|
33
49
|
const targetDir = process.cwd();
|
|
34
|
-
|
|
50
|
+
|
|
35
51
|
console.log(chalk.blue.bold('🚀 Claude Environment Initializer'));
|
|
36
52
|
console.log(chalk.gray(`Initializing in: ${targetDir}`));
|
|
37
53
|
console.log();
|
|
38
|
-
|
|
54
|
+
|
|
55
|
+
// Determine if we should overwrite .claude/settings.json
|
|
56
|
+
let overwriteSettings = false;
|
|
57
|
+
const settingsPath = path.join(targetDir, '.claude/settings.json');
|
|
58
|
+
if (await fs.pathExists(settingsPath)) {
|
|
59
|
+
overwriteSettings = await promptYesNo(
|
|
60
|
+
'Overwrite existing .claude/settings.json with template version?',
|
|
61
|
+
false
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Determine if we should prompt for overwriting .claude/commands/*.md
|
|
66
|
+
let overwriteCommandsMd = false;
|
|
67
|
+
const commandsTargetPath = path.join(targetDir, '.claude/commands');
|
|
68
|
+
const commandsTemplatePath = path.join(getTemplatesPath(), '.claude/commands');
|
|
69
|
+
|
|
70
|
+
if (await fs.pathExists(commandsTargetPath) && await fs.pathExists(commandsTemplatePath)) {
|
|
71
|
+
const templateFiles = await getFilesInDirectory(commandsTemplatePath);
|
|
72
|
+
const targetFiles = await getFilesInDirectory(commandsTargetPath);
|
|
73
|
+
|
|
74
|
+
// Check if there are any overlapping .md files
|
|
75
|
+
const mdTemplateFiles = templateFiles.filter(f => f.endsWith('.md'));
|
|
76
|
+
const hasOverlappingMd = mdTemplateFiles.some(f => targetFiles.includes(f));
|
|
77
|
+
|
|
78
|
+
if (hasOverlappingMd) {
|
|
79
|
+
overwriteCommandsMd = await promptYesNo(
|
|
80
|
+
'Overwrite existing .claude/commands/*.md with template versions?',
|
|
81
|
+
false
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Determine if we should install .claude/agents
|
|
87
|
+
const includeAgents = await promptYesNo(
|
|
88
|
+
'Install .claude/agents files?',
|
|
89
|
+
true
|
|
90
|
+
);
|
|
91
|
+
|
|
39
92
|
const tasks = [
|
|
40
93
|
{
|
|
41
94
|
name: 'CLAUDE.md',
|
|
@@ -47,17 +100,24 @@ export async function cli() {
|
|
|
47
100
|
},
|
|
48
101
|
{
|
|
49
102
|
name: '.claude/settings.json',
|
|
50
|
-
handler: () => handleSingleFile(targetDir, '.claude/settings.json')
|
|
103
|
+
handler: () => handleSingleFile(targetDir, '.claude/settings.json', { overwrite: overwriteSettings })
|
|
51
104
|
},
|
|
52
105
|
{
|
|
53
106
|
name: '.claude/commands',
|
|
54
|
-
handler: () => handleSelectiveFileCopy(targetDir, '.claude/commands'
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
handler: () => handleSelectiveFileCopy(targetDir, '.claude/agents')
|
|
107
|
+
handler: () => handleSelectiveFileCopy(targetDir, '.claude/commands', {
|
|
108
|
+
overwriteExisting: overwriteCommandsMd,
|
|
109
|
+
filter: (relPath) => relPath.endsWith('.md')
|
|
110
|
+
})
|
|
59
111
|
}
|
|
60
112
|
];
|
|
113
|
+
|
|
114
|
+
// Conditionally add .claude/agents task
|
|
115
|
+
if (includeAgents) {
|
|
116
|
+
tasks.push({
|
|
117
|
+
name: '.claude/agents',
|
|
118
|
+
handler: () => handleSelectiveFileCopy(targetDir, '.claude/agents')
|
|
119
|
+
});
|
|
120
|
+
}
|
|
61
121
|
|
|
62
122
|
let totalActions = { created: 0, updated: 0, skipped: 0, filesAdded: 0 };
|
|
63
123
|
|
|
@@ -99,9 +159,13 @@ export async function cli() {
|
|
|
99
159
|
if (totalActions.filesAdded > 0) {
|
|
100
160
|
console.log(chalk.cyan(` Files added: ${totalActions.filesAdded}`));
|
|
101
161
|
}
|
|
102
|
-
|
|
162
|
+
|
|
163
|
+
if (!includeAgents) {
|
|
164
|
+
console.log(chalk.gray(` - .claude/agents skipped by user`));
|
|
165
|
+
}
|
|
166
|
+
|
|
103
167
|
console.log();
|
|
104
|
-
|
|
168
|
+
|
|
105
169
|
if (totalActions.created > 0 || totalActions.updated > 0) {
|
|
106
170
|
console.log(chalk.green.bold('✨ Claude environment is ready!'));
|
|
107
171
|
} else {
|
package/src/fileManager.js
CHANGED
|
@@ -70,69 +70,111 @@ export async function handleDirectoryMirror(targetDir, relativePath) {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* Handle selective file copying (add missing files only)
|
|
73
|
+
* Handle selective file copying (add missing files only, optionally overwrite existing)
|
|
74
|
+
* @param {string} targetDir - The target directory
|
|
75
|
+
* @param {string} relativePath - The relative path to copy
|
|
76
|
+
* @param {Object} options - Options for the copy operation
|
|
77
|
+
* @param {boolean} options.overwriteExisting - Whether to overwrite existing files (default: false)
|
|
78
|
+
* @param {function} options.filter - Function to filter which files to overwrite (default: all files)
|
|
74
79
|
*/
|
|
75
|
-
export async function handleSelectiveFileCopy(targetDir, relativePath) {
|
|
80
|
+
export async function handleSelectiveFileCopy(targetDir, relativePath, options = {}) {
|
|
81
|
+
const { overwriteExisting = false, filter = () => true } = options;
|
|
76
82
|
const targetPath = path.join(targetDir, relativePath);
|
|
77
83
|
const templatePath = path.join(getTemplatesPath(), relativePath);
|
|
78
|
-
|
|
84
|
+
|
|
79
85
|
if (!(await fs.pathExists(targetPath))) {
|
|
80
86
|
// Directory doesn't exist, create it with all template files
|
|
81
87
|
await fs.copy(templatePath, targetPath);
|
|
82
88
|
const files = await getFilesInDirectory(templatePath);
|
|
83
|
-
return {
|
|
84
|
-
action: 'created',
|
|
89
|
+
return {
|
|
90
|
+
action: 'created',
|
|
85
91
|
details: `Created ${relativePath} with ${files.length} files`,
|
|
86
92
|
filesAdded: files.length
|
|
87
93
|
};
|
|
88
94
|
}
|
|
89
|
-
|
|
90
|
-
// Directory exists, check for missing files
|
|
95
|
+
|
|
96
|
+
// Directory exists, check for missing files and optionally overwrite existing
|
|
91
97
|
const templateFiles = await getFilesInDirectory(templatePath);
|
|
92
98
|
const targetFiles = await getFilesInDirectory(targetPath);
|
|
93
99
|
const missingFiles = templateFiles.filter(file => !targetFiles.includes(file));
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
details: `All files in ${relativePath} are up to date`,
|
|
99
|
-
filesAdded: 0
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
|
|
100
|
+
|
|
101
|
+
let filesAdded = 0;
|
|
102
|
+
let filesOverwritten = 0;
|
|
103
|
+
|
|
103
104
|
// Copy missing files
|
|
104
|
-
let copiedCount = 0;
|
|
105
105
|
for (const missingFile of missingFiles) {
|
|
106
106
|
const sourcePath = path.join(templatePath, missingFile);
|
|
107
107
|
const destPath = path.join(targetPath, missingFile);
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
// Ensure destination directory exists
|
|
110
110
|
await fs.ensureDir(path.dirname(destPath));
|
|
111
111
|
await fs.copy(sourcePath, destPath);
|
|
112
|
-
|
|
112
|
+
filesAdded++;
|
|
113
113
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
|
|
115
|
+
// Optionally overwrite existing files that match filter
|
|
116
|
+
if (overwriteExisting) {
|
|
117
|
+
const existingFiles = templateFiles.filter(file => targetFiles.includes(file) && filter(file));
|
|
118
|
+
|
|
119
|
+
for (const existingFile of existingFiles) {
|
|
120
|
+
const sourcePath = path.join(templatePath, existingFile);
|
|
121
|
+
const destPath = path.join(targetPath, existingFile);
|
|
122
|
+
|
|
123
|
+
// Ensure destination directory exists
|
|
124
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
125
|
+
await fs.copy(sourcePath, destPath);
|
|
126
|
+
filesOverwritten++;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (filesAdded === 0 && filesOverwritten === 0) {
|
|
131
|
+
return {
|
|
132
|
+
action: 'skipped',
|
|
133
|
+
details: `All files in ${relativePath} are up to date`,
|
|
134
|
+
filesAdded: 0
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const result = {
|
|
139
|
+
action: 'updated',
|
|
140
|
+
filesAdded
|
|
119
141
|
};
|
|
142
|
+
|
|
143
|
+
if (overwriteExisting && filesOverwritten > 0) {
|
|
144
|
+
result.filesOverwritten = filesOverwritten;
|
|
145
|
+
result.details = `Added ${filesAdded} missing files, overwritten ${filesOverwritten} files in ${relativePath}`;
|
|
146
|
+
} else {
|
|
147
|
+
result.details = `Added ${filesAdded} missing files to ${relativePath}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return result;
|
|
120
151
|
}
|
|
121
152
|
|
|
122
153
|
/**
|
|
123
|
-
* Handle single file copying (create if not exists, skip if exists)
|
|
154
|
+
* Handle single file copying (create if not exists, skip if exists, optionally overwrite)
|
|
155
|
+
* @param {string} targetDir - The target directory
|
|
156
|
+
* @param {string} relativePath - The relative path of the file to copy
|
|
157
|
+
* @param {Object} options - Options for the copy operation
|
|
158
|
+
* @param {boolean} options.overwrite - Whether to overwrite existing file (default: false)
|
|
124
159
|
*/
|
|
125
|
-
export async function handleSingleFile(targetDir, relativePath) {
|
|
160
|
+
export async function handleSingleFile(targetDir, relativePath, options = {}) {
|
|
161
|
+
const { overwrite = false } = options;
|
|
126
162
|
const targetPath = path.join(targetDir, relativePath);
|
|
127
163
|
const templatePath = path.join(getTemplatesPath(), relativePath);
|
|
128
|
-
|
|
164
|
+
|
|
129
165
|
if (await fs.pathExists(targetPath)) {
|
|
130
|
-
|
|
166
|
+
if (!overwrite) {
|
|
167
|
+
return { action: 'skipped', details: `File ${relativePath} already exists` };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Overwrite existing file
|
|
171
|
+
await fs.copy(templatePath, targetPath);
|
|
172
|
+
return { action: 'updated', details: `Overwrote ${relativePath} with template` };
|
|
131
173
|
}
|
|
132
|
-
|
|
174
|
+
|
|
133
175
|
// Ensure target directory exists
|
|
134
176
|
await fs.ensureDir(path.dirname(targetPath));
|
|
135
177
|
await fs.copy(templatePath, targetPath);
|
|
136
|
-
|
|
178
|
+
|
|
137
179
|
return { action: 'created', details: `Created ${relativePath}` };
|
|
138
180
|
}
|
package/src/prompt.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prompts the user for a yes/no answer.
|
|
5
|
+
* @param {string} question - The question to ask
|
|
6
|
+
* @param {boolean} defaultAnswer - The default answer if user presses Enter or if non-TTY
|
|
7
|
+
* @returns {Promise<boolean>} - True for yes, false for no
|
|
8
|
+
*/
|
|
9
|
+
export async function promptYesNo(question, defaultAnswer) {
|
|
10
|
+
// If not TTY, return default immediately
|
|
11
|
+
if (!process.stdin.isTTY) {
|
|
12
|
+
return defaultAnswer;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const rl = readline.createInterface({
|
|
16
|
+
input: process.stdin,
|
|
17
|
+
output: process.stdout
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const defaultHint = defaultAnswer ? 'Y/n' : 'y/N';
|
|
22
|
+
rl.question(`${question} (${defaultHint}): `, (answer) => {
|
|
23
|
+
rl.close();
|
|
24
|
+
|
|
25
|
+
const normalized = answer.trim().toLowerCase();
|
|
26
|
+
|
|
27
|
+
// Empty answer uses default
|
|
28
|
+
if (normalized === '') {
|
|
29
|
+
resolve(defaultAnswer);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Accept y, yes, n, no
|
|
34
|
+
if (normalized === 'y' || normalized === 'yes') {
|
|
35
|
+
resolve(true);
|
|
36
|
+
} else if (normalized === 'n' || normalized === 'no') {
|
|
37
|
+
resolve(false);
|
|
38
|
+
} else {
|
|
39
|
+
// Invalid input, use default
|
|
40
|
+
resolve(defaultAnswer);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|