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
8 changes: 0 additions & 8 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,3 @@ jobs:

- name: Run tests
run: pytest tests/ -v

- name: Build deepwork package
run: nix build

- name: Verify package output
run: |
ls -la result/bin/deepwork
./result/bin/deepwork --version
74 changes: 1 addition & 73 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

106 changes: 9 additions & 97 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,12 @@

# Claude Code with pre-built native binaries (hourly updates)
claude-code-nix.url = "github:sadjow/claude-code-nix";

pyproject-nix = {
url = "github:pyproject-nix/pyproject.nix";
inputs.nixpkgs.follows = "nixpkgs";
};

uv2nix = {
url = "github:pyproject-nix/uv2nix";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.nixpkgs.follows = "nixpkgs";
};

pyproject-build-systems = {
url = "github:pyproject-nix/build-system-pkgs";
inputs.pyproject-nix.follows = "pyproject-nix";
inputs.uv2nix.follows = "uv2nix";
inputs.nixpkgs.follows = "nixpkgs";
};
};

outputs = { self, nixpkgs, claude-code-nix, pyproject-nix, uv2nix, pyproject-build-systems, ... }:
outputs = { self, nixpkgs, claude-code-nix, ... }:
let
inherit (nixpkgs) lib;

# Systems to support
forAllSystems = lib.genAttrs [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];

# Load the uv workspace from uv.lock
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };

# Create overlay from uv.lock - prefer wheels for faster builds
overlay = workspace.mkPyprojectOverlay { sourcePreference = "wheel"; };

# Editable overlay for development (live-reload from src/)
editableOverlay = workspace.mkEditablePyprojectOverlay { root = "$REPO_ROOT"; };

# Build Python package sets for each system
pythonSets = forAllSystems (system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
python = pkgs.python311;
in
(pkgs.callPackage pyproject-nix.build.packages { inherit python; }).overrideScope
(lib.composeManyExtensions [
pyproject-build-systems.overlays.default
overlay
])
);

in
{
devShells = forAllSystems (system:
Expand All @@ -66,17 +20,11 @@
inherit system;
config.allowUnfree = true;
};

# Python set with editable overlay for development
pythonSet = pythonSets.${system}.overrideScope editableOverlay;

# Virtual environment with all dependencies (including dev extras)
virtualenv = pythonSet.mkVirtualEnv "deepwork-dev-env" workspace.deps.all;
in
{
default = pkgs.mkShell {
packages = [
virtualenv
pkgs.python311
pkgs.uv
pkgs.git
pkgs.jq
Expand All @@ -85,27 +33,25 @@
];

env = {
# Prevent uv from managing packages (Nix handles it)
UV_NO_SYNC = "1";
UV_PYTHON = "${pythonSet.python}/bin/python";
UV_PYTHON_DOWNLOADS = "never";
DEEPWORK_DEV = "1";
};

shellHook = ''
# Required for editable overlay
unset PYTHONPATH
export REPO_ROOT=$(git rev-parse --show-toplevel)

# Install deepwork as a uv tool (editable) so uvx picks up local source
# This makes the MCP config ("uvx deepwork serve") use the dev version
# Create project venv with deepwork + all dev deps (pytest, ruff, mypy, etc.)
uv sync --extra dev --quiet 2>/dev/null || true
export PATH="$REPO_ROOT/.venv/bin:$PATH"

# Also register as a uv tool so `uvx deepwork serve` uses local source
uv tool install -e "$REPO_ROOT" --quiet 2>/dev/null || true

# Only show welcome message in interactive shells
if [[ $- == *i* ]]; then
echo ""
echo "DeepWork Development Environment (uv2nix)"
echo "=========================================="
echo "DeepWork Development Environment"
echo "================================"
echo ""
echo "Python: $(python --version) | uv: $(uv --version)"
echo ""
Expand All @@ -122,39 +68,5 @@
};
}
);

# Package output - wrapped deepwork binary with isolated Python environment
# When consumed as a dependency in other flakes, the consuming devShell may
# include Python packages for a different version (e.g. python3.13 from
# azure-cli, awscli2). These pollute PYTHONPATH and cause symbol errors
# when deepwork's python3.11 tries to load python3.13 native extensions.
# Wrapping with --unset PYTHONPATH isolates deepwork from the host environment.
packages = forAllSystems (system:
let
pkgs = import nixpkgs {
inherit system;
config.allowUnfree = true;
};
venv = pythonSets.${system}.mkVirtualEnv "deepwork-env" workspace.deps.default;
wrapped = pkgs.runCommand "deepwork-wrapped" {
nativeBuildInputs = [ pkgs.makeWrapper ];
} ''
mkdir -p $out/bin
makeWrapper ${venv}/bin/deepwork $out/bin/deepwork \
--unset PYTHONPATH
'';
in {
default = wrapped;
deepwork = wrapped; # Alias for backwards compatibility
}
);

# Make deepwork runnable with 'nix run'
apps = forAllSystems (system: {
default = {
type = "app";
program = "${self.packages.${system}.default}/bin/deepwork";
};
});
};
}
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dev = [
"mypy>=1.0",
"types-PyYAML",
"types-aiofiles",
"types-jsonschema",
]

[project.scripts]
Expand Down
2 changes: 1 addition & 1 deletion src/deepwork/mcp/claude_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def _parse_wrapper(self, response_text: str) -> dict[str, Any]:
f"Claude CLI returned error: {wrapper.get('result', 'Unknown error')}"
)

data = wrapper.get("structured_output")
data: dict[str, Any] = wrapper.get("structured_output")
if data is None:
raise ClaudeCLIError(
"Claude CLI response missing 'structured_output' field. "
Expand Down
3 changes: 2 additions & 1 deletion src/deepwork/schemas/job_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
def _load_schema() -> dict[str, Any]:
"""Load the JSON schema from file."""
with open(_SCHEMA_FILE) as f:
return json.load(f)
result: dict[str, Any] = json.load(f)
return result


# Load the schema at module import time
Expand Down
2 changes: 2 additions & 0 deletions src/deepwork/utils/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ def get_repo_root(path: Path | str) -> Path:
GitError: If path is not in a Git repository
"""
repo = get_repo(path)
if repo.working_tree_dir is None:
raise GitError(f"Repository at {path} has no working tree (bare repo?)")
return Path(repo.working_tree_dir)


Expand Down
26 changes: 20 additions & 6 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.