Skip to content

Conversation

@greghughespdx
Copy link

@greghughespdx greghughespdx commented Jan 1, 2026

Watches the specs directory for changes made outside the app (CLI, AI agents, etc.) and smoothly update task views in real-time without requiring manual refresh.

Features:

  • File watcher using chokidar monitors specs directory
  • Surgical Zustand store updates (no UI flash)
  • Debounced updates (400ms) to handle rapid changes
  • Opt-in via Settings > Agent Settings > "Auto-Sync External Changes"
  • Detects: new tasks, removed tasks, and task modifications

Implementation:

  • Extended FileWatcher class with specs directory watching
  • Added IPC channels for spec events (added/removed/updated)
  • Added TASK_GET handler for fetching individual tasks
  • Surgical store handlers in task-store.ts
  • Settings toggle with dynamic watcher start/stop

🤖 Generated with Claude Code

Base Branch

  • This PR targets the develop branch (required for all feature/fix PRs)
  • This PR targets main (hotfix only - maintainers)

Description

Adds real-time sync for tasks created or modified outside the app (via CLI, AI agents, etc.) so users don't need to refresh manually. Added Settings feature to allow the user to toggle the feature on or off in Settings/Agents

Related Issue

N/A

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 📚 Documentation
  • ♻️ Refactor
  • 🧪 Test

Area

  • Frontend
  • Backend
  • Fullstack

Commit Message Format

(format was followed in commit)

Checklist

  • I've synced with develop branch
  • I've tested my changes locally
  • I've followed the code principles (SOLID, DRY, KISS)
  • My PR is small and focused (< 400 lines ideally)

CI/Testing Requirements

  • All CI checks pass
  • All existing tests pass
  • New features include test coverage
  • Bug fixes include regression tests

Screenshots

SCR-20260101-nsct-2

Feature Toggle

  • Behind localStorage flag: use_feature_name
  • Behind settings toggle
  • Behind environment variable/config
  • N/A - Feature is complete and ready for all users

Breaking Changes

Breaking: No


This is my first ever PR - it feels good to make one - thanks for building Auto-Claude!

Summary by CodeRabbit

  • New Features

    • Real-time filesystem watching for spec directories with spec-added / spec-removed / spec-updated notifications.
    • New single-task retrieval API to fetch a task by spec ID; renderer can subscribe to spec lifecycle events.
  • Settings

    • Opt-in toggle in General Settings to enable/disable filesystem watching (off by default); enabling auto-starts watchers for projects.
  • Behavior

    • Store updates reactively add, remove, or refresh tasks when specs change.
  • Localization

    • Added UI text for the new watch-filesystem option.

✏️ Tip: You can customize this high-level summary in your review settings.

Watch the specs directory for changes made outside the app (CLI, AI agents, etc.)
and update task views in real-time without requiring manual refresh.

Features:
- File watcher using chokidar monitors specs directory
- Surgical Zustand store updates (no UI flash)
- Debounced updates (400ms) to handle rapid changes
- Opt-in via Settings > Agent Settings > "Auto-Sync External Changes"
- Detects: new tasks, removed tasks, and task modifications

Implementation:
- Extended FileWatcher class with specs directory watching
- Added IPC channels for spec events (added/removed/updated)
- Added TASK_GET handler for fetching individual tasks
- Surgical store handlers in task-store.ts
- Settings toggle with dynamic watcher start/stop

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@CLAassistant
Copy link

CLAassistant commented Jan 1, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 1, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds an opt-in filesystem watcher for project spec directories with debounced spec lifecycle events (added/removed/updated), IPC plumbing and Task get-by-spec APIs, renderer store handlers to surgically update tasks, plus assorted tooling refactors (Homebrew Python utility) and PR worktree support in the backend.

Changes

Cohort / File(s) Summary
FileWatcher Core
apps/frontend/src/main/file-watcher.ts
New per-project specs watcher state, SpecsWatcherInfo/PendingUpdate, 400ms debounce, and methods: watchSpecsDirectory(), unwatchSpecsDirectory(), isWatchingSpecs(), unwatchAllSpecsDirectories(). Emits spec-added/removed/updated.
IPC Event Forwarding
apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts
Forwards backend FileWatcher events to renderer as TASK_SPEC_ADDED, TASK_SPEC_REMOVED, TASK_SPEC_UPDATED.
Settings Integration
apps/frontend/src/main/ipc-handlers/settings-handlers.ts, apps/frontend/src/shared/constants/config.ts, apps/frontend/src/shared/types/settings.ts
New watchFilesystemForExternalChanges setting (default false). Enabling starts watchers for projects via fileWatcher.watchSpecsDirectory; disabling calls fileWatcher.unwatchAllSpecsDirectories().
UI Settings Controls
apps/frontend/src/renderer/components/settings/GeneralSettings.tsx, apps/frontend/src/shared/i18n/locales/en/settings.json
Adds a toggle bound to watchFilesystemForExternalChanges with new i18n keys.
Task IPC & Fetching
apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts, apps/frontend/src/main/project-store.ts
Adds TASK_GET IPC handler and ProjectStore.getTask() with loadTaskFromSpecFolder() to load a single spec by specId; TASK_LIST may start watchers when enabled.
Preload/API & Types
apps/frontend/src/preload/api/task-api.ts, apps/frontend/src/shared/constants/ipc.ts, apps/frontend/src/shared/types/ipc.ts
Exposes getTask() and event listeners onSpecAdded/onSpecRemoved/onSpecUpdated in TaskAPI; adds IPC channels TASK_GET, TASK_SPEC_ADDED, TASK_SPEC_REMOVED, TASK_SPEC_UPDATED.
Renderer Integration
apps/frontend/src/renderer/hooks/useIpc.ts, apps/frontend/src/renderer/stores/task-store.ts
Hooks added for spec events delegating to handleSpecAdded/Removed/Updated; task store adds removeTaskBySpecId() and handlers to surgically fetch/add/remove/update tasks.
Mocks & Browser Mock
apps/frontend/src/renderer/lib/mocks/task-mock.ts
Adds mock getTask() and no-op onSpecAdded/onSpecRemoved/onSpecUpdated.
Homebrew Python Utility
apps/frontend/src/main/utils/homebrew-python.ts, apps/frontend/src/main/python-detector.ts, apps/frontend/src/main/cli-tool-manager.ts
New shared findHomebrewPython util, callers refactored to use it; adds python3.14 to candidates.
Backend PR Worktree Support
apps/backend/runners/github/context_gatherer.py, apps/backend/runners/github/services/parallel_orchestrator_reviewer.py
PRContext gains head_sha/base_sha; reviewer adds PR worktree creation/cleanup and stale worktree pruning (_create_pr_worktree, _cleanup_pr_worktree, _cleanup_stale_pr_worktrees) and integrates worktree lifecycle into review flow.
Minor / Cosmetic
apps/frontend/src/renderer/components/settings/AdvancedSettings.tsx
Small JSX formatting change (blank line).

Sequence Diagram(s)

