Skip to content

Commit 1b2cb53

Browse files
nhortonclaude
andauthored
Fix duplicate stop hooks display in Claude (#38)
* Fix stop hooks to properly return blocking JSON The policy_stop_hook.sh was passing --changed-files argument to the Python evaluate_policies module, but the module doesn't accept this argument. This caused argparse to fail, which was suppressed by `2>/dev/null || echo '{}'`, resulting in the hook always outputting empty JSON instead of blocking JSON when policies fire. Changes: - Sync installed hooks (.deepwork/jobs/) with source (src/deepwork/standard_jobs/) - Remove obsolete get_changed_files.sh and capture_work_tree.sh - Add capture_prompt_work_tree.sh for compare_to: prompt mode - Add shell script tests in tests/shell_script_tests/ to verify the hook correctly outputs blocking JSON when policies fire * Sync installed jobs with source after deepwork install Running `deepwork install` synced several updates from source: - Added browser automation capability guidance to job definition steps - Added supplemental_file_references.md documentation - Updated generated commands in .claude/ and .gemini/ * Add instructions for running deepwork CLI in web environment * Apply suggestion from @nhorton * Update documentation for hook changes - Update architecture.md to reflect current hook files: - capture_work_tree.sh → capture_prompt_work_tree.sh - Removed get_changed_files.sh (logic in Python evaluator) - Add Fixed section to CHANGELOG for stop hook blocking bug * Fix ruff formatting in shell script tests --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent aefb17c commit 1b2cb53

13 files changed

Lines changed: 511 additions & 75 deletions

File tree

.claude/commands/deepwork_policy.define.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,22 @@ If there are files that, when also changed, mean the policy shouldn't fire:
8888
- Trigger: `src/auth/**/*`
8989
- Safety: `SECURITY.md`, `docs/security_review.md`
9090

91+
### Step 3b: Choose the Comparison Mode (Optional)
92+
93+
The `compare_to` field controls what baseline is used when detecting "changed files":
94+
95+
**Options:**
96+
- `base` (default) - Compares to the base of the current branch (merge-base with main/master). This is the most common choice for feature branches, as it shows all changes made on the branch.
97+
- `default_tip` - Compares to the current tip of the default branch (main/master). Useful when you want to see the difference from what's currently in production.
98+
- `prompt` - Compares to the state at the start of each prompt. Useful for policies that should only fire based on changes made during a single agent response.
99+
100+
**When to use each:**
101+
- **base**: Best for most policies. "Did this branch change config files?" → trigger docs review
102+
- **default_tip**: For policies about what's different from production/main
103+
- **prompt**: For policies that should only consider very recent changes within the current session
104+
105+
Most policies should use the default (`base`) and don't need to specify `compare_to`.
106+
91107
### Step 4: Write the Instructions
92108

93109
Create clear, actionable instructions for what the agent should do when the policy fires.
@@ -118,6 +134,7 @@ Create or update `.deepwork.policy.yml` in the project root.
118134
- name: "[Friendly name for the policy]"
119135
trigger: "[glob pattern]" # or array: ["pattern1", "pattern2"]
120136
safety: "[glob pattern]" # optional, or array
137+
compare_to: "base" # optional: "base" (default), "default_tip", or "prompt"
121138
instructions: |
122139
[Multi-line instructions for the agent...]
123140
```
@@ -127,6 +144,7 @@ Create or update `.deepwork.policy.yml` in the project root.
127144
- name: "[Friendly name for the policy]"
128145
trigger: "[glob pattern]"
129146
safety: "[glob pattern]"
147+
compare_to: "base" # optional
130148
instructions_file: "path/to/instructions.md"
131149
```
132150
@@ -198,7 +216,10 @@ Create or update this file at the project root with the new policy entry.
198216
## Context
199217
200218
Policies are evaluated automatically when you finish working on a task. The system:
201-
1. Tracks which files you changed during the session
219+
1. Determines which files have changed based on each policy's `compare_to` setting:
220+
- `base` (default): Files changed since the branch diverged from main/master
221+
- `default_tip`: Files different from the current main/master branch
222+
- `prompt`: Files changed since the last prompt submission
202223
2. Checks if any changes match policy trigger patterns
203224
3. Skips policies where safety patterns also matched
204225
4. Prompts you with instructions for any triggered policies

.deepwork/jobs/deepwork_policy/hooks/capture_work_tree.sh renamed to .deepwork/jobs/deepwork_policy/hooks/capture_prompt_work_tree.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#!/bin/bash
2-
# capture_work_tree.sh - Captures the current git work tree state
2+
# capture_prompt_work_tree.sh - Captures the git work tree state at prompt submission
33
#
44
# This script creates a snapshot of the current git state by recording
55
# all files that have been modified, added, or deleted. This baseline
6-
# is used later to detect what changed during an agent session.
6+
# is used for policies with compare_to: prompt to detect what changed
7+
# during an agent response (between user prompts).
78

89
set -e
910

.deepwork/jobs/deepwork_policy/hooks/get_changed_files.sh

Lines changed: 0 additions & 30 deletions
This file was deleted.

.deepwork/jobs/deepwork_policy/hooks/policy_stop_hook.sh

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22
# policy_stop_hook.sh - Evaluates policies when the agent stops
33
#
44
# This script is called as a Claude Code Stop hook. It:
5-
# 1. Gets the list of files changed during the session
6-
# 2. Evaluates policies from .deepwork.policy.yml
5+
# 1. Evaluates policies from .deepwork.policy.yml
6+
# 2. Computes changed files based on each policy's compare_to setting
77
# 3. Checks for <promise> tags in the conversation transcript
88
# 4. Returns JSON to block stop if policies need attention
9-
# 5. Resets the work tree baseline for the next iteration
109

1110
set -e
1211

13-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
14-
1512
# Check if policy file exists
1613
if [ ! -f .deepwork.policy.yml ]; then
1714
# No policies defined, nothing to do
@@ -31,16 +28,6 @@ if [ -n "${HOOK_INPUT}" ]; then
3128
TRANSCRIPT_PATH=$(echo "${HOOK_INPUT}" | jq -r '.transcript_path // empty' 2>/dev/null || echo "")
3229
fi
3330

34-
# Get changed files
35-
changed_files=$("${SCRIPT_DIR}/get_changed_files.sh" 2>/dev/null || echo "")
36-
37-
# If no files changed, nothing to evaluate
38-
if [ -z "${changed_files}" ]; then
39-
# Reset baseline for next iteration
40-
"${SCRIPT_DIR}/capture_work_tree.sh" 2>/dev/null || true
41-
exit 0
42-
fi
43-
4431
# Extract conversation text from the JSONL transcript
4532
# The transcript is JSONL format - each line is a JSON object
4633
# We need to extract the text content from assistant messages
@@ -57,16 +44,13 @@ fi
5744
# Call the Python evaluator
5845
# The Python module handles:
5946
# - Parsing the policy file
47+
# - Computing changed files based on each policy's compare_to setting
6048
# - Matching changed files against triggers/safety patterns
6149
# - Checking for promise tags in the conversation context
6250
# - Generating appropriate JSON output
6351
result=$(echo "${conversation_context}" | python -m deepwork.hooks.evaluate_policies \
6452
--policy-file .deepwork.policy.yml \
65-
--changed-files "${changed_files}" \
6653
2>/dev/null || echo '{}')
6754

68-
# Reset the work tree baseline for the next iteration
69-
"${SCRIPT_DIR}/capture_work_tree.sh" 2>/dev/null || true
70-
7155
# Output the result (JSON for Claude Code hooks)
7256
echo "${result}"
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#!/bin/bash
22
# user_prompt_submit.sh - Runs on every user prompt submission
33
#
4-
# This script captures the work tree baseline if it doesn't exist yet.
5-
# This ensures we have a baseline to compare against when evaluating policies.
4+
# This script captures the work tree state at each prompt submission.
5+
# This baseline is used for policies with compare_to: prompt to detect
6+
# what changed during an agent response.
67

78
set -e
89

910
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1011

11-
# Only capture if no baseline exists yet (first prompt of session)
12-
if [ ! -f .deepwork/.last_work_tree ]; then
13-
"${SCRIPT_DIR}/capture_work_tree.sh"
14-
fi
12+
# Capture work tree state at each prompt for compare_to: prompt policies
13+
"${SCRIPT_DIR}/capture_prompt_work_tree.sh"
1514

1615
# Exit successfully - don't block the prompt
1716
exit 0

.deepwork/jobs/deepwork_policy/steps/define.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,22 @@ If there are files that, when also changed, mean the policy shouldn't fire:
5656
- Trigger: `src/auth/**/*`
5757
- Safety: `SECURITY.md`, `docs/security_review.md`
5858

59+
### Step 3b: Choose the Comparison Mode (Optional)
60+
61+
The `compare_to` field controls what baseline is used when detecting "changed files":
62+
63+
**Options:**
64+
- `base` (default) - Compares to the base of the current branch (merge-base with main/master). This is the most common choice for feature branches, as it shows all changes made on the branch.
65+
- `default_tip` - Compares to the current tip of the default branch (main/master). Useful when you want to see the difference from what's currently in production.
66+
- `prompt` - Compares to the state at the start of each prompt. Useful for policies that should only fire based on changes made during a single agent response.
67+
68+
**When to use each:**
69+
- **base**: Best for most policies. "Did this branch change config files?" → trigger docs review
70+
- **default_tip**: For policies about what's different from production/main
71+
- **prompt**: For policies that should only consider very recent changes within the current session
72+
73+
Most policies should use the default (`base`) and don't need to specify `compare_to`.
74+
5975
### Step 4: Write the Instructions
6076

6177
Create clear, actionable instructions for what the agent should do when the policy fires.
@@ -86,6 +102,7 @@ Create or update `.deepwork.policy.yml` in the project root.
86102
- name: "[Friendly name for the policy]"
87103
trigger: "[glob pattern]" # or array: ["pattern1", "pattern2"]
88104
safety: "[glob pattern]" # optional, or array
105+
compare_to: "base" # optional: "base" (default), "default_tip", or "prompt"
89106
instructions: |
90107
[Multi-line instructions for the agent...]
91108
```
@@ -95,6 +112,7 @@ Create or update `.deepwork.policy.yml` in the project root.
95112
- name: "[Friendly name for the policy]"
96113
trigger: "[glob pattern]"
97114
safety: "[glob pattern]"
115+
compare_to: "base" # optional
98116
instructions_file: "path/to/instructions.md"
99117
```
100118
@@ -166,7 +184,10 @@ Create or update this file at the project root with the new policy entry.
166184
## Context
167185
168186
Policies are evaluated automatically when you finish working on a task. The system:
169-
1. Tracks which files you changed during the session
187+
1. Determines which files have changed based on each policy's `compare_to` setting:
188+
- `base` (default): Files changed since the branch diverged from main/master
189+
- `default_tip`: Files different from the current main/master branch
190+
- `prompt`: Files changed since the last prompt submission
170191
2. Checks if any changes match policy trigger patterns
171192
3. Skips policies where safety patterns also matched
172193
4. Prompts you with instructions for any triggered policies

.gemini/commands/deepwork_jobs/learn.toml

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,42 @@ The AGENTS.md file captures project-specific knowledge that helps future agent r
174174
- This keeps AGENTS.md in sync as the codebase evolves
175175
- Pattern: "See `path/to/file.ext` for [description]"
176176
177-
3. **AGENTS.md structure**: See `.deepwork/jobs/deepwork_jobs/templates/agents.md.template` for the standard format.
177+
3. **AGENTS.md structure**:
178+
179+
```markdown
180+
# Project Context for [Job Name]
181+
182+
## Codebase Structure
183+
184+
<!-- Reference files rather than duplicating content -->
185+
- Project structure: See `README.md` for overview
186+
- API documentation: See `docs/api.md`
187+
- Configuration: See `config/README.md`
188+
189+
## Conventions
190+
191+
### Naming Conventions
192+
- [Convention]: See example in `path/to/example.ext:LINE`
193+
194+
### File Organization
195+
- [Pattern]: Reference `path/to/pattern/`
196+
197+
## Job-Specific Context
198+
199+
### [Job Name]
200+
201+
#### [Step Name]
202+
- [Learning]: Reference `relevant/file.ext`
203+
- [Context]: [Brief explanation with file reference]
204+
205+
## Known Issues and Workarounds
206+
207+
- [Issue]: [Workaround with file reference if applicable]
208+
209+
## Last Updated
210+
- Date: [YYYY-MM-DD]
211+
- From conversation about: [Brief description]
212+
```
178213
179214
4. **Writing entries**
180215
- Be concise but specific
@@ -196,14 +231,36 @@ If instruction files were modified:
196231
changes: "Improved [step] instructions based on execution learnings: [brief description]"
197232
```
198233
199-
### Step 7: Sync and Relay Instructions
234+
### Step 7: Sync and Summarize
200235
201236
1. **Run deepwork sync** (if instructions were modified)
202237
```bash
203238
deepwork sync
204239
```
205240
206-
2. **If commands were regenerated**, look at the "To use the new commands" section in the `deepwork sync` output and **relay these exact reload instructions to the user** (e.g., "Type 'exit' then run 'claude --resume'" for Claude Code)
241+
2. **Create learning_summary.md** in the working folder:
242+
```markdown
243+
# Learning Summary
244+
245+
## Job Analyzed
246+
- Job: [job_name]
247+
- Steps executed: [list of steps]
248+
249+
## Generalizable Improvements Made
250+
- [Step]: [What was improved]
251+
252+
## Bespoke Learnings Captured
253+
- Location: [path to AGENTS.md]
254+
- Entries added: [list of entries]
255+
256+
## Files Modified
257+
- [List of files changed]
258+
259+
## Recommendations
260+
- [Any additional suggestions]
261+
```
262+
263+
3. **If commands were regenerated**, look at the "To use the new commands" section in the `deepwork sync` output and **relay these exact reload instructions to the user** (e.g., "Type 'exit' then run 'claude --resume'" for Claude Code)
207264
208265
## File Reference Patterns
209266
@@ -234,6 +291,7 @@ When adding entries to AGENTS.md, prefer these patterns:
234291
- AGENTS.md created/updated with bespoke learnings
235292
- File references used instead of duplicating content
236293
- AGENTS.md is in the correct working folder
294+
- learning_summary.md documents all changes
237295
- When all criteria are met, include `<promise>✓ Quality Criteria Met</promise>`
238296
239297
## Example Dialog
@@ -293,7 +351,7 @@ I found the following job executions:
293351
294352
**Summary**
295353
296-
Updated job instructions and created AGENTS.md with bespoke learnings. To get the updated commands, type 'exit' then run 'claude --resume'.
354+
Created `learning_summary.md` documenting all changes. To get the updated commands, type 'exit' then run 'claude --resume'.
297355
```
298356
299357
## Handling Edge Cases
@@ -345,7 +403,7 @@ All work for this job should be done on a dedicated work branch:
345403
## Output Requirements
346404
347405
Create the following output(s):
348-
- `AGENTS.md`
406+
- `learning_summary.md`
349407
350408
Ensure all outputs are:
351409
- Well-formatted and complete
@@ -359,7 +417,7 @@ After completing this step:
359417
360418
2. **Inform the user**:
361419
- The learn command is complete
362-
- Outputs created: AGENTS.md
420+
- Outputs created: learning_summary.md
363421
- This command can be run again anytime to make further changes
364422
365423
## Command Complete

.gemini/commands/deepwork_policy/define.toml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ If there are files that, when also changed, mean the policy shouldn't fire:
9393
- Trigger: `src/auth/**/*`
9494
- Safety: `SECURITY.md`, `docs/security_review.md`
9595
96+
### Step 3b: Choose the Comparison Mode (Optional)
97+
98+
The `compare_to` field controls what baseline is used when detecting "changed files":
99+
100+
**Options:**
101+
- `base` (default) - Compares to the base of the current branch (merge-base with main/master). This is the most common choice for feature branches, as it shows all changes made on the branch.
102+
- `default_tip` - Compares to the current tip of the default branch (main/master). Useful when you want to see the difference from what's currently in production.
103+
- `prompt` - Compares to the state at the start of each prompt. Useful for policies that should only fire based on changes made during a single agent response.
104+
105+
**When to use each:**
106+
- **base**: Best for most policies. "Did this branch change config files?" → trigger docs review
107+
- **default_tip**: For policies about what's different from production/main
108+
- **prompt**: For policies that should only consider very recent changes within the current session
109+
110+
Most policies should use the default (`base`) and don't need to specify `compare_to`.
111+
96112
### Step 4: Write the Instructions
97113
98114
Create clear, actionable instructions for what the agent should do when the policy fires.
@@ -123,6 +139,7 @@ Create or update `.deepwork.policy.yml` in the project root.
123139
- name: "[Friendly name for the policy]"
124140
trigger: "[glob pattern]" # or array: ["pattern1", "pattern2"]
125141
safety: "[glob pattern]" # optional, or array
142+
compare_to: "base" # optional: "base" (default), "default_tip", or "prompt"
126143
instructions: |
127144
[Multi-line instructions for the agent...]
128145
```
@@ -132,6 +149,7 @@ Create or update `.deepwork.policy.yml` in the project root.
132149
- name: "[Friendly name for the policy]"
133150
trigger: "[glob pattern]"
134151
safety: "[glob pattern]"
152+
compare_to: "base" # optional
135153
instructions_file: "path/to/instructions.md"
136154
```
137155
@@ -203,7 +221,10 @@ Create or update this file at the project root with the new policy entry.
203221
## Context
204222
205223
Policies are evaluated automatically when you finish working on a task. The system:
206-
1. Tracks which files you changed during the session
224+
1. Determines which files have changed based on each policy's `compare_to` setting:
225+
- `base` (default): Files changed since the branch diverged from main/master
226+
- `default_tip`: Files different from the current main/master branch
227+
- `prompt`: Files changed since the last prompt submission
207228
2. Checks if any changes match policy trigger patterns
208229
3. Skips policies where safety patterns also matched
209230
4. Prompts you with instructions for any triggered policies

0 commit comments

Comments
 (0)