deepwork 0.3.1__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. deepwork/cli/hook.py +70 -0
  2. deepwork/cli/install.py +58 -14
  3. deepwork/cli/main.py +4 -0
  4. deepwork/cli/rules.py +32 -0
  5. deepwork/cli/sync.py +27 -1
  6. deepwork/core/adapters.py +209 -0
  7. deepwork/core/command_executor.py +26 -9
  8. deepwork/core/doc_spec_parser.py +205 -0
  9. deepwork/core/generator.py +79 -4
  10. deepwork/core/hooks_syncer.py +15 -2
  11. deepwork/core/parser.py +64 -2
  12. deepwork/hooks/__init__.py +9 -3
  13. deepwork/hooks/check_version.sh +230 -0
  14. deepwork/hooks/claude_hook.sh +13 -17
  15. deepwork/hooks/gemini_hook.sh +13 -17
  16. deepwork/hooks/rules_check.py +71 -12
  17. deepwork/hooks/wrapper.py +66 -16
  18. deepwork/schemas/doc_spec_schema.py +64 -0
  19. deepwork/schemas/job_schema.py +25 -3
  20. deepwork/standard_jobs/deepwork_jobs/doc_specs/job_spec.md +190 -0
  21. deepwork/standard_jobs/deepwork_jobs/job.yml +41 -8
  22. deepwork/standard_jobs/deepwork_jobs/steps/define.md +68 -2
  23. deepwork/standard_jobs/deepwork_jobs/steps/implement.md +3 -3
  24. deepwork/standard_jobs/deepwork_jobs/steps/learn.md +74 -5
  25. deepwork/standard_jobs/deepwork_jobs/steps/review_job_spec.md +208 -0
  26. deepwork/standard_jobs/deepwork_jobs/templates/doc_spec.md.example +86 -0
  27. deepwork/standard_jobs/deepwork_jobs/templates/doc_spec.md.template +26 -0
  28. deepwork/standard_jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh +8 -0
  29. deepwork/standard_jobs/deepwork_rules/job.yml +5 -3
  30. deepwork/templates/claude/skill-job-meta.md.jinja +7 -0
  31. deepwork/templates/claude/skill-job-step.md.jinja +60 -7
  32. deepwork/templates/gemini/skill-job-step.toml.jinja +18 -3
  33. deepwork/utils/fs.py +36 -0
  34. deepwork/utils/yaml_utils.py +24 -0
  35. {deepwork-0.3.1.dist-info → deepwork-0.4.0.dist-info}/METADATA +39 -2
  36. {deepwork-0.3.1.dist-info → deepwork-0.4.0.dist-info}/RECORD +39 -30
  37. {deepwork-0.3.1.dist-info → deepwork-0.4.0.dist-info}/WHEEL +0 -0
  38. {deepwork-0.3.1.dist-info → deepwork-0.4.0.dist-info}/entry_points.txt +0 -0
  39. {deepwork-0.3.1.dist-info → deepwork-0.4.0.dist-info}/licenses/LICENSE.md +0 -0
