claude-comms 1.0.1 → 1.0.2
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 +53 -0
- package/README.md +64 -23
- package/bin/claude-setup.js +118 -38
- package/package.json +65 -18
- 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 +676 -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 +849 -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,430 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration management for NPX installer
|
|
3
|
+
* Handles default settings, user preferences, and environment-based configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs').promises;
|
|
8
|
+
const { ErrorFactory } = require('./errors');
|
|
9
|
+
const platform = require('./platform');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Configuration manager with defaults, validation, and persistence
|
|
13
|
+
*/
|
|
14
|
+
class Config {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.configPath = options.configPath || null;
|
|
17
|
+
this.defaults = this.getDefaults();
|
|
18
|
+
this.config = { ...this.defaults };
|
|
19
|
+
this.loaded = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get default configuration values
|
|
24
|
+
*/
|
|
25
|
+
getDefaults() {
|
|
26
|
+
return {
|
|
27
|
+
// Repository settings
|
|
28
|
+
repository: {
|
|
29
|
+
owner: 'alexsavage',
|
|
30
|
+
name: 'claude-code-hooks-multi-agent-observability',
|
|
31
|
+
branch: 'main',
|
|
32
|
+
baseUrl: 'https://api.github.com',
|
|
33
|
+
rawUrl: 'https://raw.githubusercontent.com'
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Installation settings
|
|
37
|
+
installation: {
|
|
38
|
+
targetDir: process.cwd(),
|
|
39
|
+
createBackup: true,
|
|
40
|
+
overwriteExisting: false,
|
|
41
|
+
skipExistingFiles: false,
|
|
42
|
+
preservePermissions: true,
|
|
43
|
+
createDirectories: true
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
// GitHub API settings
|
|
47
|
+
github: {
|
|
48
|
+
token: null,
|
|
49
|
+
timeout: 30000,
|
|
50
|
+
retryAttempts: 3,
|
|
51
|
+
retryDelay: 1000,
|
|
52
|
+
userAgent: '@claude-code/setup-installer'
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// File operations
|
|
56
|
+
files: {
|
|
57
|
+
excludePatterns: ['.git', 'node_modules', '.DS_Store'],
|
|
58
|
+
includePatterns: ['.claude/**/*', 'CLAUDE.md'],
|
|
59
|
+
encoding: 'utf8',
|
|
60
|
+
lineEnding: 'auto', // auto, lf, crlf
|
|
61
|
+
maxFileSize: 10 * 1024 * 1024 // 10MB
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// Logging settings
|
|
65
|
+
logging: {
|
|
66
|
+
level: 'INFO',
|
|
67
|
+
silent: false,
|
|
68
|
+
timestamps: false,
|
|
69
|
+
colors: true
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// Dependency checking
|
|
73
|
+
dependencies: {
|
|
74
|
+
checkPython: true,
|
|
75
|
+
checkUv: true,
|
|
76
|
+
pythonVersion: '3.8',
|
|
77
|
+
uvVersion: '0.1.0'
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
// Performance settings
|
|
81
|
+
performance: {
|
|
82
|
+
concurrentDownloads: 5,
|
|
83
|
+
chunkSize: 64 * 1024, // 64KB
|
|
84
|
+
progressUpdateInterval: 100,
|
|
85
|
+
enableCache: true,
|
|
86
|
+
cacheExpiry: 3600000 // 1 hour
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// Security settings
|
|
90
|
+
security: {
|
|
91
|
+
validateChecksums: false,
|
|
92
|
+
allowUnsafeOperations: false,
|
|
93
|
+
restrictPaths: true,
|
|
94
|
+
maxPathDepth: 10
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Load configuration from file
|
|
101
|
+
*/
|
|
102
|
+
async load(configPath = null) {
|
|
103
|
+
const targetPath = configPath || this.configPath;
|
|
104
|
+
|
|
105
|
+
if (!targetPath) {
|
|
106
|
+
this.loaded = true;
|
|
107
|
+
return this.config;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const content = await fs.readFile(targetPath, 'utf8');
|
|
112
|
+
const fileConfig = JSON.parse(content);
|
|
113
|
+
|
|
114
|
+
// Merge with defaults
|
|
115
|
+
this.config = this.mergeDeep(this.defaults, fileConfig);
|
|
116
|
+
this.loaded = true;
|
|
117
|
+
|
|
118
|
+
// Validate configuration
|
|
119
|
+
this.validate();
|
|
120
|
+
|
|
121
|
+
return this.config;
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error.code === 'ENOENT') {
|
|
124
|
+
// File doesn't exist, use defaults
|
|
125
|
+
this.loaded = true;
|
|
126
|
+
return this.config;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw ErrorFactory.configuration(
|
|
130
|
+
`Failed to load configuration from ${targetPath}: ${error.message}`,
|
|
131
|
+
'configFile',
|
|
132
|
+
{ path: targetPath, originalError: error }
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Save configuration to file
|
|
139
|
+
*/
|
|
140
|
+
async save(configPath = null) {
|
|
141
|
+
const targetPath = configPath || this.configPath;
|
|
142
|
+
|
|
143
|
+
if (!targetPath) {
|
|
144
|
+
throw ErrorFactory.configuration('No configuration file path specified');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
// Create directory if needed
|
|
149
|
+
const dir = path.dirname(targetPath);
|
|
150
|
+
await fs.mkdir(dir, { recursive: true });
|
|
151
|
+
|
|
152
|
+
// Save configuration (exclude defaults to keep file clean)
|
|
153
|
+
const configToSave = this.getDifferences();
|
|
154
|
+
const content = JSON.stringify(configToSave, null, 2);
|
|
155
|
+
await fs.writeFile(targetPath, content, 'utf8');
|
|
156
|
+
|
|
157
|
+
return targetPath;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
throw ErrorFactory.filesystem(
|
|
160
|
+
`Failed to save configuration to ${targetPath}: ${error.message}`,
|
|
161
|
+
{ path: targetPath, originalError: error }
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get configuration value with dot notation support
|
|
168
|
+
*/
|
|
169
|
+
get(key, defaultValue = undefined) {
|
|
170
|
+
const keys = key.split('.');
|
|
171
|
+
let value = this.config;
|
|
172
|
+
|
|
173
|
+
for (const k of keys) {
|
|
174
|
+
if (value && typeof value === 'object' && k in value) {
|
|
175
|
+
value = value[k];
|
|
176
|
+
} else {
|
|
177
|
+
return defaultValue;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return value;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Set configuration value with dot notation support
|
|
186
|
+
*/
|
|
187
|
+
set(key, value) {
|
|
188
|
+
const keys = key.split('.');
|
|
189
|
+
const lastKey = keys.pop();
|
|
190
|
+
let target = this.config;
|
|
191
|
+
|
|
192
|
+
// Navigate to parent object
|
|
193
|
+
for (const k of keys) {
|
|
194
|
+
if (!(k in target) || typeof target[k] !== 'object') {
|
|
195
|
+
target[k] = {};
|
|
196
|
+
}
|
|
197
|
+
target = target[k];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Set value
|
|
201
|
+
target[lastKey] = value;
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Update configuration with partial object
|
|
207
|
+
*/
|
|
208
|
+
update(updates) {
|
|
209
|
+
this.config = this.mergeDeep(this.config, updates);
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Reset configuration to defaults
|
|
215
|
+
*/
|
|
216
|
+
reset() {
|
|
217
|
+
this.config = { ...this.defaults };
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Get differences from defaults (for saving)
|
|
223
|
+
*/
|
|
224
|
+
getDifferences() {
|
|
225
|
+
return this.getDifferencesBetween(this.defaults, this.config);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get differences between two objects
|
|
230
|
+
*/
|
|
231
|
+
getDifferencesBetween(base, current, result = {}) {
|
|
232
|
+
for (const key in current) {
|
|
233
|
+
if (!(key in base)) {
|
|
234
|
+
// New key
|
|
235
|
+
result[key] = current[key];
|
|
236
|
+
} else if (typeof current[key] === 'object' && current[key] !== null && !Array.isArray(current[key])) {
|
|
237
|
+
// Nested object
|
|
238
|
+
const nested = this.getDifferencesBetween(base[key], current[key]);
|
|
239
|
+
if (Object.keys(nested).length > 0) {
|
|
240
|
+
result[key] = nested;
|
|
241
|
+
}
|
|
242
|
+
} else if (JSON.stringify(current[key]) !== JSON.stringify(base[key])) {
|
|
243
|
+
// Different value
|
|
244
|
+
result[key] = current[key];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Deep merge objects
|
|
252
|
+
*/
|
|
253
|
+
mergeDeep(target, source) {
|
|
254
|
+
const result = { ...target };
|
|
255
|
+
|
|
256
|
+
for (const key in source) {
|
|
257
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
258
|
+
result[key] = this.mergeDeep(result[key] || {}, source[key]);
|
|
259
|
+
} else {
|
|
260
|
+
result[key] = source[key];
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Validate configuration values
|
|
269
|
+
*/
|
|
270
|
+
validate() {
|
|
271
|
+
const errors = [];
|
|
272
|
+
|
|
273
|
+
// Validate repository settings
|
|
274
|
+
if (!this.config.repository.owner) {
|
|
275
|
+
errors.push('repository.owner is required');
|
|
276
|
+
}
|
|
277
|
+
if (!this.config.repository.name) {
|
|
278
|
+
errors.push('repository.name is required');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Validate installation settings
|
|
282
|
+
if (!this.config.installation.targetDir) {
|
|
283
|
+
errors.push('installation.targetDir is required');
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Validate GitHub settings
|
|
287
|
+
if (this.config.github.timeout < 1000) {
|
|
288
|
+
errors.push('github.timeout must be at least 1000ms');
|
|
289
|
+
}
|
|
290
|
+
if (this.config.github.retryAttempts < 0 || this.config.github.retryAttempts > 10) {
|
|
291
|
+
errors.push('github.retryAttempts must be between 0 and 10');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Validate file settings
|
|
295
|
+
if (this.config.files.maxFileSize < 1024) {
|
|
296
|
+
errors.push('files.maxFileSize must be at least 1KB');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Validate performance settings
|
|
300
|
+
if (this.config.performance.concurrentDownloads < 1 || this.config.performance.concurrentDownloads > 20) {
|
|
301
|
+
errors.push('performance.concurrentDownloads must be between 1 and 20');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (errors.length > 0) {
|
|
305
|
+
throw ErrorFactory.validation(
|
|
306
|
+
`Configuration validation failed: ${errors.join(', ')}`,
|
|
307
|
+
'config',
|
|
308
|
+
{ errors }
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Apply environment variable overrides
|
|
315
|
+
*/
|
|
316
|
+
applyEnvironmentOverrides() {
|
|
317
|
+
const envMappings = {
|
|
318
|
+
'CLAUDE_GITHUB_TOKEN': 'github.token',
|
|
319
|
+
'CLAUDE_TARGET_DIR': 'installation.targetDir',
|
|
320
|
+
'CLAUDE_LOG_LEVEL': 'logging.level',
|
|
321
|
+
'CLAUDE_SKIP_DEPS': 'dependencies.checkPython',
|
|
322
|
+
'CLAUDE_REPO_BRANCH': 'repository.branch'
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
for (const [envVar, configKey] of Object.entries(envMappings)) {
|
|
326
|
+
const value = process.env[envVar];
|
|
327
|
+
if (value !== undefined) {
|
|
328
|
+
// Convert string values appropriately
|
|
329
|
+
let convertedValue = value;
|
|
330
|
+
if (value === 'true') convertedValue = true;
|
|
331
|
+
else if (value === 'false') convertedValue = false;
|
|
332
|
+
else if (/^\d+$/.test(value)) convertedValue = parseInt(value, 10);
|
|
333
|
+
|
|
334
|
+
this.set(configKey, convertedValue);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return this;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Get configuration summary for debugging
|
|
343
|
+
*/
|
|
344
|
+
getSummary() {
|
|
345
|
+
return {
|
|
346
|
+
loaded: this.loaded,
|
|
347
|
+
configPath: this.configPath,
|
|
348
|
+
repository: `${this.config.repository.owner}/${this.config.repository.name}@${this.config.repository.branch}`,
|
|
349
|
+
targetDir: this.config.installation.targetDir,
|
|
350
|
+
logLevel: this.config.logging.level,
|
|
351
|
+
hasGitHubToken: !!this.config.github.token,
|
|
352
|
+
differences: Object.keys(this.getDifferences()).length
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Create default configuration file template
|
|
358
|
+
*/
|
|
359
|
+
static createTemplate(filePath) {
|
|
360
|
+
const template = {
|
|
361
|
+
$schema: 'https://raw.githubusercontent.com/alexsavage/claude-code-hooks-multi-agent-observability/main/packages/setup-installer/config-schema.json',
|
|
362
|
+
repository: {
|
|
363
|
+
branch: 'main'
|
|
364
|
+
},
|
|
365
|
+
installation: {
|
|
366
|
+
createBackup: true,
|
|
367
|
+
overwriteExisting: false
|
|
368
|
+
},
|
|
369
|
+
github: {
|
|
370
|
+
token: '${GITHUB_TOKEN}'
|
|
371
|
+
},
|
|
372
|
+
logging: {
|
|
373
|
+
level: 'INFO'
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
path: filePath,
|
|
379
|
+
content: JSON.stringify(template, null, 2),
|
|
380
|
+
instructions: [
|
|
381
|
+
'Edit this file to customize the installation',
|
|
382
|
+
'Set GITHUB_TOKEN environment variable for API rate limits',
|
|
383
|
+
'Use \'auto\' for platform-specific defaults',
|
|
384
|
+
'Remove unused sections to keep defaults'
|
|
385
|
+
]
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Create a configured instance with common patterns
|
|
392
|
+
*/
|
|
393
|
+
function createConfig(options = {}) {
|
|
394
|
+
// Determine config file path
|
|
395
|
+
let configPath = options.configPath;
|
|
396
|
+
|
|
397
|
+
if (!configPath && options.useGlobal) {
|
|
398
|
+
configPath = path.join(platform.getConfigDir(), 'installer.json');
|
|
399
|
+
} else if (!configPath && options.useLocal) {
|
|
400
|
+
configPath = path.join(process.cwd(), '.claude-installer.json');
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const config = new Config({ configPath });
|
|
404
|
+
|
|
405
|
+
// Apply environment overrides if requested
|
|
406
|
+
if (options.useEnvironment !== false) {
|
|
407
|
+
config.applyEnvironmentOverrides();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return config;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Quick access to common configuration patterns
|
|
415
|
+
*/
|
|
416
|
+
const configUtils = {
|
|
417
|
+
// Create configurations
|
|
418
|
+
create: (options) => createConfig(options),
|
|
419
|
+
createGlobal: () => createConfig({ useGlobal: true }),
|
|
420
|
+
createLocal: () => createConfig({ useLocal: true }),
|
|
421
|
+
createMemory: () => new Config(),
|
|
422
|
+
|
|
423
|
+
// Template utilities
|
|
424
|
+
createTemplate: (filePath) => Config.createTemplate(filePath),
|
|
425
|
+
|
|
426
|
+
// Direct access to Config class
|
|
427
|
+
Config
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
module.exports = configUtils;
|