Skip to content
Open
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
27 changes: 17 additions & 10 deletions apps/backend/cli/batch_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

import json
import os

Check failure on line 9 in apps/backend/cli/batch_commands.py

View workflow job for this annotation

GitHub Actions / python

Ruff (F401)

apps/backend/cli/batch_commands.py:9:8: F401 `os` imported but unused

Check notice

Code scanning / CodeQL

Unused import Note

Import of 'os' is not used.

Copilot Autofix

AI 5 days ago

To fix the problem, remove the unused os import from apps/backend/cli/batch_commands.py. In general terms, unused imports should be deleted to keep the code clean, avoid confusion about dependencies, and slightly reduce module load overhead.

In this specific case, edit apps/backend/cli/batch_commands.py near the top of the file. Delete the line import os at line 9, leaving the remaining imports (json, Path, and from ui import highlight, print_status) unchanged. No additional methods, imports, or definitions are required; this is a simple deletion that does not alter runtime behavior since os is not referenced in the shown code.

Suggested changeset 1
apps/backend/cli/batch_commands.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/backend/cli/batch_commands.py b/apps/backend/cli/batch_commands.py
--- a/apps/backend/cli/batch_commands.py
+++ b/apps/backend/cli/batch_commands.py
@@ -6,7 +6,6 @@
 """
 
 import json
-import os
 from pathlib import Path
 
 from ui import highlight, print_status
EOF
@@ -6,7 +6,6 @@
"""

import json
import os
from pathlib import Path

from ui import highlight, print_status
Copilot is powered by AI and may make mistakes. Always verify output.
from pathlib import Path

from ui import highlight, print_status
Expand Down Expand Up @@ -174,17 +175,23 @@

def handle_batch_cleanup_command(project_dir: str, dry_run: bool = True) -> bool:
"""
Clean up completed specs and worktrees.
Args:
project_dir: Project directory
dry_run: If True, show what would be deleted
Clean up completed spec directories and their associated worktree paths.
Finds spec directories under <project_dir>/.auto-claude/specs that contain a `qa_report.md` (treated as completed),
and, when run in dry-run mode, prints the specs and corresponding worktree paths that would be removed.
Parameters:
project_dir (str): Path to the project root.
dry_run (bool): If True, print what would be removed instead of performing deletions.
Returns:
True if successful
True if the command completed.
"""
from core.config import get_worktree_base_path

specs_dir = Path(project_dir) / ".auto-claude" / "specs"
worktrees_dir = Path(project_dir) / ".worktrees"
worktree_base_path = get_worktree_base_path(Path(project_dir))
worktrees_dir = Path(project_dir) / worktree_base_path

if not specs_dir.exists():
print_status("No specs directory found", "info")
Expand All @@ -209,8 +216,8 @@
print(f" - {spec_name}")
wt_path = worktrees_dir / spec_name
if wt_path.exists():
print(f" └─ .worktrees/{spec_name}/")
print(f" └─ {worktree_base_path}/{spec_name}/")
print()
print("Run with --no-dry-run to actually delete")

return True
return True

Check failure on line 223 in apps/backend/cli/batch_commands.py

View workflow job for this annotation

GitHub Actions / python

Ruff (W292)

apps/backend/cli/batch_commands.py:223:16: W292 No newline at end of file
16 changes: 14 additions & 2 deletions apps/backend/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,36 @@
if str(_PARENT_DIR) not in sys.path:
sys.path.insert(0, str(_PARENT_DIR))

from dotenv import load_dotenv


from .batch_commands import (
handle_batch_cleanup_command,
handle_batch_create_command,
handle_batch_status_command,
)
from .build_commands import handle_build_command
from .followup_commands import handle_followup_command
from .qa_commands import (
handle_qa_command,
handle_qa_status_command,
handle_review_status_command,
)
from .spec_commands import print_specs_list
from .utils import (
DEFAULT_MODEL,
find_spec,
get_project_dir,
print_banner,
setup_environment,
)
from .workspace_commands import (
handle_cleanup_worktrees_command,
handle_discard_command,
handle_list_worktrees_command,
handle_merge_command,
handle_review_command,
)

