diff --git a/.deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh b/.deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh index a3323e42..3f2cefad 100755 --- a/.deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh +++ b/.deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh @@ -1,24 +1,27 @@ #!/bin/bash # capture_prompt_work_tree.sh - Captures the git work tree state at prompt submission # -# This script creates a snapshot of the current git state by recording -# all files that have been modified, added, or deleted. This baseline -# is used for policies with compare_to: prompt to detect what changed -# during an agent response (between user prompts). +# This script creates a snapshot of ALL tracked files at the time the prompt +# is submitted. This baseline is used for rules with compare_to: prompt and +# created: mode to detect truly NEW files (not modifications to existing ones). +# +# 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. set -e # Ensure .deepwork directory exists mkdir -p .deepwork -# Stage all changes so we can diff against HEAD -git add -A 2>/dev/null || true - -# Save the current state of changed files -# Using git diff --name-only HEAD to get the list of all changed files -git diff --name-only HEAD > .deepwork/.last_work_tree 2>/dev/null || true +# 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 +# - Modified existing files (in baseline) -> should NOT trigger created: rules +git ls-files > .deepwork/.last_work_tree 2>/dev/null || true -# Also include untracked files not yet in the index +# Also include untracked files that exist at prompt time +# These are files the user may have created before submitting the prompt git ls-files --others --exclude-standard >> .deepwork/.last_work_tree 2>/dev/null || true # Sort and deduplicate diff --git a/.deepwork/rules/manual-test-created-mode.md b/.deepwork/rules/manual-test-created-mode.md new file mode 100644 index 00000000..abb6108d --- /dev/null +++ b/.deepwork/rules/manual-test-created-mode.md @@ -0,0 +1,22 @@ +--- +name: "Manual Test: Created Mode" +created: manual_tests/test_created_mode/*.yml +compare_to: prompt +--- + +# Manual Test: Created Mode (File Creation Trigger) + +A new test file was created in the created mode test directory! + +**Created:** `{created_files}` + +## What to do: + +1. Verify the created mode detection is working correctly +2. Acknowledge with `Manual Test: Created Mode` + +## This tests: + +The "created" detection mode where rules only fire for newly created files, +not for modifications to existing files. This is useful for enforcing standards +on new code specifically. diff --git a/manual_tests/README.md b/manual_tests/README.md index 11906456..42569421 100644 --- a/manual_tests/README.md +++ b/manual_tests/README.md @@ -36,6 +36,7 @@ Each test has two cases: one where the rule SHOULD fire, and one where it should | **Multi Safety** | Edit `.py` only | Edit `.py` AND any safety file | Manual Test: Multi Safety | | **Infinite Block Prompt** | Edit `.py` (always blocks) | Provide `` tag | Manual Test: Infinite Block Prompt | | **Infinite Block Command** | Edit `.py` (command fails) | Provide `` tag | Manual Test: Infinite Block Command | +| **Created Mode** | Create NEW `.yml` file | Modify EXISTING `.yml` file | Manual Test: Created Mode | ## Test Results Tracking @@ -49,6 +50,7 @@ Each test has two cases: one where the rule SHOULD fire, and one where it should | Multi Safety | ☐ | ☐ | | Infinite Block Prompt | ☐ | ☐ | | Infinite Block Command | ☐ | ☐ | +| Created Mode | ☐ | ☐ | ## Test Folders @@ -61,6 +63,7 @@ Each test has two cases: one where the rule SHOULD fire, and one where it should | `test_multi_safety/` | Multiple Safety | Fires unless ANY of the safety files also edited | | `test_infinite_block_prompt/` | Infinite Block (Prompt) | Always blocks with prompt; only promise can bypass | | `test_infinite_block_command/` | Infinite Block (Command) | Command always fails; tests if promise skips command | +| `test_created_mode/` | Created (New Files Only) | Fires ONLY when NEW files are created, not when existing modified | ## Corresponding Rules @@ -72,3 +75,4 @@ Rules are defined in `.deepwork/rules/`: - `manual-test-multi-safety.md` - `manual-test-infinite-block-prompt.md` - `manual-test-infinite-block-command.md` +- `manual-test-created-mode.md` diff --git a/manual_tests/test_created_mode/existing_file.yml b/manual_tests/test_created_mode/existing_file.yml new file mode 100644 index 00000000..dec0e532 --- /dev/null +++ b/manual_tests/test_created_mode/existing_file.yml @@ -0,0 +1 @@ +# This is a modification test diff --git a/src/deepwork/hooks/rules_check.py b/src/deepwork/hooks/rules_check.py index 548bcee9..0dae0777 100644 --- a/src/deepwork/hooks/rules_check.py +++ b/src/deepwork/hooks/rules_check.py @@ -199,9 +199,16 @@ def get_changed_files_default_tip() -> list[str]: def get_changed_files_prompt() -> list[str]: - """Get files changed since prompt was submitted.""" - baseline_path = Path(".deepwork/.last_work_tree") + """Get files changed since prompt was submitted. + + Returns ALL files with staged changes (modified, added, deleted). + 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. + """ try: subprocess.run(["git", "add", "-A"], capture_output=True, check=False) @@ -214,13 +221,7 @@ def get_changed_files_prompt() -> list[str]: 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 baseline_path.exists(): - baseline_files = set(baseline_path.read_text().strip().split("\n")) - baseline_files = {f for f in baseline_files if f} - new_files = current_files - baseline_files - return sorted(new_files) - else: - return sorted(current_files) + return sorted(current_files) except (subprocess.CalledProcessError, OSError): return [] diff --git a/src/deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh b/src/deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh index a3323e42..3f2cefad 100755 --- a/src/deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh +++ b/src/deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh @@ -1,24 +1,27 @@ #!/bin/bash # capture_prompt_work_tree.sh - Captures the git work tree state at prompt submission # -# This script creates a snapshot of the current git state by recording -# all files that have been modified, added, or deleted. This baseline -# is used for policies with compare_to: prompt to detect what changed -# during an agent response (between user prompts). +# This script creates a snapshot of ALL tracked files at the time the prompt +# is submitted. This baseline is used for rules with compare_to: prompt and +# created: mode to detect truly NEW files (not modifications to existing ones). +# +# 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. set -e # Ensure .deepwork directory exists mkdir -p .deepwork -# Stage all changes so we can diff against HEAD -git add -A 2>/dev/null || true - -# Save the current state of changed files -# Using git diff --name-only HEAD to get the list of all changed files -git diff --name-only HEAD > .deepwork/.last_work_tree 2>/dev/null || true +# 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 +# - Modified existing files (in baseline) -> should NOT trigger created: rules +git ls-files > .deepwork/.last_work_tree 2>/dev/null || true -# Also include untracked files not yet in the index +# Also include untracked files that exist at prompt time +# These are files the user may have created before submitting the prompt git ls-files --others --exclude-standard >> .deepwork/.last_work_tree 2>/dev/null || true # Sort and deduplicate