Skip to content

Commit 0cffca3

Browse files
committed
refactor: Convert bash hooks to Python for cross-platform compatibility
Replace bash hook scripts with Python module implementations that work on Windows, macOS, and Linux without requiring bash. This is a general improvement extracted from the Windows support work. Changes: - Add capture_prompt.py, user_prompt_submit.py, hook_entry.py modules - Update global_hooks.yml to use module references instead of .sh scripts - Update hooks_syncer.py to use forward slashes for cross-platform paths - Remove deprecated bash scripts (capture_prompt_work_tree.sh, user_prompt_submit.sh) - Remove make_new_job.sh bash permission (no longer needed) - Add cross-platform hooks tests https://claude.ai/code/session_011mvvEHLPHc8o9w6NwayWC6
1 parent 643326f commit 0cffca3

15 files changed

Lines changed: 387 additions & 159 deletions

File tree

.deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh

Lines changed: 0 additions & 38 deletions
This file was deleted.
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# DeepWork Rules Hooks Configuration
22
# Maps lifecycle events to hook scripts or Python modules
3+
#
4+
# All hooks use Python modules for cross-platform compatibility (Windows, macOS, Linux).
5+
# The module syntax ensures hooks work regardless of how DeepWork was installed.
36

47
UserPromptSubmit:
5-
- user_prompt_submit.sh
8+
- module: deepwork.hooks.user_prompt_submit
69

710
Stop:
811
- module: deepwork.hooks.rules_check

.deepwork/jobs/deepwork_rules/hooks/user_prompt_submit.sh

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

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2020
- Meta-skill template reorganized to show "Workflows" and "Standalone Skills" sections separately
2121
- Updated `deepwork_jobs` standard job to v1.0.0 with explicit `new_job` workflow
2222
- SessionStart hook now skips non-initial sessions (resume, compact/clear) by checking the `source` field in stdin JSON, reducing noise and redundant checks
23+
- Converted bash hook scripts to Python modules for cross-platform compatibility
24+
- `user_prompt_submit.sh` replaced by `deepwork.hooks.user_prompt_submit` Python module
25+
- `capture_prompt_work_tree.sh` replaced by `deepwork.hooks.capture_prompt` Python module
26+
- Added `hook_entry.py` for cross-platform hook invocation
27+
- Updated `global_hooks.yml` to use module references instead of shell scripts
28+
- Hooks now work on Windows, macOS, and Linux without requiring bash
2329

2430
### Fixed
2531
- Fixed skill template generating malformed YAML frontmatter with fields concatenated on single lines
@@ -28,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2834
- Affects `src/deepwork/templates/claude/skill-job-step.md.jinja`
2935

3036
### Removed
37+
- Removed deprecated bash hook scripts (`user_prompt_submit.sh`, `capture_prompt_work_tree.sh`)
38+
- Removed `make_new_job.sh` permission from Claude adapter (no longer needed)
3139

3240
## [0.5.1] - 2026-01-24
3341