@@ -0,0 +1,208 @@
1
+ # Review Job Specification
2
+
3
+ ## Objective
4
+
5
+ Review the `job.yml` created in the define step against the doc spec quality criteria using a sub-agent for unbiased evaluation, then iterate on fixes until all criteria pass.
6
+
7
+ ## Why This Step Exists
8
+
9
+ The define step focuses on understanding user requirements and creating a job specification. This review step ensures the specification meets quality standards before implementation. Using a sub-agent provides an unbiased "fresh eyes" review that catches issues the main agent might miss after being deeply involved in the definition process.
10
+
11
+ ## Task
12
+
13
+ Use a sub-agent to review the job.yml against all 9 doc spec quality criteria, then fix any failed criteria. Repeat until all criteria pass.
14
+
15
+ ### Step 1: Read the Job Specification
16
+
17
+ Read the `job.yml` file created in the define step:
18
+
19
+ ```
20
+ .deepwork/jobs/[job_name]/job.yml
21
+ ```
22
+
23
+ Also read the doc spec for reference:
24
+
25
+ ```
26
+ .deepwork/doc_specs/job_spec.md
27
+ ```
28
+
29
+ ### Step 2: Spawn Review Sub-Agent
30
+
31
+ Use the Task tool to spawn a sub-agent that will provide an unbiased review:
32
+
33
+ ```
34
+ Task tool parameters:
35
+ - subagent_type: "general-purpose"
36
+ - model: "haiku"
37
+ - description: "Review job.yml against doc spec"
38
+ - prompt: [see below]
39
+ ```
40
+
41
+ **Sub-agent prompt template:**
42
+
43
+ ```
44
+ Review this job.yml against the following 9 quality criteria from the doc spec.
45
+
46
+ For each criterion, respond with:
47
+ - PASS or FAIL
48
+ - If FAIL: specific issue and suggested fix
49
+
50
+ ## job.yml Content
51
+
52
+ [paste the full job.yml content here]
53
+
54
+ ## Quality Criteria
55
+
56
+ 1. **Valid Identifier**: Job name must be lowercase with underscores, no spaces or special characters (e.g., `competitive_research`, `monthly_report`)
57
+
58
+ 2. **Semantic Version**: Version must follow semantic versioning format X.Y.Z (e.g., `1.0.0`, `2.1.3`)
59
+
60
+ 3. **Concise Summary**: Summary must be under 200 characters and clearly describe what the job accomplishes
61
+
62
+ 4. **Rich Description**: Description must be multi-line and explain: the problem solved, the process, expected outcomes, and target users
63
+
64
+ 5. **Changelog Present**: Must include a changelog array with at least the initial version entry
65
+
66
+ 6. **Complete Steps**: Each step must have: id (lowercase_underscores), name, description, instructions_file, outputs (at least one), and dependencies array
67
+
68
+ 7. **Valid Dependencies**: Dependencies must reference existing step IDs with no circular references
69
+
70
+ 8. **Input Consistency**: File inputs with `from_step` must reference a step that is in the dependencies array
71
+
72
+ 9. **Output Paths**: Outputs must be valid filenames or paths (e.g., `report.md` or `reports/analysis.md`)
73
+
74
+ ## Response Format
75
+
76
+ Respond with a structured evaluation:
77
+
78
+ ### Overall: [X/9 PASS]
79
+
80
+ ### Criterion Results
81
+
82
+ 1. Valid Identifier: [PASS/FAIL]
83
+ [If FAIL: Issue and fix]
84
+
85
+ 2. Semantic Version: [PASS/FAIL]
86
+ [If FAIL: Issue and fix]
87
+
88
+ [... continue for all 9 criteria ...]
89
+
90
+ ### Summary of Required Fixes
91
+
92
+ [List any fixes needed, or "No fixes required - all criteria pass"]
93
+ ```
94
+
95
+ ### Step 3: Review Sub-Agent Findings
96
+
97
+ Parse the sub-agent's response:
98
+
99
+ 1. **Count passing criteria** - How many of the 9 criteria passed?
100
+ 2. **Identify failures** - List specific criteria that failed
101
+ 3. **Note suggested fixes** - What changes does the sub-agent recommend?
102
+
103
+ ### Step 4: Fix Failed Criteria
104
+
105
+ For each failed criterion, edit the job.yml to address the issue:
106
+
107
+ **Common fixes by criterion:**
108
+
109
+ | Criterion | Common Issue | Fix |
110
+ |-----------|-------------|-----|
111
+ | Valid Identifier | Spaces or uppercase | Convert to lowercase_underscores |
112
+ | Semantic Version | Missing or invalid format | Set to `"1.0.0"` or fix format |
113
+ | Concise Summary | Too long or vague | Shorten to <200 chars, be specific |
114
+ | Rich Description | Single line or missing context | Add multi-line explanation with problem/process/outcome/users |
115
+ | Changelog Present | Missing changelog | Add `changelog:` with initial version entry |
116
+ | Complete Steps | Missing required fields | Add id, name, description, instructions_file, outputs, dependencies |
117
+ | Valid Dependencies | Non-existent step or circular | Fix step ID reference or reorder dependencies |
118
+ | Input Consistency | from_step not in dependencies | Add the referenced step to dependencies array |
119
+ | Output Paths | Invalid characters or format | Use valid filename/path format |
120
+
121
+ ### Step 5: Re-Run Review (If Needed)
122
+
123
+ If any criteria failed:
124
+
125
+ 1. **Spawn a new sub-agent** with the updated job.yml content
126
+ 2. **Review the new findings**
127
+ 3. **Fix any remaining issues**
128
+ 4. **Repeat until all 9 criteria pass**
129
+
130
+ ### Step 6: Confirm Completion
131
+
132
+ When all 9 criteria pass:
133
+
134
+ 1. **Announce success**: "All 9 doc spec quality criteria pass."
135
+ 2. **List what was validated**:
136
+ - Valid Identifier
137
+ - Semantic Version
138
+ - Concise Summary
139
+ - Rich Description
140
+ - Changelog Present
141
+ - Complete Steps
142
+ - Valid Dependencies
143
+ - Input Consistency
144
+ - Output Paths
145
+ 3. **Include the promise**: `<promise>Quality Criteria Met</promise>`
146
+ 4. **Guide to next step**: "Run `/deepwork_jobs.implement` to generate the step instruction files."
147
+
148
+ ## Example Review Cycle
149
+
150
+ ```
151
+ Claude: Let me review the job.yml against the doc spec quality criteria.
152
+
153
+ [Spawns sub-agent with haiku model]
154
+
155
+ Sub-agent response:
156
+ ### Overall: 7/9 PASS
157
+
158
+ ### Criterion Results
159
+ 1. Valid Identifier: PASS
160
+ 2. Semantic Version: PASS
161
+ 3. Concise Summary: FAIL
162
+ Issue: Summary is 245 characters, exceeds 200 char limit
163
+ Fix: Shorten to "Competitive analysis workflow for product positioning"
164
+ 4. Rich Description: PASS
165
+ 5. Changelog Present: FAIL
166
+ Issue: No changelog array present
167
+ Fix: Add changelog with initial version entry
168
+ 6. Complete Steps: PASS
169
+ 7. Valid Dependencies: PASS
170
+ 8. Input Consistency: PASS
171
+ 9. Output Paths: PASS
172
+
173
+ ### Summary of Required Fixes
174
+ 1. Shorten summary to under 200 characters
175
+ 2. Add changelog array with initial version
176
+
177
+ Claude: I found 2 issues. Let me fix them.
178
+
179
+ [Edits job.yml to shorten summary and add changelog]
180
+
181
+ Claude: Let me verify the fixes with another review.
182
+
183
+ [Spawns new sub-agent]
184
+
185
+ Sub-agent response:
186
+ ### Overall: 9/9 PASS
187
+
188
+ ### Summary of Required Fixes
189
+ No fixes required - all criteria pass
190
+
191
+ Claude: All 9 doc spec quality criteria pass.
192
+
193
+ <promise>Quality Criteria Met</promise>
194
+
195
+ **Next step:** Run `/deepwork_jobs.implement` to generate the step instruction files.
196
+ ```
197
+
198
+ ## Quality Criteria
199
+
200
+ - **Sub-Agent Used**: A sub-agent was spawned to provide unbiased review (not just self-review)
201
+ - **All doc spec Criteria Evaluated**: The sub-agent assessed all 9 quality criteria from the doc spec
202
+ - **Findings Addressed**: All failed criteria were fixed by the main agent
203
+ - **Validation Loop Complete**: The review-fix cycle continued until all criteria passed
204
+ - **Promise Included**: The response includes `<promise>Quality Criteria Met</promise>` when complete
205
+
206
+ ## Output
207
+
208
+ The validated `job.yml` file at `.deepwork/jobs/[job_name]/job.yml` that passes all 9 doc spec quality criteria.
@@ -0,0 +1,86 @@
1
+ ---
2
+ name: "Monthly AWS Spending Report"
3
+ description: "A Markdown summary of AWS spend across accounts for finance and engineering leadership"
4
+ path_patterns:
5
+ - "finance/aws-reports/*.md"
6
+ - "reports/aws/*.md"
7
+ target_audience: "Finance team and Engineering leadership"
8
+ frequency: "Monthly, following AWS invoice arrival"
9
+ quality_criteria:
10
+ - name: Executive Summary
11
+ description: Must include a 2-3 sentence summary of total spend, month-over-month change percentage, and top cost driver
12
+ - name: Visualization
13
+ description: Must include at least one Mermaid.js chart showing spend breakdown by service (pie chart) or trend over time (line chart)
14
+ - name: Variance Analysis
15
+ description: Must compare current month against previous month with percentage changes for top 5 services
16
+ - name: Cost Attribution
17
+ description: Must break down costs by team/project using AWS tags where available
18
+ - name: Action Items
19
+ description: Must include 2-3 specific, actionable recommendations for cost optimization
20
+ ---
21
+
22
+ # Monthly AWS Spending Report: [Month, Year]
23
+
24
+ ## Executive Summary
25
+
26
+ [2-3 sentences summarizing:
27
+ - Total spend this month
28
+ - Percentage change from last month
29
+ - The primary cost driver]
30
+
31
+ ## Spend Overview
32
+
33
+ ### Total Spend by Service
34
+
35
+ ```mermaid
36
+ pie title AWS Spend by Service
37
+ "EC2" : 45
38
+ "RDS" : 25
39
+ "S3" : 15
40
+ "Lambda" : 10
41
+ "Other" : 5
42
+ ```
43
+
44
+ ### Month-over-Month Trend
45
+
46
+ ```mermaid
47
+ xychart-beta
48
+ title "Monthly AWS Spend Trend"
49
+ x-axis [Jan, Feb, Mar, Apr, May, Jun]
50
+ y-axis "Spend ($K)" 0 --> 100
51
+ line [45, 48, 52, 49, 55, 58]
52
+ ```
53
+
54
+ ## Variance Analysis
55
+
56
+ | Service | Last Month | This Month | Change | % Change |
57
+ |---------|-----------|------------|--------|----------|
58
+ | EC2 | $X,XXX | $X,XXX | +$XXX | +X.X% |
59
+ | RDS | $X,XXX | $X,XXX | -$XXX | -X.X% |
60
+ | S3 | $X,XXX | $X,XXX | +$XXX | +X.X% |
61
+ | Lambda | $X,XXX | $X,XXX | +$XXX | +X.X% |
62
+ | Other | $X,XXX | $X,XXX | +$XXX | +X.X% |
63
+
64
+ ## Cost Attribution by Team
65
+
66
+ | Team/Project | Spend | % of Total |
67
+ |--------------|-------|------------|
68
+ | Platform | $X,XXX | XX% |
69
+ | Data | $X,XXX | XX% |
70
+ | Product | $X,XXX | XX% |
71
+ | Shared | $X,XXX | XX% |
72
+
73
+ ## Recommendations
74
+
75
+ 1. **[Action Item 1]**: [Specific recommendation with expected savings]
76
+ 2. **[Action Item 2]**: [Specific recommendation with expected savings]
77
+ 3. **[Action Item 3]**: [Specific recommendation with expected savings]
78
+
79
+ ## Appendix
80
+
81
+ ### Data Sources
82
+ - AWS Cost Explorer
83
+ - AWS Tags: team, project, environment
84
+
85
+ ### Report Methodology
86
+ [Brief explanation of how costs are calculated and attributed]
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: "[Document Name]"
3
+ description: "[Brief description of the document's purpose]"
4
+ path_patterns:
5
+ - "[path/to/documents/*.md]"
6
+ target_audience: "[Who reads this document]"
7
+ frequency: "[How often produced, e.g., Monthly, Per sprint, On demand]"
8
+ quality_criteria:
9
+ - name: "[Criterion Name]"
10
+ description: "[What this criterion requires - be specific]"
11
+ - name: "[Criterion Name]"
12
+ description: "[What this criterion requires - be specific]"
13
+ - name: "[Criterion Name]"
14
+ description: "[What this criterion requires - be specific]"
15
+ ---
16
+
17
+ # [Document Title]: [Variables like Month, Year, Sprint]
18
+
19
+ ## Section 1
20
+ [Describe what goes in this section]
21
+
22
+ ## Section 2
23
+ [Describe what goes in this section]
24
+
25
+ ## Section 3
26
+ [Describe what goes in this section]
@@ -8,12 +8,20 @@
8
8
  # The baseline contains ALL tracked files (not just changed files) so that
