claude-comms 2.0.5 → 2.0.7

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-comms",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "NPX installer for Claude Code workflow engine client setup",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -9,6 +9,8 @@
9
9
  * These interfaces serve as the integration points between CLI and core modules.
10
10
  */
11
11
 
12
+ const { ROOT_FILES } = require('../utils/constants');
13
+
12
14
  /**
13
15
  * GitHub Fetcher Interface
14
16
  * Will be implemented in WP03 - GitHub API Integration
@@ -279,14 +281,14 @@ class MockGitHubFetcher extends GitHubFetcherInterface {
279
281
  encoding: 'utf-8',
280
282
  sha: 'mock-sha-1'
281
283
  },
282
- {
283
- path: 'CLAUDE.md',
284
- content: '# Claude Setup\n\nProject configured for Claude.',
284
+ ...ROOT_FILES.map((name, i) => ({
285
+ path: name,
286
+ content: `# ${name}\n\nProject configured for Claude.`,
285
287
  mode: 0o644,
286
288
  size: 45,
287
289
  encoding: 'utf-8',
288
- sha: 'mock-sha-2'
289
- }
290
+ sha: `mock-sha-${i + 2}`
291
+ }))
290
292
  ],
291
293
  directories: ['.claude', '.claude/hooks', '.claude/agents'],
292
294
  metadata: {
@@ -12,6 +12,7 @@ const prompts = require('prompts');
12
12
  const path = require('path');
13
13
  const fs = require('fs').promises;
14
14
  const chalk = require('chalk');
15
+ const { ROOT_FILES } = require('../utils/constants');
15
16
 
16
17
  /**
17
18
  * Main prompt orchestrator for installation flow
@@ -124,7 +125,7 @@ async function checkExistingFiles(targetDir) {
124
125
  const conflictFiles = [];
125
126
  const checkFiles = [
126
127
  '.claude',
127
- 'CLAUDE.md',
128
+ ...ROOT_FILES,
128
129
  '.claude/settings.json',
129
130
  '.claude/hooks',
130
131
  '.claude/agents'
package/src/cli/runner.js CHANGED
@@ -17,6 +17,7 @@ const fs = require('fs').promises;
17
17
 
18
18
  const { parseArguments, displayHelp } = require('./parser');
19
19
  const { runInstallationPrompts, setupInterruptHandler } = require('./prompts');
20
+ const { ROOT_FILES } = require('../utils/constants');
20
21
 
21
22
  /**
22
23
  * Main CLI entry point
@@ -321,11 +322,11 @@ function generateMockFileStructure(_tag) {
321
322
  content: '# Architect Agent\n\nSystem design and architecture specialist.\n',
322
323
  mode: 0o644
323
324
  },
324
- {
325
- path: 'CLAUDE.md',
326
- content: '# Claude Code Setup\n\nThis project has been configured for Claude multi-agent orchestration.\n',
325
+ ...ROOT_FILES.map(name => ({
326
+ path: name,
327
+ content: `# ${name}\n\nThis project has been configured for Claude multi-agent orchestration.\n`,
327
328
  mode: 0o644
328
- }
329
+ }))
329
330
  ];
330
331
  }
331
332
 
@@ -402,7 +403,7 @@ async function setExecutablePermissions(targetDir) {
402
403
  async function validateInstallation(targetDir) {
403
404
  const requiredFiles = [
404
405
  '.claude/settings.json',
405
- 'CLAUDE.md'
406
+ ...ROOT_FILES
406
407
  ];
407
408
 
408
409
  for (const file of requiredFiles) {
@@ -7,6 +7,7 @@ const fetch = require('node-fetch');
7
7
  const tar = require('tar');
8
8
  const { EventEmitter } = require('events');
9
9
  const { GitHubAPIError } = require('../utils/errors');
10
+ const { ROOT_FILES } = require('../utils/constants');
10
11
  const fs = require('fs');
11
12
  const path = require('path');
12
13
  const os = require('os');
@@ -336,8 +337,8 @@ class GitHubFetcher extends EventEmitter {
336
337
  '.agents/AGENTS.md',
337
338
  '.agents/repo.md',
338
339
 
339
- // Root CLAUDE.md
340
- 'CLAUDE.md'
340
+ // Root files (single source of truth: ROOT_FILES)
341
+ ...ROOT_FILES
341
342
  ];
342
343
 
343
344
  const files = [];
@@ -346,7 +347,7 @@ class GitHubFetcher extends EventEmitter {
346
347
  if (filePath.startsWith(path) ||
347
348
  (path === '.claude' && filePath.startsWith('.claude')) ||
348
349
  (path === '.agents' && filePath.startsWith('.agents')) ||
349
- (path === 'CLAUDE.md' && filePath === 'CLAUDE.md')) {
350
+ (ROOT_FILES.includes(path) && filePath === path)) {
350
351
  try {
351
352
  const content = await this._fetchSingleFileRaw(filePath, options);
352
353
  files.push(content);
@@ -913,11 +914,11 @@ class GitHubFetcher extends EventEmitter {
913
914
  // Only extract files we care about
914
915
  const isClaudeFile = actualPath.startsWith('.claude/') || actualPath === '.claude';
915
916
  const isAgentsFile = actualPath.startsWith('.agents/') || actualPath === '.agents';
916
- const isClaudeMd = actualPath === 'CLAUDE.md';
917
+ const isRootFile = ROOT_FILES.includes(actualPath);
917
918
 
918
- console.log(`DEBUG: ${actualPath} -> claude: ${isClaudeFile}, agents: ${isAgentsFile}, md: ${isClaudeMd}, type: ${entry.type}`);
919
+ console.log(`DEBUG: ${actualPath} -> claude: ${isClaudeFile}, agents: ${isAgentsFile}, root: ${isRootFile}, type: ${entry.type}`);
919
920
 
920
- if ((isClaudeFile || isAgentsFile || isClaudeMd) && entry.type === 'File') {
921
+ if ((isClaudeFile || isAgentsFile || isRootFile) && entry.type === 'File') {
921
922
  wantedFiles.set(filePath, actualPath);
922
923
  console.log(`DEBUG: Will extract: ${filePath} -> ${actualPath}`);
923
924
  return true;
@@ -954,15 +955,15 @@ class GitHubFetcher extends EventEmitter {
954
955
  const content = await fs.promises.readFile(fullPath, 'utf-8');
955
956
  console.log(`DEBUG: Read ${actualPath} (${content.length} bytes)`);
956
957
 
957
- if (actualPath === 'CLAUDE.md') {
958
- const claudeMdFileObj = {
959
- path: 'CLAUDE.md',
958
+ if (ROOT_FILES.includes(actualPath)) {
959
+ const rootFileObj = {
960
+ path: actualPath,
960
961
  content: content,
961
962
  encoding: 'utf-8',
962
963
  type: 'file'
963
964
  };
964
- extractedFiles['CLAUDE.md'] = claudeMdFileObj;
965
- console.log('DEBUG: Added CLAUDE.md to result');
965
+ extractedFiles[actualPath] = rootFileObj;
966
+ console.log(`DEBUG: Added ${actualPath} to result`);
966
967
  } else if (actualPath.startsWith('.claude/')) {
967
968
  // Add to .claude files
968
969
  const fileObj = {
@@ -1001,7 +1002,10 @@ class GitHubFetcher extends EventEmitter {
1001
1002
  }
1002
1003
  }
1003
1004
 
1004
- console.log(`DEBUG: Final result - .claude has ${extractedFiles['.claude'].files.length} files, .agents has ${extractedFiles['.agents'].files.length} files, CLAUDE.md: ${extractedFiles['CLAUDE.md'] ? 'present' : 'missing'}`);
1005
+ const rootFilesSummary = ROOT_FILES
1006
+ .map(name => `${name}: ${extractedFiles[name] ? 'present' : 'missing'}`)
1007
+ .join(', ');
1008
+ console.log(`DEBUG: Final result - .claude has ${extractedFiles['.claude'].files.length} files, .agents has ${extractedFiles['.agents'].files.length} files, ${rootFilesSummary}`);
1005
1009
 
1006
1010
  // Clean up temp directory
1007
1011
  try {
@@ -1086,14 +1090,18 @@ async function fetchRepository(gitRef = 'main', options = {}) {
1086
1090
  console.log('Attempting tarball fetch strategy...');
1087
1091
  const tarballFiles = await fetcher.fetchAsTarball({ version: gitRef });
1088
1092
 
1093
+ const rootFiles = ROOT_FILES.map(name => tarballFiles[name]);
1094
+
1089
1095
  return {
1090
1096
  claudeDirectory: tarballFiles['.claude'],
1091
1097
  agentsDirectory: tarballFiles['.agents'],
1098
+ // Per-filename root file keys remain top-level siblings on the result.
1092
1099
  claudeFile: tarballFiles['CLAUDE.md'],
1100
+ ...Object.fromEntries(ROOT_FILES.map(name => [name, tarballFiles[name]])),
1093
1101
  files: [
1094
1102
  ...(tarballFiles['.claude'].files || []),
1095
1103
  ...(tarballFiles['.agents'].files || []),
1096
- tarballFiles['CLAUDE.md']
1104
+ ...rootFiles
1097
1105
  ]
1098
1106
  };
1099
1107
 
@@ -1103,17 +1111,19 @@ async function fetchRepository(gitRef = 'main', options = {}) {
1103
1111
 
1104
1112
  try {
1105
1113
  // Strategy 2: Individual file fetch (fallback)
1106
- const [claudeDir, agentsDir, claudeMd] = await Promise.all([
1114
+ const [claudeDir, agentsDir, ...rootFiles] = await Promise.all([
1107
1115
  fetcher.fetchDirectory('.claude', { version: gitRef }),
1108
1116
  fetcher.fetchDirectory('.agents', { version: gitRef }),
1109
- fetcher.fetchFile('CLAUDE.md', { version: gitRef })
1117
+ ...ROOT_FILES.map(name => fetcher.fetchFile(name, { version: gitRef }))
1110
1118
  ]);
1111
1119
 
1112
1120
  return {
1113
1121
  claudeDirectory: claudeDir,
1114
1122
  agentsDirectory: agentsDir,
1115
- claudeFile: claudeMd,
1116
- files: [...(claudeDir.files || []), ...(agentsDir.files || []), claudeMd]
1123
+ // Per-filename root file keys remain top-level siblings on the result.
1124
+ claudeFile: rootFiles[ROOT_FILES.indexOf('CLAUDE.md')],
1125
+ ...Object.fromEntries(ROOT_FILES.map((name, i) => [name, rootFiles[i]])),
1126
+ files: [...(claudeDir.files || []), ...(agentsDir.files || []), ...rootFiles]
1117
1127
  };
1118
1128
 
1119
1129
  } catch (fallbackError) {
@@ -15,6 +15,7 @@
15
15
  const EventEmitter = require('events');
16
16
  const path = require('path');
17
17
  const { InstallerError } = require('../utils/errors');
18
+ const { ROOT_FILES } = require('../utils/constants');
18
19
 
19
20
  // Error codes enum - matches interface contracts
20
21
  const ErrorCode = {
@@ -281,18 +282,19 @@ class Installer extends EventEmitter {
281
282
  retryCount: 3
282
283
  });
283
284
 
284
- // Fetch CLAUDE.md file
285
- const claudeMd = await this.fetcher.fetchFile('CLAUDE.md', {
286
- version: this.options.version,
287
- useCache: this.options.cache
288
- });
289
-
285
+ // Fetch root files (single source of truth: ROOT_FILES) as sibling keys
290
286
  const fetchedFiles = {
291
287
  '.claude': claudeFiles,
292
- '.agents': agentsFiles,
293
- 'CLAUDE.md': claudeMd
288
+ '.agents': agentsFiles
294
289
  };
295
290
 
291
+ for (const rootFile of ROOT_FILES) {
292
+ fetchedFiles[rootFile] = await this.fetcher.fetchFile(rootFile, {
293
+ version: this.options.version,
294
+ useCache: this.options.cache
295
+ });
296
+ }
297
+
296
298
  // Calculate total files for progress tracking
297
299
  this.progress.total = this._countFiles(fetchedFiles);
298
300
 
@@ -419,7 +421,7 @@ class Installer extends EventEmitter {
419
421
  const requiredPaths = [
420
422
  path.join(this.options.targetDir, '.claude'),
421
423
  path.join(this.options.targetDir, '.agents'),
422
- path.join(this.options.targetDir, 'CLAUDE.md')
424
+ ...ROOT_FILES.map(name => path.join(this.options.targetDir, name))
423
425
  ];
424
426
 
425
427
  const missingPaths = [];
@@ -434,7 +436,7 @@ class Installer extends EventEmitter {
434
436
  if (this.writer.validateInstallationCompleteness) {
435
437
  const completenessResult = await this.writer.validateInstallationCompleteness(
436
438
  this.options.targetDir,
437
- ['CLAUDE.md']
439
+ [...ROOT_FILES]
438
440
  );
439
441
 
440
442
  if (!completenessResult.complete) {
@@ -677,7 +679,6 @@ class Installer extends EventEmitter {
677
679
  // Check for existing directories and files that will be overwritten
678
680
  const claudeDir = path.join(this.options.targetDir, '.claude');
679
681
  const agentsDir = path.join(this.options.targetDir, '.agents');
680
- const claudeMd = path.join(this.options.targetDir, 'CLAUDE.md');
681
682
 
682
683
  const existingPaths = [];
683
684
  if (await this.validator.pathExists(claudeDir)) {
@@ -686,8 +687,10 @@ class Installer extends EventEmitter {
686
687
  if (await this.validator.pathExists(agentsDir)) {
687
688
  existingPaths.push('.agents/');
688
689
  }
689
- if (await this.validator.pathExists(claudeMd)) {
690
- existingPaths.push('CLAUDE.md');
690
+ for (const rootFile of ROOT_FILES) {
691
+ if (await this.validator.pathExists(path.join(this.options.targetDir, rootFile))) {
692
+ existingPaths.push(rootFile);
693
+ }
691
694
  }
692
695
 
693
696
  if (existingPaths.length > 0) {
@@ -718,12 +721,16 @@ class Installer extends EventEmitter {
718
721
  });
719
722
  }
720
723
 
721
- if (!fetchedFiles['CLAUDE.md']) {
722
- issues.push({
723
- type: 'missing_component',
724
- component: 'CLAUDE.md',
725
- message: 'Missing CLAUDE.md file'
726
- });
724
+ // Root files are required (same semantics as CLAUDE.md): if any ROOT_FILES
725
+ // entry is missing after fetch, validation reports it incomplete.
726
+ for (const rootFile of ROOT_FILES) {
727
+ if (!fetchedFiles[rootFile]) {
728
+ issues.push({
729
+ type: 'missing_component',
730
+ component: rootFile,
731
+ message: `Missing ${rootFile} file`
732
+ });
733
+ }
727
734
  }
728
735
 
729
736
  // Check .claude structure if present
@@ -16,6 +16,7 @@ const ora = require('ora');
16
16
  // Import core modules (these will be implemented in WP02-06)
17
17
  const logger = require('./utils/logger');
18
18
  const { InstallationError } = require('./utils/errors');
19
+ const { ROOT_FILES } = require('../utils/constants');
19
20
  const fetcher = require('./fetcher/github');
20
21
  const fileWriter = require('./installer/file-writer');
21
22
  const dependencyChecker = require('./installer/dependency-check');
@@ -104,11 +105,12 @@ async function validateEnvironment(targetDir, checkPython) {
104
105
  */