doc/architecture.md

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,13 @@ deepwork/ # DeepWork tool repository
5252
│ │ ├── rules_queue.py # Rule state queue system
5353
│ │ ├── command_executor.py # Command action execution
5454
│ │ └── hooks_syncer.py # Hook syncing to platforms
55-
│ ├── hooks/ # Hook system and cross-platform wrappers
55+
│ ├── hooks/ # Hook system (cross-platform Python modules)
5656
│ │ ├── __init__.py
5757
│ │ ├── wrapper.py # Cross-platform input/output normalization
58-
│ │ ├── claude_hook.sh # Shell wrapper for Claude Code
59-
│ │ ├── gemini_hook.sh # Shell wrapper for Gemini CLI
60-
│ │ └── rules_check.py # Cross-platform rule evaluation hook
58+
│ │ ├── rules_check.py # Cross-platform rule evaluation hook
59+
│ │ ├── user_prompt_submit.py # Captures work tree on prompt submission
60+
│ │ ├── capture_prompt.py # Git work tree state capture utility
61+
│ │ └── hook_entry.py # Cross-platform hook entry point
6162
│ ├── templates/ # Skill templates for each platform
6263
│ │ ├── claude/
6364
│ │ │ └── skill-job-step.md.jinja
@@ -73,10 +74,8 @@ deepwork/ # DeepWork tool repository
7374
│ │ ├── job.yml
7475
│ │ ├── steps/
7576
│ │ │ └── define.md
76-
│ │ └── hooks/ # Hook scripts
77-
│ │ ├── global_hooks.yml
78-
│ │ ├── user_prompt_submit.sh
79-
│ │ └── capture_prompt_work_tree.sh
77+
│ │ └── hooks/ # Hook configuration
78+
│ │ └── global_hooks.yml # Maps events to Python modules
8079
│ ├── schemas/ # Definition schemas
8180
│ │ ├── job_schema.py
8281
│ │ ├── doc_spec_schema.py # Doc spec schema definition
@@ -310,10 +309,8 @@ my-project/ # User's project (target)
310309
│ │ ├── job.yml
311310
│ │ ├── steps/
312311
│ │ │ └── define.md
313-
│ │ └── hooks/ # Hook scripts (installed from standard_jobs)
314-
│ │ ├── global_hooks.yml
315-
│ │ ├── user_prompt_submit.sh
316-
│ │ └── capture_prompt_work_tree.sh
312+
│ │ └── hooks/ # Hook configuration (installed from standard_jobs)
313+
│ │ └── global_hooks.yml # Maps events to Python modules
317314
│ ├── competitive_research/
318315
│ │ ├── job.yml # Job metadata
319316
│ │ └── steps/
@@ -1142,19 +1139,20 @@ This prevents re-prompting for the same rule violation within a session.
11421139

11431140
### Hook Integration
11441141

1145-
The v2 rules system uses the cross-platform hook wrapper:
1142+
The v2 rules system uses cross-platform Python hooks:
11461143

11471144
```
11481145
src/deepwork/hooks/
1149-
├── wrapper.py # Cross-platform input/output normalization
1150-
├── rules_check.py # Rule evaluation hook (v2)
1151-
├── claude_hook.sh # Claude Code shell wrapper
1152-
└── gemini_hook.sh # Gemini CLI shell wrapper
1146+
├── wrapper.py # Cross-platform input/output normalization
1147+
├── rules_check.py # Rule evaluation hook (v2)
1148+
├── user_prompt_submit.py # Captures work tree on prompt submission
1149+
├── capture_prompt.py # Git work tree state capture utility
1150+
└── hook_entry.py # Cross-platform hook entry point
11531151
```
11541152

1155-
Hooks are called via the shell wrappers:
1153+
Hooks are invoked via the `deepwork hook` CLI command:
11561154
```bash
1157-
claude_hook.sh deepwork.hooks.rules_check
1155+
deepwork hook rules_check
11581156
```
11591157

11601158
The hooks are installed to `.claude/settings.json` during `deepwork sync`:
@@ -1169,26 +1167,25 @@ The hooks are installed to `.claude/settings.json` during `deepwork sync`:
11691167
}
11701168
```
11711169

1172-
### Cross-Platform Hook Wrapper System
1170+
### Cross-Platform Hook System
11731171

1174-
The `hooks/` module provides a wrapper system that allows writing hooks once in Python and running them on multiple platforms. This normalizes the differences between Claude Code and Gemini CLI hook systems.
1172+
The `hooks/` module provides a cross-platform hook system that works on Windows, macOS, and Linux without requiring bash. Hooks are written in Python and invoked via the `deepwork hook` CLI command.
11751173

11761174
**Architecture:**
11771175
```
11781176
┌─────────────────┐ ┌─────────────────┐
11791177
│ Claude Code │ │ Gemini CLI │
11801178
│ (Stop event) │ │ (AfterAgent) │
1181-
└────────┬────────┘ └────────┬────────┘
1182-
│ │
1183-
▼ ▼
1184-
┌─────────────────┐ ┌─────────────────┐
1185-
│ claude_hook.sh │ │ gemini_hook.sh │
1186-
│ (shell wrapper) │ │ (shell wrapper) │
11871179
└────────┬────────┘ └────────┬────────┘
11881180
│ │
11891181
└───────────┬───────────┘
11901182
11911183
┌─────────────────┐
1184+
│ deepwork hook │
1185+
│ (CLI entry) │
1186+
└────────┬────────┘
1187+
1188+
┌─────────────────┐
11921189
│ wrapper.py │
11931190
│ (normalization) │
11941191
└────────┬────────┘
@@ -1214,7 +1211,7 @@ def my_hook(input: HookInput) -> HookOutput:
12141211
return HookOutput(decision="block", reason="Complete X first")
12151212
return HookOutput()
12161213

