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.
- package/CHANGELOG.md +76 -0
- package/README.md +64 -23
- package/bin/claude-setup.js +118 -38
- package/package.json +67 -19
- package/src/cli/index.js +30 -0
- package/src/cli/interfaces.js +386 -0
- package/src/cli/parser.js +158 -0
- package/src/cli/prompts.js +317 -0
- package/src/cli/runner.js +436 -0
- package/src/fetcher/github.js +1092 -0
- package/src/index.js +221 -0
- package/src/installer/dependency-check.js +19 -0
- package/src/installer/file-writer.js +648 -0
- package/src/orchestrator/installer.js +875 -0
- package/src/orchestrator/main.js +267 -0
- package/src/orchestrator/mocks/index.js +388 -0
- package/src/utils/config.js +430 -0
- package/src/utils/constants.js +397 -0
- package/src/utils/errors.js +361 -0
- package/src/utils/index.js +46 -0
- package/src/utils/logger.js +177 -0
- package/src/utils/platform.js +362 -0
- package/src/utils/retry.js +358 -0
- package/templates/settings.local.json +1 -0
- package/.claude/hooks/comms/messages_UmaMatrix.json +0 -10
- package/.claude/hooks/comms/messages_VictorSpiral.json +0 -10
- package/.claude/hooks/comms/messages_WendyQuantum.json +0 -10
- package/.claude/hooks/comms/messages_XavierPrism.json +0 -10
- package/templates/get_unread_messages.py +0 -54
- package/templates/send_message.py +0 -75
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Runner - Main orchestrator for @claude-code/setup-installer
|
|
3
|
+
*
|
|
4
|
+
* Coordinates the complete installation flow:
|
|
5
|
+
* - CLI argument parsing
|
|
6
|
+
* - User interaction prompts
|
|
7
|
+
* - Progress indicators
|
|
8
|
+
* - Mock GitHub operations (for early development)
|
|
9
|
+
* - File installation coordination
|
|
10
|
+
* - Error handling and recovery
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const ora = require('ora');
|
|
14
|
+
const chalk = require('chalk');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const fs = require('fs').promises;
|
|
17
|
+
|
|
18
|
+
const { parseArguments, displayHelp } = require('./parser');
|
|
19
|
+
const { runInstallationPrompts, setupInterruptHandler } = require('./prompts');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Main CLI entry point
|
|
23
|
+
* @param {string[]} argv - Command line arguments
|
|
24
|
+
* @returns {Promise<void>}
|
|
25
|
+
*/
|
|
26
|
+
async function run(argv = process.argv) {
|
|
27
|
+
setupInterruptHandler();
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// Parse command line arguments
|
|
31
|
+
const parseResult = parseArguments(argv);
|
|
32
|
+
|
|
33
|
+
if (!parseResult.success) {
|
|
34
|
+
console.error(chalk.red(`Error: ${parseResult.error}`));
|
|
35
|
+
displayHelp(parseResult.command);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { options } = parseResult;
|
|
40
|
+
|
|
41
|
+
// Show help if requested
|
|
42
|
+
if (options.help) {
|
|
43
|
+
displayHelp(parseResult.command);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Show version if requested
|
|
48
|
+
if (options.version) {
|
|
49
|
+
console.log('1.0.0');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Run installation flow
|
|
54
|
+
await runInstallationFlow(options);
|
|
55
|
+
|
|
56
|
+
} catch (error) {
|
|
57
|
+
handleUnexpectedError(error);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Main installation flow orchestrator
|
|
64
|
+
* @param {Object} options - Parsed CLI options
|
|
65
|
+
*/
|
|
66
|
+
async function runInstallationFlow(options) {
|
|
67
|
+
console.log(chalk.cyan.bold('š Claude Code Setup Installer v1.0.0'));
|
|
68
|
+
console.log(chalk.gray('Installing multi-agent orchestration setup\n'));
|
|
69
|
+
|
|
70
|
+
if (options.dryRun) {
|
|
71
|
+
console.log(chalk.yellow('š DRY RUN MODE - No files will be modified\n'));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Step 1: User interaction and confirmation
|
|
75
|
+
const promptResult = await runInstallationPrompts(options);
|
|
76
|
+
|
|
77
|
+
if (!promptResult.confirmed) {
|
|
78
|
+
console.log(chalk.yellow('\nā Installation cancelled'));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Step 2: Pre-installation checks
|
|
83
|
+
await runPreInstallationChecks(promptResult);
|
|
84
|
+
|
|
85
|
+
// Step 3: Fetch files (mocked for now)
|
|
86
|
+
const fetchedFiles = await fetchInstallationFiles(promptResult, options);
|
|
87
|
+
|
|
88
|
+
// Step 4: Install files
|
|
89
|
+
await installFiles(fetchedFiles, promptResult, options);
|
|
90
|
+
|
|
91
|
+
// Step 5: Post-installation tasks
|
|
92
|
+
await runPostInstallationTasks(promptResult, options);
|
|
93
|
+
|
|
94
|
+
// Step 6: Success message
|
|
95
|
+
displaySuccessMessage(promptResult);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Run pre-installation validation checks
|
|
100
|
+
* @param {Object} promptResult - User prompt responses
|
|
101
|
+
*/
|
|
102
|
+
async function runPreInstallationChecks(promptResult) {
|
|
103
|
+
const spinner = ora('Running pre-installation checks...').start();
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
// Check target directory
|
|
107
|
+
await validateTargetDirectory(promptResult.targetDir);
|
|
108
|
+
spinner.text = 'Validating target directory...';
|
|
109
|
+
|
|
110
|
+
// Check Python dependencies if requested
|
|
111
|
+
if (promptResult.pythonCheck) {
|
|
112
|
+
await checkPythonDependencies();
|
|
113
|
+
spinner.text = 'Checking Python dependencies...';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Check disk space
|
|
117
|
+
await checkDiskSpace(promptResult.targetDir);
|
|
118
|
+
spinner.text = 'Checking available disk space...';
|
|
119
|
+
|
|
120
|
+
spinner.succeed('Pre-installation checks completed');
|
|
121
|
+
} catch (error) {
|
|
122
|
+
spinner.fail(`Pre-installation check failed: ${error.message}`);
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Fetch installation files (MOCKED for early development)
|
|
129
|
+
* @param {Object} promptResult - User prompt responses
|
|
130
|
+
* @param {Object} options - CLI options
|
|
131
|
+
* @returns {Promise<Array>} Fetched files data
|
|
132
|
+
*/
|
|
133
|
+
async function fetchInstallationFiles(promptResult, options) {
|
|
134
|
+
const spinner = ora('Fetching files from GitHub...').start();
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
// MOCK: Simulate fetching from GitHub
|
|
138
|
+
spinner.text = 'Connecting to GitHub API...';
|
|
139
|
+
await mockDelay(1000);
|
|
140
|
+
|
|
141
|
+
spinner.text = 'Fetching .claude directory structure...';
|
|
142
|
+
await mockDelay(1500);
|
|
143
|
+
|
|
144
|
+
spinner.text = 'Downloading hooks and agents...';
|
|
145
|
+
await mockDelay(2000);
|
|
146
|
+
|
|
147
|
+
spinner.text = 'Downloading configuration files...';
|
|
148
|
+
await mockDelay(1000);
|
|
149
|
+
|
|
150
|
+
// MOCK: Return simulated file structure
|
|
151
|
+
const mockFiles = generateMockFileStructure(options.tag);
|
|
152
|
+
|
|
153
|
+
spinner.succeed(`Fetched ${mockFiles.length} files from ${options.tag}`);
|
|
154
|
+
|
|
155
|
+
if (options.verbose) {
|
|
156
|
+
console.log(chalk.gray('Files to install:'));
|
|
157
|
+
mockFiles.forEach(file => {
|
|
158
|
+
console.log(chalk.gray(` ⢠${file.path}`));
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return mockFiles;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
spinner.fail(`Failed to fetch files: ${error.message}`);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Install fetched files to target directory
|
|
171
|
+
* @param {Array} files - Files to install
|
|
172
|
+
* @param {Object} promptResult - User prompt responses
|
|
173
|
+
* @param {Object} options - CLI options
|
|
174
|
+
*/
|
|
175
|
+
async function installFiles(files, promptResult, options) {
|
|
176
|
+
const spinner = ora('Installing files...').start();
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
let installed = 0;
|
|
180
|
+
let skipped = 0;
|
|
181
|
+
let backed_up = 0;
|
|
182
|
+
|
|
183
|
+
for (const file of files) {
|
|
184
|
+
const targetPath = path.join(promptResult.targetDir, file.path);
|
|
185
|
+
|
|
186
|
+
spinner.text = `Installing ${file.path}...`;
|
|
187
|
+
|
|
188
|
+
if (options.dryRun) {
|
|
189
|
+
console.log(chalk.gray(`[DRY RUN] Would install: ${file.path}`));
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Handle existing files based on strategy
|
|
194
|
+
const exists = await fileExists(targetPath);
|
|
195
|
+
if (exists) {
|
|
196
|
+
switch (promptResult.overwriteStrategy) {
|
|
197
|
+
case 'skip':
|
|
198
|
+
skipped++;
|
|
199
|
+
continue;
|
|
200
|
+
case 'backup':
|
|
201
|
+
await createBackup(targetPath);
|
|
202
|
+
backed_up++;
|
|
203
|
+
break;
|
|
204
|
+
case 'overwrite':
|
|
205
|
+
// Will overwrite below
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Create directory if needed
|
|
211
|
+
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
212
|
+
|
|
213
|
+
// Write file content (MOCKED)
|
|
214
|
+
await fs.writeFile(targetPath, file.content, { mode: file.mode });
|
|
215
|
+
installed++;
|
|
216
|
+
|
|
217
|
+
// Show progress for verbose mode
|
|
218
|
+
if (options.verbose) {
|
|
219
|
+
console.log(chalk.green(` ā ${file.path}`));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const message = `Installed ${installed} files` +
|
|
224
|
+
(skipped > 0 ? `, skipped ${skipped}` : '') +
|
|
225
|
+
(backed_up > 0 ? `, backed up ${backed_up}` : '');
|
|
226
|
+
|
|
227
|
+
spinner.succeed(message);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
spinner.fail(`Installation failed: ${error.message}`);
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Run post-installation tasks
|
|
236
|
+
* @param {Object} promptResult - User prompt responses
|
|
237
|
+
* @param {Object} options - CLI options
|
|
238
|
+
*/
|
|
239
|
+
async function runPostInstallationTasks(promptResult, _options) {
|
|
240
|
+
const spinner = ora('Completing setup...').start();
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
// Generate settings.local.json
|
|
244
|
+
await generateSettingsFile(promptResult);
|
|
245
|
+
spinner.text = 'Creating settings.local.json...';
|
|
246
|
+
|
|
247
|
+
// Set executable permissions on hooks
|
|
248
|
+
await setExecutablePermissions(promptResult.targetDir);
|
|
249
|
+
spinner.text = 'Setting executable permissions...';
|
|
250
|
+
|
|
251
|
+
// Validate installation
|
|
252
|
+
await validateInstallation(promptResult.targetDir);
|
|
253
|
+
spinner.text = 'Validating installation...';
|
|
254
|
+
|
|
255
|
+
spinner.succeed('Setup completed successfully');
|
|
256
|
+
} catch (error) {
|
|
257
|
+
spinner.fail(`Post-installation failed: ${error.message}`);
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Display success message and next steps
|
|
264
|
+
* @param {Object} promptResult - User prompt responses
|
|
265
|
+
*/
|
|
266
|
+
function displaySuccessMessage(_promptResult) {
|
|
267
|
+
console.log(chalk.green.bold('\nā
Installation Complete!'));
|
|
268
|
+
console.log(chalk.gray('\nClaude multi-agent orchestration setup has been installed.'));
|
|
269
|
+
|
|
270
|
+
console.log(chalk.blue('\nš Next Steps:'));
|
|
271
|
+
console.log(chalk.gray('1. Review .claude/settings.local.json for configuration'));
|
|
272
|
+
console.log(chalk.gray('2. Ensure Python and uv are installed for hooks functionality'));
|
|
273
|
+
console.log(chalk.gray('3. Start the observability server (optional)'));
|
|
274
|
+
console.log(chalk.gray('4. Open your project in Claude Code'));
|
|
275
|
+
|
|
276
|
+
console.log(chalk.blue('\nš Resources:'));
|
|
277
|
+
console.log(chalk.gray('⢠Documentation: /docs/project/guides/installation-guide.md'));
|
|
278
|
+
console.log(chalk.gray('⢠Troubleshooting: /docs/project/guides/troubleshooting.md'));
|
|
279
|
+
console.log(chalk.gray('⢠GitHub: https://github.com/alexsavage/claude-code-hooks-multi-agent-observability'));
|
|
280
|
+
|
|
281
|
+
console.log(chalk.cyan('\nHappy coding with Claude! š¤\n'));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// ============================================================================
|
|
285
|
+
// MOCK IMPLEMENTATIONS (Replace with real implementations in future WPs)
|
|
286
|
+
// ============================================================================
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Generate mock file structure for testing
|
|
290
|
+
* @param {string} tag - Git tag/branch
|
|
291
|
+
* @returns {Array} Mock file structure
|
|
292
|
+
*/
|
|
293
|
+
function generateMockFileStructure(_tag) {
|
|
294
|
+
return [
|
|
295
|
+
{
|
|
296
|
+
path: '.claude/settings.json',
|
|
297
|
+
content: JSON.stringify({
|
|
298
|
+
version: '1.0.0',
|
|
299
|
+
observability_server: 'http://localhost:4000',
|
|
300
|
+
hooks_enabled: true
|
|
301
|
+
}, null, 2),
|
|
302
|
+
mode: 0o644
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
path: '.claude/hooks/comms/send_message.py',
|
|
306
|
+
content: '#!/usr/bin/env python3\n# Mock hook for team communication\nprint("Mock hook: send_message")\n',
|
|
307
|
+
mode: 0o755
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
path: '.claude/hooks/comms/get_unread_messages.py',
|
|
311
|
+
content: '#!/usr/bin/env python3\n# Mock hook for message retrieval\nprint("Mock hook: get_unread_messages")\n',
|
|
312
|
+
mode: 0o755
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
path: '.claude/agents/engineer.md',
|
|
316
|
+
content: '# Engineer Agent\n\nImplementation specialist for code development.\n',
|
|
317
|
+
mode: 0o644
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
path: '.claude/agents/architect.md',
|
|
321
|
+
content: '# Architect Agent\n\nSystem design and architecture specialist.\n',
|
|
322
|
+
mode: 0o644
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
path: 'CLAUDE.md',
|
|
326
|
+
content: '# Claude Code Setup\n\nThis project has been configured for Claude multi-agent orchestration.\n',
|
|
327
|
+
mode: 0o644
|
|
328
|
+
}
|
|
329
|
+
];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ============================================================================
|
|
333
|
+
// UTILITY FUNCTIONS
|
|
334
|
+
// ============================================================================
|
|
335
|
+
|
|
336
|
+
async function mockDelay(ms) {
|
|
337
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async function validateTargetDirectory(targetDir) {
|
|
341
|
+
try {
|
|
342
|
+
await fs.access(targetDir);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function checkPythonDependencies() {
|
|
349
|
+
// MOCK: In real implementation, check for python and uv
|
|
350
|
+
await mockDelay(500);
|
|
351
|
+
// For now, assume they exist
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function checkDiskSpace(_targetDir) {
|
|
355
|
+
// MOCK: In real implementation, check available disk space
|
|
356
|
+
await mockDelay(300);
|
|
357
|
+
// Assume sufficient space
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function fileExists(filePath) {
|
|
361
|
+
try {
|
|
362
|
+
await fs.access(filePath);
|
|
363
|
+
return true;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async function createBackup(filePath) {
|
|
370
|
+
const backupPath = `${filePath}.backup-${Date.now()}`;
|
|
371
|
+
await fs.copyFile(filePath, backupPath);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function generateSettingsFile(promptResult) {
|
|
375
|
+
const settingsPath = path.join(promptResult.targetDir, '.claude/settings.local.json');
|
|
376
|
+
const settings = {
|
|
377
|
+
observability_server: promptResult.configUrl,
|
|
378
|
+
installation_date: new Date().toISOString(),
|
|
379
|
+
installation_mode: 'npx-installer'
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
383
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function setExecutablePermissions(targetDir) {
|
|
387
|
+
const hookFiles = [
|
|
388
|
+
'.claude/hooks/comms/send_message.py',
|
|
389
|
+
'.claude/hooks/comms/get_unread_messages.py'
|
|
390
|
+
];
|
|
391
|
+
|
|
392
|
+
for (const hookFile of hookFiles) {
|
|
393
|
+
const hookPath = path.join(targetDir, hookFile);
|
|
394
|
+
try {
|
|
395
|
+
await fs.chmod(hookPath, 0o755);
|
|
396
|
+
} catch (error) {
|
|
397
|
+
// Hook file may not exist, continue
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function validateInstallation(targetDir) {
|
|
403
|
+
const requiredFiles = [
|
|
404
|
+
'.claude/settings.json',
|
|
405
|
+
'CLAUDE.md'
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
for (const file of requiredFiles) {
|
|
409
|
+
const filePath = path.join(targetDir, file);
|
|
410
|
+
await fs.access(filePath);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function handleUnexpectedError(error) {
|
|
415
|
+
console.error(chalk.red('\nš„ Unexpected error occurred:'));
|
|
416
|
+
console.error(chalk.red(error.message));
|
|
417
|
+
|
|
418
|
+
if (process.env.NODE_ENV === 'development') {
|
|
419
|
+
console.error(chalk.gray('\nStack trace:'));
|
|
420
|
+
console.error(chalk.gray(error.stack));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
console.error(chalk.yellow('\nPlease report this issue at:'));
|
|
424
|
+
console.error(chalk.blue('https://github.com/alexsavage/claude-code-hooks-multi-agent-observability/issues'));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
module.exports = {
|
|
428
|
+
run,
|
|
429
|
+
runInstallationFlow,
|
|
430
|
+
runPreInstallationChecks,
|
|
431
|
+
fetchInstallationFiles,
|
|
432
|
+
installFiles,
|
|
433
|
+
runPostInstallationTasks,
|
|
434
|
+
displaySuccessMessage,
|
|
435
|
+
generateMockFileStructure
|
|
436
|
+
};
|