Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/claude-code-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
# Verify the fruits fixture parses correctly via deepwork's parser
uv run python -c "
from pathlib import Path
from deepwork.core.parser import parse_job_definition
from deepwork.jobs.parser import parse_job_definition

job = parse_job_definition(Path('tests/fixtures/jobs/fruits'))

Expand Down
2 changes: 1 addition & 1 deletion src/deepwork/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
def __getattr__(name: str) -> object:
"""Lazy import for core modules."""
if name in ("JobDefinition", "ParseError", "Step", "StepInput", "parse_job_definition"):
from deepwork.core.parser import (
from deepwork.jobs.parser import (
JobDefinition,
ParseError,
Step,
Expand Down
2 changes: 1 addition & 1 deletion src/deepwork/cli/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def _serve_mcp(
tmp_dir.mkdir(parents=True, exist_ok=True)

# Create and run server
from deepwork.mcp.server import create_server
from deepwork.jobs.mcp.server import create_server

server = create_server(
project_root=project_path,
Expand Down
11 changes: 11 additions & 0 deletions src/deepwork/jobs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""DeepWork Jobs module.

This module provides job definition parsing, discovery, MCP server integration,
and workflow session management for AI agent-driven workflows.

Submodules:
- discovery: Job folder discovery and loading
- parser: Job definition (job.yml) parsing
- schema: Job YAML schema definition
- mcp: MCP server, tools, state, quality gate, and Claude CLI
"""
4 changes: 2 additions & 2 deletions src/deepwork/core/jobs.py → src/deepwork/jobs/discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
from dataclasses import dataclass
from pathlib import Path

from deepwork.core.parser import JobDefinition, ParseError, parse_job_definition
from deepwork.jobs.parser import JobDefinition, ParseError, parse_job_definition

logger = logging.getLogger("deepwork.core.jobs")
logger = logging.getLogger("deepwork.jobs.discovery")


@dataclass
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

def create_server(*args, **kwargs): # type: ignore
"""Lazy import to avoid loading fastmcp at module import time."""
from deepwork.mcp.server import create_server as _create_server
from deepwork.jobs.mcp.server import create_server as _create_server

return _create_server(*args, **kwargs)

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@

import aiofiles

from deepwork.mcp.claude_cli import ClaudeCLI
from deepwork.mcp.schemas import (
from deepwork.jobs.mcp.claude_cli import ClaudeCLI
from deepwork.jobs.mcp.schemas import (
QualityCriteriaResult,
QualityGateResult,
ReviewResult,
Expand Down Expand Up @@ -517,7 +517,7 @@ async def evaluate(
file_count = len(self._flatten_output_paths(outputs))
timeout = self.compute_timeout(file_count)

from deepwork.mcp.claude_cli import ClaudeCLIError
from deepwork.jobs.mcp.claude_cli import ClaudeCLIError

try:
data = await self._cli.run(
Expand Down
File renamed without changes.
12 changes: 6 additions & 6 deletions src/deepwork/mcp/server.py → src/deepwork/jobs/mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@

from fastmcp import FastMCP

from deepwork.mcp.claude_cli import ClaudeCLI
from deepwork.mcp.quality_gate import QualityGate
from deepwork.mcp.schemas import (
from deepwork.jobs.mcp.claude_cli import ClaudeCLI
from deepwork.jobs.mcp.quality_gate import QualityGate
from deepwork.jobs.mcp.schemas import (
AbortWorkflowInput,
FinishedStepInput,
StartWorkflowInput,
)
from deepwork.mcp.state import StateManager
from deepwork.mcp.tools import WorkflowTools
from deepwork.jobs.mcp.state import StateManager
from deepwork.jobs.mcp.tools import WorkflowTools

# Configure logging
logger = logging.getLogger("deepwork.mcp")
logger = logging.getLogger("deepwork.jobs.mcp")


def create_server(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import aiofiles

from deepwork.mcp.schemas import StackEntry, StepProgress, WorkflowSession
from deepwork.jobs.mcp.schemas import StackEntry, StepProgress, WorkflowSession


class StateError(Exception):
Expand Down
24 changes: 12 additions & 12 deletions src/deepwork/mcp/tools.py → src/deepwork/jobs/mcp/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,8 @@

import aiofiles

from deepwork.core.jobs import JobLoadError, find_job_dir, load_all_jobs
from deepwork.core.parser import (
JobDefinition,
OutputSpec,
ParseError,
Workflow,
parse_job_definition,
)
from deepwork.mcp.schemas import (
from deepwork.jobs.discovery import JobLoadError, find_job_dir, load_all_jobs
from deepwork.jobs.mcp.schemas import (
AbortWorkflowInput,
AbortWorkflowResponse,
ActiveStepInfo,
Expand All @@ -38,12 +31,19 @@
StepStatus,
WorkflowInfo,
)
from deepwork.mcp.state import StateManager
from deepwork.jobs.mcp.state import StateManager
from deepwork.jobs.parser import (
JobDefinition,
OutputSpec,
ParseError,
Workflow,
parse_job_definition,
)

logger = logging.getLogger("deepwork.mcp")
logger = logging.getLogger("deepwork.jobs.mcp")

if TYPE_CHECKING:
from deepwork.mcp.quality_gate import QualityGate
from deepwork.jobs.mcp.quality_gate import QualityGate


class ToolError(Exception):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import Path
from typing import Any

from deepwork.schemas.job_schema import JOB_SCHEMA, LIFECYCLE_HOOK_EVENTS
from deepwork.jobs.schema import JOB_SCHEMA, LIFECYCLE_HOOK_EVENTS
from deepwork.utils.validation import ValidationError, validate_against_schema
from deepwork.utils.yaml_utils import YAMLError, load_yaml

Expand Down
File renamed without changes.
8 changes: 4 additions & 4 deletions tests/e2e/test_claude_code_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

import pytest

from deepwork.mcp.state import StateManager
from deepwork.mcp.tools import WorkflowTools
from deepwork.jobs.mcp.state import StateManager
from deepwork.jobs.mcp.tools import WorkflowTools

# Test input for deterministic validation
TEST_INPUT = "apple, car, banana, chair, orange, table, mango, laptop, grape, bicycle"
Expand Down Expand Up @@ -249,7 +249,7 @@ async def test_start_workflow_creates_session(self, project_with_job: Path) -> N
assert len(fruits_job.workflows) >= 1
workflow_name = fruits_job.workflows[0].name

from deepwork.mcp.schemas import StartWorkflowInput
from deepwork.jobs.mcp.schemas import StartWorkflowInput

input_data = StartWorkflowInput(
goal="Test identifying and classifying fruits",
Expand Down Expand Up @@ -283,7 +283,7 @@ async def test_workflow_step_progression(self, project_with_job: Path) -> None:
assert len(fruits_job.workflows) >= 1
workflow_name = fruits_job.workflows[0].name

from deepwork.mcp.schemas import FinishedStepInput, StartWorkflowInput
from deepwork.jobs.mcp.schemas import FinishedStepInput, StartWorkflowInput

start_input = StartWorkflowInput(
goal="Test workflow progression",
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/test_quality_gate_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# - Use the MockQualityGate class
#
# If you need to test parsing logic or edge cases, add those tests to:
# tests/unit/mcp/test_quality_gate.py
# tests/unit/jobs/mcp/test_quality_gate.py
#
# These tests are SKIPPED in CI because they require Claude Code CLI to be
# installed and authenticated. They are meant to be run locally during
Expand All @@ -28,8 +28,8 @@

import pytest

from deepwork.mcp.claude_cli import ClaudeCLI
from deepwork.mcp.quality_gate import QualityGate
from deepwork.jobs.mcp.claude_cli import ClaudeCLI
from deepwork.jobs.mcp.quality_gate import QualityGate

# Skip marker for tests that require real Claude CLI
# GitHub Actions sets CI=true, as do most other CI systems
Expand Down
1 change: 1 addition & 0 deletions tests/unit/jobs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for jobs module."""
1 change: 1 addition & 0 deletions tests/unit/jobs/mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for jobs MCP module."""
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import inspect
from pathlib import Path

from deepwork.mcp.claude_cli import ClaudeCLI
from deepwork.mcp.quality_gate import MockQualityGate, QualityGate
from deepwork.mcp.state import StateManager
from deepwork.mcp.tools import WorkflowTools
from deepwork.jobs.mcp.claude_cli import ClaudeCLI
from deepwork.jobs.mcp.quality_gate import MockQualityGate, QualityGate
from deepwork.jobs.mcp.state import StateManager
from deepwork.jobs.mcp.tools import WorkflowTools


class TestAsyncInterfaceRegression:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import pytest

from deepwork.mcp.claude_cli import ClaudeCLI, ClaudeCLIError
from deepwork.jobs.mcp.claude_cli import ClaudeCLI, ClaudeCLIError


def create_mock_subprocess(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import pytest

from deepwork.mcp.claude_cli import ClaudeCLI, ClaudeCLIError
from deepwork.mcp.quality_gate import (
from deepwork.jobs.mcp.claude_cli import ClaudeCLI, ClaudeCLIError
from deepwork.jobs.mcp.quality_gate import (
QUALITY_GATE_RESPONSE_SCHEMA,
MockQualityGate,
QualityGate,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Tests for MCP schemas."""

from deepwork.mcp.schemas import (
from deepwork.jobs.mcp.schemas import (
ActiveStepInfo,
ExpectedOutput,
FinishedStepInput,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import pytest

from deepwork.mcp.state import StateError, StateManager
from deepwork.jobs.mcp.state import StateError, StateManager


@pytest.fixture
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@

import pytest

from deepwork.mcp.quality_gate import MockQualityGate
from deepwork.mcp.schemas import (
from deepwork.jobs.mcp.quality_gate import MockQualityGate
from deepwork.jobs.mcp.schemas import (
AbortWorkflowInput,
FinishedStepInput,
StartWorkflowInput,
StepStatus,
)
from deepwork.mcp.state import StateError, StateManager
from deepwork.mcp.tools import ToolError, WorkflowTools
from deepwork.jobs.mcp.state import StateError, StateManager
from deepwork.jobs.mcp.tools import ToolError, WorkflowTools


@pytest.fixture(autouse=True)
Expand All @@ -22,7 +22,7 @@ def _isolate_job_folders(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Non
Each test gets only its own .deepwork/jobs/ directory.
"""
monkeypatch.setattr(
"deepwork.core.jobs.get_job_folders",
"deepwork.jobs.discovery.get_job_folders",
lambda project_root: [project_root / ".deepwork" / "jobs"],
)

Expand Down Expand Up @@ -1452,7 +1452,7 @@ class TestExternalRunnerSelfReview:
@pytest.fixture
def tools_self_review(self, project_root: Path, state_manager: StateManager) -> WorkflowTools:
"""Create WorkflowTools with quality gate but no external runner (self-review mode)."""
from deepwork.mcp.quality_gate import QualityGate
from deepwork.jobs.mcp.quality_gate import QualityGate

return WorkflowTools(
project_root=project_root,
Expand Down
Loading