claude-comms 2.0.19 → 2.0.21

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.19",
3
+ "version": "2.0.21",
4
4
  "description": "NPX installer for Claude Code workflow engine client setup",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -211,6 +211,32 @@ class GitHubFetcher extends EventEmitter {
211
211
  }
212
212
  }
213
213
 
214
+ /**
215
+ * Resolve a Git ref to the full commit SHA, best-effort.
216
+ * @param {string} ref - Branch, tag, or commit ref
217
+ * @returns {Promise<string|null>} Full commit SHA or null when unavailable
218
+ */
219
+ async resolveCommitSha(ref) {
220
+ const commitRef = ref || this.repository.branch;
221
+ const encodedRef = encodeURIComponent(commitRef);
222
+ const url = `${this.config.baseUrl}/repos/${this.repository.owner}/${this.repository.repo}/commits/${encodedRef}`;
223
+
224
+ try {
225
+ const response = await this._makeRequest(url);
226
+ const data = await response.json();
227
+
228
+ if (data && typeof data.sha === 'string' && data.sha.length > 0) {
229
+ return data.sha;
230
+ }
231
+
232
+ console.warn(`Unable to resolve commit SHA for ${commitRef}: response did not include sha`);
233
+ return null;
234
+ } catch (error) {
235
+ console.warn(`Unable to resolve commit SHA for ${commitRef}: ${error.message}`);
236
+ return null;
237
+ }
238
+ }
239
+
214
240
  /**
215
241
  * Strategy 1: GitHub Trees API for recursive directory fetch
216
242
  * @private
@@ -16,6 +16,9 @@ const EventEmitter = require('events');
16
16
  const path = require('path');
17
17
  const { InstallerError } = require('../utils/errors');
18
18
  const { ROOT_FILES } = require('../utils/constants');
19
+ const { version: ENGINE_VERSION } = require('../../package.json');
20
+
21
+ const ENGINE_MANIFEST_PATH = '.agents/engine-manifest.json';
19
22
 
20
23
  // Error codes enum - matches interface contracts
21
24
  const ErrorCode = {
@@ -317,9 +320,12 @@ class Installer extends EventEmitter {
317
320
  this.state = InstallationState.WRITING;
318
321
  this._emitProgress('Installing files...', 50);
319
322
 
323
+ const flattenedFiles = this._flattenFiles(fetchedFiles);
324
+ const filesWithManifest = await this._appendEngineManifest(flattenedFiles);
325
+
320
326
  if (this.options.dryRun) {
321
327
  this.logger.info('DRY RUN: Would install files to:', this.options.targetDir);
322
- this._simulateInstallation(fetchedFiles);
328
+ this._simulateInstallation(filesWithManifest);
323
329
  return;
324
330
  }
325
331
 
@@ -337,7 +343,7 @@ class Installer extends EventEmitter {
337
343
  await this._createBackupsIfNeeded(fetchedFiles);
338
344
 
339
345
  // Flatten files and filter out user-customizable files if they already exist
340
- let filesToWrite = this._flattenFiles(fetchedFiles);
346
+ let filesToWrite = filesWithManifest;
341
347
 
342
348
  // Files that should not be overwritten if they exist (user customizations)
343
349
  const preserveIfExists = [
@@ -657,13 +663,76 @@ class Installer extends EventEmitter {
657
663
  return flattened;
658
664
  }
659
665
 
666
+ /**
667
+ * Append the synthetic engine manifest to the normal write set.
668
+ */
669
+ async _appendEngineManifest(filesToWrite) {
670
+ const engineGitSha = await this._resolveEngineGitSha(this.options.version);
671
+ const withoutManifest = filesToWrite.filter(file => file.path !== ENGINE_MANIFEST_PATH);
672
+
673
+ return [
674
+ ...withoutManifest,
675
+ this._createEngineManifestFile(engineGitSha)
676
+ ];
677
+ }
678
+
679
+ /**
680
+ * Resolve the fetched ref to a commit SHA without blocking installation.
681
+ */
682
+ async _resolveEngineGitSha(ref) {
683
+ if (!this.fetcher || typeof this.fetcher.resolveCommitSha !== 'function') {
684
+ return null;
685
+ }
686
+
687
+ try {
688
+ return await this.fetcher.resolveCommitSha(ref);
689
+ } catch (error) {
690
+ this.logger.warn(`Unable to resolve engine commit SHA: ${error.message}`);
691
+ return null;
692
+ }
693
+ }
694
+
695
+ /**
696
+ * Create the manifest file stamped into installed .agents copies.
697
+ */
698
+ _createEngineManifestFile(engineGitSha) {
699
+ const ref = this.options.version;
700
+ const manifest = {
701
+ engineVersion: ENGINE_VERSION,
702
+ engineGitSha: typeof engineGitSha === 'string' ? engineGitSha : null,
703
+ installedFrom: `${this._getRepositorySlug()}#${ref}`,
704
+ installedAt: new Date().toISOString()
705
+ };
706
+
707
+ return {
708
+ path: ENGINE_MANIFEST_PATH,
709
+ content: JSON.stringify(manifest, null, 2),
710
+ encoding: 'utf-8'
711
+ };
712
+ }
713
+
714
+ /**
715
+ * Repository identity used in the install manifest.
716
+ */
717
+ _getRepositorySlug() {
718
+ const repository = this.fetcher && this.fetcher.repository;
719
+ const owner = repository && repository.owner;
720
+ const repo = repository && (repository.repo || repository.name);
721
+
722
+ if (owner && repo) {
723
+ return `${owner}/${repo}`;
724
+ }
725
+
726
+ return 'unknown/unknown';
727
+ }
728
+
660
729
  /**
661
730
  * Simulate installation for dry-run mode
662
731
  */
663
- _simulateInstallation(fetchedFiles) {
732
+ _simulateInstallation(filesToWrite) {
664
733
  this.logger.info('DRY RUN: Files that would be installed:');
665
734
 
666
- const flattened = this._flattenFiles(fetchedFiles);
735
+ const flattened = Array.isArray(filesToWrite) ? filesToWrite : this._flattenFiles(filesToWrite);
667
736
  flattened.forEach(file => {
668
737
  const targetPath = path.join(this.options.targetDir, file.path);
669
738
  this.logger.info(` • ${targetPath}`);