Check failure on line 47 in apps/backend/cli/main.py

View workflow job for this annotation

GitHub Actions / python

Ruff (I001)

apps/backend/cli/main.py:18:1: I001 Import block is un-sorted or un-formatted


def parse_args() -> argparse.Namespace:
Expand Down Expand Up @@ -259,7 +261,11 @@


def main() -> None:
"""Main CLI entry point."""
"""
Entry point for the CLI: sets up the environment, parses arguments, and dispatches the requested command.

This function initializes runtime environment and debugging, resolves the project directory (and loads a project-specific .auto-claude/.env file if present), determines the model choice from the CLI or the AUTO_BUILD_MODEL environment variable, and routes control to the appropriate handler based on parsed CLI flags (examples include listing specs, worktree management, batch operations, merge/preview/review/discard flows, QA and follow-up commands, or the normal build flow). Exits the process with a non-zero status when required by invalid input or failing command outcomes.
"""
# Set up environment first
setup_environment()

Expand All @@ -276,6 +282,12 @@
project_dir = get_project_dir(args.project_dir)
debug("run.py", f"Using project directory: {project_dir}")

# Load project-specific .env file (overrides backend .env)
project_env = project_dir / ".auto-claude" / ".env"
if project_env.exists():
load_dotenv(project_env, override=True)
debug("run.py", f"Loaded project .env from: {project_env}")

# Get model from CLI arg or env var (None if not explicitly set)
# This allows get_phase_model() to fall back to task_metadata.json
model = args.model or os.environ.get("AUTO_BUILD_MODEL")
Expand Down Expand Up @@ -410,4 +422,4 @@


if __name__ == "__main__":
main()
main()

Check failure on line 425 in apps/backend/cli/main.py

View workflow job for this annotation

GitHub Actions / python

Ruff (W292)

apps/backend/cli/main.py:425:11: W292 No newline at end of file
78 changes: 78 additions & 0 deletions apps/backend/core/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""
Core configuration for Auto Claude.

This module provides centralized configuration management for Auto Claude,
including worktree path resolution and validation. It ensures consistent
configuration access across the entire backend codebase.

Constants:
WORKTREE_BASE_PATH_VAR (str): Environment variable name for custom worktree base path.
DEFAULT_WORKTREE_PATH (str): Default worktree directory name relative to project root.

Example:
>>> from core.config import get_worktree_base_path
>>> from pathlib import Path
>>>
>>> # Get worktree path with validation
>>> project_dir = Path("/path/to/project")
>>> worktree_path = get_worktree_base_path(project_dir)
>>> full_path = project_dir / worktree_path
"""

import os
from pathlib import Path

# Environment variable names
WORKTREE_BASE_PATH_VAR = "WORKTREE_BASE_PATH"
"""str: Environment variable name for configuring custom worktree base path.

Users can set this environment variable in their project's .env file to specify
a custom location for worktree directories, supporting both relative and absolute paths.
"""

# Default values
DEFAULT_WORKTREE_PATH = ".worktrees"
"""str: Default worktree directory name.

This is the fallback value used when WORKTREE_BASE_PATH is not set or when
validation fails (e.g., path points to .auto-claude/ or .git/ directories).
"""


def get_worktree_base_path(project_dir: Path | None = None) -> str:
"""
Determine the validated worktree base path from the WORKTREE_BASE_PATH environment variable or the default.

Parameters:
project_dir (Path | None): Optional project root used to resolve relative paths and perform stricter validation. If omitted, only basic pattern checks are applied.