9
9
  # the rules_check hook can determine which files are genuinely new vs which
10
10
  # files existed before and were just modified.
11
+ #
12
+ # It also captures the HEAD commit ref so that committed changes can be detected
13
+ # by comparing HEAD at Stop time to the captured ref.
11
14
 
12
15
  set -e
13
16
 
14
17
  # Ensure .deepwork directory exists
15
18
  mkdir -p .deepwork
16
19
 
20
+ # Save the current HEAD commit ref for detecting committed changes
21
+ # This is used by get_changed_files_prompt() to detect files changed since prompt,
22
+ # even if those changes were committed during the agent response.
23
+ git rev-parse HEAD > .deepwork/.last_head_ref 2>/dev/null || echo "" > .deepwork/.last_head_ref
24
+
17
25
  # Save ALL tracked files (not just changed files)
18
26
  # This is critical for created: mode rules to distinguish between:
19
27
  # - Newly created files (not in baseline) -> should trigger created: rules
@@ -1,6 +1,6 @@
1
1
  name: deepwork_rules
2
- version: "0.3.0"
3
- summary: "Rules enforcement for AI agent sessions"
2
+ version: "0.4.0"
3
+ summary: "Creates file-change rules that enforce guidelines during AI sessions. Use when automating documentation sync or code review triggers."
4
4
  description: |
