Files
zooqueenandHanzo Dev b144973bfc fix(lint): resolve all ruff + black errors across all packages
Fix 35 ruff errors (E722, F841, B904, B905, B007, E741, S202, S310,
S306, S110, TID251, UP042, F401) and run black on 526 files.

Co-authored-by: Hanzo Dev <[email protected]>
2026-02-21 23:32:32 -08:00

394 lines
12 KiB
Python

"""
Unified Development Tools - 6 Orthogonal Commands
=================================================
edit, fmt, test, build, lint, guard
Each tool is one word, orthogonal, composable, works across languages/backends,
and is go.work aware.
"""
import os
import subprocess
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional, Union
from pydantic import BaseModel, Field
# Common types
LanguageType = Literal["auto", "go", "ts", "py", "rs", "cc", "sol", "schema"]
BackendType = Literal[
"auto",
"go",
"pnpm",
"yarn",
"npm",
"bun",
"pytest",
"uv",
"poetry",
"cargo",
"cmake",
"ninja",
"make",
"buf",
"capnp",
]
class DevResult(BaseModel):
"""Common output for all dev tools"""
ok: bool
root: str
language_used: Union[str, List[str]]
backend_used: Union[str, List[str]]
scope_resolved: Union[str, List[str]]
touched_files: List[str] = Field(default_factory=list)
stdout: str = ""
stderr: str = ""
exit_code: int = 0
errors: List[str] = Field(default_factory=list)
class WorkspaceDetector:
"""Detects workspace root and configuration"""
@staticmethod
def detect(target_path: str) -> Dict[str, Any]:
"""Detect workspace root and type from target path"""
path = Path(target_path).resolve()
# Walk up to find workspace markers
current = path if path.is_dir() else path.parent
while current != current.parent:
# Check for workspace files in order of preference
if (current / "go.work").exists():
return {
"root": str(current),
"type": "go",
"config": "go.work",
"primary_language": "go",
}
if (current / "pnpm-workspace.yaml").exists():
return {
"root": str(current),
"type": "pnpm",
"config": "pnpm-workspace.yaml",
"primary_language": "ts",
}
if (current / "package.json").exists():
return {
"root": str(current),
"type": "npm",
"config": "package.json",
"primary_language": "ts",
}
if (current / "pyproject.toml").exists():
return {
"root": str(current),
"type": "python",
"config": "pyproject.toml",
"primary_language": "py",
}
if (current / "Cargo.toml").exists():
return {
"root": str(current),
"type": "rust",
"config": "Cargo.toml",
"primary_language": "rs",
}
current = current.parent
# Fallback to target directory
return {
"root": str(path.parent if path.is_file() else path),
"type": "unknown",
"config": None,
"primary_language": "auto",
}
class TargetResolver:
"""Resolves target specifications to file lists and scopes"""
@staticmethod
def resolve(target: str, workspace: Dict[str, Any]) -> Dict[str, Any]:
"""Resolve target to concrete scope"""
if target.startswith("file:"):
file_path = target[5:]
return {
"type": "file",
"files": [file_path],
"scope": file_path,
"package": TargetResolver._infer_package(file_path, workspace),
}
if target.startswith("dir:"):
dir_path = target[4:]
return {
"type": "directory",
"files": TargetResolver._scan_directory(dir_path),
"scope": dir_path,
"package": dir_path,
}
if target.startswith("pkg:"):
pkg_spec = target[4:]
return {
"type": "package",
"files": TargetResolver._resolve_package(pkg_spec, workspace),
"scope": pkg_spec,
"package": pkg_spec,
}
if target == "ws":
return {
"type": "workspace",
"files": TargetResolver._scan_workspace(workspace),
"scope": "workspace",
"package": ".",
}
if target == "changed":
return {
"type": "changed",
"files": TargetResolver._get_changed_files(workspace),
"scope": "changed files",
"package": ".",
}
# Default to file if it's a plain path
return TargetResolver.resolve(f"file:{target}", workspace)
@staticmethod
def _infer_package(file_path: str, workspace: Dict[str, Any]) -> str:
"""Infer package from file path"""
path = Path(file_path)
root = Path(workspace["root"])
if workspace["type"] == "go":
# Walk up to find go.mod
current = path.parent
while current >= root:
if (current / "go.mod").exists():
return str(current.relative_to(root)) or "."
current = current.parent
return "."
elif workspace["type"] in ["npm", "pnpm"]:
# Walk up to find package.json
current = path.parent
while current >= root:
if (current / "package.json").exists():
return str(current.relative_to(root)) or "."
current = current.parent
return "."
return str(path.parent.relative_to(root)) if path.parent >= root else "."
@staticmethod
def _scan_directory(dir_path: str) -> List[str]:
"""Scan directory for relevant files"""
path = Path(dir_path)
files = []
# Common source file patterns
patterns = [
"**/*.go",
"**/*.ts",
"**/*.tsx",
"**/*.js",
"**/*.jsx",
"**/*.py",
"**/*.rs",
]
for pattern in patterns:
files.extend([str(f) for f in path.glob(pattern) if f.is_file()])
return files
@staticmethod
def _resolve_package(pkg_spec: str, workspace: Dict[str, Any]) -> List[str]:
"""Resolve package specification to files"""
root = Path(workspace["root"])
if workspace["type"] == "go":
# Use go list to resolve package
try:
result = subprocess.run(
["go", "list", "-f", "{{.Dir}}", pkg_spec],
cwd=root,
capture_output=True,
text=True,
env={**os.environ, "GOWORK": "auto"},
)
if result.returncode == 0:
pkg_dirs = result.stdout.strip().split("\n")
files = []
for pkg_dir in pkg_dirs:
files.extend(Path(pkg_dir).glob("*.go"))
return [str(f) for f in files]
except Exception:
pass
# Fallback to directory scan
pkg_path = root / pkg_spec.replace("./", "").replace("...", "")
return TargetResolver._scan_directory(str(pkg_path))
@staticmethod
def _scan_workspace(workspace: Dict[str, Any]) -> List[str]:
"""Scan entire workspace"""
return TargetResolver._scan_directory(workspace["root"])
@staticmethod
def _get_changed_files(workspace: Dict[str, Any]) -> List[str]:
"""Get changed files from git"""
try:
result = subprocess.run(
["git", "diff", "--name-only", "HEAD"],
cwd=workspace["root"],
capture_output=True,
text=True,
)
if result.returncode == 0:
files = [f.strip() for f in result.stdout.split("\n") if f.strip()]
return [str(Path(workspace["root"]) / f) for f in files]
except Exception:
pass
return []
class DevToolBase:
"""Base class for development tools"""
def __init__(
self,
target: str,
language: LanguageType = "auto",
backend: BackendType = "auto",
root: Optional[str] = None,
env: Optional[Dict[str, str]] = None,
dry_run: bool = False,
):
self.target = target
self.language = language
self.backend = backend
self.env = env or {}
self.dry_run = dry_run
# Detect workspace
workspace_root = (
root or TargetResolver.resolve(target, {"root": "."})["package"]
)
self.workspace = WorkspaceDetector.detect(workspace_root)
# Resolve target
self.resolved = TargetResolver.resolve(target, self.workspace)
# Auto-detect language if needed
if language == "auto":
self.language = self._detect_language()
# Auto-detect backend if needed
if backend == "auto":
self.backend = self._detect_backend()
def _detect_language(self) -> str:
"""Auto-detect language from files and workspace"""
if self.workspace["primary_language"] != "auto":
return self.workspace["primary_language"]
# Analyze file extensions
files = self.resolved["files"]
extensions = [Path(f).suffix for f in files]
if any(ext == ".go" for ext in extensions):
return "go"
if any(ext in [".ts", ".tsx", ".js", ".jsx"] for ext in extensions):
return "ts"
if any(ext == ".py" for ext in extensions):
return "py"
if any(ext == ".rs" for ext in extensions):
return "rs"
return self.workspace["primary_language"]
def _detect_backend(self) -> str:
"""Auto-detect backend from workspace type and language"""
lang = self.language
ws_type = self.workspace["type"]
backend_map = {
"go": "go",
"ts": "pnpm" if ws_type == "pnpm" else "npm",
"py": (
"uv" if Path(self.workspace["root"], "uv.lock").exists() else "pytest"
),
"rs": "cargo",
"cc": "cmake",
"sol": "forge",
"schema": "buf",
}
return backend_map.get(lang, "auto")
def _run_command(
self, cmd: List[str], cwd: Optional[str] = None
) -> subprocess.CompletedProcess:
"""Run command with proper environment"""
env = {**os.environ, **self.env}
# Add go.work environment for Go
if self.language == "go":
env["GOWORK"] = "auto"
work_dir = cwd or self.workspace["root"]
if self.dry_run:
return subprocess.CompletedProcess(cmd, 0, f"DRY RUN: {' '.join(cmd)}", "")
return subprocess.run(
cmd, cwd=work_dir, env=env, capture_output=True, text=True
)
# Individual tool implementations will be in separate files
# This establishes the foundation for edit, fmt, test, build, lint, guard
def create_dev_result(
ok: bool,
root: str,
language_used: Union[str, List[str]],
backend_used: Union[str, List[str]],
scope_resolved: Union[str, List[str]],
stdout: str = "",
stderr: str = "",
exit_code: int = 0,
touched_files: List[str] = None,
errors: List[str] = None,
) -> DevResult:
"""Helper to create DevResult"""
return DevResult(
ok=ok,
root=root,
language_used=language_used,
backend_used=backend_used,
scope_resolved=scope_resolved,
stdout=stdout,
stderr=stderr,
exit_code=exit_code,
touched_files=touched_files or [],
errors=errors or [],
)