sequenceDiagram
    participant UI as Settings UI
    participant Main as Main Process (settings-handlers)
    participant FileWatcher as FileWatcher (main)
    participant FS as File System
    participant IPCMain as IPC (main)
    participant Preload as Preload API
    participant Renderer as Renderer
    participant TaskStore as Task Store
    participant ProjectStore as ProjectStore

    UI->>Main: SETTINGS_SAVE(toggle watchFilesystemForExternalChanges)
    alt enable
        Main->>FileWatcher: watchSpecsDirectory(projectId, specsPath)
        FileWatcher->>FS: start watching spec dirs & key JSON files
    else disable
        Main->>FileWatcher: unwatchAllSpecsDirectories()
    end

    Note over FS,FileWatcher: External file change (e.g., task_metadata.json)
    FS->>FileWatcher: file event
    FileWatcher->>FileWatcher: debounce 400ms, classify event
    FileWatcher->>IPCMain: emit spec-added/spec-removed/spec-updated
    IPCMain->>Preload: forward TASK_SPEC_* events
    Preload->>Renderer: invoke onSpec* callbacks
    Renderer->>TaskStore: handleSpecAdded/Removed/Updated
    alt need spec data
        TaskStore->>IPCMain: TASK_GET(projectId, specId)
        IPCMain->>ProjectStore: ProjectStore.getTask(projectId, specId)
        ProjectStore-->>IPCMain: Task|null
        IPCMain-->>TaskStore: Task|null
        TaskStore->>Renderer: update UI state
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

area/frontend, 🔄 Checking, size/M

Suggested reviewers

  • AlexMadera
  • MikeeBuilds
  • AndyMik90

Poem

🐇 I hop through specs with keen delight,
Files whisper changes late at night.
Debounced I listen, then give a tap,
Added, removed, or updated — I map.
A rabbit cheers — the app stays bright!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding real-time task synchronization for external changes, which is the core objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Thanks for your first PR!

A maintainer will review it soon. Please make sure:

  • Your branch is synced with develop
  • CI checks pass
  • You've followed our contribution guide

Welcome to the Auto Claude community!

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @greghughespdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a significant enhancement to the application's task management by enabling real-time synchronization of tasks. Previously, users had to manually refresh the application to see changes made to task files by external tools like the CLI or AI agents. This PR addresses that by integrating a robust file watcher that monitors the 'specs' directory. When changes are detected—whether a new task is added, an existing one is removed, or its metadata is updated—the application's UI is automatically and efficiently refreshed. The implementation uses debouncing to consolidate rapid file system events and ensures a seamless user experience through surgical updates to the task store. This feature is configurable and can be toggled on or off via a new setting in the application.

Highlights

  • Real-time Task Synchronization: Implemented a new feature to automatically sync task views when changes occur in the 'specs' directory outside the application.
  • File System Watcher: Utilizes 'chokidar' to monitor the 'specs' directory for new, removed, or modified task files ('implementation_plan.json', 'task_metadata.json').
  • Surgical UI Updates: Task views are updated efficiently using surgical updates to the Zustand store, preventing full UI re-renders and ensuring a smooth user experience.
  • Debounced Updates: File system events are debounced (400ms) to handle rapid changes and prevent excessive updates.
  • Opt-in Setting: The real-time sync feature is opt-in, controlled by a new 'Auto-Sync External Changes' toggle in the application settings.
  • New IPC Channels: Introduced new Inter-Process Communication (IPC) channels for 'spec-added', 'spec-removed', 'spec-updated' events and a 'TASK_GET' channel for fetching individual tasks.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a real-time task synchronization feature, which is a great enhancement for user experience when working with external tools. The implementation is well-structured, using chokidar for file watching, IPC for communication, and surgical Zustand store updates. The feature is also thoughtfully made opt-in. My review includes a couple of suggestions to improve performance and maintainability. One is a high-severity suggestion to optimize how individual tasks are fetched to avoid loading all tasks on every update. The other is a medium-severity suggestion to refactor some duplicated code in the file watcher for better maintainability. Overall, this is a solid contribution.