5
5
  Manages rules that automatically trigger when certain files change during an AI agent session.
6
6
  Rules help ensure that code changes follow team guidelines, documentation is updated,
@@ -33,11 +33,13 @@ changelog:
33
33
  changes: "Standardized on 'ask structured questions' phrasing for user input"
34
34
  - version: "0.3.0"
35
35
  changes: "Migrated to v2 format - individual markdown files in .deepwork/rules/"
36
+ - version: "0.4.0"
37
+ changes: "Improved skill descriptions with third-person voice and 'Use when...' triggers for better discoverability"
36
38
 
37
39
  steps:
38
40
  - id: define
39
41
  name: "Define Rule"
40
- description: "Create a new rule file in .deepwork/rules/"
42
+ description: "Creates a rule file that triggers when specified files change. Use when setting up documentation sync, code review requirements, or automated commands."
41
43
  instructions_file: steps/define.md
42
44
  inputs:
43
45
  - name: rule_purpose
@@ -65,6 +65,13 @@ If user intent is unclear, use AskUserQuestion to clarify:
65
65
  - Present available steps as numbered options
66
66
  - Let user select the starting point
67
67
 
68
+ ## Guardrails
69
+
70
+ - Do NOT copy/paste step instructions directly; always use the Skill tool to invoke steps
71
+ - Do NOT skip steps in the workflow unless the user explicitly requests it
72
+ - Do NOT proceed to the next step if the current step's outputs are incomplete
73
+ - Do NOT make assumptions about user intent; ask for clarification when ambiguous
74
+
68
75
  ## Context Files