105
106
  async function handleExistingFiles(targetDir, force, verify, dryRun) {
106
107
  const claudeDir = path.join(targetDir, '.claude');
107
- const claudeMd = path.join(targetDir, 'CLAUDE.md');
108
108
 
109
109
  const existingFiles = [];
110
110
  if (await fs.pathExists(claudeDir)) existingFiles.push('.claude/');
111
- if (await fs.pathExists(claudeMd)) existingFiles.push('CLAUDE.md');
111
+ for (const rootFile of ROOT_FILES) {
112
+ if (await fs.pathExists(path.join(targetDir, rootFile))) existingFiles.push(rootFile);
113
+ }
112
114
 
113
115
  if (existingFiles.length === 0) {
114
116
  logger.info('No existing files found - proceeding with fresh installation');
@@ -232,13 +234,12 @@ async function verifyInstallation(targetDir) {
232
234
 
233
235
  try {
234
236
  const claudeDir = path.join(targetDir, '.claude');
235
- const claudeMd = path.join(targetDir, 'CLAUDE.md');
236
237
  const settingsPath = path.join(claudeDir, 'settings.local.json');
237
238
 
238
239
  // Check required files exist
239
240
  const checks = [
240
241
  { path: claudeDir, name: '.claude directory' },
241
- { path: claudeMd, name: 'CLAUDE.md' },
242
+ ...ROOT_FILES.map(name => ({ path: path.join(targetDir, name), name })),
242
243
  { path: settingsPath, name: 'settings.local.json' }
243
244
  ];
244
245
 
@@ -133,6 +133,12 @@ class MockFetcher {
133
133
  content: '# Claude Code Multi-Agent Setup\n\nThis project is configured for Claude multi-agent orchestration.\n\n## Features\n- Multi-agent team coordination\n- Observability and monitoring\n- Hook-based extensibility\n\n## Getting Started\nReview .claude/settings.local.json for configuration.',
134
134
  encoding: 'utf-8',
135
135
  sha: 'mock-claude-md-sha'
136
+ },
137
+ 'GEMINI.md': {
138
+ path: 'GEMINI.md',
139
+ content: '# Gemini Multi-Agent Setup\n\nThis project is configured for Gemini multi-agent orchestration.\n\n## Features\n- Multi-agent team coordination\n- Observability and monitoring\n- Hook-based extensibility\n\n## Getting Started\nReview .claude/settings.local.json for configuration.',
140
+ encoding: 'utf-8',
141
+ sha: 'mock-gemini-md-sha'
136
142
  }
137
143
  }
138
144
  };
@@ -7,6 +7,7 @@ const path = require('path');
7
7
  const fs = require('fs').promises;
8
8
  const { ErrorFactory } = require('./errors');
9
9
  const platform = require('./platform');
10
+ const { ROOT_FILES } = require('./constants');
10
11
 
11
12
  /**
12
13
  * Configuration manager with defaults, validation, and persistence
@@ -55,7 +56,7 @@ class Config {
55
56
  // File operations
56
57
  files: {
57
58
  excludePatterns: ['.git', 'node_modules', '.DS_Store'],
58
- includePatterns: ['.claude/**/*', 'CLAUDE.md'],
59
+ includePatterns: ['.claude/**/*', ...ROOT_FILES],
59
60
  encoding: 'utf8',
60
61
  lineEnding: 'auto', // auto, lf, crlf
61
62
  maxFileSize: 10 * 1024 * 1024 // 10MB
@@ -44,6 +44,15 @@ const GITHUB_API = {
44
44
  }
45
45
  };
46
46
 
47
+ /**
48
+ * Root-level files fetched/installed/validated as siblings.
49
+ *
50
+ * SINGLE SOURCE OF TRUTH for root filenames: adding the next root file is a
51
+ * one-line change to this array. Every fetch strategy (tarball, raw-URL,
52
+ * individual), every validation, and every verify step derives from this list.
53
+ */
54
+ const ROOT_FILES = ['CLAUDE.md', 'GEMINI.md'];
55
+
47
56
  /**
48
57
  * File paths and patterns
49
58
  */
@@ -52,6 +61,9 @@ const FILE_PATHS = {
52
61
  CLAUDE_DIR: '.claude',
53
62
  CLAUDE_MD: 'CLAUDE.md',
54
63
 
64
+ // Root-level files (single source of truth)
65
+ ROOT_FILES,
66
+
55
67
  // Claude directory structure
56
68
  CLAUDE_SUBDIRS: [
57
69
  '.claude/agents',
@@ -60,12 +72,12 @@ const FILE_PATHS = {
60
72
  '.claude/hooks/comms'
61
73
  ],
62
74
 
63
- // Key files to install
75
+ // Key files to install (root files derived from ROOT_FILES)
64
76
  REQUIRED_FILES: [
65
77
  '.claude/settings.json',
66
78
  '.claude/hooks/comms/get_unread_messages.py',
67
79
  '.claude/hooks/comms/send_message.py',
68
- 'CLAUDE.md'
80
+ ...ROOT_FILES
69
81
  ],
70
82
 
71
83
  // Configuration files
@@ -382,6 +394,7 @@ module.exports = {
382
394
  GITHUB_API,
383
395
  GITHUB_URLS,
384
396
  FILE_PATHS,
397
+ ROOT_FILES,
385
398
  ERROR_CODES,
386
399
  HTTP_STATUS,
387
400
  RETRY_CONFIG,