Returns:
str: The configured worktree base path string, or DEFAULT_WORKTREE_PATH ('.worktrees') if the configured value is invalid or points inside the project's `.auto-claude` or `.git` directories.
"""
worktree_base_path = os.getenv(WORKTREE_BASE_PATH_VAR, DEFAULT_WORKTREE_PATH)

# If no project_dir provided, return as-is (basic validation only)
if not project_dir:
# Check for obviously dangerous patterns
normalized = Path(worktree_base_path).as_posix()
if ".auto-claude" in normalized or ".git" in normalized:
return DEFAULT_WORKTREE_PATH
return worktree_base_path

# Resolve the absolute path
if Path(worktree_base_path).is_absolute():
resolved = Path(worktree_base_path).resolve()
else:
resolved = (project_dir / worktree_base_path).resolve()

# Prevent paths inside .auto-claude/ or .git/
auto_claude_dir = (project_dir / ".auto-claude").resolve()
git_dir = (project_dir / ".git").resolve()

resolved_str = str(resolved)
if resolved_str.startswith(str(auto_claude_dir)) or resolved_str.startswith(
str(git_dir)
):
return DEFAULT_WORKTREE_PATH

return worktree_base_path

Check failure on line 78 in apps/backend/core/config.py

View workflow job for this annotation

GitHub Actions / python

Ruff (W292)

apps/backend/core/config.py:78:30: W292 No newline at end of file
21 changes: 14 additions & 7 deletions apps/backend/core/workspace/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Data classes and enums for workspace management.
"""

import os

Check failure on line 9 in apps/backend/core/workspace/models.py

View workflow job for this annotation

GitHub Actions / python

Ruff (F401)

apps/backend/core/workspace/models.py:9:8: F401 `os` imported but unused

Check notice

Code scanning / CodeQL

Unused import Note

Import of 'os' is not used.

Copilot Autofix

AI 5 days ago

To fix an unused import, the general approach is to remove the import statement for the module that is not referenced anywhere in the file. This eliminates an unnecessary dependency and slightly simplifies the module’s namespace.

In this specific case, the best fix is to delete the import os line from apps/backend/core/workspace/models.py. The other imports (dataclass, Enum, Path) are used and must remain. No other code changes are required, since nothing in the shown file uses os. Concretely, remove line 9 (import os) and leave the rest of the file unchanged.

Suggested changeset 1
apps/backend/core/workspace/models.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/backend/core/workspace/models.py b/apps/backend/core/workspace/models.py
--- a/apps/backend/core/workspace/models.py
+++ b/apps/backend/core/workspace/models.py
@@ -6,7 +6,6 @@
 Data classes and enums for workspace management.
 """
 
-import os
 from dataclasses import dataclass
 from enum import Enum
 from pathlib import Path
EOF
@@ -6,7 +6,6 @@
Data classes and enums for workspace management.
"""

import os
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
Copilot is powered by AI and may make mistakes. Always verify output.
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
Expand Down Expand Up @@ -73,7 +74,7 @@

def __enter__(self):
"""Acquire the merge lock."""
import os

Check failure on line 77 in apps/backend/core/workspace/models.py

View workflow job for this annotation

GitHub Actions / python

Ruff (F811)

apps/backend/core/workspace/models.py:77:16: F811 Redefinition of unused `os` from line 9
import time

self.lock_dir.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -164,7 +165,7 @@

def __enter__(self) -> "SpecNumberLock":
"""Acquire the spec numbering lock."""
import os

Check failure on line 168 in apps/backend/core/workspace/models.py

View workflow job for this annotation

GitHub Actions / python

Ruff (F811)

apps/backend/core/workspace/models.py:168:16: F811 Redefinition of unused `os` from line 9
import time

self.lock_dir.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -227,12 +228,15 @@

