Skip to content
Open
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
38 changes: 0 additions & 38 deletions .deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh

This file was deleted.

5 changes: 4 additions & 1 deletion .deepwork/jobs/deepwork_rules/hooks/global_hooks.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# DeepWork Rules Hooks Configuration
# Maps lifecycle events to hook scripts or Python modules
#
# All hooks use Python modules for cross-platform compatibility (Windows, macOS, Linux).
# The module syntax ensures hooks work regardless of how DeepWork was installed.

UserPromptSubmit:
- user_prompt_submit.sh
- module: deepwork.hooks.user_prompt_submit

Stop:
- module: deepwork.hooks.rules_check
16 changes: 0 additions & 16 deletions .deepwork/jobs/deepwork_rules/hooks/user_prompt_submit.sh

This file was deleted.

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

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

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

## [0.5.1] - 2026-01-24

Expand Down
53 changes: 25 additions & 28 deletions doc/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ deepwork/ # DeepWork tool repository
│ │ ├── rules_queue.py # Rule state queue system
│ │ ├── command_executor.py # Command action execution
│ │ └── hooks_syncer.py # Hook syncing to platforms
│ ├── hooks/ # Hook system and cross-platform wrappers
│ ├── hooks/ # Hook system (cross-platform Python modules)
│ │ ├── __init__.py
│ │ ├── wrapper.py # Cross-platform input/output normalization
│ │ ├── claude_hook.sh # Shell wrapper for Claude Code
│ │ ├── gemini_hook.sh # Shell wrapper for Gemini CLI
│ │ └── rules_check.py # Cross-platform rule evaluation hook
│ │ ├── rules_check.py # Cross-platform rule evaluation hook
│ │ ├── user_prompt_submit.py # Captures work tree on prompt submission
│ │ ├── capture_prompt.py # Git work tree state capture utility
│ │ └── hook_entry.py # Cross-platform hook entry point
│ ├── templates/ # Skill templates for each platform
│ │ ├── claude/
│ │ │ └── skill-job-step.md.jinja
Expand All @@ -73,10 +74,8 @@ deepwork/ # DeepWork tool repository
│ │ ├── job.yml
│ │ ├── steps/
│ │ │ └── define.md
│ │ └── hooks/ # Hook scripts
│ │ ├── global_hooks.yml
│ │ ├── user_prompt_submit.sh
│ │ └── capture_prompt_work_tree.sh
│ │ └── hooks/ # Hook configuration
│ │ └── global_hooks.yml # Maps events to Python modules
│ ├── schemas/ # Definition schemas
│ │ ├── job_schema.py
│ │ ├── doc_spec_schema.py # Doc spec schema definition
Expand Down Expand Up @@ -310,10 +309,8 @@ my-project/ # User's project (target)
│ │ ├── job.yml
│ │ ├── steps/
│ │ │ └── define.md
│ │ └── hooks/ # Hook scripts (installed from standard_jobs)
│ │ ├── global_hooks.yml
│ │ ├── user_prompt_submit.sh
│ │ └── capture_prompt_work_tree.sh
│ │ └── hooks/ # Hook configuration (installed from standard_jobs)
│ │ └── global_hooks.yml # Maps events to Python modules
│ ├── competitive_research/
│ │ ├── job.yml # Job metadata
│ │ └── steps/
Expand Down Expand Up @@ -1142,19 +1139,20 @@ This prevents re-prompting for the same rule violation within a session.

### Hook Integration

The v2 rules system uses the cross-platform hook wrapper:
The v2 rules system uses cross-platform Python hooks:

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

Hooks are called via the shell wrappers:
Hooks are invoked via the `deepwork hook` CLI command:
```bash
claude_hook.sh deepwork.hooks.rules_check
deepwork hook rules_check
```

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

### Cross-Platform Hook Wrapper System
### Cross-Platform Hook System

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.
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.

**Architecture:**
```
┌─────────────────┐ ┌─────────────────┐
│ Claude Code │ │ Gemini CLI │
│ (Stop event) │ │ (AfterAgent) │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ claude_hook.sh │ │ gemini_hook.sh │
│ (shell wrapper) │ │ (shell wrapper) │
└────────┬────────┘ └────────┬────────┘
│ │
└───────────┬───────────┘
┌─────────────────┐
│ deepwork hook │
│ (CLI entry) │
└────────┬────────┘
┌─────────────────┐
│ wrapper.py │
│ (normalization) │
└────────┬────────┘
Expand All @@ -1214,7 +1211,7 @@ def my_hook(input: HookInput) -> HookOutput:
return HookOutput(decision="block", reason="Complete X first")
return HookOutput()

# Called via: claude_hook.sh mymodule or gemini_hook.sh mymodule
# Called via: deepwork hook mymodule
```

See `doc/platforms/` for detailed platform-specific hook documentation.
Expand Down
15 changes: 12 additions & 3 deletions src/deepwork/core/hooks_syncer.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def get_command(self, project_path: Path) -> str:
"""
Get the command to run this hook.

This generates a cross-platform command that works on Windows, macOS, and Linux.
For module-based hooks, uses `deepwork hook <name>` which works everywhere.
For script-based hooks, uses forward slashes (works in bash on all platforms).

Args:
project_path: Path to project root

Expand All @@ -43,10 +47,15 @@ def get_command(self, project_path: Path) -> str:
# Script path is: .deepwork/jobs/{job_name}/hooks/{script}
script_path = self.job_dir / "hooks" / self.script
try:
return str(script_path.relative_to(project_path))
rel_path = script_path.relative_to(project_path)
except ValueError:
# If not relative, return the full path
return str(script_path)
# If not relative, use the full path
rel_path = script_path

# Always use forward slashes for cross-platform compatibility
# Claude Code runs hooks via bash (even on Windows via Git Bash/WSL)
# and bash expects forward slashes
return str(rel_path).replace("\\", "/")
else:
raise ValueError("HookEntry must have either script or module")

Expand Down
29 changes: 12 additions & 17 deletions src/deepwork/hooks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,36 @@

This package provides:

1. Cross-platform hook wrapper system:
1. Cross-platform hook system (Windows, macOS, Linux):
- wrapper.py: Normalizes input/output between Claude Code and Gemini CLI
- claude_hook.sh: Shell wrapper for Claude Code hooks
- gemini_hook.sh: Shell wrapper for Gemini CLI hooks
- All hooks use Python modules for cross-platform compatibility

2. Hook implementations:
- rules_check.py: Evaluates rules on after_agent events
- user_prompt_submit.py: Captures work tree state on prompt submission
- capture_prompt.py: Git work tree state capture utility

Usage with wrapper system:
# Register hook in .claude/settings.json:
Usage:
# Hooks are registered in .claude/settings.json by `deepwork sync`:
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": ".deepwork/hooks/claude_hook.sh rules_check"
"command": "deepwork hook rules_check"
}]
}]
}
}

# Register hook in .gemini/settings.json:
{
"hooks": {
"AfterAgent": [{
}],
"UserPromptSubmit": [{
"hooks": [{
"type": "command",
"command": ".gemini/hooks/gemini_hook.sh rules_check"
"command": "deepwork hook user_prompt_submit"
}]
}]
}
}

The shell wrappers call `deepwork hook <hook_name>` which works regardless
of how deepwork was installed (pipx, uv, nix flake, etc.).
The `deepwork hook <name>` command works on all platforms regardless
of how deepwork was installed (pip, pipx, uv, Windows EXE, etc.).

Writing custom hooks:
from deepwork.hooks.wrapper import (
Expand Down
Loading
Loading