claude-comms 1.0.1 → 1.0.3

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.
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Interactive Prompts for @claude-code/setup-installer
3
+ *
4
+ * Handles user interaction through prompts for:
5
+ * - Installation confirmation
6
+ * - Directory selection
7
+ * - Overwrite warnings
8
+ * - Configuration options
9
+ */
10
+
11
+ const prompts = require('prompts');
12
+ const path = require('path');
13
+ const fs = require('fs').promises;
14
+ const chalk = require('chalk');
15
+
16
+ /**
17
+ * Main prompt orchestrator for installation flow
18
+ * @param {Object} options - Parsed CLI options
19
+ * @returns {Promise<Object>} User responses and validated settings
20
+ */
21
+ async function runInstallationPrompts(options) {
22
+ const responses = {};
23
+
24
+ console.log(chalk.cyan('🚀 Claude Code Setup Installer'));
25
+ console.log(chalk.gray('Setting up multi-agent orchestration in your project\n'));
26
+
27
+ // Skip prompts if force mode
28
+ if (options.force) {
29
+ return {
30
+ confirmed: true,
31
+ targetDir: options.targetDir,
32
+ overwriteStrategy: 'overwrite',
33
+ configUrl: options.configUrl || 'http://localhost:4000',
34
+ pythonCheck: options.pythonCheck
35
+ };
36
+ }
37
+
38
+ // Confirm installation location
39
+ const dirConfirmation = await confirmTargetDirectory(options.targetDir);
40
+ if (!dirConfirmation.confirmed) {
41
+ return { confirmed: false, cancelled: true };
42
+ }
43
+ responses.targetDir = dirConfirmation.targetDir;
44
+
45
+ // Check for existing files
46
+ const existingFiles = await checkExistingFiles(responses.targetDir);
47
+ if (existingFiles.hasConflicts) {
48
+ const overwriteResponse = await handleExistingFiles(existingFiles);
49
+ if (!overwriteResponse.confirmed) {
50
+ return { confirmed: false, cancelled: true };
51
+ }
52
+ responses.overwriteStrategy = overwriteResponse.strategy;
53
+ } else {
54
+ responses.overwriteStrategy = 'none';
55
+ }
56
+
57
+ // Configuration options
58
+ const configResponse = await promptConfigurationOptions(options);
59
+ responses.configUrl = configResponse.configUrl;
60
+ responses.pythonCheck = configResponse.pythonCheck;
61
+
62
+ // Final confirmation
63
+ const finalConfirmation = await promptFinalConfirmation(responses);
64
+ if (!finalConfirmation) {
65
+ return { confirmed: false, cancelled: true };
66
+ }
67
+
68
+ return {
69
+ confirmed: true,
70
+ ...responses
71
+ };
72
+ }
73
+
74
+ /**
75
+ * Confirm target directory for installation
76
+ * @param {string} defaultDir - Default directory path
77
+ * @returns {Promise<Object>} Directory confirmation result
78
+ */
79
+ async function confirmTargetDirectory(defaultDir) {
80
+ try {
81
+ await fs.access(defaultDir);
82
+ } catch (error) {
83
+ console.log(chalk.yellow(`⚠️ Directory ${defaultDir} does not exist.`));
84
+
85
+ const createResponse = await prompts({
86
+ type: 'confirm',
87
+ name: 'create',
88
+ message: 'Would you like to create it?',
89
+ initial: true
90
+ });
91
+
92
+ if (!createResponse.create) {
93
+ return { confirmed: false };
94
+ }
95
+ }
96
+
97
+ const dirResponse = await prompts({
98
+ type: 'text',
99
+ name: 'targetDir',
100
+ message: 'Installation directory:',
101
+ initial: defaultDir,
102
+ validate: (value) => {
103
+ if (!value.trim()) return 'Directory path cannot be empty';
104
+ return true;
105
+ }
106
+ });
107
+
108
+ if (!dirResponse.targetDir) {
109
+ return { confirmed: false };
110
+ }
111
+
112
+ return {
113
+ confirmed: true,
114
+ targetDir: path.resolve(dirResponse.targetDir)
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Check for existing files that would conflict
120
+ * @param {string} targetDir - Target installation directory
121
+ * @returns {Promise<Object>} Conflict detection result
122
+ */
123
+ async function checkExistingFiles(targetDir) {
124
+ const conflictFiles = [];
125
+ const checkFiles = [
126
+ '.claude',
127
+ 'CLAUDE.md',
128
+ '.claude/settings.json',
129
+ '.claude/hooks',
130
+ '.claude/agents'
131
+ ];
132
+
133
+ for (const file of checkFiles) {
134
+ const filePath = path.join(targetDir, file);
135
+ try {
136
+ await fs.access(filePath);
137
+ conflictFiles.push(file);
138
+ } catch (error) {
139
+ // File doesn't exist, no conflict
140
+ }
141
+ }
142
+
143
+ return {
144
+ hasConflicts: conflictFiles.length > 0,
145
+ conflictFiles
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Handle existing files through user prompts
151
+ * @param {Object} existingFiles - Existing file detection result
152
+ * @returns {Promise<Object>} User decision on handling conflicts
153
+ */
154
+ async function handleExistingFiles(existingFiles) {
155
+ console.log(chalk.yellow('\n⚠️ Found existing Claude setup files:'));
156
+ existingFiles.conflictFiles.forEach(file => {
157
+ console.log(chalk.gray(` • ${file}`));
158
+ });
159
+
160
+ const strategy = await prompts({
161
+ type: 'select',
162
+ name: 'strategy',
163
+ message: 'How would you like to handle existing files?',
164
+ choices: [
165
+ {
166
+ title: 'Backup and replace',
167
+ description: 'Create .backup files and install fresh setup',
168
+ value: 'backup'
169
+ },
170
+ {
171
+ title: 'Skip existing files',
172
+ description: 'Only install missing files, keep existing ones',
173
+ value: 'skip'
174
+ },
175
+ {
176
+ title: 'Overwrite all',
177
+ description: 'Replace all existing files (no backup)',
178
+ value: 'overwrite'
179
+ },
180
+ {
181
+ title: 'Cancel installation',
182
+ description: 'Exit without making changes',
183
+ value: 'cancel'
184
+ }
185
+ ]
186
+ });
187
+
188
+ if (!strategy.strategy || strategy.strategy === 'cancel') {
189
+ return { confirmed: false };
190
+ }
191
+
192
+ return {
193
+ confirmed: true,
194
+ strategy: strategy.strategy
195
+ };
196
+ }
197
+
198
+ /**
199
+ * Prompt for configuration options
200
+ * @param {Object} options - CLI options
201
+ * @returns {Promise<Object>} Configuration responses
202
+ */
203
+ async function promptConfigurationOptions(options) {
204
+ console.log(chalk.blue('\n⚙️ Configuration Options'));
205
+
206
+ const configUrl = await prompts({
207
+ type: 'text',
208
+ name: 'url',
209
+ message: 'Observability server URL (optional):',
210
+ initial: options.configUrl || 'http://localhost:4000',
211
+ validate: (value) => {
212
+ if (!value.trim()) return true; // Optional field
213
+ try {
214
+ new URL(value);
215
+ return true;
216
+ } catch (error) {
217
+ return 'Please enter a valid URL';
218
+ }
219
+ }
220
+ });
221
+
222
+ const pythonCheck = await prompts({
223
+ type: 'confirm',
224
+ name: 'check',
225
+ message: 'Check for Python and uv dependencies?',
226
+ initial: options.pythonCheck !== false
227
+ });
228
+
229
+ return {
230
+ configUrl: configUrl.url || 'http://localhost:4000',
231
+ pythonCheck: pythonCheck.check
232
+ };
233
+ }
234
+
235
+ /**
236
+ * Final confirmation before installation
237
+ * @param {Object} responses - Collected user responses
238
+ * @returns {Promise<boolean>} Final confirmation
239
+ */
240
+ async function promptFinalConfirmation(responses) {
241
+ console.log(chalk.green('\n📋 Installation Summary:'));
242
+ console.log(chalk.gray(` Directory: ${responses.targetDir}`));
243
+ console.log(chalk.gray(` Overwrite strategy: ${responses.overwriteStrategy}`));
244
+ console.log(chalk.gray(` Config URL: ${responses.configUrl}`));
245
+ console.log(chalk.gray(` Python check: ${responses.pythonCheck ? 'Yes' : 'No'}`));
246
+
247
+ const confirmation = await prompts({
248
+ type: 'confirm',
249
+ name: 'proceed',
250
+ message: 'Proceed with installation?',
251
+ initial: true
252
+ });
253
+
254
+ return confirmation.proceed;
255
+ }
256
+
257
+ /**
258
+ * Handle user interruption (Ctrl+C)
259
+ */
260
+ function setupInterruptHandler() {
261
+ process.on('SIGINT', () => {
262
+ console.log(chalk.yellow('\n\n⚠️ Installation cancelled by user'));
263
+ process.exit(0);
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Prompt for specific installation scenarios
269
+ */
270
+ const scenarios = {
271
+ /**
272
+ * Prompt for force mode confirmation when destructive
273
+ */
274
+ async confirmForceMode(conflictFiles) {
275
+ console.log(chalk.red('\n⚠️ Force mode will overwrite:'));
276
+ conflictFiles.forEach(file => {
277
+ console.log(chalk.gray(` • ${file}`));
278
+ });
279
+
280
+ return await prompts({
281
+ type: 'confirm',
282
+ name: 'proceed',
283
+ message: 'Continue with force installation?',
284
+ initial: false
285
+ });
286
+ },
287
+
288
+ /**
289
+ * Prompt for Python installation if missing
290
+ */
291
+ async promptPythonInstall() {
292
+ console.log(chalk.yellow('\n⚠️ Python or uv not found'));
293
+ console.log(chalk.gray('The Claude hooks require Python and uv to function properly.'));
294
+
295
+ return await prompts({
296
+ type: 'select',
297
+ name: 'action',
298
+ message: 'How would you like to proceed?',
299
+ choices: [
300
+ { title: 'Continue anyway', value: 'continue' },
301
+ { title: 'Open installation guide', value: 'guide' },
302
+ { title: 'Cancel installation', value: 'cancel' }
303
+ ]
304
+ });
305
+ }
306
+ };
307
+
308
+ module.exports = {
309
+ runInstallationPrompts,
310
+ confirmTargetDirectory,
311
+ checkExistingFiles,
312
+ handleExistingFiles,
313
+ promptConfigurationOptions,
314
+ promptFinalConfirmation,
315
+ setupInterruptHandler,
316
+ scenarios
317
+ };