def get_next_spec_number(self) -> int:
"""
Scan all spec locations and return the next available spec number.

Must be called while lock is held.

Compute the next global spec number by scanning the project's specs and all worktree specs.
Requires the spec-numbering lock to be held; caches the computed maximum for subsequent calls.
Returns:
Next available spec number (global max + 1)
int: The next available spec number (highest existing spec number + 1).

Raises:
SpecNumberLockError: If the lock has not been acquired when called.
"""
if not self.acquired:
raise SpecNumberLockError(
Expand All @@ -249,7 +253,10 @@
max_number = max(max_number, self._scan_specs_dir(main_specs_dir))

# 2. Scan all worktree specs
worktrees_dir = self.project_dir / ".worktrees"
from core.config import get_worktree_base_path

worktree_base_path = get_worktree_base_path(self.project_dir)
worktrees_dir = self.project_dir / worktree_base_path
if worktrees_dir.exists():
for worktree in worktrees_dir.iterdir():
if worktree.is_dir():
Expand All @@ -272,4 +279,4 @@
except ValueError:
pass

return max_num
return max_num

Check failure on line 282 in apps/backend/core/workspace/models.py

View workflow job for this annotation

GitHub Actions / python

Ruff (W292)

apps/backend/core/workspace/models.py:282:23: W292 No newline at end of file
17 changes: 15 additions & 2 deletions apps/backend/core/worktree.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,22 @@
"""

def __init__(self, project_dir: Path, base_branch: str | None = None):
"""
Initialize the WorktreeManager for a repository, determining base branch and worktrees directory.

Parameters:
project_dir (Path): Root path of the repository managed by this instance.

Check failure on line 60 in apps/backend/core/worktree.py

View workflow job for this annotation

GitHub Actions / python

Ruff (E101)

apps/backend/core/worktree.py:60:1: E101 Indentation contains mixed spaces and tabs
base_branch (str | None): Optional explicit base branch to use; if omitted, the base branch is auto-detected.
"""
from core.config import get_worktree_base_path

self.project_dir = project_dir
self.base_branch = base_branch or self._detect_base_branch()
self.worktrees_dir = project_dir / ".worktrees"

# Use custom worktree path from environment variable with validation
worktree_base_path = get_worktree_base_path(project_dir)
self.worktrees_dir = project_dir / worktree_base_path

self._merge_lock = asyncio.Lock()

def _detect_base_branch(self) -> str:
Expand Down Expand Up @@ -664,4 +677,4 @@


# Keep STAGING_WORKTREE_NAME for backward compatibility in imports
STAGING_WORKTREE_NAME = "auto-claude"
STAGING_WORKTREE_NAME = "auto-claude"
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import { AVAILABLE_MODELS } from '../../../shared/constants';
import type {
Project,
ProjectSettings as ProjectSettingsType,
AutoBuildVersionInfo
AutoBuildVersionInfo,
ProjectEnvConfig
} from '../../../shared/types';
import { WorktreeSettings } from './WorktreeSettings';

interface GeneralSettingsProps {
project: Project;
Expand All @@ -32,16 +34,34 @@ interface GeneralSettingsProps {
isCheckingVersion: boolean;
isUpdating: boolean;
handleInitialize: () => Promise<void>;
envConfig: ProjectEnvConfig | null;
updateEnvConfig: (updates: Partial<ProjectEnvConfig>) => void;
}

/**
* Render general project settings, including auto-build integration, agent configuration, worktree location, and notification toggles.
*
* @param project - Project data used to determine auto-build state and provide paths.
* @param settings - Current project settings that drive form controls and toggles.
* @param setSettings - State updater used to replace the project settings object.
* @param versionInfo - Optional auto-build version and initialization status displayed when available.
* @param isCheckingVersion - When true, displays a loading indicator while checking auto-build status.
* @param isUpdating - When true, disables initialization controls and shows an initializing state.
* @param handleInitialize - Called to initialize the auto-build integration.
* @param envConfig - Optional environment configuration passed into worktree settings.
* @param updateEnvConfig - Function to apply partial updates to the environment configuration.
* @returns The rendered settings UI as JSX.
*/
export function GeneralSettings({
project,
settings,
setSettings,
versionInfo,
isCheckingVersion,
isUpdating,
handleInitialize
handleInitialize,
envConfig,
updateEnvConfig
}: GeneralSettingsProps) {
const { t } = useTranslation(['settings']);

Expand Down Expand Up @@ -150,6 +170,18 @@ export function GeneralSettings({

<Separator />

{/* Worktree Location */}
<section className="space-y-4">
<h3 className="text-sm font-semibold text-foreground">{t('projectSections.worktree.title')}</h3>
<WorktreeSettings
envConfig={envConfig}
updateEnvConfig={updateEnvConfig}
projectPath={project.path}
/>
</section>

<Separator />

{/* Notifications */}
<section className="space-y-4">
<h3 className="text-sm font-semibold text-foreground">Notifications</h3>
Expand Down Expand Up @@ -220,4 +252,4 @@ export function GeneralSettings({
)}
</>
);
}
}
Loading
Loading