69
76
 
70
77
  - Job definition: `.deepwork/jobs/{{ job_name }}/job.yml`
@@ -46,7 +46,8 @@ user-invocable: false
46
46
  {% if quality_criteria or hooks %}
47
47
  hooks:
48
48
  {% if quality_criteria %}
49
- Stop:
49
+ {% for event_name in ["Stop", "SubagentStop"] %}
50
+ {{ event_name }}:
50
51
  - hooks:
51
52
  - type: prompt
52
53
  prompt: |
@@ -63,14 +64,32 @@ hooks:
63
64
  Review the conversation and determine if ALL quality criteria above have been satisfied.
64
65
  Look for evidence that each criterion has been addressed.
65
66
 
66
- If the agent has included `<promise>✓ Quality Criteria Met</promise>` in their response AND
67
+ If the agent has included `<promise>✓ Quality Criteria Met</promise>` in their response OR
67
68
  all criteria appear to be met, respond with: {"ok": true}
68
69
 
69
- If criteria are NOT met OR the promise tag is missing, respond with:
70
+ If criteria are NOT met AND the promise tag is missing, respond with:
70
71
  {"ok": false, "reason": "**AGENT: TAKE ACTION** - [which criteria failed and why]"}
72
+ {% endfor %}
71
73
  {% endif %}
72
74
  {% for event_name, event_hooks in hooks.items() %}
