Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,12 @@ Command-line file watchers and daemon tools usually listen for KeyboardInterrupt

Action:
Always register a SIGTERM handler on POSIX systems (`if platform.system() != "Windows"`) that performs the same graceful shutdown and subprocess termination steps as the KeyboardInterrupt handler.

## 2026-04-29 — Ignore Filter Relpath & Compound Loop Overhead

Learning:
Inside the `_is_ignored_impl` hot path, `os.path.relpath` is computationally expensive because it inherently resolves absolute paths. While optimizations existed for exact prefix matching, simple relative paths (e.g., `src/file.py`) against a `.` base path would fall through and trigger a `relpath` call, slowing down high-volume events. Additionally, reconstructing cumulative directory prefixes (`foo`, `foo/bar`) to test against exact/wildcard ignores consumes significant CPU cycles and is entirely unnecessary if the user specified no compound ignore patterns (i.e., no slashes in any pattern).

Action:
In `watchdog` event path normalization, bypass the computationally expensive `os.path.relpath` for the common case where `base_path` is `.` and the path is already relative by adding a fast-path condition: `elif self.base_path == "." and not os.path.isabs(path) and not path.startswith(".."): pass`.
To optimize ignore pattern matching in hot loops, pre-compute a flag during initialization (e.g., `self._has_compound_ignores = any('/' in p for p in self.ignore_patterns)`) and use it to short-circuit the evaluation of compound directory paths if no slash-based ignore patterns exist.
8 changes: 8 additions & 0 deletions .jules/warden.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,11 @@ Observed the preceding agent optimized process lifecycle management by adding a

Alignment / Deferred:
Version bumped to `0.1.22` as a patch release. Updated CHANGELOG.md. No heavy pruning or major dependency updates required.

## 2026-04-30 — Assessment & Lifecycle

Observation / Pruned:
Observed the preceding agent optimized the ignore file watcher hot paths by explicitly bypassing `os.path.relpath` for the common case, and short-circuiting compound directory evaluations when no slash-based ignore patterns exist. Verified test execution, linting, and dead code pruning without issues. No unused imports or variables were found. No heavy pruning required.

Alignment / Deferred:
Version bumped to `0.1.23` as a patch release. Updated CHANGELOG.md.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [0.1.23] - 2026-04-30

### Changed
* **[Performance]:** Optimized ignore file filtering in hot paths by fast-tracking common relative paths and avoiding compound loop iterations when unnecessary, significantly reducing CPU cycles on burst saves.

## [0.1.22] - 2026-04-29

### Changed
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "echo-watcher"
version = "0.1.22"
version = "0.1.23"
description = "📡 Lightweight file watcher. Trigger commands on changes. <5MB RAM, single binary."
authors = [
{ name = "shenald-dev", email = "bot@shenald.dev" }
Expand Down
5 changes: 4 additions & 1 deletion src/echo/watcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def __init__(self, command: str, base_path: str = ".", ignore_patterns: list[str
self.exact_ignores = {p for p in self.ignore_patterns if not any(c in p for c in ('*', '?', '['))}
wildcard_ignores = [p for p in self.ignore_patterns if any(c in p for c in ('*', '?', '['))]
self.wildcard_regex = None
self._has_compound_ignores = any('/' in p for p in self.ignore_patterns)
if wildcard_ignores:
regex_str = "|".join(f"(?:{fnmatch.translate(p)})" for p in wildcard_ignores)
self.wildcard_regex = re.compile(regex_str)
Expand Down Expand Up @@ -171,6 +172,8 @@ def _is_ignored_impl(self, path: str) -> bool:
path = path[len(self._base_prefix):]
elif path == self.base_path or path == self._abs_base_path.rstrip(os.sep):
path = "."
elif self.base_path == "." and not os.path.isabs(path) and not path.startswith(".."):
pass
else:
try:
path = os.path.relpath(path, self.base_path)
Expand All @@ -191,7 +194,7 @@ def _is_ignored_impl(self, path: str) -> bool:
return True

# Check for exact and wildcard ignore patterns matching cumulative prefix directories
if len(parts) > 1:
if self._has_compound_ignores and len(parts) > 1:
prefix = parts[0]
# Prefix for parts[0] is already evaluated via earlier exact match `isdisjoint()`
# and wildcard matching, so we start accumulating from the second part.
Expand Down
Loading