1217-
# Called via: claude_hook.sh mymodule or gemini_hook.sh mymodule
1214+
# Called via: deepwork hook mymodule
12181215
```
12191216

12201217
See `doc/platforms/` for detailed platform-specific hook documentation.

src/deepwork/core/hooks_syncer.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def get_command(self, project_path: Path) -> str:
2828
"""
2929
Get the command to run this hook.
3030
31+
This generates a cross-platform command that works on Windows, macOS, and Linux.
32+
For module-based hooks, uses `deepwork hook <name>` which works everywhere.
33+
For script-based hooks, uses forward slashes (works in bash on all platforms).
34+
3135
Args:
3236
project_path: Path to project root
3337
@@ -43,10 +47,15 @@ def get_command(self, project_path: Path) -> str:
4347
# Script path is: .deepwork/jobs/{job_name}/hooks/{script}
4448
script_path = self.job_dir / "hooks" / self.script
4549
try:
46-
return str(script_path.relative_to(project_path))
50+
rel_path = script_path.relative_to(project_path)
4751
except ValueError:
48-
# If not relative, return the full path
49-
return str(script_path)
52+
# If not relative, use the full path
53+
rel_path = script_path
54+
55+
# Always use forward slashes for cross-platform compatibility
56+
# Claude Code runs hooks via bash (even on Windows via Git Bash/WSL)
57+
# and bash expects forward slashes
58+
return str(rel_path).replace("\\", "/")
5059
else:
5160
raise ValueError("HookEntry must have either script or module")
5261

src/deepwork/hooks/__init__.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,36 @@
22
33
This package provides:
44
5-
1. Cross-platform hook wrapper system:
5+
1. Cross-platform hook system (Windows, macOS, Linux):
66
- wrapper.py: Normalizes input/output between Claude Code and Gemini CLI
7-
- claude_hook.sh: Shell wrapper for Claude Code hooks
8-
- gemini_hook.sh: Shell wrapper for Gemini CLI hooks
7+
- All hooks use Python modules for cross-platform compatibility
98
109
2. Hook implementations:
1110
- rules_check.py: Evaluates rules on after_agent events
11+
- user_prompt_submit.py: Captures work tree state on prompt submission
12+
- capture_prompt.py: Git work tree state capture utility
1213
13-
Usage with wrapper system:
14-
# Register hook in .claude/settings.json:
14+
Usage:
15+
# Hooks are registered in .claude/settings.json by `deepwork sync`:
1516
{
1617
"hooks": {
1718
"Stop": [{
1819
"hooks": [{
1920
"type": "command",
20-
"command": ".deepwork/hooks/claude_hook.sh rules_check"
21+
"command": "deepwork hook rules_check"
2122
}]
22-
}]
23-
}
24-
}
25-
26-
# Register hook in .gemini/settings.json:
27-
{
28-
"hooks": {
29-
"AfterAgent": [{
23+
}],
24+
"UserPromptSubmit": [{
3025
"hooks": [{
3126
"type": "command",
32-
"command": ".gemini/hooks/gemini_hook.sh rules_check"
27+
"command": "deepwork hook user_prompt_submit"
3328
}]
3429
}]
3530
}
3631
}
3732
38-
The shell wrappers call `deepwork hook <hook_name>` which works regardless
39-
of how deepwork was installed (pipx, uv, nix flake, etc.).
33+
The `deepwork hook <name>` command works on all platforms regardless
34+
of how deepwork was installed (pip, pipx, uv, Windows EXE, etc.).
4035
4136
Writing custom hooks:
4237
from deepwork.hooks.wrapper import (

0 commit comments

Comments
 (0)