From a36eace6369e373790f1fe4a95a72c2e426bdc9b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 17:51:42 +0000 Subject: [PATCH 1/3] feat: add supplementary .md file support for step instructions Step instructions can now reference additional .md files placed in the steps/ directory. When slash commands are generated, references to these files (in backticks, markdown links, or quoted strings) are automatically transformed to relative paths from the project root. Changes: - Add _find_supplementary_files() to detect extra .md files in steps dir - Add _transform_md_references() to convert filename refs to full paths - Update Claude and Gemini templates to show supplementary files section - Document the feature in implement.md for deepwork_jobs - Add comprehensive tests for the new functionality --- src/deepwork/core/generator.py | 116 ++++++++++++ .../deepwork_jobs/steps/implement.md | 37 ++++ .../claude/command-job-step.md.jinja | 9 + .../gemini/command-job-step.toml.jinja | 9 + tests/unit/test_generator.py | 173 +++++++++++++++++- 5 files changed, 343 insertions(+), 1 deletion(-) diff --git a/src/deepwork/core/generator.py b/src/deepwork/core/generator.py index 380ab5b5..2817b03b 100644 --- a/src/deepwork/core/generator.py +++ b/src/deepwork/core/generator.py @@ -1,5 +1,6 @@ """Slash-command file generator using Jinja2 templates.""" +import re from pathlib import Path from typing import Any @@ -112,6 +113,112 @@ def _build_hook_context(self, job: JobDefinition, hook_action: Any) -> dict[str, hook_ctx["path"] = hook_action.script return hook_ctx + def _find_supplementary_files( + self, job: JobDefinition, step: Step + ) -> list[dict[str, str]]: + """ + Find supplementary .md files in the steps directory. + + Supplementary files are markdown files in the steps directory that are + NOT the main instruction file for any step. These can be referenced + in step instructions for additional context. + + Args: + job: Job definition + step: Current step + + Returns: + List of dicts with 'name' (filename) and 'path' (relative from project root) + """ + steps_dir = job.job_dir / "steps" + if not steps_dir.exists(): + return [] + + # Get all step instruction filenames (just the filename, not full path) + step_instruction_files = { + Path(s.instructions_file).name for s in job.steps + } + + supplementary_files = [] + for md_file in steps_dir.glob("*.md"): + # Skip if this is a main step instruction file + if md_file.name in step_instruction_files: + continue + + # Calculate relative path from project root + # job_dir is like .deepwork/jobs/[job_name] + # so the full path is .deepwork/jobs/[job_name]/steps/[filename] + relative_path = f".deepwork/jobs/{job.name}/steps/{md_file.name}" + + supplementary_files.append({ + "name": md_file.name, + "path": relative_path, + }) + + return sorted(supplementary_files, key=lambda x: x["name"]) + + def _transform_md_references( + self, content: str, supplementary_files: list[dict[str, str]] + ) -> str: + """ + Transform references to supplementary .md files into relative paths. + + This transforms references like `foo.md` into `.deepwork/jobs/[job_name]/steps/foo.md` + so that when the slash command is generated, the AI agent can find the files. + + Handles various reference patterns: + - `foo.md` (backtick code) + - [link](foo.md) (markdown links) + - "foo.md" or 'foo.md' (quoted strings) + + Args: + content: The instruction file content + supplementary_files: List of supplementary file info dicts + + Returns: + Content with references transformed to relative paths + """ + if not supplementary_files: + return content + + # Build a map of filename -> relative path + file_map = {f["name"]: f["path"] for f in supplementary_files} + + for filename, relative_path in file_map.items(): + # Escape special regex characters in filename + escaped_filename = re.escape(filename) + + # Pattern 1: backtick references like `foo.md` + # Only match if it's just the filename, not already a path + content = re.sub( + rf'`(? dict[str, Any]: @@ -127,12 +234,20 @@ def _build_step_context( Returns: Template context dictionary """ + # Find supplementary .md files in the steps directory + supplementary_files = self._find_supplementary_files(job, step) + # Read step instructions instructions_file = job.job_dir / step.instructions_file instructions_content = safe_read(instructions_file) if instructions_content is None: raise GeneratorError(f"Step instructions file not found: {instructions_file}") + # Transform references to supplementary files into relative paths + instructions_content = self._transform_md_references( + instructions_content, supplementary_files + ) + # Separate user inputs and file inputs user_inputs = [ {"name": inp.name, "description": inp.description} @@ -199,6 +314,7 @@ def _build_step_context( "is_standalone": is_standalone, "hooks": hooks, # New: all hooks by platform event name "stop_hooks": stop_hooks, # Backward compat: after_agent hooks only + "supplementary_files": supplementary_files, # Additional .md files in steps dir } def generate_step_command( diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md index de77741d..eb654dff 100644 --- a/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md +++ b/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md @@ -144,6 +144,43 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. +### Using Supplementary Reference Files + +Step instructions can reference additional `.md` files placed in the same `steps/` directory. These supplementary files are useful for: + +- Providing detailed examples or templates that would clutter the main instruction file +- Sharing common reference material across multiple steps +- Including technical specifications, API documentation, or style guides + +**How it works:** + +1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files +2. Reference them by filename in your step instructions using any of these patterns: + - Backticks: `` `example_template.md` `` + - Markdown links: `[see template](example_template.md)` + - Quoted strings: `"example_template.md"` or `'example_template.md'` + +3. When `deepwork sync` generates the slash commands, these references are automatically transformed to relative paths from the project root (e.g., `.deepwork/jobs/my_job/steps/example_template.md`) + +**Example:** + +If your job has a step instruction that says: +```markdown +Use the template in `api_spec.md` to structure your API endpoints. +``` + +And you have `.deepwork/jobs/my_job/steps/api_spec.md` containing the template, the generated slash command will transform this to: +```markdown +Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints. +``` + +The generated command will also list all supplementary files in a "Supplementary Reference Files" section at the end. + +**Important notes:** +- Supplementary files must be `.md` files in the `steps/` directory +- Files that match step instruction filenames (e.g., `define.md`, `implement.md`) are NOT treated as supplementary +- Only filename references are transformed; paths that already include directories are left unchanged + ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it. diff --git a/src/deepwork/templates/claude/command-job-step.md.jinja b/src/deepwork/templates/claude/command-job-step.md.jinja index ff7c2837..fc2f390c 100644 --- a/src/deepwork/templates/claude/command-job-step.md.jinja +++ b/src/deepwork/templates/claude/command-job-step.md.jinja @@ -208,3 +208,12 @@ Consider: - Job definition: `.deepwork/jobs/{{ job_name }}/job.yml` - Step instructions: `.deepwork/jobs/{{ job_name }}/{{ instructions_file }}` +{% if supplementary_files %} + +### Supplementary Reference Files + +The following additional reference files are available for this step: +{% for file in supplementary_files %} +- `{{ file.path }}` +{% endfor %} +{% endif %} diff --git a/src/deepwork/templates/gemini/command-job-step.toml.jinja b/src/deepwork/templates/gemini/command-job-step.toml.jinja index bb5d69be..ca5a8353 100644 --- a/src/deepwork/templates/gemini/command-job-step.toml.jinja +++ b/src/deepwork/templates/gemini/command-job-step.toml.jinja @@ -166,4 +166,13 @@ Consider: - Job definition: `.deepwork/jobs/{{ job_name }}/job.yml` - Step instructions: `.deepwork/jobs/{{ job_name }}/{{ instructions_file }}` +{% if supplementary_files %} + +### Supplementary Reference Files + +The following additional reference files are available for this step: +{% for file in supplementary_files %} +- `{{ file.path }}` +{% endfor %} +{% endif %} """ diff --git a/tests/unit/test_generator.py b/tests/unit/test_generator.py index f83d20a2..eb79b8fc 100644 --- a/tests/unit/test_generator.py +++ b/tests/unit/test_generator.py @@ -6,7 +6,7 @@ from deepwork.core.adapters import ClaudeAdapter from deepwork.core.generator import CommandGenerator, GeneratorError -from deepwork.core.parser import parse_job_definition +from deepwork.core.parser import JobDefinition, Step, parse_job_definition class TestCommandGenerator: @@ -197,3 +197,174 @@ def test_generate_all_commands(self, fixtures_dir: Path, temp_dir: Path) -> None ] actual_names = [p.name for p in command_paths] assert actual_names == expected_names + + +class TestSupplementaryFiles: + """Tests for supplementary file handling.""" + + def test_find_supplementary_files_empty_when_no_extras( + self, fixtures_dir: Path + ) -> None: + """Test that no supplementary files are found when only step files exist.""" + job_dir = fixtures_dir / "jobs" / "simple_job" + job = parse_job_definition(job_dir) + + generator = CommandGenerator() + supplementary = generator._find_supplementary_files(job, job.steps[0]) + + assert supplementary == [] + + def test_find_supplementary_files_detects_extra_md_files( + self, fixtures_dir: Path, temp_dir: Path + ) -> None: + """Test that supplementary .md files are detected.""" + # Copy simple job to temp dir and add supplementary file + job_dir = temp_dir / ".deepwork" / "jobs" / "test_job" + steps_dir = job_dir / "steps" + steps_dir.mkdir(parents=True) + + # Create job.yml + (job_dir / "job.yml").write_text(""" +name: test_job +version: "1.0.0" +summary: "Test job" +description: "A test job" +steps: + - id: step_one + name: "Step One" + description: "First step" + instructions_file: steps/step_one.md + outputs: + - output.md +""") + + # Create step instruction file + (steps_dir / "step_one.md").write_text("# Step One\n\nDo the thing.") + + # Create supplementary files + (steps_dir / "reference.md").write_text("# Reference\n\nSome reference content.") + (steps_dir / "template.md").write_text("# Template\n\nA template.") + + job = parse_job_definition(job_dir) + generator = CommandGenerator() + supplementary = generator._find_supplementary_files(job, job.steps[0]) + + assert len(supplementary) == 2 + assert supplementary[0]["name"] == "reference.md" + assert supplementary[0]["path"] == ".deepwork/jobs/test_job/steps/reference.md" + assert supplementary[1]["name"] == "template.md" + assert supplementary[1]["path"] == ".deepwork/jobs/test_job/steps/template.md" + + def test_transform_md_references_backticks(self) -> None: + """Test transformation of backtick references.""" + generator = CommandGenerator() + supplementary = [ + {"name": "foo.md", "path": ".deepwork/jobs/my_job/steps/foo.md"} + ] + + content = "Read the file `foo.md` for details." + result = generator._transform_md_references(content, supplementary) + + assert result == "Read the file `.deepwork/jobs/my_job/steps/foo.md` for details." + + def test_transform_md_references_markdown_links(self) -> None: + """Test transformation of markdown link references.""" + generator = CommandGenerator() + supplementary = [ + {"name": "template.md", "path": ".deepwork/jobs/my_job/steps/template.md"} + ] + + content = "See [the template](template.md) for examples." + result = generator._transform_md_references(content, supplementary) + + assert result == "See [the template](.deepwork/jobs/my_job/steps/template.md) for examples." + + def test_transform_md_references_quoted_strings(self) -> None: + """Test transformation of quoted string references.""" + generator = CommandGenerator() + supplementary = [ + {"name": "spec.md", "path": ".deepwork/jobs/my_job/steps/spec.md"} + ] + + content = 'Load "spec.md" or \'spec.md\' for the specification.' + result = generator._transform_md_references(content, supplementary) + + expected = 'Load ".deepwork/jobs/my_job/steps/spec.md" or \'.deepwork/jobs/my_job/steps/spec.md\' for the specification.' + assert result == expected + + def test_transform_md_references_preserves_paths(self) -> None: + """Test that existing paths are not double-transformed.""" + generator = CommandGenerator() + supplementary = [ + {"name": "foo.md", "path": ".deepwork/jobs/my_job/steps/foo.md"} + ] + + # Content with already-transformed path should not be affected + content = "Already transformed: `.deepwork/jobs/my_job/steps/foo.md`" + result = generator._transform_md_references(content, supplementary) + + # Should remain unchanged (the regex shouldn't match paths) + assert result == content + + def test_transform_md_references_no_supplementary_files(self) -> None: + """Test that content is unchanged when no supplementary files exist.""" + generator = CommandGenerator() + + content = "Read `something.md` for details." + result = generator._transform_md_references(content, []) + + assert result == content + + def test_transform_md_references_multiple_files(self) -> None: + """Test transformation with multiple supplementary files.""" + generator = CommandGenerator() + supplementary = [ + {"name": "api.md", "path": ".deepwork/jobs/my_job/steps/api.md"}, + {"name": "schema.md", "path": ".deepwork/jobs/my_job/steps/schema.md"}, + ] + + content = "See `api.md` for the API and `schema.md` for the schema." + result = generator._transform_md_references(content, supplementary) + + expected = "See `.deepwork/jobs/my_job/steps/api.md` for the API and `.deepwork/jobs/my_job/steps/schema.md` for the schema." + assert result == expected + + def test_generate_command_includes_supplementary_files( + self, temp_dir: Path + ) -> None: + """Test that generated commands include supplementary files section.""" + # Create a job with supplementary files + job_dir = temp_dir / ".deepwork" / "jobs" / "test_job" + steps_dir = job_dir / "steps" + steps_dir.mkdir(parents=True) + + (job_dir / "job.yml").write_text(""" +name: test_job +version: "1.0.0" +summary: "Test job" +description: "A test job" +steps: + - id: step_one + name: "Step One" + description: "First step" + instructions_file: steps/step_one.md + outputs: + - output.md +""") + + (steps_dir / "step_one.md").write_text("# Step One\n\nUse `reference.md` as a guide.") + (steps_dir / "reference.md").write_text("# Reference\n\nReference content.") + + job = parse_job_definition(job_dir) + generator = CommandGenerator() + adapter = ClaudeAdapter() + + command_path = generator.generate_step_command(job, job.steps[0], adapter, temp_dir) + content = command_path.read_text() + + # Check that the reference was transformed + assert ".deepwork/jobs/test_job/steps/reference.md" in content + + # Check that supplementary files section exists + assert "Supplementary Reference Files" in content + assert "`.deepwork/jobs/test_job/steps/reference.md`" in content From 1759dd3fa65fb55222ed52a4c83bc84ed7ef982a Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 18:10:58 +0000 Subject: [PATCH 2/3] refactor: simplify supplementary .md file support to docs-only approach Remove automatic path transformation code in favor of documenting that users should reference supplementary files using full paths from the project root (e.g., .deepwork/jobs/job_name/steps/reference.md). This is simpler and more explicit - no magic transformations needed. Changes: - Remove _find_supplementary_files() and _transform_md_references() from generator - Remove supplementary_files template section from both Claude and Gemini templates - Remove supplementary files tests - Add supplemental_file_references.md documentation file - Update implement.md to reference the new doc file with full path --- .claude/commands/deepwork_jobs.implement.md | 6 + .../jobs/deepwork_jobs/steps/implement.md | 6 + .../steps/supplemental_file_references.md | 40 ++++ .gemini/commands/deepwork_jobs/implement.toml | 6 + src/deepwork/core/generator.py | 116 ------------ .../deepwork_jobs/steps/implement.md | 35 +--- .../steps/supplemental_file_references.md | 40 ++++ .../claude/command-job-step.md.jinja | 9 - .../gemini/command-job-step.toml.jinja | 9 - tests/unit/test_generator.py | 173 +----------------- 10 files changed, 101 insertions(+), 339 deletions(-) create mode 100644 .deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md create mode 100644 src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md diff --git a/.claude/commands/deepwork_jobs.implement.md b/.claude/commands/deepwork_jobs.implement.md index b2c3d7e8..7868cff1 100644 --- a/.claude/commands/deepwork_jobs.implement.md +++ b/.claude/commands/deepwork_jobs.implement.md @@ -213,6 +213,12 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. +### Using Supplementary Reference Files + +Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. + +See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. + ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it. diff --git a/.deepwork/jobs/deepwork_jobs/steps/implement.md b/.deepwork/jobs/deepwork_jobs/steps/implement.md index de77741d..c5f55ae6 100644 --- a/.deepwork/jobs/deepwork_jobs/steps/implement.md +++ b/.deepwork/jobs/deepwork_jobs/steps/implement.md @@ -144,6 +144,12 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. +### Using Supplementary Reference Files + +Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. + +See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. + ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it. diff --git a/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md b/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md new file mode 100644 index 00000000..81b6494a --- /dev/null +++ b/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md @@ -0,0 +1,40 @@ +# Supplementary Reference Files + +Step instructions can include additional `.md` files in the `steps/` directory. These supplementary files are useful for: + +- Providing detailed examples or templates that would clutter the main instruction file +- Sharing common reference material across multiple steps +- Including technical specifications, API documentation, or style guides + +## How to Use + +1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files +2. Reference them in your step instructions using the **full path from the project root** + +## Example + +If you have a job called `my_job` and want to include an API specification template: + +1. Create the file at `.deepwork/jobs/my_job/steps/api_spec.md` +2. Reference it in your step instructions like this: + +```markdown +Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints. +``` + +## Path Format + +Always use the full relative path from the project root: + +``` +.deepwork/jobs/[job_name]/steps/[filename].md +``` + +For example: +- `.deepwork/jobs/competitive_research/steps/competitor_template.md` +- `.deepwork/jobs/api_design/steps/endpoint_schema.md` +- `.deepwork/jobs/onboarding/steps/checklist_template.md` + +## Benefits + +Using supplementary files keeps your main step instructions focused and readable while allowing you to provide detailed reference material when needed. The AI agent can read these files during execution to get additional context. diff --git a/.gemini/commands/deepwork_jobs/implement.toml b/.gemini/commands/deepwork_jobs/implement.toml index d5f7dc19..2ec020c3 100644 --- a/.gemini/commands/deepwork_jobs/implement.toml +++ b/.gemini/commands/deepwork_jobs/implement.toml @@ -181,6 +181,12 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. +### Using Supplementary Reference Files + +Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. + +See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. + ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it. diff --git a/src/deepwork/core/generator.py b/src/deepwork/core/generator.py index 2817b03b..380ab5b5 100644 --- a/src/deepwork/core/generator.py +++ b/src/deepwork/core/generator.py @@ -1,6 +1,5 @@ """Slash-command file generator using Jinja2 templates.""" -import re from pathlib import Path from typing import Any @@ -113,112 +112,6 @@ def _build_hook_context(self, job: JobDefinition, hook_action: Any) -> dict[str, hook_ctx["path"] = hook_action.script return hook_ctx - def _find_supplementary_files( - self, job: JobDefinition, step: Step - ) -> list[dict[str, str]]: - """ - Find supplementary .md files in the steps directory. - - Supplementary files are markdown files in the steps directory that are - NOT the main instruction file for any step. These can be referenced - in step instructions for additional context. - - Args: - job: Job definition - step: Current step - - Returns: - List of dicts with 'name' (filename) and 'path' (relative from project root) - """ - steps_dir = job.job_dir / "steps" - if not steps_dir.exists(): - return [] - - # Get all step instruction filenames (just the filename, not full path) - step_instruction_files = { - Path(s.instructions_file).name for s in job.steps - } - - supplementary_files = [] - for md_file in steps_dir.glob("*.md"): - # Skip if this is a main step instruction file - if md_file.name in step_instruction_files: - continue - - # Calculate relative path from project root - # job_dir is like .deepwork/jobs/[job_name] - # so the full path is .deepwork/jobs/[job_name]/steps/[filename] - relative_path = f".deepwork/jobs/{job.name}/steps/{md_file.name}" - - supplementary_files.append({ - "name": md_file.name, - "path": relative_path, - }) - - return sorted(supplementary_files, key=lambda x: x["name"]) - - def _transform_md_references( - self, content: str, supplementary_files: list[dict[str, str]] - ) -> str: - """ - Transform references to supplementary .md files into relative paths. - - This transforms references like `foo.md` into `.deepwork/jobs/[job_name]/steps/foo.md` - so that when the slash command is generated, the AI agent can find the files. - - Handles various reference patterns: - - `foo.md` (backtick code) - - [link](foo.md) (markdown links) - - "foo.md" or 'foo.md' (quoted strings) - - Args: - content: The instruction file content - supplementary_files: List of supplementary file info dicts - - Returns: - Content with references transformed to relative paths - """ - if not supplementary_files: - return content - - # Build a map of filename -> relative path - file_map = {f["name"]: f["path"] for f in supplementary_files} - - for filename, relative_path in file_map.items(): - # Escape special regex characters in filename - escaped_filename = re.escape(filename) - - # Pattern 1: backtick references like `foo.md` - # Only match if it's just the filename, not already a path - content = re.sub( - rf'`(? dict[str, Any]: @@ -234,20 +127,12 @@ def _build_step_context( Returns: Template context dictionary """ - # Find supplementary .md files in the steps directory - supplementary_files = self._find_supplementary_files(job, step) - # Read step instructions instructions_file = job.job_dir / step.instructions_file instructions_content = safe_read(instructions_file) if instructions_content is None: raise GeneratorError(f"Step instructions file not found: {instructions_file}") - # Transform references to supplementary files into relative paths - instructions_content = self._transform_md_references( - instructions_content, supplementary_files - ) - # Separate user inputs and file inputs user_inputs = [ {"name": inp.name, "description": inp.description} @@ -314,7 +199,6 @@ def _build_step_context( "is_standalone": is_standalone, "hooks": hooks, # New: all hooks by platform event name "stop_hooks": stop_hooks, # Backward compat: after_agent hooks only - "supplementary_files": supplementary_files, # Additional .md files in steps dir } def generate_step_command( diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md index eb654dff..c5f55ae6 100644 --- a/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md +++ b/src/deepwork/standard_jobs/deepwork_jobs/steps/implement.md @@ -146,40 +146,9 @@ This alignment ensures the AI agent knows exactly what will be validated and can ### Using Supplementary Reference Files -Step instructions can reference additional `.md` files placed in the same `steps/` directory. These supplementary files are useful for: +Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. -- Providing detailed examples or templates that would clutter the main instruction file -- Sharing common reference material across multiple steps -- Including technical specifications, API documentation, or style guides - -**How it works:** - -1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files -2. Reference them by filename in your step instructions using any of these patterns: - - Backticks: `` `example_template.md` `` - - Markdown links: `[see template](example_template.md)` - - Quoted strings: `"example_template.md"` or `'example_template.md'` - -3. When `deepwork sync` generates the slash commands, these references are automatically transformed to relative paths from the project root (e.g., `.deepwork/jobs/my_job/steps/example_template.md`) - -**Example:** - -If your job has a step instruction that says: -```markdown -Use the template in `api_spec.md` to structure your API endpoints. -``` - -And you have `.deepwork/jobs/my_job/steps/api_spec.md` containing the template, the generated slash command will transform this to: -```markdown -Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints. -``` - -The generated command will also list all supplementary files in a "Supplementary Reference Files" section at the end. - -**Important notes:** -- Supplementary files must be `.md` files in the `steps/` directory -- Files that match step instruction filenames (e.g., `define.md`, `implement.md`) are NOT treated as supplementary -- Only filename references are transformed; paths that already include directories are left unchanged +See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. ### Step 4: Verify job.yml Location diff --git a/src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md b/src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md new file mode 100644 index 00000000..81b6494a --- /dev/null +++ b/src/deepwork/standard_jobs/deepwork_jobs/steps/supplemental_file_references.md @@ -0,0 +1,40 @@ +# Supplementary Reference Files + +Step instructions can include additional `.md` files in the `steps/` directory. These supplementary files are useful for: + +- Providing detailed examples or templates that would clutter the main instruction file +- Sharing common reference material across multiple steps +- Including technical specifications, API documentation, or style guides + +## How to Use + +1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files +2. Reference them in your step instructions using the **full path from the project root** + +## Example + +If you have a job called `my_job` and want to include an API specification template: + +1. Create the file at `.deepwork/jobs/my_job/steps/api_spec.md` +2. Reference it in your step instructions like this: + +```markdown +Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints. +``` + +## Path Format + +Always use the full relative path from the project root: + +``` +.deepwork/jobs/[job_name]/steps/[filename].md +``` + +For example: +- `.deepwork/jobs/competitive_research/steps/competitor_template.md` +- `.deepwork/jobs/api_design/steps/endpoint_schema.md` +- `.deepwork/jobs/onboarding/steps/checklist_template.md` + +## Benefits + +Using supplementary files keeps your main step instructions focused and readable while allowing you to provide detailed reference material when needed. The AI agent can read these files during execution to get additional context. diff --git a/src/deepwork/templates/claude/command-job-step.md.jinja b/src/deepwork/templates/claude/command-job-step.md.jinja index fc2f390c..ff7c2837 100644 --- a/src/deepwork/templates/claude/command-job-step.md.jinja +++ b/src/deepwork/templates/claude/command-job-step.md.jinja @@ -208,12 +208,3 @@ Consider: - Job definition: `.deepwork/jobs/{{ job_name }}/job.yml` - Step instructions: `.deepwork/jobs/{{ job_name }}/{{ instructions_file }}` -{% if supplementary_files %} - -### Supplementary Reference Files - -The following additional reference files are available for this step: -{% for file in supplementary_files %} -- `{{ file.path }}` -{% endfor %} -{% endif %} diff --git a/src/deepwork/templates/gemini/command-job-step.toml.jinja b/src/deepwork/templates/gemini/command-job-step.toml.jinja index ca5a8353..bb5d69be 100644 --- a/src/deepwork/templates/gemini/command-job-step.toml.jinja +++ b/src/deepwork/templates/gemini/command-job-step.toml.jinja @@ -166,13 +166,4 @@ Consider: - Job definition: `.deepwork/jobs/{{ job_name }}/job.yml` - Step instructions: `.deepwork/jobs/{{ job_name }}/{{ instructions_file }}` -{% if supplementary_files %} - -### Supplementary Reference Files - -The following additional reference files are available for this step: -{% for file in supplementary_files %} -- `{{ file.path }}` -{% endfor %} -{% endif %} """ diff --git a/tests/unit/test_generator.py b/tests/unit/test_generator.py index eb79b8fc..f83d20a2 100644 --- a/tests/unit/test_generator.py +++ b/tests/unit/test_generator.py @@ -6,7 +6,7 @@ from deepwork.core.adapters import ClaudeAdapter from deepwork.core.generator import CommandGenerator, GeneratorError -from deepwork.core.parser import JobDefinition, Step, parse_job_definition +from deepwork.core.parser import parse_job_definition class TestCommandGenerator: @@ -197,174 +197,3 @@ def test_generate_all_commands(self, fixtures_dir: Path, temp_dir: Path) -> None ] actual_names = [p.name for p in command_paths] assert actual_names == expected_names - - -class TestSupplementaryFiles: - """Tests for supplementary file handling.""" - - def test_find_supplementary_files_empty_when_no_extras( - self, fixtures_dir: Path - ) -> None: - """Test that no supplementary files are found when only step files exist.""" - job_dir = fixtures_dir / "jobs" / "simple_job" - job = parse_job_definition(job_dir) - - generator = CommandGenerator() - supplementary = generator._find_supplementary_files(job, job.steps[0]) - - assert supplementary == [] - - def test_find_supplementary_files_detects_extra_md_files( - self, fixtures_dir: Path, temp_dir: Path - ) -> None: - """Test that supplementary .md files are detected.""" - # Copy simple job to temp dir and add supplementary file - job_dir = temp_dir / ".deepwork" / "jobs" / "test_job" - steps_dir = job_dir / "steps" - steps_dir.mkdir(parents=True) - - # Create job.yml - (job_dir / "job.yml").write_text(""" -name: test_job -version: "1.0.0" -summary: "Test job" -description: "A test job" -steps: - - id: step_one - name: "Step One" - description: "First step" - instructions_file: steps/step_one.md - outputs: - - output.md -""") - - # Create step instruction file - (steps_dir / "step_one.md").write_text("# Step One\n\nDo the thing.") - - # Create supplementary files - (steps_dir / "reference.md").write_text("# Reference\n\nSome reference content.") - (steps_dir / "template.md").write_text("# Template\n\nA template.") - - job = parse_job_definition(job_dir) - generator = CommandGenerator() - supplementary = generator._find_supplementary_files(job, job.steps[0]) - - assert len(supplementary) == 2 - assert supplementary[0]["name"] == "reference.md" - assert supplementary[0]["path"] == ".deepwork/jobs/test_job/steps/reference.md" - assert supplementary[1]["name"] == "template.md" - assert supplementary[1]["path"] == ".deepwork/jobs/test_job/steps/template.md" - - def test_transform_md_references_backticks(self) -> None: - """Test transformation of backtick references.""" - generator = CommandGenerator() - supplementary = [ - {"name": "foo.md", "path": ".deepwork/jobs/my_job/steps/foo.md"} - ] - - content = "Read the file `foo.md` for details." - result = generator._transform_md_references(content, supplementary) - - assert result == "Read the file `.deepwork/jobs/my_job/steps/foo.md` for details." - - def test_transform_md_references_markdown_links(self) -> None: - """Test transformation of markdown link references.""" - generator = CommandGenerator() - supplementary = [ - {"name": "template.md", "path": ".deepwork/jobs/my_job/steps/template.md"} - ] - - content = "See [the template](template.md) for examples." - result = generator._transform_md_references(content, supplementary) - - assert result == "See [the template](.deepwork/jobs/my_job/steps/template.md) for examples." - - def test_transform_md_references_quoted_strings(self) -> None: - """Test transformation of quoted string references.""" - generator = CommandGenerator() - supplementary = [ - {"name": "spec.md", "path": ".deepwork/jobs/my_job/steps/spec.md"} - ] - - content = 'Load "spec.md" or \'spec.md\' for the specification.' - result = generator._transform_md_references(content, supplementary) - - expected = 'Load ".deepwork/jobs/my_job/steps/spec.md" or \'.deepwork/jobs/my_job/steps/spec.md\' for the specification.' - assert result == expected - - def test_transform_md_references_preserves_paths(self) -> None: - """Test that existing paths are not double-transformed.""" - generator = CommandGenerator() - supplementary = [ - {"name": "foo.md", "path": ".deepwork/jobs/my_job/steps/foo.md"} - ] - - # Content with already-transformed path should not be affected - content = "Already transformed: `.deepwork/jobs/my_job/steps/foo.md`" - result = generator._transform_md_references(content, supplementary) - - # Should remain unchanged (the regex shouldn't match paths) - assert result == content - - def test_transform_md_references_no_supplementary_files(self) -> None: - """Test that content is unchanged when no supplementary files exist.""" - generator = CommandGenerator() - - content = "Read `something.md` for details." - result = generator._transform_md_references(content, []) - - assert result == content - - def test_transform_md_references_multiple_files(self) -> None: - """Test transformation with multiple supplementary files.""" - generator = CommandGenerator() - supplementary = [ - {"name": "api.md", "path": ".deepwork/jobs/my_job/steps/api.md"}, - {"name": "schema.md", "path": ".deepwork/jobs/my_job/steps/schema.md"}, - ] - - content = "See `api.md` for the API and `schema.md` for the schema." - result = generator._transform_md_references(content, supplementary) - - expected = "See `.deepwork/jobs/my_job/steps/api.md` for the API and `.deepwork/jobs/my_job/steps/schema.md` for the schema." - assert result == expected - - def test_generate_command_includes_supplementary_files( - self, temp_dir: Path - ) -> None: - """Test that generated commands include supplementary files section.""" - # Create a job with supplementary files - job_dir = temp_dir / ".deepwork" / "jobs" / "test_job" - steps_dir = job_dir / "steps" - steps_dir.mkdir(parents=True) - - (job_dir / "job.yml").write_text(""" -name: test_job -version: "1.0.0" -summary: "Test job" -description: "A test job" -steps: - - id: step_one - name: "Step One" - description: "First step" - instructions_file: steps/step_one.md - outputs: - - output.md -""") - - (steps_dir / "step_one.md").write_text("# Step One\n\nUse `reference.md` as a guide.") - (steps_dir / "reference.md").write_text("# Reference\n\nReference content.") - - job = parse_job_definition(job_dir) - generator = CommandGenerator() - adapter = ClaudeAdapter() - - command_path = generator.generate_step_command(job, job.steps[0], adapter, temp_dir) - content = command_path.read_text() - - # Check that the reference was transformed - assert ".deepwork/jobs/test_job/steps/reference.md" in content - - # Check that supplementary files section exists - assert "Supplementary Reference Files" in content - assert "`.deepwork/jobs/test_job/steps/reference.md`" in content From eebe2e67e3cefeece08162ee7b71c9a23ffd1b10 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Jan 2026 18:19:42 +0000 Subject: [PATCH 3/3] fix: remove manually copied files from .deepwork/ The .deepwork/ directory should only be modified by `deepwork install`, not by manual file copies. Remove the supplementary files that were incorrectly added to the installed jobs directory. The supplementary file documentation remains in the source at src/deepwork/standard_jobs/deepwork_jobs/steps/ and will be installed when users run `deepwork install`. --- .claude/commands/deepwork_jobs.implement.md | 6 --- .../jobs/deepwork_jobs/steps/implement.md | 6 --- .../steps/supplemental_file_references.md | 40 ------------------- .gemini/commands/deepwork_jobs/implement.toml | 6 --- 4 files changed, 58 deletions(-) delete mode 100644 .deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md diff --git a/.claude/commands/deepwork_jobs.implement.md b/.claude/commands/deepwork_jobs.implement.md index 7868cff1..b2c3d7e8 100644 --- a/.claude/commands/deepwork_jobs.implement.md +++ b/.claude/commands/deepwork_jobs.implement.md @@ -213,12 +213,6 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. -### Using Supplementary Reference Files - -Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. - -See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. - ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it. diff --git a/.deepwork/jobs/deepwork_jobs/steps/implement.md b/.deepwork/jobs/deepwork_jobs/steps/implement.md index c5f55ae6..de77741d 100644 --- a/.deepwork/jobs/deepwork_jobs/steps/implement.md +++ b/.deepwork/jobs/deepwork_jobs/steps/implement.md @@ -144,12 +144,6 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. -### Using Supplementary Reference Files - -Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. - -See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. - ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it. diff --git a/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md b/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md deleted file mode 100644 index 81b6494a..00000000 --- a/.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md +++ /dev/null @@ -1,40 +0,0 @@ -# Supplementary Reference Files - -Step instructions can include additional `.md` files in the `steps/` directory. These supplementary files are useful for: - -- Providing detailed examples or templates that would clutter the main instruction file -- Sharing common reference material across multiple steps -- Including technical specifications, API documentation, or style guides - -## How to Use - -1. Place additional `.md` files in the `steps/` directory alongside the main step instruction files -2. Reference them in your step instructions using the **full path from the project root** - -## Example - -If you have a job called `my_job` and want to include an API specification template: - -1. Create the file at `.deepwork/jobs/my_job/steps/api_spec.md` -2. Reference it in your step instructions like this: - -```markdown -Use the template in `.deepwork/jobs/my_job/steps/api_spec.md` to structure your API endpoints. -``` - -## Path Format - -Always use the full relative path from the project root: - -``` -.deepwork/jobs/[job_name]/steps/[filename].md -``` - -For example: -- `.deepwork/jobs/competitive_research/steps/competitor_template.md` -- `.deepwork/jobs/api_design/steps/endpoint_schema.md` -- `.deepwork/jobs/onboarding/steps/checklist_template.md` - -## Benefits - -Using supplementary files keeps your main step instructions focused and readable while allowing you to provide detailed reference material when needed. The AI agent can read these files during execution to get additional context. diff --git a/.gemini/commands/deepwork_jobs/implement.toml b/.gemini/commands/deepwork_jobs/implement.toml index 2ec020c3..d5f7dc19 100644 --- a/.gemini/commands/deepwork_jobs/implement.toml +++ b/.gemini/commands/deepwork_jobs/implement.toml @@ -181,12 +181,6 @@ If a step in the job.yml has `stop_hooks` defined, the generated instruction fil This alignment ensures the AI agent knows exactly what will be validated and can self-check before completing. -### Using Supplementary Reference Files - -Step instructions can include additional `.md` files in the `steps/` directory for detailed examples, templates, or reference material. Reference them using the full path from the project root. - -See `.deepwork/jobs/deepwork_jobs/steps/supplemental_file_references.md` for detailed documentation and examples. - ### Step 4: Verify job.yml Location Verify that `job.yml` is in the correct location at `.deepwork/jobs/[job_name]/job.yml`. The define step should have created it there. If for some reason it's not there, you may need to create or move it.