claude-dev-env 8.35.1 → 8.35.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/bin/install.mjs CHANGED
@@ -76,6 +76,7 @@ import {
76
76
  } from './resolve-package-managed-directory.mjs';
77
77
  import {
78
78
  ensureDirectoryPointer,
79
+ POINTER_ACTION_CREATED,
79
80
  } from './publish-directory-pointer.mjs';
80
81
  import {
81
82
  parseInstallTargetSelectionFromArgv,
@@ -872,7 +873,11 @@ function owningManagedRoot(installedFilePath) {
872
873
  * so those two paths are pointers. A real directory from an older install at a
873
874
  * lookup path is merged into the agents home before the pointer is written.
874
875
  *
875
- * @returns {string[]} The lookup paths this run published.
876
+ * A pointer that already aimed at its target is left out of the result, so a
877
+ * rollback that removes this run's written paths keeps the pointer a prior
878
+ * install published.
879
+ *
880
+ * @returns {string[]} The lookup paths this run created.
876
881
  */
877
882
  function publishManagedLookupPointers() {
878
883
  const pointerPairs = [
@@ -897,10 +902,10 @@ function publishManagedLookupPointers() {
897
902
  INSTALL_ROOT_RESOLUTION.hooksInstallDirectory,
898
903
  ],
899
904
  ];
900
- for (const [pointerPath, targetPath] of pointerPairs) {
901
- ensureDirectoryPointer(pointerPath, targetPath);
902
- }
903
- return pointerPairs.map(([pointerPath]) => pointerPath);
905
+ return pointerPairs
906
+ .map(([pointerPath, targetPath]) => ensureDirectoryPointer(pointerPath, targetPath))
907
+ .filter((pointerResult) => pointerResult.action === POINTER_ACTION_CREATED)
908
+ .map((pointerResult) => pointerResult.pointerPath);
904
909
  }
905
910
 
906
911
  /**
@@ -6,9 +6,11 @@ import { test } from 'node:test';
6
6
  import { strict as assert } from 'node:assert';
7
7
  import {
8
8
  existsSync,
9
+ lstatSync,
9
10
  mkdtempSync,
10
11
  mkdirSync,
11
12
  readFileSync,
13
+ realpathSync,
12
14
  rmSync,
13
15
  writeFileSync,
14
16
  } from 'node:fs';
@@ -501,3 +503,44 @@ test('successful install after prior install leaves one manifest and no fault',
501
503
  rmSync(homeDirectory, { recursive: true, force: true });
502
504
  }
503
505
  });
506
+
507
+ test('installer fault keeps the lookup pointers a prior install published', () => {
508
+ const homeDirectory = mkdtempSync(join(tmpdir(), 'cdev-txn-e2e-pointers-'));
509
+ try {
510
+ writeFileSync(join(homeDirectory, '.gitconfig'), '');
511
+ const homeOnlyEnvironment = { CLAUDE_CONFIG_DIR: '' };
512
+ const okRun = runInstaller(homeDirectory, ['--only', 'core'], {
513
+ environment: homeOnlyEnvironment,
514
+ });
515
+ assert.equal(okRun.status, 0, okRun.stdout + okRun.stderr);
516
+ const allPointerTargets = [
517
+ ...['skills', 'agents', 'hooks', 'scripts'].map((name) => ({
518
+ pointerPath: join(homeDirectory, '.claude', name),
519
+ targetPath: realpathSync(join(homeDirectory, '.agents', name)),
520
+ })),
521
+ {
522
+ pointerPath: join(homeDirectory, '.codex', 'hooks'),
523
+ targetPath: realpathSync(join(homeDirectory, '.agents', 'hooks')),
524
+ },
525
+ ];
526
+
527
+ for (const faultPhase of [FAULT_PHASES.AFTER_FILE_STAGING, FAULT_PHASES.AFTER_MANIFEST_WRITE]) {
528
+ const failedRun = runInstaller(homeDirectory, ['--only', 'core'], {
529
+ faultPhase,
530
+ environment: homeOnlyEnvironment,
531
+ });
532
+ assert.notEqual(failedRun.status, 0, failedRun.stdout + failedRun.stderr);
533
+ assert.match(`${failedRun.stdout}${failedRun.stderr}`, /prior installation restored/);
534
+ for (const { pointerPath, targetPath } of allPointerTargets) {
535
+ assert.equal(
536
+ lstatSync(pointerPath, { throwIfNoEntry: false })?.isSymbolicLink(),
537
+ true,
538
+ `${pointerPath} survives a fault at ${faultPhase}`,
539
+ );
540
+ assert.equal(realpathSync(pointerPath), targetPath);
541
+ }
542
+ }
543
+ } finally {
544
+ rmSync(homeDirectory, { recursive: true, force: true });
545
+ }
546
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.35.1",
3
+ "version": "8.35.2",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {