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
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,20 @@
# The baseline contains ALL tracked files (not just changed files) so that
# the rules_check hook can determine which files are genuinely new vs which
# files existed before and were just modified.
#
# It also captures the HEAD commit ref so that committed changes can be detected
# by comparing HEAD at Stop time to the captured ref.

set -e

# Ensure .deepwork directory exists
mkdir -p .deepwork

# Save the current HEAD commit ref for detecting committed changes
# This is used by get_changed_files_prompt() to detect files changed since prompt,
# even if those changes were committed during the agent response.
git rev-parse HEAD > .deepwork/.last_head_ref 2>/dev/null || echo "" > .deepwork/.last_head_ref

# Save ALL tracked files (not just changed files)
# This is critical for created: mode rules to distinguish between:
# - Newly created files (not in baseline) -> should trigger created: rules
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,8 @@ dmypy.json
*.tmp
.temp/
tmp/

# DeepWork runtime artifacts
.deepwork/.last_work_tree
.deepwork/.last_head_ref
.deepwork/tmp/
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ All notable changes to DeepWork will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.1] - 2026-01-20

### Added
- `created` rule mode for matching only newly created files (#76)
- Rules with `mode: created` only fire when files are first added, not on modifications
- Useful for enforcing patterns on new files without triggering on existing file edits

### Fixed
- Fixed `created` mode rules incorrectly firing on modified files (#83)
- Fixed `compare_to: prompt` mode not detecting files that were committed during agent response
- Rules like `uv-lock-sync` now correctly fire even when changes are committed before the Stop hook runs

## [0.3.0] - 2026-01-18

### Added
Expand Down Expand Up @@ -100,6 +112,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Initial version.

[0.3.1]: https://github.com/anthropics/deepwork/releases/tag/0.3.1
[0.3.0]: https://github.com/anthropics/deepwork/releases/tag/0.3.0
[0.1.1]: https://github.com/anthropics/deepwork/releases/tag/0.1.1
[0.1.0]: https://github.com/anthropics/deepwork/releases/tag/0.1.0
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "deepwork"
version = "0.3.0"
version = "0.3.1"
description = "Framework for enabling AI agents to perform complex, multi-step work tasks"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
48 changes: 40 additions & 8 deletions src/deepwork/hooks/rules_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,27 +201,59 @@ def get_changed_files_default_tip() -> list[str]:
def get_changed_files_prompt() -> list[str]:
"""Get files changed since prompt was submitted.

Returns ALL files with staged changes (modified, added, deleted).
Returns files that changed since the prompt was submitted, including:
- Committed changes (compared to captured HEAD ref)
- Staged changes (not yet committed)
- Untracked files

This is used by trigger/safety, set, and pair mode rules to detect
file modifications during the agent response.

Note: The baseline file (.last_work_tree) is NOT used here - it's only
used by get_created_files_prompt() to detect truly NEW files for
created: mode rules.
"""
baseline_ref_path = Path(".deepwork/.last_head_ref")
changed_files: set[str] = set()

try:
# Stage all changes first
subprocess.run(["git", "add", "-A"], capture_output=True, check=False)

# If we have a captured HEAD ref, compare committed changes against it
if baseline_ref_path.exists():
baseline_ref = baseline_ref_path.read_text().strip()
if baseline_ref:
# Get files changed in commits since the baseline
result = subprocess.run(
["git", "diff", "--name-only", baseline_ref, "HEAD"],
capture_output=True,
text=True,
check=False,
)
if result.returncode == 0 and result.stdout.strip():
committed_files = set(result.stdout.strip().split("\n"))
changed_files.update(f for f in committed_files if f)

# Also get currently staged changes (in case not everything is committed)
result = subprocess.run(
["git", "diff", "--name-only", "--cached"],
capture_output=True,
text=True,
check=False,
)
current_files = set(result.stdout.strip().split("\n")) if result.stdout.strip() else set()
current_files = {f for f in current_files if f}
if result.stdout.strip():
staged_files = set(result.stdout.strip().split("\n"))
changed_files.update(f for f in staged_files if f)

# Include untracked files
result = subprocess.run(
["git", "ls-files", "--others", "--exclude-standard"],
capture_output=True,
text=True,
check=False,
)
if result.stdout.strip():
untracked_files = set(result.stdout.strip().split("\n"))
changed_files.update(f for f in untracked_files if f)

return sorted(current_files)
return sorted(changed_files)

except (subprocess.CalledProcessError, OSError):
return []
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.