Comment on lines 65 to 66
const tasks = projectStore.getTasks(projectId);
const task = tasks.find(t => t.specId === specId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The TASK_GET handler currently loads all tasks for a project using projectStore.getTasks(projectId) and then filters them. This is inefficient, especially for projects with a large number of tasks, as it reads all task files from disk for every single task update event. To improve performance, I recommend creating a more targeted method in projectStore, like getTask(projectId, specId), which would load only the necessary files for the specified task.

Comment on lines 204 to 237
watcher.on('change', (filePath: string) => {
const fileName = path.basename(filePath);
// Only watch for our target files
if (fileName !== 'implementation_plan.json' && fileName !== 'task_metadata.json') {
return;
}

// Extract specId from path: specsPath/specId/filename.json
const relativePath = path.relative(specsPath, filePath);
const parts = relativePath.split(path.sep);
if (parts.length !== 2) {
return; // Not a file directly inside a spec folder
}

const specId = parts[0];
this.debouncedSpecUpdate(projectId, specId);
});

// Also handle file additions (new JSON files in existing spec folders)
watcher.on('add', (filePath: string) => {
const fileName = path.basename(filePath);
if (fileName !== 'implementation_plan.json' && fileName !== 'task_metadata.json') {
return;
}

const relativePath = path.relative(specsPath, filePath);
const parts = relativePath.split(path.sep);
if (parts.length !== 2) {
return;
}

const specId = parts[0];
this.debouncedSpecUpdate(projectId, specId);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for handling change and add events for files is duplicated. This could be refactored into a single helper function to improve maintainability and reduce code repetition. A single function can handle both events, making the code cleaner and easier to manage.

      const handleFileEvent = (filePath: string) => {
        const fileName = path.basename(filePath);
        // Only watch for our target files
        if (fileName !== 'implementation_plan.json' && fileName !== 'task_metadata.json') {
          return;
        }

        // Extract specId from path: specsPath/specId/filename.json
        const relativePath = path.relative(specsPath, filePath);
        const parts = relativePath.split(path.sep);
        if (parts.length !== 2) {
          return; // Not a file directly inside a spec folder
        }

        const specId = parts[0];
        this.debouncedSpecUpdate(projectId, specId);
      };

      watcher.on('change', handleFileEvent);
      watcher.on('add', handleFileEvent);

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 344ec65 and 99d2c01.

⛔ Files ignored due to path filters (1)
  • apps/frontend/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (14)
  • apps/frontend/src/main/file-watcher.ts
  • apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts
  • apps/frontend/src/main/ipc-handlers/settings-handlers.ts
  • apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts
  • apps/frontend/src/preload/api/task-api.ts
  • apps/frontend/src/renderer/components/settings/AdvancedSettings.tsx
  • apps/frontend/src/renderer/components/settings/GeneralSettings.tsx
  • apps/frontend/src/renderer/hooks/useIpc.ts
  • apps/frontend/src/renderer/stores/task-store.ts
  • apps/frontend/src/shared/constants/config.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/shared/i18n/locales/en/settings.json
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/shared/types/settings.ts
🧰 Additional context used
📓 Path-based instructions (4)
apps/frontend/src/**/*.{ts,tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use i18n translation keys for all user-facing text in the frontend instead of hardcoded strings

Files:

  • apps/frontend/src/main/ipc-handlers/settings-handlers.ts
  • apps/frontend/src/renderer/hooks/useIpc.ts
  • apps/frontend/src/shared/types/settings.ts
  • apps/frontend/src/renderer/components/settings/AdvancedSettings.tsx
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts
  • apps/frontend/src/renderer/stores/task-store.ts
  • apps/frontend/src/preload/api/task-api.ts
  • apps/frontend/src/main/file-watcher.ts
  • apps/frontend/src/shared/constants/config.ts
  • apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/renderer/components/settings/GeneralSettings.tsx
apps/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use useTranslation() hook with namespace prefixes (e.g., 'navigation:items.key') for accessing translation strings in React components

Files:

  • apps/frontend/src/main/ipc-handlers/settings-handlers.ts
  • apps/frontend/src/renderer/hooks/useIpc.ts
  • apps/frontend/src/shared/types/settings.ts
  • apps/frontend/src/renderer/components/settings/AdvancedSettings.tsx
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts
  • apps/frontend/src/renderer/stores/task-store.ts
  • apps/frontend/src/preload/api/task-api.ts
  • apps/frontend/src/main/file-watcher.ts
  • apps/frontend/src/shared/constants/config.ts
  • apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/renderer/components/settings/GeneralSettings.tsx
apps/frontend/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.

Files:

  • apps/frontend/src/main/ipc-handlers/settings-handlers.ts
  • apps/frontend/src/renderer/hooks/useIpc.ts
  • apps/frontend/src/shared/types/settings.ts
  • apps/frontend/src/renderer/components/settings/AdvancedSettings.tsx
  • apps/frontend/src/shared/types/ipc.ts
  • apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts
  • apps/frontend/src/renderer/stores/task-store.ts
  • apps/frontend/src/preload/api/task-api.ts
  • apps/frontend/src/main/file-watcher.ts
  • apps/frontend/src/shared/constants/config.ts
  • apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts
  • apps/frontend/src/shared/constants/ipc.ts
  • apps/frontend/src/renderer/components/settings/GeneralSettings.tsx
apps/frontend/src/shared/i18n/locales/**/*.json

📄 CodeRabbit inference engine (CLAUDE.md)

apps/frontend/src/shared/i18n/locales/**/*.json: Store translation strings in namespace-organized JSON files at apps/frontend/src/shared/i18n/locales/{lang}/*.json for each supported language
When implementing new frontend features, add translation keys to all language files (minimum: en/.json and fr/.json)

Files:

  • apps/frontend/src/shared/i18n/locales/en/settings.json
🧠 Learnings (2)
📚 Learning: 2025-12-30T16:38:36.314Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T16:38:36.314Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : When implementing new frontend features, add translation keys to all language files (minimum: en/*.json and fr/*.json)

Applied to files:

  • apps/frontend/src/shared/i18n/locales/en/settings.json
📚 Learning: 2025-12-30T16:38:36.314Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T16:38:36.314Z
Learning: Applies to apps/frontend/src/shared/i18n/locales/**/*.json : Store translation strings in namespace-organized JSON files at `apps/frontend/src/shared/i18n/locales/{lang}/*.json` for each supported language

Applied to files:

  • apps/frontend/src/shared/i18n/locales/en/settings.json
🧬 Code graph analysis (6)
apps/frontend/src/main/ipc-handlers/settings-handlers.ts (3)
apps/frontend/src/main/project-store.ts (1)
  • projectStore (785-785)
apps/frontend/src/main/file-watcher.ts (1)
  • fileWatcher (303-303)
apps/frontend/src/shared/constants/config.ts (1)
  • getSpecsDir (106-109)
apps/frontend/src/renderer/hooks/useIpc.ts (1)
apps/frontend/src/renderer/stores/task-store.ts (3)
  • handleSpecAdded (560-585)
  • handleSpecRemoved (591-596)
  • handleSpecUpdated (602-625)
apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts (6)
apps/frontend/src/main/settings-utils.ts (1)
  • readSettingsFile (29-43)
apps/frontend/src/shared/types/settings.ts (1)
  • AppSettings (217-270)
apps/frontend/src/shared/constants/config.ts (1)
  • DEFAULT_APP_SETTINGS (19-54)
apps/frontend/src/main/project-store.ts (1)
  • projectStore (785-785)
apps/frontend/src/main/file-watcher.ts (1)
  • fileWatcher (303-303)
apps/frontend/src/shared/constants/ipc.ts (1)
  • IPC_CHANNELS (6-488)
apps/frontend/src/preload/api/task-api.ts (2)
apps/frontend/src/shared/types/task.ts (1)
  • Task (237-257)
apps/frontend/src/shared/constants/ipc.ts (1)
  • IPC_CHANNELS (6-488)
apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts (1)
apps/frontend/src/main/file-watcher.ts (1)
  • fileWatcher (303-303)
apps/frontend/src/renderer/components/settings/GeneralSettings.tsx (1)
apps/frontend/src/renderer/components/settings/utils/hookProxyFactory.ts (1)
  • settings (15-15)
🔇 Additional comments (27)
apps/frontend/src/shared/types/settings.ts (1)

268-269: LGTM! Well-defined optional property.

The new watchFilesystemForExternalChanges property is appropriately typed as an optional boolean, which aligns with the opt-in nature of this feature. The descriptive comment clearly documents its purpose.

apps/frontend/src/renderer/components/settings/GeneralSettings.tsx (1)

166-182: LGTM! Excellent adherence to React and i18n patterns.

This implementation correctly follows established patterns:

  • Uses useTranslation() hook with proper translation keys (no hardcoded strings)
  • Properly binds to settings.watchFilesystemForExternalChanges with safe ?? false default
  • Matches the structure and style of adjacent settings (e.g., autoNameTerminals)
  • Correctly propagates changes via onSettingsChange

As per coding guidelines for i18n usage and React patterns.

apps/frontend/src/main/ipc-handlers/agent-events-handlers.ts (1)

203-230: LGTM! Event handlers follow established patterns and the complete event flow is properly implemented.

The three new spec lifecycle event handlers are well-implemented:

  • Consistent with existing event handler patterns in this file
  • Proper null-safety checks for mainWindow
  • Helpful console logging for debugging
  • Clear section comment for code organization

The event signatures are logical and correctly mapped:

  • spec-added includes specPath (useful for new specs) → forwarded via TASK_SPEC_ADDED
  • spec-removed and spec-updated only need projectId and specId → forwarded via TASK_SPEC_REMOVED and TASK_SPEC_UPDATED

The complete event flow is properly implemented: fileWatcher emits events → agent-events-handlers forwards via IPC → preload API listeners in task-api.ts → task-store processes renderer-side updates.

apps/frontend/src/shared/constants/config.ts (1)

51-53: LGTM!

The new watchFilesystemForExternalChanges setting is correctly added with a sensible default of false for this opt-in feature. The inline comment clearly documents the purpose.

apps/frontend/src/main/ipc-handlers/settings-handlers.ts (2)

6-6: LGTM!

Import additions are correct and necessary for the new filesystem watching functionality.


17-18: LGTM!

Correct imports for fileWatcher singleton and projectStore to enable watcher lifecycle management.

apps/frontend/src/renderer/hooks/useIpc.ts (3)

2-2: LGTM!

Correct import of the spec event handlers from task-store.


169-189: LGTM!

The spec event listeners follow the established pattern in this file. The async handlers (handleSpecAdded, handleSpecUpdated) are intentionally called without await for fire-and-forget behavior, which is appropriate here since error handling is done within the handlers themselves.

The unused _specPath parameter is correctly prefixed with underscore.


204-206: LGTM!

Cleanup functions properly added to prevent memory leaks when the component unmounts.

apps/frontend/src/shared/types/ipc.ts (2)

150-150: LGTM!

The getTask method signature correctly returns IPCResult<Task | null> to handle both found and not-found cases.


666-675: LGTM!

The spec event listener types are well-defined and follow the established pattern for IPC event listeners in this file. The cleanup function return type () => void is consistent with other listeners.

apps/frontend/src/shared/constants/ipc.ts (1)

51-56: LGTM!

New IPC channel constants are well-organized and follow the existing naming conventions. The placement in the Task events section with a clear subsection comment for single task operations improves code organization.

apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts (3)

2-3: LGTM!

Necessary imports for the new functionality including DEFAULT_APP_SETTINGS, AppSettings, and getSpecsDir.


10-11: LGTM!

Imports for fileWatcher singleton and readSettingsFile utility.


55-76: LGTM!

The TASK_GET handler is clean and follows the established pattern. Returning { success: true, data: null } for not-found cases is appropriate since a missing task isn't an error condition in this context (the spec may have been deleted).

apps/frontend/src/renderer/stores/task-store.ts (5)

22-22: LGTM!

New action added to the TaskState interface for spec-based task removal.


167-179: LGTM!

Clean implementation that correctly:

  1. Returns early if task not found (no-op)
  2. Clears selectedTaskId when the removed task was selected
  3. Uses immutable filter pattern consistent with the store

552-585: Well-designed surgical update with appropriate fallback.

The handleSpecAdded implementation correctly:

  • Attempts a surgical fetch of just the new task
  • Checks for duplicates before adding
  • Updates existing task if already present (handles race conditions)
  • Falls back to full reload only on error

This is a robust pattern for handling external file changes.


587-596: LGTM!

Simple and correct. The unused projectId parameter maintains API consistency with other handlers and enables future enhancements (e.g., project-scoped validation).


598-625: LGTM!

The handleSpecUpdated implementation correctly:

  • Skips processing if task isn't in the store (avoids unnecessary IPC)
  • Performs surgical update without full reload
  • Doesn't need a fallback since we're only updating existing tasks

The lack of fallback (unlike handleSpecAdded) is appropriate here since update failures for existing tasks are less critical than missing new tasks entirely.

apps/frontend/src/main/file-watcher.ts (4)

13-24: LGTM!

The new interfaces are well-structured for tracking per-project specs watchers and debounce state.


32-34: LGTM!

The new class properties are appropriate. The 400ms debounce is a reasonable choice for handling rapid filesystem changes.


146-244: LGTM!

The watchSpecsDirectory implementation is well-designed:

  • Proper depth limiting (depth: 1) ensures only spec folders and their direct children are watched.
  • Immediate emission for directory add/remove events, with debouncing for file changes, is the correct approach.
  • Path validation correctly filters for direct children only.

250-267: LGTM!

The debounce implementation is correct with proper timeout cleanup and composite key for per-spec deduplication.

apps/frontend/src/preload/api/task-api.ts (3)

17-82: LGTM!

The interface additions are well-typed and consistent with existing patterns:

  • getTask correctly returns IPCResult<Task | null> for when a task may not exist.
  • Spec event listeners follow the established pattern of returning cleanup functions.

89-91: LGTM!

The getTask implementation is straightforward and follows the existing pattern for IPC invoke calls.


292-342: LGTM!

The spec event listener implementations are well-structured:

  • Follow the established pattern from existing event listeners in this file.
  • Properly return cleanup functions using removeListener to prevent memory leaks.
  • Handler signatures correctly match the expected IPC event parameters.

Comment on lines +204 to +229
// Handle filesystem watcher setting change
if (settings.watchFilesystemForExternalChanges !== undefined) {
const wasEnabled = currentSettings.watchFilesystemForExternalChanges ?? false;
const isNowEnabled = settings.watchFilesystemForExternalChanges;

if (!wasEnabled && isNowEnabled) {
// Setting was turned ON - start watching all active projects
console.log('[SETTINGS_SAVE] Filesystem watching enabled, starting watchers for all projects');
const projects = projectStore.getProjects();
for (const project of projects) {
if (!fileWatcher.isWatchingSpecs(project.id)) {
const specsBaseDir = getSpecsDir(project.autoBuildPath);
const specsPath = path.join(project.path, specsBaseDir);
fileWatcher.watchSpecsDirectory(project.id, specsPath).catch((err) => {
console.error(`[SETTINGS_SAVE] Failed to start specs watcher for project ${project.id}:`, err);
});
}
}
} else if (wasEnabled && !isNowEnabled) {
// Setting was turned OFF - stop all watchers
console.log('[SETTINGS_SAVE] Filesystem watching disabled, stopping all watchers');
fileWatcher.unwatchAllSpecsDirectories().catch((err) => {
console.error('[SETTINGS_SAVE] Failed to stop specs watchers:', err);
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Implementation is correct, but consider aggregating errors for user feedback.

The logic correctly handles the ON/OFF transitions for the filesystem watching feature. However, when enabling watchers fails for some projects, errors are only logged to console. Consider aggregating failures and including them in the response so the UI can inform the user which projects failed to start watching.

Current approach is acceptable for an opt-in feature, but worth considering for improved UX.

🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/settings-handlers.ts around lines
204-229, when enabling filesystem watchers failures are only logged to console;
collect any errors encountered while starting per-project watchers into an array
(including project id and error message), await all watch attempts (e.g.,
Promise.allSettled or sequential try/catch), and add that aggregated error list
to the handler's response payload so the UI can surface which projects failed to
start watching; do the same for unwatchAllSpecsDirectories failures (include its
error in the response) and ensure the saved-settings result schema is extended
to carry optional watcherErrors.

Comment on lines +13 to +20
/**
* Check if filesystem watching is enabled in settings
*/
function isFilesystemWatchingEnabled(): boolean {
const savedSettings = readSettingsFile() as Partial<AppSettings> | undefined;
const settings = { ...DEFAULT_APP_SETTINGS, ...savedSettings };
return settings.watchFilesystemForExternalChanges ?? false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider caching or memoizing the settings read.

The helper reads settings from disk on every call. While this isn't a hot path (called only during TASK_LIST), consider whether the settings could be cached or accessed via a centralized settings manager to avoid repeated disk I/O.

Current implementation is functionally correct and acceptable for the use case.

🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts around lines 13 to
20, the helper currently reads the settings file on every call which causes
repeated disk I/O; change it to read once and cache the parsed settings in
memory (e.g., a module-scoped variable) and return the cached value on
subsequent calls, and ensure you provide a means to invalidate or refresh the
cache when settings are updated (or alternatively fetch from the app’s
centralized settings manager if available) so the cached settings remain
consistent.

Comment on lines +35 to +49

// Start watching the specs directory for this project (if enabled in settings)
const project = projectStore.getProject(projectId);
if (project && isFilesystemWatchingEnabled()) {
const specsBaseDir = getSpecsDir(project.autoBuildPath);
const specsPath = path.join(project.path, specsBaseDir);

// Only start watching if not already watching
if (!fileWatcher.isWatchingSpecs(projectId)) {
console.log(`[IPC] TASK_LIST starting specs watcher for project ${projectId}`);
fileWatcher.watchSpecsDirectory(projectId, specsPath).catch((err) => {
console.error(`[IPC] Failed to start specs watcher for project ${projectId}:`, err);
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Side effect in TASK_LIST is acceptable but worth documenting.

Initializing the specs watcher as a side effect of TASK_LIST is a pragmatic approach that ensures watching starts when the user views tasks. The guard conditions (isFilesystemWatchingEnabled() and !fileWatcher.isWatchingSpecs(projectId)) prevent unnecessary operations.

Consider adding a brief comment explaining why the watcher is started here (e.g., "// Lazily start watcher when tasks are first viewed").

🤖 Prompt for AI Agents
In apps/frontend/src/main/ipc-handlers/task/crud-handlers.ts around lines 35 to
49, add a brief explanatory comment above the block that starts the specs
watcher to document the intentional side effect: state that the watcher is
started lazily when TASK_LIST runs so watchers are only created when the user
views tasks (and that existing guards prevent duplicate or unwanted watching).
Keep the comment short, reference the guards isFilesystemWatchingEnabled() and
fileWatcher.isWatchingSpecs(projectId), and mention it’s deliberate to avoid
moving logic elsewhere.

Comment on lines +103 to +104
"watchFilesystem": "Auto-Sync External Changes",
"watchFilesystemDescription": "Automatically update task views when tasks are created or modified outside the app (e.g., via CLI or AI agents)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if French translations exist for watchFilesystem keys

if [ -f "apps/frontend/src/shared/i18n/locales/fr/settings.json" ]; then
  echo "French settings file found. Checking for watchFilesystem keys..."
  cat apps/frontend/src/shared/i18n/locales/fr/settings.json | grep -A 1 "watchFilesystem"
  echo ""
  echo "Full context around line 103-104 in French file:"
  # Get approximate line count to find similar location
  wc -l apps/frontend/src/shared/i18n/locales/fr/settings.json
else
  echo "French settings file not found"
  exit 1
fi

Repository: AndyMik90/Auto-Claude

Length of output: 238


🏁 Script executed:

#!/bin/bash
# Check English settings file for the keys
sed -n '100,110p' apps/frontend/src/shared/i18n/locales/en/settings.json

Repository: AndyMik90/Auto-Claude

Length of output: 680


Add French translations for the new watch filesystem keys.

The watchFilesystem and watchFilesystemDescription keys are missing from apps/frontend/src/shared/i18n/locales/fr/settings.json. Per the coding guidelines, translation keys must be added to all supported language files (minimum: en/.json and fr/.json).

🤖 Prompt for AI Agents
In apps/frontend/src/shared/i18n/locales/fr/settings.json around where lines
103–104 map to in en, the keys "watchFilesystem" and
"watchFilesystemDescription" are missing; add them with French translations,
e.g. "watchFilesystem": "Synchronisation automatique des changements externes"
and "watchFilesystemDescription": "Met à jour automatiquement les vues de tâches
lorsque des tâches sont créées ou modifiées en dehors de l'application (par ex.
via CLI ou agents IA)", and ensure proper JSON syntax (quotes, commas) to match
surrounding entries.

greghughespdx and others added 2 commits January 1, 2026 15:51
- Extract duplicate file event handlers into handleSpecFileEvent helper
- Add optimized getTask() method to ProjectStore for single-task loading
- Add loadTaskFromSpecFolder() private method to avoid loading all tasks
- Update TASK_GET handler to use efficient single-task loader

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@MikeeBuilds MikeeBuilds added feature New feature or request area/fullstack This is Frontend + Backend size/L Large (500-999 lines) labels Jan 2, 2026
@adryserage
Copy link
Contributor

Review Feedback Acknowledged

Thanks for the review suggestions! Key points noted:

High Priority

  1. Performance: Create getTask(projectId, specId) for targeted loading instead of loading all tasks
  2. Memory leak: Clear pending debounced updates when stopping watchers

Medium Priority

  1. DRY: Refactor duplicated change/add event handlers into single handleFileEvent function
  2. UX: Aggregate watcher errors for user feedback

Low Priority

  1. Caching: Consider caching settings reads

These are valid improvements for a follow-up PR. The current implementation is functional for the MVP.

@adryserage
Copy link
Contributor

⚠️ Multiple PRs from develop branch

There are multiple open PRs from the develop branch:

Recommendation: These PRs may have conflicting or overlapping changes. Consider:

  1. Merging them into a single consolidated PR
  2. Or rebasing each to have distinct branches

Please review and consolidate or close stale ones.

mirzaaghazadeh and others added 3 commits January 1, 2026 21:24
)

* Enhance Python detection to find versioned Homebrew installations

Fixes issue where users with Python 3.9.6 at /usr/bin/python3 would get
"Auto Claude requires Python 3.10 or higher" error even when they had
newer Python versions installed via Homebrew.

Changes:
- Updated findHomebrewPython() to check for versioned Python installations
- Now searches for python3.13, python3.12, python3.11, python3.10 in addition to generic python3
- Validates each found Python to ensure it meets version requirements
- Checks both Apple Silicon (/opt/homebrew/bin) and Intel Mac (/usr/local/bin) locations

This ensures the app automatically finds and uses the latest compatible Python
version instead of falling back to the potentially outdated system Python.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>

* Update apps/frontend/src/main/python-detector.ts

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>

* Address PR review findings for Python detection enhancement

Addresses all review findings from Auto Claude PR Review:

1. [HIGH] Align version ordering between python-detector.ts and cli-tool-manager.ts
   - Both now use consistent order: versioned first (3.13→3.10), then generic python3
   - This ensures different parts of the app use the same Python version
   - Added validation in cli-tool-manager.ts (was missing before)

2. [MEDIUM] Add try/catch around validatePythonVersion calls
   - Wrapped validation in try/catch to handle timeouts and permission errors
   - Follows same pattern as findPythonCommand()
   - Ensures graceful fallback to next candidate on validation failure

3. [LOW] Add debug logging for Python detection
   - Added console.log for successful detection with version info
   - Added console.warn for rejected candidates with reason
   - Added logging when no valid Python found
   - Improves troubleshooting of user Python detection issues

4. [LOW] Document maintenance requirement for version list
   - Added JSDoc note about updating list for new Python releases
   - Added TODO comment for Python 3.14+ updates
   - Applied to both files for consistency

Additional improvements:
- Fixed bug in cli-tool-manager.ts that returned first found Python without validation
- Both detection systems now validate Python version requirements (3.10+)
- Consistent logging format between both detection systems

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>

* Add Python 3.14 support to version detection

Python 3.14 was released, so adding it to the detection lists:
- Updated pythonNames arrays in both python-detector.ts and cli-tool-manager.ts
- Added python3.14 to SAFE_PYTHON_COMMANDS set
- Updated JSDoc comments to reflect Python 3.14 support
- Removed TODO about Python 3.14 (now implemented)

This ensures the app can detect and use Python 3.14 installations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>

* Refactor: Extract shared Homebrew Python detection logic

Eliminated code duplication by extracting shared Python detection logic
into a reusable utility module.

Changes:
- Created apps/frontend/src/main/utils/homebrew-python.ts
  - Exported findHomebrewPython() utility function
  - Accepts validation function and log prefix as parameters
  - Contains all shared detection logic (version list, validation, logging)

- Updated python-detector.ts
  - Removed duplicate findHomebrewPython() implementation (45 lines)
  - Now imports and delegates to shared utility
  - Maintains identical behavior and error semantics

- Updated cli-tool-manager.ts
  - Removed duplicate findHomebrewPython() implementation (52 lines)
  - Now imports and delegates to shared utility
  - Maintains identical behavior and error semantics

Benefits:
- Single source of truth for Homebrew Python detection
- Easier to maintain (update version list in one place)
- Consistent behavior across the application
- Reduced code duplication (~90 lines eliminated)

The refactored code maintains 100% backward compatibility with identical
return values, logging behavior, and error handling.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>

---------

Co-authored-by: Claude Sonnet 4.5 <[email protected]>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Andy <[email protected]>
…ik90#532)

PR review agents were reading files from the current checkout branch
(e.g., develop) instead of the actual PR branch when using Read/Grep/Glob
tools. This caused incorrect review findings.

The fix creates a temporary detached worktree at the PR head commit for
each review, ensuring agents read from the correct branch state:

- Add head_sha/base_sha fields to PRContext dataclass
- Create worktree at PR commit before spawning specialist agents
- Use worktree path as project_dir for SDK client
- Cleanup worktree after review with fallback chain
- Add startup cleanup for orphaned worktrees from crashed runs

Worktrees are stored in .auto-claude/pr-review-worktrees/ (already
gitignored) to avoid /tmp filesystem boundary issues and support
concurrent reviews.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.5 <[email protected]>
Addresses PR feedback - prevents memory leaks by clearing pending
setTimeout callbacks when unwatchSpecsDirectory() or
unwatchAllSpecsDirectories() is called.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 188c7b0 and ca89425.

📒 Files selected for processing (6)
  • apps/backend/runners/github/context_gatherer.py
  • apps/backend/runners/github/services/parallel_orchestrator_reviewer.py
  • apps/frontend/src/main/cli-tool-manager.ts
  • apps/frontend/src/main/file-watcher.ts
  • apps/frontend/src/main/python-detector.ts
  • apps/frontend/src/main/utils/homebrew-python.ts
🧰 Additional context used
📓 Path-based instructions (4)
apps/frontend/src/**/*.{ts,tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use i18n translation keys for all user-facing text in the frontend instead of hardcoded strings

Files:

  • apps/frontend/src/main/python-detector.ts
  • apps/frontend/src/main/utils/homebrew-python.ts
  • apps/frontend/src/main/cli-tool-manager.ts
  • apps/frontend/src/main/file-watcher.ts
apps/frontend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use useTranslation() hook with namespace prefixes (e.g., 'navigation:items.key') for accessing translation strings in React components

Files:

  • apps/frontend/src/main/python-detector.ts
  • apps/frontend/src/main/utils/homebrew-python.ts
  • apps/frontend/src/main/cli-tool-manager.ts
  • apps/frontend/src/main/file-watcher.ts
apps/frontend/**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

apps/frontend/**/*.{ts,tsx}: Review React patterns and TypeScript type safety.
Check for proper state management and component composition.

Files:

  • apps/frontend/src/main/python-detector.ts
  • apps/frontend/src/main/utils/homebrew-python.ts
  • apps/frontend/src/main/cli-tool-manager.ts
  • apps/frontend/src/main/file-watcher.ts
apps/backend/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

apps/backend/**/*.py: Always use the Claude Agent SDK (claude-agent-sdk package) for all AI interactions, never use the Anthropic API directly
Use the create_client() function from apps/backend/core/client.py to instantiate Claude SDK clients, not direct ClaudeSDKClient initialization

Files:

  • apps/backend/runners/github/context_gatherer.py
  • apps/backend/runners/github/services/parallel_orchestrator_reviewer.py

⚙️ CodeRabbit configuration file

apps/backend/**/*.py: Focus on Python best practices, type hints, and async patterns.
Check for proper error handling and security considerations.
Verify compatibility with Python 3.12+.

Files:

  • apps/backend/runners/github/context_gatherer.py
  • apps/backend/runners/github/services/parallel_orchestrator_reviewer.py
🧠 Learnings (1)
📚 Learning: 2025-12-30T16:38:36.314Z
Learnt from: CR
Repo: AndyMik90/Auto-Claude PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-30T16:38:36.314Z
Learning: Use git worktrees for isolated agent builds with branches following the naming pattern `auto-claude/{spec-name}`

Applied to files:

  • apps/backend/runners/github/services/parallel_orchestrator_reviewer.py
🧬 Code graph analysis (2)
apps/frontend/src/main/python-detector.ts (2)
apps/frontend/src/main/cli-tool-manager.ts (1)
  • findHomebrewPython (739-744)
apps/frontend/src/main/utils/homebrew-python.ts (1)
  • findHomebrewPython (33-77)
apps/frontend/src/main/utils/homebrew-python.ts (1)
apps/frontend/src/main/cli-tool-manager.ts (1)
  • findHomebrewPython (739-744)
🔇 Additional comments (18)
apps/backend/runners/github/context_gatherer.py (2)

204-206: LGTM! Clean dataclass field additions.

The new head_sha and base_sha fields are well-designed:

  • Proper type hints with sensible defaults (empty strings)
  • Clear documentation explaining their purpose (worktree creation for PR review isolation)
  • Non-breaking change due to default values
  • Consistent naming convention

297-298: LGTM! Safe field population.

The new fields are populated correctly:

  • Uses .get() with appropriate fallback to empty strings
  • Keys (headRefOid, baseRefOid) are already included in the metadata fetch (lines 312-313)
  • Consistent with the existing pattern for extracting PR metadata
  • SHAs are validated before use in git commands elsewhere (lines 338-347)
apps/frontend/src/main/python-detector.ts (2)

5-5: LGTM! Clean delegation to shared utility.

The refactoring successfully centralizes Homebrew Python detection logic while maintaining the existing function signature. The import alias prevents naming conflicts, and the delegation properly passes the validation callback and log prefix.

Also applies to: 38-43


301-310: Python 3.14 is already released and available. Python 3.14.0 was released on October 7, 2025, with bugfix releases (3.14.1, 3.14.2) already available. The addition of python3.14 to the safe commands list is justified and correct. The comment reminder to update when new Python versions are released is helpful for future maintenance.

apps/frontend/src/main/cli-tool-manager.ts (1)

30-30: LGTM! Consistent delegation pattern.

The refactoring mirrors the approach in python-detector.ts, properly delegating to the shared utility. The arrow function correctly preserves the this context for the validatePython instance method, and the log prefix appropriately identifies the source module.

Also applies to: 735-743

apps/frontend/src/main/utils/homebrew-python.ts (2)

15-19: LGTM! Well-defined validation interface.

The PythonValidation interface provides a clear contract for validation results. The optional version field and required message field enable both success and failure scenarios to be communicated effectively.


33-77: LGTM! Solid implementation with good error handling.

The function implements a sensible detection strategy:

  • Prioritizes newer Python versions for better compatibility
  • Checks architecture-specific Homebrew paths (Apple Silicon and Intel)
  • Validates each candidate before returning
  • Handles errors gracefully without blocking other candidates
  • Provides comprehensive logging for debugging

The same verification about Python 3.14 availability applies here as in the other files.

apps/backend/runners/github/services/parallel_orchestrator_reviewer.py (3)

23-25: LGTM: Clean imports and constant definition.

The new imports (shutil, subprocess, uuid) are appropriate for worktree management, and the project-local worktree directory follows good conventions.

Also applies to: 66-67


166-197: LGTM: Robust cleanup with fallback chain.

The cleanup implementation correctly handles edge cases with defensive checks, git worktree remove as primary method, shutil.rmtree fallback, and appropriate error logging without raising exceptions (suitable for cleanup paths).


707-710: LGTM: Proper cleanup in finally block.

The finally block ensures worktree cleanup occurs even on errors, preventing resource leaks. This aligns with the PR objectives to address memory leak concerns.

apps/frontend/src/main/file-watcher.ts (8)

13-24: LGTM! Well-structured interfaces.

The new interfaces are cleanly designed and properly typed to support specs directory watching and debounced updates.


28-34: LGTM! Clean state management.

The state management additions are well-organized, properly scoped, and the 400ms debounce window is appropriate for coalescing rapid file system events.


203-224: Excellent refactoring! Duplication issue resolved.

The handleSpecFileEvent helper function elegantly addresses the previous review feedback about duplicated logic for handling change and add events. The implementation is clean and maintainable.


237-254: LGTM! Correct debounce implementation.

The debounce logic properly clears existing timeouts before scheduling new ones, preventing memory leaks and ensuring only the last update fires after the debounce window.


259-268: Excellent fix! Memory leak concern resolved.

The method now properly clears pending debounced updates before closing the watcher (line 264), completely addressing the previous review feedback about potential memory leaks from pending spec-updated events.


273-280: LGTM! Clean helper implementation.

The helper method correctly clears all pending debounced updates for a specific project, ensuring no dangling timers remain after unwatching.


285-287: LGTM!

Simple and correct query method consistent with the existing isWatching pattern.


292-306: Excellent implementation! Bulk cleanup properly handled.

The method correctly clears all pending debounced updates before closing watchers (lines 293-297), fully addressing the previous review feedback. The parallel watcher closure with Promise.all is efficient.

Comment on lines +144 to +149
subprocess.run(
["git", "fetch", "origin", head_sha],
cwd=self.project_dir,
capture_output=True,
timeout=60,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling for git fetch command.

The git fetch operation doesn't verify success. If the fetch fails (network issues, invalid SHA, permissions), the subsequent git worktree add will fail with a less informative error message.

🔎 Proposed fix
-        subprocess.run(
+        fetch_result = subprocess.run(
             ["git", "fetch", "origin", head_sha],
             cwd=self.project_dir,
             capture_output=True,
+            text=True,
             timeout=60,
         )
+        
+        if fetch_result.returncode != 0:
+            raise RuntimeError(
+                f"Failed to fetch commit {head_sha}: {fetch_result.stderr}"
+            )
🤖 Prompt for AI Agents
In apps/backend/runners/github/services/parallel_orchestrator_reviewer.py around
lines 144 to 149, the git fetch subprocess call does not check for failure;
change the invocation to detect errors (either pass check=True or inspect the
CompletedProcess.returncode), capture and log stderr/stdout on failure, and
raise or return a clear exception so callers get a meaningful message (include
head_sha and project_dir in the log). Ensure timeout remains and include the
subprocess output in the logged/raised error to aid debugging.

Comment on lines +152 to +157
result = subprocess.run(
["git", "worktree", "add", "--detach", str(worktree_path), head_sha],
cwd=self.project_dir,
capture_output=True,
text=True,
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add timeout to git worktree add command.

The git worktree add command lacks a timeout, which could cause the review process to hang indefinitely if the operation stalls.

🔎 Proposed fix
         result = subprocess.run(
             ["git", "worktree", "add", "--detach", str(worktree_path), head_sha],
             cwd=self.project_dir,
             capture_output=True,
             text=True,
+            timeout=60,
         )
🤖 Prompt for AI Agents
In apps/backend/runners/github/services/parallel_orchestrator_reviewer.py around
lines 152 to 157, the subprocess.run call invoking "git worktree add" has no
timeout and can hang; add a timeout argument (e.g., timeout=60 or a configurable
constant), wrap the call in a try/except catching subprocess.TimeoutExpired, log
an error including the timeout and command output, and abort/return failure in
that branch so the reviewer process does not hang indefinitely.

Comment on lines +212 to +216
registered = {
Path(line.split(" ", 1)[1])
for line in result.stdout.split("\n")
if line.startswith("worktree ")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add defensive parsing for git worktree list output.

Line 213 assumes each line starting with "worktree " has a space and path component. If the git output is malformed or the format changes, this will raise an IndexError.

🔎 Proposed fix
         registered = {
-            Path(line.split(" ", 1)[1])
+            Path(parts[1])
             for line in result.stdout.split("\n")
-            if line.startswith("worktree ")
+            if line.startswith("worktree ") and (parts := line.split(" ", 1)) and len(parts) == 2
         }
🤖 Prompt for AI Agents
In apps/backend/runners/github/services/parallel_orchestrator_reviewer.py around
lines 212 to 216, the current comprehension assumes every line that starts with
"worktree " contains a space and a valid path and may raise IndexError on
malformed git output; change the parsing to be defensive by iterating lines,
trimming whitespace, skipping empty lines, verifying the line starts with
"worktree " and that splitting with maxsplit=1 yields a non-empty path component
(or use str.partition(" ") and check the third part), and only then construct
Path(path); optionally log or ignore malformed entries rather than letting an
exception propagate.

Comment on lines +535 to +564
# Create temporary worktree at PR head commit for isolated review
# This ensures agents read from the correct PR state, not the current checkout
head_sha = context.head_sha or context.head_branch
if not head_sha:
logger.warning(
"[ParallelOrchestrator] No head_sha available, using current checkout"
)
# Fallback to original behavior if no SHA available
project_root = (
self.project_dir.parent.parent
if self.project_dir.name == "backend"
else self.project_dir
)
else:
try:
worktree_path = self._create_pr_worktree(
head_sha, context.pr_number
)
project_root = worktree_path
except RuntimeError as e:
logger.warning(
f"[ParallelOrchestrator] Worktree creation failed, "
f"using current checkout: {e}"
)
# Fallback to original behavior if worktree creation fails
project_root = (
self.project_dir.parent.parent
if self.project_dir.name == "backend"
else self.project_dir
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Refactor duplicate project_root calculation logic.

The project_root fallback calculation appears twice (lines 543-546 and 560-563) with identical logic. This violates the DRY principle and makes the code harder to maintain.

🔎 Proposed refactor
+            # Calculate fallback project root
+            def get_fallback_project_root() -> Path:
+                return (
+                    self.project_dir.parent.parent
+                    if self.project_dir.name == "backend"
+                    else self.project_dir
+                )
+
             # Create temporary worktree at PR head commit for isolated review
             # This ensures agents read from the correct PR state, not the current checkout
             head_sha = context.head_sha or context.head_branch
             if not head_sha:
                 logger.warning(
                     "[ParallelOrchestrator] No head_sha available, using current checkout"
                 )
-                # Fallback to original behavior if no SHA available
-                project_root = (
-                    self.project_dir.parent.parent
-                    if self.project_dir.name == "backend"
-                    else self.project_dir
-                )
+                project_root = get_fallback_project_root()
             else:
                 try:
                     worktree_path = self._create_pr_worktree(
                         head_sha, context.pr_number
                     )
                     project_root = worktree_path
                 except RuntimeError as e:
                     logger.warning(
                         f"[ParallelOrchestrator] Worktree creation failed, "
                         f"using current checkout: {e}"
                     )
-                    # Fallback to original behavior if worktree creation fails
-                    project_root = (
-                        self.project_dir.parent.parent
-                        if self.project_dir.name == "backend"
-                        else self.project_dir
-                    )
+                    project_root = get_fallback_project_root()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Create temporary worktree at PR head commit for isolated review
# This ensures agents read from the correct PR state, not the current checkout
head_sha = context.head_sha or context.head_branch
if not head_sha:
logger.warning(
"[ParallelOrchestrator] No head_sha available, using current checkout"
)
# Fallback to original behavior if no SHA available
project_root = (
self.project_dir.parent.parent
if self.project_dir.name == "backend"
else self.project_dir
)
else:
try:
worktree_path = self._create_pr_worktree(
head_sha, context.pr_number
)
project_root = worktree_path
except RuntimeError as e:
logger.warning(
f"[ParallelOrchestrator] Worktree creation failed, "
f"using current checkout: {e}"
)
# Fallback to original behavior if worktree creation fails
project_root = (
self.project_dir.parent.parent
if self.project_dir.name == "backend"
else self.project_dir
)
# Calculate fallback project root
def get_fallback_project_root() -> Path:
return (
self.project_dir.parent.parent
if self.project_dir.name == "backend"
else self.project_dir
)
# Create temporary worktree at PR head commit for isolated review
# This ensures agents read from the correct PR state, not the current checkout
head_sha = context.head_sha or context.head_branch
if not head_sha:
logger.warning(
"[ParallelOrchestrator] No head_sha available, using current checkout"
)
project_root = get_fallback_project_root()
else:
try:
worktree_path = self._create_pr_worktree(
head_sha, context.pr_number
)
project_root = worktree_path
except RuntimeError as e:
logger.warning(
f"[ParallelOrchestrator] Worktree creation failed, "
f"using current checkout: {e}"
)
project_root = get_fallback_project_root()
🤖 Prompt for AI Agents
In apps/backend/runners/github/services/parallel_orchestrator_reviewer.py around
lines 535 to 564, the fallback project_root calculation is duplicated in both
the head_sha-missing branch and the worktree-creation-exception branch; extract
that fallback computation into a single variable or small helper (e.g.,
compute_fallback_project_root or fallback_project_root =
(self.project_dir.parent.parent if self.project_dir.name == "backend" else
self.project_dir)) defined before the if/else, then use that variable in both
places so the logic is only defined once and behavior remains identical.

Comment on lines +151 to +231
async watchSpecsDirectory(projectId: string, specsPath: string): Promise<void> {
// Stop any existing watcher for this project
await this.unwatchSpecsDirectory(projectId);

// Check if specs directory exists
if (!existsSync(specsPath)) {
console.log(`[FileWatcher] Specs directory not found, skipping watch: ${specsPath}`);
return;
}

console.log(`[FileWatcher] Starting specs directory watcher for project ${projectId}: ${specsPath}`);

// Watch for new directories and file changes inside spec folders
const watcher = chokidar.watch(specsPath, {
persistent: true,
ignoreInitial: true,
depth: 1, // Watch spec folders and their direct children (JSON files)
awaitWriteFinish: {
stabilityThreshold: 300,
pollInterval: 100
}
});

// Store watcher info
this.specsWatchers.set(projectId, {
projectId,
watcher,
specsPath
});

// Handle new spec directory added
watcher.on('addDir', (dirPath: string) => {
// Only emit for direct children of specs directory
const relativePath = path.relative(specsPath, dirPath);
if (relativePath && !relativePath.includes(path.sep)) {
const specId = path.basename(dirPath);
console.log(`[FileWatcher] Spec directory added: ${specId} in project ${projectId}`);
this.emit('spec-added', projectId, specId, dirPath);
}
});

// Handle spec directory removed
watcher.on('unlinkDir', (dirPath: string) => {
// Only emit for direct children of specs directory
const relativePath = path.relative(specsPath, dirPath);
if (relativePath && !relativePath.includes(path.sep)) {
const specId = path.basename(dirPath);
console.log(`[FileWatcher] Spec directory removed: ${specId} in project ${projectId}`);
this.emit('spec-removed', projectId, specId);
}
});

// Helper to handle file change/add events for spec files
const handleSpecFileEvent = (filePath: string) => {
const fileName = path.basename(filePath);
// Only watch for our target files
if (fileName !== 'implementation_plan.json' && fileName !== 'task_metadata.json') {
return;
}

// Extract specId from path: specsPath/specId/filename.json
const relativePath = path.relative(specsPath, filePath);
const parts = relativePath.split(path.sep);
if (parts.length !== 2) {
return; // Not a file directly inside a spec folder
}

const specId = parts[0];
this.debouncedSpecUpdate(projectId, specId);
};

// Handle file changes and additions inside spec folders
watcher.on('change', handleSpecFileEvent);
watcher.on('add', handleSpecFileEvent);

// Handle errors
watcher.on('error', (error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
console.error(`[FileWatcher] Specs watcher error for project ${projectId}:`, message);
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider emitting error events for consistency.

The specs watching implementation is solid, but error handling differs from the existing task watching pattern. The watch() method emits 'error' events (lines 47, 83), while watchSpecsDirectory() uses console.error (line 230) without emitting events.

For consistency and to allow consuming code to handle errors uniformly, consider emitting error events for both directory-not-found (line 157) and watcher errors (line 230).

🔎 Optional: align error handling with existing pattern
     // Check if specs directory exists
     if (!existsSync(specsPath)) {
-      console.log(`[FileWatcher] Specs directory not found, skipping watch: ${specsPath}`);
+      this.emit('specs-error', projectId, `Specs directory not found: ${specsPath}`);
       return;
     }
     // Handle errors
     watcher.on('error', (error: unknown) => {
       const message = error instanceof Error ? error.message : String(error);
       console.error(`[FileWatcher] Specs watcher error for project ${projectId}:`, message);
+      this.emit('specs-error', projectId, message);
     });
🤖 Prompt for AI Agents
In apps/frontend/src/main/file-watcher.ts around lines 151 to 231, the specs
watcher currently logs errors to the console instead of emitting them like the
existing task watcher; update error handling so it emits an 'error' event for
consistency: when the specs directory does not exist (line ~157) create and emit
an Error with context (projectId and specsPath) before returning, and in the
watcher.on('error') handler (line ~230) emit the same error event (e.g.,
this.emit('error', projectId, error)) in addition to any existing console
logging so consumers can handle errors uniformly.

@greghughespdx
Copy link
Author

Closing to resubmit from proper feature branch (feat/real-time-task-sync) instead of develop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/fullstack This is Frontend + Backend feature New feature or request size/L Large (500-999 lines)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants