claude-dev-env 8.51.0 → 8.51.1

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-dev-env",
3
- "version": "8.51.0",
3
+ "version": "8.51.1",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,12 +36,29 @@ def _log_walk_error(os_error: OSError) -> None:
36
36
  logging.warning("cannot scan %s -- %s", os_error.filename, os_error.strerror)
37
37
 
38
38
 
39
+ def _is_older_than(directory_path: str, now: float, min_age_seconds: int) -> bool:
40
+ """Report whether a directory ctime precedes *now* by more than the minimum age."""
41
+ try:
42
+ raw_ctime = os.path.getctime(directory_path)
43
+ except FileNotFoundError:
44
+ return False
45
+ except PermissionError:
46
+ logging.warning("permission denied -- %s", directory_path)
47
+ return False
48
+ except OSError:
49
+ return False
50
+ ctime = min(raw_ctime, now)
51
+ return now - ctime > min_age_seconds
52
+
53
+
39
54
  def sweep(root: str, min_age_seconds: int) -> list[str]:
40
55
  """Remove empty directories under *root* older than *min_age_seconds*.
41
56
 
42
- Walks bottom-up so nested empty directories are cleaned from the leaves
43
- inward. Relies on os.rmdir to fail harmlessly for non-empty directories
44
- instead of checking snapshotted subdirectory lists.
57
+ Reads the age of every directory before the first removal, because on
58
+ POSIX removing a child directory moves its parent's ctime to the present.
59
+ Then removes bottom-up so nested empty directories are cleaned from the
60
+ leaves inward. os.rmdir decides emptiness and fails harmlessly for a
61
+ directory that still holds an entry.
45
62
 
46
63
  Args:
47
64
  root: Root directory to walk; directories below the root are
@@ -56,35 +73,28 @@ def sweep(root: str, min_age_seconds: int) -> list[str]:
56
73
  all_removed: list[str] = []
57
74
 
58
75
  now = time.time()
59
- for each_directory_path, _, _ in os.walk(
60
- root, onerror=_log_walk_error, topdown=False
61
- ):
62
- if each_directory_path == root:
63
- continue
76
+ all_expired_directories = [
77
+ each_directory_path
78
+ for each_directory_path, _, _ in os.walk(
79
+ root, onerror=_log_walk_error, topdown=False
80
+ )
81
+ if each_directory_path != root
82
+ and _is_older_than(each_directory_path, now, min_age_seconds)
83
+ ]
84
+ for each_directory_path in all_expired_directories:
64
85
  try:
65
- raw_ctime = os.path.getctime(each_directory_path)
86
+ os.rmdir(each_directory_path)
87
+ logging.info("deleted: %s", each_directory_path)
88
+ all_removed.append(each_directory_path)
66
89
  except FileNotFoundError:
67
- continue
68
- except PermissionError:
69
- logging.warning("permission denied -- %s", each_directory_path)
70
- continue
71
- except OSError:
72
- continue
73
- ctime = min(raw_ctime, now)
74
- if now - ctime > min_age_seconds:
75
- try:
76
- os.rmdir(each_directory_path)
77
- logging.info("deleted: %s", each_directory_path)
78
- all_removed.append(each_directory_path)
79
- except FileNotFoundError:
80
- pass
81
- except OSError as e:
82
- if e.errno not in (errno.ENOTEMPTY, errno.EEXIST):
83
- logging.warning(
84
- "could not remove %s -- %s",
85
- each_directory_path,
86
- e,
87
- )
90
+ pass
91
+ except OSError as e:
92
+ if e.errno not in (errno.ENOTEMPTY, errno.EEXIST):
93
+ logging.warning(
94
+ "could not remove %s -- %s",
95
+ each_directory_path,
96
+ e,
97
+ )
88
98
 
89
99
  return all_removed
90
100
 
@@ -107,6 +107,17 @@ def test_sweep_removes_nested_empty_dirs(tmp_path: Path) -> None:
107
107
  assert not (tmp_path / "level1").exists()
108
108
 
109
109
 
110
+ def test_sweep_reports_a_nested_chain_removed_leaf_first_in_one_pass(tmp_path: Path) -> None:
111
+ """sweep reads every age before it removes, so one pass clears the whole chain."""
112
+ outer = tmp_path / "outer"
113
+ inner = outer / "inner"
114
+ inner.mkdir(parents=True)
115
+
116
+ removed = sweep(str(tmp_path), min_age_seconds=0)
117
+
118
+ assert removed == [str(inner), str(outer)]
119
+
120
+
110
121
  def test_sweep_removes_only_old_enough_directories(tmp_path: Path) -> None:
111
122
  """sweep does not remove directories newer than the age threshold."""
112
123
  young_dir = tmp_path / "young"