73
- {% if not (event_name == "Stop" and quality_criteria) %}
75
+ {% if not (event_name == "Stop" and quality_criteria) and not (event_name == "SubagentStop" and "Stop" in hooks) %}
76
+ {# For Stop events, generate both Stop and SubagentStop blocks #}
77
+ {% if event_name == "Stop" %}
78
+ {% for stop_event in ["Stop", "SubagentStop"] %}
79
+ {{ stop_event }}:
80
+ - hooks:
81
+ {% for hook in event_hooks %}
82
+ {% if hook.type == "script" %}
83
+ - type: command
84
+ command: ".deepwork/jobs/{{ job_name }}/{{ hook.path }}"
85
+ {% else %}
86
+ - type: prompt
87
+ prompt: |
88
+ {{ hook.content | indent(12) }}
89
+ {% endif %}
90
+ {% endfor %}
91
+ {% endfor %}
92
+ {% else %}
74
93
  {{ event_name }}:
75
94
  - hooks:
76
95
  {% for hook in event_hooks %}
@@ -84,6 +103,7 @@ hooks:
84
103
  {% endif %}
85
104
  {% endfor %}
86
105
  {% endif %}
106
+ {% endif %}
87
107
  {% endfor %}
88
108
  {% endif %}
89
109
  ---
@@ -149,12 +169,45 @@ Use branch format: `deepwork/{{ job_name }}-[instance]-YYYYMMDD`
149
169
  {% if outputs %}
150
170
  **Required outputs**:
151
171
  {% for output in outputs %}
152
- - `{{ output }}`{% if output.endswith('/') %} (directory){% endif %}
172
+ - `{{ output.file }}`{% if output.file.endswith('/') %} (directory){% endif %}
173
+
174
+ {% if output.has_doc_spec and output.doc_spec %}
175
+ **Doc Spec**: {{ output.doc_spec.name }}
176
+ > {{ output.doc_spec.description }}
177
+ **Definition**: `{{ output.doc_spec.path }}`
178
+ {% if output.doc_spec.target_audience %}
179
+ **Target Audience**: {{ output.doc_spec.target_audience }}
180
+ {% endif %}
181
+ {% if output.doc_spec.quality_criteria %}
182
+ **Quality Criteria**:
183
+ {% for criterion in output.doc_spec.quality_criteria %}
184
+ {{ loop.index }}. **{{ criterion.name }}**: {{ criterion.description }}
185
+ {% endfor %}
186
+ {% endif %}
187
+ {% if output.doc_spec.example_document %}
188
+
189
+ <details>
190
+ <summary>Example Document Structure</summary>
191
+
192
+ ```markdown
193
+ {{ output.doc_spec.example_document | indent(2) }}
194
+ ```
195
+
196
+ </details>
197
+ {% endif %}
198
+ {% endif %}
153
199
  {% endfor %}
154
200
  {% else %}
155
201
  No specific file outputs required.
156
202
  {% endif %}
157
203
 
204
+ ## Guardrails
205
+
206
+ - Do NOT skip prerequisite verification if this step has dependencies
207
+ - Do NOT produce partial outputs; complete all required outputs before finishing
208
+ - Do NOT proceed without required inputs; ask the user if any are missing
209
+ - Do NOT modify files outside the scope of this step's defined outputs
210
+
158
211
  {% if quality_criteria or stop_hooks %}
159
212
  ## Quality Validation
160
213
 
@@ -180,12 +233,12 @@ Stop hooks will automatically validate your work. The loop continues until all c
180
233
 
181
234
  {% if is_standalone %}
182
235
  1. Verify outputs are created
183
- 2. Inform user: "{{ step_id }} complete{% if outputs %}, outputs: {{ outputs | join(', ') }}{% endif %}"
236
+ 2. Inform user: "{{ step_id }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
184
237
 
185
238
  This standalone skill can be re-run anytime.
186
239
  {% else %}
187
240
  1. Verify outputs are created
188
- 2. Inform user: "Step {{ step_number }}/{{ total_steps }} complete{% if outputs %}, outputs: {{ outputs | join(', ') }}{% endif %}"
241
+ 2. Inform user: "Step {{ step_number }}/{{ total_steps }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
189
242
  {% if next_step %}
190
243
  3. **Continue workflow**: Use Skill tool to invoke `/{{ job_name }}.{{ next_step }}`
191
244
  {% else %}
@@ -106,7 +106,22 @@ Use branch format: `deepwork/{{ job_name }}-[instance]-YYYYMMDD`
106
106
  {% if outputs %}
107
107
  **Required outputs**:
108
108
  {% for output in outputs %}
109
- - `{{ output }}`{% if output.endswith('/') %} (directory){% endif %}
109
+ - `{{ output.file }}`{% if output.file.endswith('/') %} (directory){% endif %}
110
+
111
+ {% if output.has_doc_spec and output.doc_spec %}
112
+ **Doc Spec**: {{ output.doc_spec.name }}
113
+ > {{ output.doc_spec.description }}
114
+ **Definition**: `{{ output.doc_spec.path }}`
115
+ {% if output.doc_spec.target_audience %}
116
+ **Target Audience**: {{ output.doc_spec.target_audience }}
117
+ {% endif %}
118
+ {% if output.doc_spec.quality_criteria %}
119
+ **Quality Criteria**:
120
+ {% for criterion in output.doc_spec.quality_criteria %}
121
+ {{ loop.index }}. **{{ criterion.name }}**: {{ criterion.description }}
122
+ {% endfor %}
123
+ {% endif %}
124
+ {% endif %}
110
125
  {% endfor %}
111
126
  {% else %}
112
127
  No specific file outputs required.
@@ -128,12 +143,12 @@ No specific file outputs required.
128
143
 
129
144
  {% if is_standalone %}
130
145
  1. Verify outputs are created
131
- 2. Inform user: "{{ step_id }} complete{% if outputs %}, outputs: {{ outputs | join(', ') }}{% endif %}"
146
+ 2. Inform user: "{{ step_id }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
132
147
 
133
148
  This standalone command can be re-run anytime.
134
149
  {% else %}
135
150
  1. Verify outputs are created
136
- 2. Inform user: "Step {{ step_number }}/{{ total_steps }} complete{% if outputs %}, outputs: {{ outputs | join(', ') }}{% endif %}"
151
+ 2. Inform user: "Step {{ step_number }}/{{ total_steps }} complete{% if outputs %}, outputs: {{ outputs | map(attribute='file') | join(', ') }}{% endif %}"
137
152
  {% if next_step %}
138
153
  3. **Tell user next command**: `/{{ job_name }}:{{ next_step }}`
139
154
  {% else %}
deepwork/utils/fs.py CHANGED
@@ -1,9 +1,43 @@
1
1
  """Filesystem utilities for safe file operations."""
2
2
 
3
3
  import shutil
4
+ import stat
4
5
  from pathlib import Path
5
6
 
6
7
 
8
+ def fix_permissions(path: Path | str) -> None:
9
+ """
10
+ Fix file permissions after copying to ensure files are user-writable.
11
+
12
+ This is needed because shutil.copytree/copy preserve source permissions,
13
+ and if the source was installed with restrictive permissions (e.g., read-only),
14
+ the copied files would also be read-only.
15
+
16
+ For directories: Sets rwx for user (0o700 minimum)
17
+ For files: Sets rw for user (0o600 minimum), preserves executable bit
18
+
19
+ Args:
20
+ path: File or directory path to fix permissions for
21
+ """
22
+ path_obj = Path(path)
23
+
24
+ if path_obj.is_file():
25
+ # Get current permissions
26
+ current_mode = path_obj.stat().st_mode
27
+ # Ensure user can read and write, preserve executable bit
28
+ new_mode = current_mode | stat.S_IRUSR | stat.S_IWUSR
29
+ path_obj.chmod(new_mode)
30
+ elif path_obj.is_dir():
31
+ # Fix directory permissions first (need execute to traverse)
32
+ current_mode = path_obj.stat().st_mode
33
+ new_mode = current_mode | stat.S_IRWXU # rwx for user
34
+ path_obj.chmod(new_mode)
35
+
36
+ # Recursively fix all contents
37
+ for item in path_obj.iterdir():
38
+ fix_permissions(item)
39
+
40
+
7
41
  def ensure_dir(path: Path | str) -> Path:
8
42
  """
9
43
  Create directory if it doesn't exist.
@@ -94,6 +128,8 @@ def copy_dir(src: Path | str, dst: Path | str, ignore_patterns: list[str] | None
94
128
  ignore_func = _ignore
95
129
 
96
130
  shutil.copytree(src_path, dst_path, ignore=ignore_func, dirs_exist_ok=True)
131
+ # Fix permissions - source may have restrictive permissions (e.g., read-only)
132
+ fix_permissions(dst_path)
97
133
 
98
134
 
99
135
  def find_files(directory: Path | str, pattern: str) -> list[Path]:
@@ -69,6 +69,30 @@ def save_yaml(path: Path | str, data: dict[str, Any]) -> None:
69
69
  raise YAMLError(f"Failed to write YAML file {path_obj}: {e}") from e
70
70
 
71
71
 
72
+ def load_yaml_from_string(content: str) -> dict[str, Any] | None:
73
+ """
74
+ Load YAML from a string and return parsed data.
75
+
76
+ Args:
77
+ content: YAML content as string
78
+
79
+ Returns:
80
+ Parsed YAML data as dictionary, or None if content is empty
81
+
82
+ Raises:
83
+ YAMLError: If YAML parsing fails
84
+ """
85
+ try:
86
+ data = yaml.safe_load(content)
87
+ if data is None:
88
+ return None
89
+ if not isinstance(data, dict):
90
+ raise YAMLError(f"YAML content must be a dictionary, got {type(data).__name__}")
91
+ return data
92
+ except yaml.YAMLError as e:
93
+ raise YAMLError(f"Failed to parse YAML content: {e}") from e
94
+
95
+
72
96
  def validate_yaml_structure(data: dict[str, Any], required_keys: list[str]) -> None:
73
97
  """
74
98
  Validate that YAML data contains required keys.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepwork
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: Framework for enabling AI agents to perform complex, multi-step work tasks
5
5
  Project-URL: Homepage, https://github.com/deepwork/deepwork
6
6
  Project-URL: Documentation, https://github.com/deepwork/deepwork#readme
@@ -73,7 +73,11 @@ Please fully install https://pypi.org/project/deepwork/ and make sure you see th
73
73
  ### Install DeepWork
74
74
 
75
75
  ```bash
76
- # Using pipx (recommended - isolated environment)
76
+ # Using Homebrew (macOS/Linux)
77
+ brew tap unsupervisedcom/deepwork
78
+ brew install deepwork
79
+
80
+ # Using pipx (isolated environment)
77
81
  pipx install deepwork
78
82
 
79
83
  # Or using uv
@@ -235,7 +239,39 @@ your-project/
235
239
  ## Documentation
236
240
 
237
241
  - **[Architecture](doc/architecture.md)**: Complete design specification
242
+ - **[Doc Specs](doc/doc-specs.md)**: Document specification format for output quality criteria
238
243
  - **[Contributing](CONTRIBUTING.md)**: Setup development environment and contribute
244
+ - **[Nix Flakes Guide](doc/nix-flake.md)**: Comprehensive guide for using DeepWork with Nix flakes
245
+
246
+ ## Development with Nix
247
+
248
+ DeepWork is available as a Nix flake for reproducible development environments:
249
+
250
+ ```bash
251
+ # Using Nix flakes
252
+ nix develop
253
+
254
+ # Or with direnv (automatic activation - recommended)
255
+ echo "use flake" > .envrc
256
+ direnv allow
257
+ ```
258
+
259
+ The Nix environment provides all dependencies including Python 3.11, uv, pytest, ruff, and mypy.
260
+
261
+ ### Installing DeepWork from Flake
262
+
263
+ You can also install deepwork directly from the flake:
264
+
265
+ ```bash
266
+ # Install deepwork from this flake
267
+ nix profile install github:Unsupervisedcom/deepwork
268
+
269
+ # Or run it without installing
270
+ nix run github:Unsupervisedcom/deepwork -- --help
271
+
272
+ # Or build the package
273
+ nix build github:Unsupervisedcom/deepwork
274
+ ```
239
275
 
240
276
  ## Project Structure
241
277
 
@@ -276,6 +312,7 @@ Define structured, multi-step workflows where each step has clear requirements a
276
312
  - **Artifact Passing**: Seamlessly use file outputs from one step as inputs for future steps.
277
313
  - **Dynamic Inputs**: Support for both fixed file references and interactive user parameters.
278
314
  - **Human-Readable YAML**: Simple, declarative job definitions that are easy to version and maintain.
315
+ - **Doc Specs**: Reference doc specs to enforce quality criteria on document outputs (see [doc specs documentation](doc/doc-specs.md)).
279
316
 
280
317
  ### Git-Native Workflow
281
318
  Maintain a clean repository with automatic branch management and isolation.