Skip to content

Conversation

@amoscicki
Copy link

@amoscicki amoscicki commented Dec 28, 2025

Summary

This PR adds an automatic update system that checks for new versions from an upstream repository and allows users to update with a single click. The system uses git-based version comparison to detect available updates and performs safe fast-forward merges.

Features

  • Automatic update checking at configurable intervals (1-60 minutes)
  • Manual check/update from Settings > Updates panel
  • Toast notifications with "Update Now" / "Later" options
  • Git-based version comparison using merge-base --is-ancestor
  • Fast-forward only merges (safe, no merge conflicts)
  • Configurable upstream repository URL
  • Dependency injection ready architecture for swapping update backends
image

Architecture

flowchart TB
    subgraph UI["UI Layer"]
        UN[UpdateNotifier]
        US[UpdatesSection]
    end

    subgraph State["State Management"]
        Store[updates-store.ts]
        Hook[use-update-polling.ts]
    end

    subgraph API["API Layer"]
        Client[IUpdatesApiClient]
    end

    subgraph Server["Server Routes"]
        Check[GET /api/updates/check]
        Pull[POST /api/updates/pull]
        Info[GET /api/updates/info]
    end

    subgraph Git["Git Operations"]
        Fetch[git fetch]
        Merge[git merge --ff-only]
        Compare[git merge-base --is-ancestor]
    end

    UN --> Store
    US --> Store
    Hook --> Store
    Store --> Client
    Client --> Check
    Client --> Pull
    Client --> Info
    Check --> Fetch
    Check --> Compare
    Pull --> Fetch
    Pull --> Merge
Loading

Update Check Flow

sequenceDiagram
    participant U as UI (Polling/Manual)
    participant S as Store
    participant A as API Client
    participant R as Server Route
    participant G as Git

    U->>S: checkForUpdates()
    S->>A: checkForUpdates()
    A->>R: GET /api/updates/check
    R->>G: git fetch (temp remote)
    R->>G: git merge-base --is-ancestor
    G-->>R: exit code (0=behind, 1=ahead)
    R-->>A: UpdateCheckResult
    A-->>S: {updateAvailable, versions}
    S-->>U: State updated
    Note over U: Toast shown if update available
Loading

Files Changed

New Files

  • apps/server/src/routes/updates/ - Server routes for update operations
  • apps/ui/src/store/updates-store.ts - Zustand store with DI support
  • apps/ui/src/hooks/use-update-polling.ts - Polling logic (when to check)
  • apps/ui/src/components/updates/ - UpdateNotifier component
  • apps/ui/src/components/views/settings-view/updates/ - Settings UI
  • libs/types/src/updates.ts - Type definitions
  • docs/auto-updates.md - Documentation with DI examples

Modified Files

  • libs/types/src/settings.ts - Added AutoUpdateSettings interface
  • apps/ui/src/store/app-store.ts - Added autoUpdate state
  • apps/ui/src/hooks/use-settings-migration.ts - Sync autoUpdate to server
  • apps/server/src/index.ts - Register update routes
  • README.md - Added auto-updates section

Settings

autoUpdate: {
  enabled: boolean;             // default: true
  checkIntervalMinutes: number; // 1-60, default: 15
  upstreamUrl: string;          // default: AutoMaker-Org/automaker
}

Test Plan

  • Enable auto-updates in Settings > Updates
  • Set interval to 1 minute for testing
  • Observe toast notification when update is available
  • Click "Later" - toast dismisses, reappears after interval
  • Click "Update Now" - update pulls, restart prompt appears
  • Verify manual "Check for Updates" button works
  • Verify upstream URL can be changed
  • Verify settings persist across restarts

Summary by CodeRabbit

  • New Features

    • Built-in auto-update system with check/info/pull APIs and one‑click install.
  • UI

    • Updates settings panel (enable, interval, upstream URL) and background notifier showing “Update Available”, “Updating…”, and “Update installed!” toasts with Restart action; automatic background checks on app start.
  • Stores & Hooks

    • Centralized updates store and polling hook for immediate and periodic checks.
  • API

    • Updates exposed via app API/client and desktop bridge (info/check/pull).
  • Documentation

    • Added Auto‑Updates guide and README entry.
  • Types

    • Public update-related types and default auto-update settings.

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

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @amoscicki, 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 new, fully-featured auto-update system for the application. It enables users to automatically check for new versions from an upstream Git repository at configurable intervals, receive toast notifications when updates are available, and apply updates with a single click. The system is built with a flexible architecture that supports dependency injection, making it adaptable to different update mechanisms in the future.

Highlights

  • Automated Updates: Introduced a comprehensive system for automatic updates, including configurable intervals and manual update options.
  • Git-Based Versioning: Implemented a robust git-based mechanism for checking and applying updates, ensuring safe fast-forward merges.
  • User Interface & Notifications: Added a dedicated "Updates" section in settings and toast notifications for update availability and installation.
  • Dependency Injection Architecture: Designed the update system with DI principles, allowing for easy swapping of update backends (e.g., from git to release-based).
  • Server-Side API: Exposed new API endpoints (/api/updates/check, /api/updates/pull, /api/updates/info) for update operations.

🧠 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.

@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

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 a Git-backed auto-update subsystem: server routes (/api/updates: check, pull, info) and git utilities; new update types and defaults; DI-capable updates Zustand store; polling hook and notifier UI; settings integration, dev proxy, HTTP/Electron API surfaces, and documentation.

Changes

Cohort / File(s) Summary
Docs & README
README.md, docs/auto-updates.md
Adds Auto-Updates entry to README TOC and a comprehensive docs/auto-updates.md describing features, architecture, APIs, DI examples, server workflows, and troubleshooting.
Server — Routes & Utilities
apps/server/src/index.ts, apps/server/src/routes/updates/*, apps/server/src/routes/updates/.../common.ts
Mounts /api/updates router; new common.ts with git/exec/env helpers and repo discovery; handlers for GET /check, POST /pull, GET /info that use a temporary git remote, fetch, and ff-only merge flows, returning structured UpdateApiResponse payloads and logging errors.
UI — Components & Integration
apps/ui/src/components/updates/*, apps/ui/src/components/views/settings-view/*, apps/ui/src/routes/__root.tsx
Adds UpdateNotifier (toast-driven) and UpdatesSection settings UI; adds Settings nav item and view case; mounts notifier and activates polling in root layout; provides barrel export for update components.
UI — Store, Hooks & State
apps/ui/src/store/updates-store.ts, apps/ui/src/store/app-store.ts, apps/ui/src/hooks/use-update-polling.ts, apps/ui/src/hooks/use-settings-migration.ts
New DI-friendly updates-store (Zustand) with actions (fetchInfo, checkForUpdates, pullUpdates) and event emission; app store gains autoUpdate + setAutoUpdate persisted; adds useUpdatePolling hook; sync includes autoUpdate in server payload.
UI — API Clients & Types
apps/ui/src/lib/http-api-client.ts, apps/ui/src/lib/electron.ts, libs/types/src/updates.ts, libs/types/src/settings.ts, libs/types/src/index.ts
HttpApiClient gains updates.{check,pull,info}; ElectronAPI types include optional updates group; adds UpdateInfo/UpdateCheckResult/UpdatePullResult/UpdateApiResponse types and AutoUpdateSettings + DEFAULT_AUTO_UPDATE_SETTINGS; extends GlobalSettings.
UI — Dev Config & Utils
apps/ui/vite.config.mts, apps/ui/src/lib/utils.ts
Adds Vite dev server proxy for /api to backend; adds getRepoDisplayName(url) util for repo labeling in toasts.
Misc — UI wiring & exports
apps/ui/src/components/updates/index.ts, apps/ui/src/store/updates-store.ts, apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
Barrel export for update components; exports/selectors from updates store; extends SettingsViewId union to include 'updates'.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant UI as UI (Settings / Notifier)
    participant Store as UpdatesStore
    participant Poll as Polling Hook
    participant API as HttpApiClient / ElectronAPI
    participant Server as Backend (/api/updates)
    participant Git as Git (local & remote)

    Note over Poll, UI: manual or periodic check triggers
    Poll->>API: check()
    API->>Server: GET /api/updates/check
    Server->>Git: add temp remote & fetch main
    Git-->>Server: remote SHAs
    Server-->>API: UpdateCheckResult
    API->>Store: set updateAvailable & remoteVersion
    Store->>UI: state update → show toast

    UI->>Store: pullUpdates() (user action)
    Store->>API: POST /api/updates/pull
    API->>Server: POST /api/updates/pull
    Server->>Git: fetch & git merge --ff-only
    Git-->>Server: newVersion / alreadyUpToDate
    Server-->>API: UpdatePullResult
    API->>Store: fetchInfo() & emit updatePulled
    Store->>UI: state update → show installed toast
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

Enhancement, Documentation, Ready-To-Merge

Poem

🐇 I sniffed the remotes, found a newer tree,
Toasts chimed softly: "A fresh commit's free",
I nudged a fast‑forward, tidy and neat,
Click Update or Later — hop, your app's complete!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.42% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly describes the main feature being added: an auto-update system with git-based version checking. This accurately reflects the primary objective and the core technical approach implemented throughout the changeset.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3e47a0d and 14f2c92.

📒 Files selected for processing (4)
  • apps/server/src/index.ts
  • apps/server/src/routes/updates/index.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/store/updates-store.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/server/src/index.ts
  • apps/server/src/routes/updates/index.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/store/updates-store.ts
apps/server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use createEventEmitter() from lib/events.ts for all server operations to emit events that stream to frontend via WebSocket

Files:

  • apps/server/src/routes/updates/routes/pull.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/store/updates-store.ts
🧬 Code graph analysis (2)
apps/server/src/routes/updates/routes/pull.ts (2)
apps/server/src/routes/updates/common.ts (11)
  • getAutomakerRoot (73-97)
  • isGitAvailable (102-109)
  • isGitRepo (114-121)
  • hasLocalChanges (142-145)
  • isValidGitUrl (151-163)
  • withTempGitRemote (169-196)
  • getCurrentCommit (126-129)
  • getShortCommit (134-137)
  • execAsync (15-15)
  • execEnv (59-62)
  • logError (19-19)
libs/types/src/updates.ts (1)
  • UpdatePullResult (77-98)
apps/ui/src/store/updates-store.ts (3)
libs/types/src/updates.ts (4)
  • UpdateApiResponse (103-107)
  • UpdateInfo (13-43)
  • UpdateCheckResult (48-72)
  • UpdatePullResult (77-98)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (773-782)
apps/ui/src/lib/http-api-client.ts (1)
  • get (589-611)
🔇 Additional comments (10)
apps/server/src/routes/updates/routes/pull.ts (4)

1-24: Well-structured imports and documentation.

Imports correctly use shared packages (@automaker/types) and local modules. The file header clearly documents the endpoint's purpose.


26-73: Solid defensive validation chain.

The pre-flight checks correctly validate prerequisites in sequence with appropriate HTTP status codes. The _events parameter reservation for future WebSocket streaming is a good forward-compatible design.


75-116: Clean implementation using the withTempGitRemote helper.

The pull logic correctly leverages the shared helper for temp remote lifecycle management, and the --ff-only constraint ensures safe, linear updates. The dual detection for "already up to date" (merge output text and version comparison) provides robustness.


117-148: Comprehensive error handling with actionable messages.

The error detection correctly distinguishes between fast-forward failures and merge conflicts, providing clear guidance to users. The outer catch ensures no unhandled exceptions escape.

apps/ui/src/store/updates-store.ts (6)

1-18: Proper imports from shared packages.

Correctly imports types from @automaker/types and uses the @/lib/electron path alias. The file documentation clearly explains the DI pattern.


19-87: Clean interface design enabling DI and testability.

The interfaces provide a clear contract for swapping update mechanisms (e.g., git → HTTP downloads as documented). The default implementations include null-safe API checks and use standard browser CustomEvent for inter-component communication.


88-154: Comprehensive state and action definitions.

The state structure covers the full update lifecycle (info, availability, loading states, timestamps). Action signatures clearly indicate their async nature and return types.


285-290: Past review concern properly addressed.

The fetchInfo() call is now awaited with a catch block that swallows errors. The comment clearly documents this as best-effort behavior since the primary pull operation succeeded. This addresses the previous concern about fire-and-forget calls.


316-334: SSR-safe default instance and useful selectors.

The default store instance is created at module load, but the underlying API and window access is deferred to method invocation time, maintaining SSR safety. The selectIsLoading selector correctly combines all loading states.


166-314: Well-architected store with consistent patterns.

The store factory implements a clean DI pattern with consistent error handling across all actions. Concurrent call guards prevent race conditions, and state transitions are predictable. The event emission after successful pulls enables loose coupling with other UI components.


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
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 comprehensive auto-update system, which is a significant and well-architected feature. The use of dependency injection for the update client is excellent, allowing for future flexibility. The overall structure is clean, with good separation of concerns between UI, state management, and server-side logic. However, I've identified a critical security vulnerability related to command injection due to improper handling of user-configurable URLs. I've also included several medium-severity comments to improve maintainability, robustness, and consistency across the new modules. Addressing these points will make this great feature even better.

Copy link

@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: 4

🧹 Nitpick comments (9)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)

215-226: Consider handling edge cases for the interval input.

The current implementation silently ignores invalid input values. Users might be confused if typing doesn't update the field. Consider allowing the field to show the typed value and validating on blur, or providing visual feedback for invalid values.

apps/ui/src/hooks/use-update-polling.ts (1)

62-87: Consider stabilizing the onCheck callback reference.

Including onCheck in the dependency array could cause the interval to reset on every render if the callback identity changes. While Zustand's checkForUpdates should be stable, the options.onCheck override might not be. Consider using useCallback or useRef to stabilize the reference.

🔎 Proposed fix using useRef
+import { useEffect, useRef, useCallback } from 'react';
-import { useEffect, useRef } from 'react';
 import { useAppStore } from '@/store/app-store';
 import { useUpdatesStore } from '@/store/updates-store';

 // ... types ...

 export function useUpdatePolling(options: UseUpdatePollingOptions = {}): UseUpdatePollingResult {
   const { autoUpdate } = useAppStore();
   const { checkForUpdates, lastChecked } = useUpdatesStore();

   // Allow overrides for testing
   const isEnabled = options.enabled ?? autoUpdate.enabled;
   const intervalMinutes = options.intervalMinutes ?? autoUpdate.checkIntervalMinutes;
-  const onCheck = options.onCheck ?? checkForUpdates;
+  
+  // Stabilize the check function reference
+  const onCheckRef = useRef(options.onCheck ?? checkForUpdates);
+  onCheckRef.current = options.onCheck ?? checkForUpdates;
+  
+  const stableOnCheck = useCallback(() => onCheckRef.current(), []);

   const intervalRef = useRef<NodeJS.Timeout | null>(null);

   useEffect(() => {
     // Clear any existing interval
     if (intervalRef.current) {
       clearInterval(intervalRef.current);
       intervalRef.current = null;
     }

     // Don't set up polling if disabled
     if (!isEnabled) {
       return;
     }

     // Check immediately on enable
-    onCheck();
+    stableOnCheck();

     // Set up interval
     const intervalMs = intervalMinutes * 60 * 1000;
-    intervalRef.current = setInterval(onCheck, intervalMs);
+    intervalRef.current = setInterval(stableOnCheck, intervalMs);

     return () => {
       if (intervalRef.current) {
         clearInterval(intervalRef.current);
         intervalRef.current = null;
       }
     };
-  }, [isEnabled, intervalMinutes, onCheck]);
+  }, [isEnabled, intervalMinutes, stableOnCheck]);

   return {
     isPollingActive: isEnabled,
-    checkNow: onCheck,
+    checkNow: stableOnCheck,
     lastChecked,
   };
 }
apps/server/src/routes/updates/routes/pull.ts (1)

90-95: Unused variable: currentBranch is retrieved but never used.

The current branch is fetched but not referenced anywhere in the code. If it's not needed, consider removing it to reduce unnecessary git operations.

🔎 Proposed fix
-        // Get current branch
-        const { stdout: branchOutput } = await execAsync('git rev-parse --abbrev-ref HEAD', {
-          cwd: installPath,
-          env: execEnv,
-        });
-        const currentBranch = branchOutput.trim();
-
         // Merge the fetched changes
docs/auto-updates.md (1)

16-21: Align documented defaults and fix markdown lint issues

Two things worth tightening here:

  1. Default interval mismatch

    • Docs table lists “Check interval” default as 5, but the server GET /info handler falls back to checkIntervalMinutes: 15. Please align the table with the actual default (or vice‑versa) so users see the real behavior.
  2. Markdownlint fixes

    • Line 28: the fenced diagram block has no language; change the inner fence to something like ```text to satisfy MD040.

    • Line 304: “Last updated: December 2025” is flagged as emphasis‑as‑heading (MD036). Consider making it a heading, e.g.:

      ### Last updated
      
      December 2025

Also applies to: 28-68, 304-304

apps/ui/src/lib/electron.ts (1)

629-673: Consider reusing shared update types for the updates.info contract

updates.info currently inlines a custom result shape (including isGitRepo and gitAvailable) instead of directly using UpdateApiResponse<UpdateInfo>. That works structurally, but it duplicates the server contract and can drift if UpdateInfo changes (e.g., new fields, changes under mechanismInfo).

Consider tightening this in one of these ways:

  • Type the method as returning UpdateApiResponse<UpdateInfo> and surface isGitRepo / gitAvailable via mechanismInfo on the UI side, or
  • Keep the current UI-friendly shape but have it explicitly derived from UpdateInfo in the HTTP client (e.g., map from mechanismInfo.isGitRepo / gitAvailable), so the server type remains the single source of truth.

This keeps the ElectronAPI surface in sync with the shared @automaker/types definitions and reduces maintenance risk.

apps/server/src/routes/updates/common.ts (1)

23-57: Minor env hardening and make use of isValidGitUrl

Two small improvements here:

  1. When building additionalPaths, process.env.HOME can be undefined on some environments, which would yield a bogus 'undefined/.local/bin' entry:
additionalPaths.push(`${process.env.HOME}/.local/bin`);

Guard it before pushing:

const home = process.env.HOME;
if (home) {
  additionalPaths.push(`${home}/.local/bin`);
}
  1. You already expose isValidGitUrl(), but it’s not used in the update routes yet. It’s a good candidate to validate upstreamUrl before passing it into git commands, to catch obviously bad values early and centralize URL hygiene.

Also applies to: 121-137

apps/ui/src/components/updates/update-notifier.tsx (1)

51-53: Clarify “Later” behavior and isPulling label handling in the update toast

Two subtle UX/logic points in the notifier:

  1. “Later” doesn’t currently cause a future reminder for the same version

    • The comment says “will show again on next check if update still available”, but:
      • shownToastForCommitRef.current is set to remoteVersionShort both when the toast is shown and again in the “Later” handler.
      • The effect early‑returns whenever shownToastForCommitRef.current === remoteVersionShort, and its deps don’t include lastChecked.
    • Net result: for a given remoteVersionShort, once the user has seen the toast (and clicked “Later”), they won’t be reminded again until either updateAvailable drops to false or a new remote version appears.

    Decide which behavior you want and either:

    • Adjust the comment to describe “don’t remind again for this version”, or
    • Wire in a notion of “check tick” (e.g., include lastChecked in the deps and reset shownToastForCommitRef in the cancel handler) so periodic checks can resurface the toast.
  2. isPulling label doesn’t actually update the existing toast

    • The action label is computed with isPulling ? 'Updating...' : 'Update Now', but once the toast is created, the effect will not recreate it while shownToastForCommitRef.current === remoteVersionShort.
    • If you want to surface progress, you’ll need to update the existing toast via its id (per Sonner’s API) instead of relying on the effect’s isPulling dependency.

Neither issue is a blocker, but tightening this will make the notification behavior more predictable.

Also applies to: 95-107, 119-139

libs/types/src/updates.ts (2)

10-43: LGTM! Well-designed interface with good flexibility.

The nullable version fields appropriately handle cases where version info cannot be determined. The optional updateType and mechanismInfo fields provide the flexibility needed for different update mechanisms while maintaining backward compatibility.

Optional: Consider whether mechanismInfo could use a discriminated union based on updateType for better type safety:

mechanismInfo?: 
  | { type: 'git'; isGitRepo: boolean; gitAvailable: boolean }
  | { type: 'release'; channel: string }
  | { type: 'custom'; [key: string]: unknown };

This would provide autocomplete and type checking for known mechanisms while still allowing custom implementations.


100-107: Consider discriminated unions for stronger type safety.

The current design allows result to be undefined even when success is true, requiring runtime checks. A discriminated union would enforce the relationship between success, result, and error at compile time:

🔎 Suggested type-safe refactor
-/**
- * API response wrapper for update operations
- */
-export interface UpdateApiResponse<T> {
-  success: boolean;
-  result?: T;
-  error?: string;
-}
+/**
+ * API response wrapper for update operations
+ */
+export type UpdateApiResponse<T> =
+  | { success: true; result: T }
+  | { success: false; error: string };

Benefits:

  • TypeScript guarantees result exists when success is true
  • No need for optional chaining: response.result?.field
  • Compiler enforces error handling

Trade-off: Less flexible for partial success or warnings (though you could add a third variant if needed).

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a708aa and 1d58029.

📒 Files selected for processing (25)
  • README.md
  • apps/server/src/index.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/info.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/updates/index.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/ui/src/lib/electron.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/routes/__root.tsx
  • apps/ui/src/store/app-store.ts
  • apps/ui/src/store/updates-store.ts
  • apps/ui/vite.config.mts
  • docs/auto-updates.md
  • libs/types/src/index.ts
  • libs/types/src/settings.ts
  • libs/types/src/updates.ts
🧰 Additional context used
🧬 Code graph analysis (10)
libs/types/src/updates.ts (1)
libs/types/src/index.ts (4)
  • UpdateInfo (113-113)
  • UpdateCheckResult (114-114)
  • UpdatePullResult (115-115)
  • UpdateApiResponse (116-116)
apps/server/src/routes/updates/index.ts (4)
apps/server/src/services/settings-service.ts (1)
  • SettingsService (104-583)
apps/server/src/routes/updates/routes/check.ts (1)
  • createCheckHandler (22-167)
apps/server/src/routes/updates/routes/pull.ts (1)
  • createPullHandler (23-174)
apps/server/src/routes/updates/routes/info.ts (1)
  • createInfoHandler (23-129)
apps/ui/src/store/updates-store.ts (2)
libs/types/src/updates.ts (4)
  • UpdateApiResponse (103-107)
  • UpdateInfo (13-43)
  • UpdateCheckResult (48-72)
  • UpdatePullResult (77-98)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (752-761)
apps/server/src/index.ts (1)
apps/server/src/routes/updates/index.ts (1)
  • createUpdatesRoutes (16-29)
apps/ui/src/hooks/use-update-polling.ts (2)
apps/ui/src/store/app-store.ts (1)
  • useAppStore (969-2754)
apps/ui/src/store/updates-store.ts (2)
  • useUpdatesStore (320-320)
  • checkForUpdates (62-68)
apps/ui/src/routes/__root.tsx (2)
apps/ui/src/components/updates/update-notifier.tsx (1)
  • UpdateNotifier (41-171)
apps/ui/src/components/updates/index.ts (1)
  • UpdateNotifier (7-7)
libs/types/src/settings.ts (1)
libs/types/src/index.ts (2)
  • AutoUpdateSettings (65-65)
  • DEFAULT_AUTO_UPDATE_SETTINGS (72-72)
apps/server/src/routes/updates/routes/pull.ts (2)
apps/server/src/routes/updates/common.ts (9)
  • getAutomakerRoot (67-76)
  • isGitAvailable (81-88)
  • isGitRepo (93-100)
  • hasLocalChanges (121-124)
  • getCurrentCommit (105-108)
  • getShortCommit (113-116)
  • execAsync (13-13)
  • execEnv (54-57)
  • logError (17-17)
libs/types/src/updates.ts (1)
  • UpdatePullResult (77-98)
apps/ui/src/components/updates/update-notifier.tsx (3)
apps/ui/src/components/updates/index.ts (2)
  • UpdateNotifierProps (8-8)
  • UpdateNotifier (7-7)
apps/ui/src/store/updates-store.ts (2)
  • useUpdatesStore (320-320)
  • pullUpdates (70-76)
apps/ui/src/hooks/use-update-polling.ts (1)
  • useUpdatePolling (51-94)
apps/ui/src/components/views/settings-view.tsx (1)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)
  • UpdatesSection (39-301)
🪛 markdownlint-cli2 (0.18.1)
docs/auto-updates.md

28-28: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


304-304: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (21)
apps/ui/src/components/views/settings-view/config/navigation.ts (1)

12-12: LGTM!

The RefreshCw icon is a sensible choice for the Updates section, and placing it before Danger Zone follows good UX ordering (less destructive options first).

Also applies to: 32-32

apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts (1)

12-12: LGTM!

Type extension correctly adds 'updates' to the union, consistent with the navigation item added in navigation.ts.

apps/ui/vite.config.mts (1)

64-71: LGTM!

The proxy configuration correctly forwards /api requests to the backend server during development. The default port 3008 aligns with the server's default, and WebSocket proxying is appropriately enabled for existing real-time features.

apps/ui/src/hooks/use-settings-migration.ts (1)

234-234: LGTM!

Adding autoUpdate to the sync payload ensures the auto-update settings are persisted to server storage, consistent with how other settings are synchronized.

apps/ui/src/components/updates/index.ts (1)

1-8: LGTM!

Clean barrel file following project conventions, enabling simplified imports like '@/components/updates'.

apps/ui/src/routes/__root.tsx (1)

14-14: LGTM!

The UpdateNotifier is appropriately placed in the main layout (excluding setup route) alongside the Toaster component it uses for notifications. Since it returns null, it doesn't affect the DOM structure while enabling background polling and toast notifications.

Also applies to: 179-179

README.md (1)

56-56: LGTM!

Clear and concise documentation addition. The Auto-Updates section provides a good high-level overview with appropriate links to detailed documentation.

Also applies to: 536-536, 569-578

apps/server/src/index.ts (1)

52-52: LGTM!

The updates routes are correctly mounted after the auth middleware, ensuring all endpoints are protected. The dependency injection of settingsService follows the established pattern used by other routes.

Also applies to: 166-166

apps/ui/src/components/views/settings-view.tsx (1)

20-21: LGTM!

Clean integration of the UpdatesSection component following the existing patterns in the settings view. The props are correctly passed and the switch case is consistent with other sections.

Also applies to: 54-56, 160-161

apps/server/src/routes/updates/index.ts (1)

1-29: LGTM!

Well-structured router module with clean separation of concerns. The factory pattern for handlers with dependency injection of settingsService is a good approach for testability.

apps/ui/src/lib/http-api-client.ts (1)

1090-1137: LGTM!

The updates API surface follows the established patterns in this file. The response type structures align with the server-side route implementations and the UpdatePullResult/UpdateInfo types defined in libs/types/src/updates.ts.

libs/types/src/index.ts (1)

65-66: LGTM!

Clean addition of the new type exports following the established pattern in the index file.

Also applies to: 72-73, 110-117

libs/types/src/settings.ts (2)

437-456: LGTM!

Well-documented interface with sensible defaults. The 15-minute check interval is a reasonable balance between staying current and not being overly aggressive with network requests.


304-308: LGTM!

Clean integration of autoUpdate into GlobalSettings with proper default values.

Also applies to: 487-488

apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)

39-105: LGTM on the component logic and store integration.

Good use of the centralized updates store with clear separation between UI and state management. The toast notifications provide appropriate user feedback for both success and error states.

apps/server/src/routes/updates/routes/pull.ts (1)

23-173: Overall implementation is well-structured with proper error handling.

Good patterns observed:

  • Cleanup of temporary remote in both success and error paths
  • Specific error messages for common failure cases (non-fast-forward, conflicts)
  • Use of --ff-only to prevent merge commits and conflicts
  • Proper HTTP status codes (400 for user errors, 500 for server errors)
apps/server/src/routes/updates/routes/info.ts (1)

7-128: /info handler logic looks solid

The handler correctly distinguishes between “no git”, “not a repo”, and “valid repo” and always returns a well‑formed UpdateInfo, including mechanismInfo flags. Error logging and 500 responses also look appropriate.

apps/ui/src/store/updates-store.ts (1)

23-309: Updates store DI design and state transitions look correct

The updates store cleanly encapsulates update state, guards concurrent checkForUpdates/pullUpdates, and uses DI for the API client and event emitter. Error handling and timestamp updates (lastChecked / lastPulled) are consistent with the UpdateApiResponse and Update*Result contracts.

libs/types/src/updates.ts (3)

1-8: LGTM! Clear documentation of design intent.

The header effectively communicates the abstraction goal and the three supported update mechanisms (git, release, custom).


45-72: LGTM! Type design is appropriate for the use case.

The required localVersion (vs nullable currentVersion in UpdateInfo) correctly reflects that an update check requires knowing the current version. The nullable remoteVersion appropriately handles check failures.


74-98: LGTM! Clean interface for update results.

All version fields being required is appropriate since an update operation (even if it fails or is already up-to-date) should know the before and after versions.

Copy link

@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: 0

♻️ Duplicate comments (1)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)

243-249: Add security attributes to window.open call.

Opening external URLs without noopener,noreferrer can expose the application to tabnapping attacks where the opened page can access window.opener.

🔎 Proposed fix
             <Button
               variant="outline"
               size="icon"
               onClick={() => {
                 const url = autoUpdate.upstreamUrl.replace(/\.git$/, '');
-                window.open(url, '_blank');
+                window.open(url, '_blank', 'noopener,noreferrer');
               }}
               title="Open repository"
             >
🧹 Nitpick comments (4)
apps/ui/src/components/updates/update-notifier.tsx (2)

124-146: Toast action label won't update when isPulling changes.

The isPulling state is in the dependency array, but once the toast is shown, changing isPulling won't update the button label from "Update Now" to "Updating..." because the toast content is static after creation. The early return on line 103 prevents re-creating the toast.

Consider either:

  1. Remove isPulling from dependencies since it doesn't effectively update the toast
  2. Use Sonner's toast update API to modify the existing toast when isPulling changes
🔎 Option 1: Remove isPulling from dependencies
   }, [
     updateAvailable,
     remoteVersionShort,
     autoUpdate.upstreamUrl,
-    isPulling,
     handleUpdateNow,
     onUpdateAvailable,
   ]);

131-137: Redundant ref assignment in "Later" onClick handler.

Setting shownToastForCommitRef.current = remoteVersionShort on line 135 is redundant because it's already set to this value on line 107 before the toast is shown.

🔎 Proposed fix
       cancel: {
         label: 'Later',
         onClick: () => {
-          // Dismiss toast - won't show again for this version until a new version appears
-          shownToastForCommitRef.current = remoteVersionShort;
+          // Toast dismisses automatically - ref already set, won't show again for this version
         },
       },
apps/server/src/routes/updates/routes/pull.ts (1)

70-95: Consider using execFile instead of exec for defense in depth.

While isValidGitUrl provides protection against shell injection, using execFile (or execFileSync) with an argument array would provide an additional layer of security by avoiding shell interpretation entirely. This is especially relevant since sourceUrl originates from user settings.

🔎 Example using execFileAsync
import { execFile } from 'child_process';
const execFileAsync = promisify(execFile);

// Instead of:
await execAsync(`git remote add ${tempRemoteName} "${sourceUrl}"`, { cwd, env });

// Use:
await execFileAsync('git', ['remote', 'add', tempRemoteName, sourceUrl], { cwd, env });
apps/server/src/routes/updates/common.ts (1)

70-79: Fragile path resolution - consider adding a marker file check.

The fixed 5-level directory traversal to find the monorepo root will break if this file is moved. While the inline comment documents the expected path, consider adding a validation check.

🔎 Proposed defensive check
 export function getAutomakerRoot(): string {
   // In ESM, we use import.meta.url to get the current file path
   // This file is at: apps/server/src/routes/updates/common.ts
   // So we need to go up 5 levels to get to the monorepo root
   const __filename = fileURLToPath(import.meta.url);
   const __dirname = path.dirname(__filename);

   // Go up from: updates -> routes -> src -> server -> apps -> root
-  return path.resolve(__dirname, '..', '..', '..', '..', '..');
+  const root = path.resolve(__dirname, '..', '..', '..', '..', '..');
+  
+  // Sanity check - verify we found the monorepo root
+  // This will throw if the file was moved and the path is wrong
+  const fs = await import('fs');
+  const packageJsonPath = path.join(root, 'package.json');
+  if (!fs.existsSync(packageJsonPath)) {
+    throw new Error(`Could not locate monorepo root. Expected package.json at: ${packageJsonPath}`);
+  }
+  
+  return root;
 }

Note: This would require making the function async or using existsSync.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d58029 and c63b6c0.

📒 Files selected for processing (9)
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/info.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/ui/src/lib/utils.ts
  • docs/auto-updates.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/ui/src/hooks/use-update-polling.ts
  • docs/auto-updates.md
  • apps/server/src/routes/updates/routes/info.ts
🧰 Additional context used
🧬 Code graph analysis (1)
apps/ui/src/components/updates/update-notifier.tsx (4)
apps/ui/src/components/updates/index.ts (2)
  • UpdateNotifierProps (8-8)
  • UpdateNotifier (7-7)
apps/ui/src/store/updates-store.ts (2)
  • useUpdatesStore (320-320)
  • pullUpdates (70-76)
apps/ui/src/hooks/use-update-polling.ts (1)
  • useUpdatePolling (51-99)
apps/ui/src/lib/utils.ts (1)
  • getRepoDisplayName (72-75)
🔇 Additional comments (11)
apps/ui/src/lib/utils.ts (1)

67-75: LGTM!

The utility function correctly extracts the owner/repo format from GitHub URLs, handling both HTTPS (/) and SSH (:) path separators. The regex appropriately excludes the .git suffix via the [^/.]+ pattern, and the fallback to 'upstream' is sensible for non-GitHub URLs.

apps/ui/src/components/views/settings-view/updates/updates-section.tsx (2)

39-58: LGTM!

The component properly uses the centralized updates store and fetches info on mount. The Zustand store action fetchInfo is stable by design, so the useEffect dependency is correct.


60-105: LGTM!

The async handlers correctly access store state via getState() after awaiting async operations, avoiding stale closure issues. Toast notifications provide appropriate user feedback for all outcomes.

apps/ui/src/components/updates/update-notifier.tsx (1)

42-54: LGTM!

Good use of refs to track toast state and prevent duplicate notifications. The DI-ready props pattern allows for testing flexibility.

apps/server/src/routes/updates/routes/pull.ts (2)

25-69: LGTM! Previous security concerns addressed.

The handler properly validates Git availability, repository status, local changes, and the upstream URL format using isValidGitUrl before executing any shell commands. This addresses the command injection concerns from previous reviews.


75-95: LGTM! Concurrency and cleanup properly handled.

Using crypto.randomBytes for the temporary remote name prevents conflicts with concurrent pulls, and the try...finally block ensures the remote is always cleaned up regardless of success or failure.

apps/server/src/routes/updates/routes/check.ts (3)

24-59: LGTM! Security validations properly implemented.

Consistent with pull.ts, the handler validates Git availability, repository status, and upstream URL format before executing commands.


95-113: LGTM! Correct update detection logic.

The git merge-base --is-ancestor approach correctly determines update availability: if the local commit is an ancestor of the remote commit, an update is available. If local is ahead or branches have diverged, no update is shown, which is the right behavior for a safe fast-forward-only update system.


129-145: The API contract is properly handled by the UI despite the unconventional pattern.

The UI correctly checks result.error (apps/ui/src/store/updates-store.ts line 232) and treats responses with an error field as a failed check, returning false and storing the error message for display. This prevents clients from displaying stale local version info when upstream is unreachable—instead, the error is properly surfaced to the user. The pattern is unconventional but working as designed.

apps/server/src/routes/updates/common.ts (2)

133-145: LGTM! Comprehensive shell metacharacter blocking.

The isValidGitUrl function properly validates protocol prefixes and blocks shell metacharacters that could enable command injection. The regex covers the critical characters: ;, backticks, |, &, <>, (), $, !, \, [], and spaces.


23-60: LGTM! Comprehensive PATH extension for cross-platform Git discovery.

The extended PATH configuration covers common Git installation locations across Windows, macOS (both Intel and Apple Silicon via Homebrew), and Linux. This ensures Git commands work correctly in Electron environments where the PATH may be limited.

@amoscicki
Copy link
Author

@coderabbitai generate docstrings

@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #292

Copy link

@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: 1

🧹 Nitpick comments (5)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (2)

61-74: Consider handling the case when checkForUpdates throws an exception.

The handleCheckForUpdates function doesn't have a try-catch wrapper. If checkForUpdates() throws (e.g., network failure), the error won't be caught and no toast will be shown.

🔎 Proposed fix
   const handleCheckForUpdates = async () => {
     clearError();
-    const hasUpdate = await checkForUpdates();
-
-    if (hasUpdate) {
-      toast.success('Update available!', {
-        description: `New version: ${useUpdatesStore.getState().remoteVersionShort}`,
-      });
-    } else if (!useUpdatesStore.getState().error) {
-      toast.success('You are up to date!');
-    } else {
-      toast.error(useUpdatesStore.getState().error || 'Failed to check for updates');
+    try {
+      const hasUpdate = await checkForUpdates();
+
+      if (hasUpdate) {
+        toast.success('Update available!', {
+          description: `New version: ${useUpdatesStore.getState().remoteVersionShort}`,
+        });
+      } else if (!useUpdatesStore.getState().error) {
+        toast.success('You are up to date!');
+      } else {
+        toast.error(useUpdatesStore.getState().error || 'Failed to check for updates');
+      }
+    } catch (err) {
+      toast.error('Failed to check for updates');
     }
   };

85-100: Persistent toast with duration: Infinity may cause UX friction.

The update success toast stays on screen indefinitely until the user interacts with it. If the user dismisses it accidentally, they would need to trigger another update check to see the restart prompt again.

Consider using a long but finite duration (e.g., 30 seconds) or providing an alternative way to trigger a restart from the Updates section UI.

apps/ui/src/components/updates/update-notifier.tsx (1)

124-138: Toast action label won't update dynamically while pulling.

The toast is created once with the isPulling value at creation time. When pullUpdates() is called and isPulling becomes true, the existing toast's button label won't change to "Updating..." because Sonner toasts don't re-render on prop changes.

Consider disabling the action button after click instead, or dismissing and recreating the toast when pulling starts.

🔎 Quick fix: disable button via onClick guard

The handleUpdateNow already prevents duplicate calls via isPulling check in the store's pullUpdates. The visual feedback issue is minor since the pull typically completes quickly.

apps/server/src/routes/updates/common.ts (1)

70-79: Path resolution relies on fixed directory depth.

The function assumes this file is always at apps/server/src/routes/updates/common.ts. While this works for the current structure, moving the file would break the path resolution.

A past review suggested using a marker-file approach (e.g., finding package.json), but given the stable monorepo structure and low likelihood of moving this file, the current approach is acceptable.

apps/server/src/routes/updates/routes/check.ts (1)

76-79: Hardcoded main branch may not work for all upstream configurations.

The endpoint assumes the upstream uses a main branch. Some repositories still use master or other default branch names.

Consider making the branch configurable or detecting the default branch dynamically:

#!/bin/bash
# Check if there are common alternative default branch patterns in the codebase
rg -n "main|master" apps/server/src/routes/updates/ --type ts
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c63b6c0 and b131c2c.

📒 Files selected for processing (26)
  • README.md
  • apps/server/src/index.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/info.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/updates/index.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/ui/src/lib/electron.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/routes/__root.tsx
  • apps/ui/src/store/app-store.ts
  • apps/ui/src/store/updates-store.ts
  • apps/ui/vite.config.mts
  • docs/auto-updates.md
  • libs/types/src/index.ts
  • libs/types/src/settings.ts
  • libs/types/src/updates.ts
✅ Files skipped from review due to trivial changes (2)
  • README.md
  • docs/auto-updates.md
🚧 Files skipped from review as they are similar to previous changes (10)
  • apps/server/src/routes/updates/index.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • libs/types/src/settings.ts
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • libs/types/src/updates.ts
  • apps/ui/src/components/updates/index.ts
  • apps/server/src/routes/updates/routes/info.ts
🧰 Additional context used
🧬 Code graph analysis (6)
apps/ui/src/components/views/settings-view.tsx (1)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)
  • UpdatesSection (39-295)
apps/server/src/routes/updates/routes/pull.ts (3)
apps/server/src/routes/updates/common.ts (10)
  • getAutomakerRoot (70-79)
  • isGitAvailable (84-91)
  • isGitRepo (96-103)
  • hasLocalChanges (124-127)
  • isValidGitUrl (133-145)
  • getCurrentCommit (108-111)
  • getShortCommit (116-119)
  • execAsync (13-13)
  • execEnv (57-60)
  • logError (17-17)
libs/types/src/index.ts (1)
  • UpdatePullResult (115-115)
libs/types/src/updates.ts (1)
  • UpdatePullResult (77-98)
apps/server/src/routes/updates/common.ts (2)
apps/server/src/routes/common.ts (1)
  • createLogError (34-38)
apps/server/src/routes/github/routes/common.ts (1)
  • extendedPath (11-19)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (4)
libs/types/src/index.ts (1)
  • AutoUpdateSettings (65-65)
libs/types/src/settings.ts (1)
  • AutoUpdateSettings (444-451)
apps/ui/src/store/updates-store.ts (3)
  • useUpdatesStore (320-320)
  • checkForUpdates (62-68)
  • pullUpdates (70-76)
apps/ui/src/lib/utils.ts (2)
  • cn (5-7)
  • getRepoDisplayName (72-75)
apps/ui/src/routes/__root.tsx (2)
apps/ui/src/components/updates/update-notifier.tsx (1)
  • UpdateNotifier (42-170)
apps/ui/src/components/updates/index.ts (1)
  • UpdateNotifier (7-7)
apps/server/src/index.ts (1)
apps/server/src/routes/updates/index.ts (1)
  • createUpdatesRoutes (16-29)
🔇 Additional comments (23)
apps/ui/vite.config.mts (1)

64-71: LGTM! Proxy configuration is correctly set up for the updates API.

The proxy correctly routes /api requests to the backend server during development, with WebSocket support enabled for real-time event streaming.

apps/ui/src/routes/__root.tsx (1)

14-14: LGTM! UpdateNotifier is correctly integrated into the root layout.

The component is appropriately placed after Toaster (since it uses sonner for notifications) and is only rendered in the main layout, excluding the setup route.

Also applies to: 179-179

apps/ui/src/components/views/settings-view.tsx (1)

20-20: LGTM! Updates section is properly integrated into settings view.

The integration follows the established pattern for other settings sections, correctly passing autoUpdate state and setAutoUpdate handler as props.

Also applies to: 56-57, 164-165

apps/server/src/index.ts (1)

52-52: LGTM! Updates routes are correctly mounted with proper authentication.

The routes are mounted after authMiddleware ensuring they require authentication, and the settingsService dependency injection follows the established pattern.

Also applies to: 166-166

libs/types/src/index.ts (1)

65-65: LGTM! Type exports are well-organized and follow existing conventions.

The new exports for AutoUpdateSettings, DEFAULT_AUTO_UPDATE_SETTINGS, and update-related types are properly grouped and documented.

Also applies to: 72-72, 110-117

apps/ui/src/components/views/settings-view/updates/updates-section.tsx (2)

21-21: Past review issue resolved: getRepoDisplayName is now imported from shared utils.

The code duplication concern has been addressed by extracting the helper to @/lib/utils.


243-253: Past review issue resolved: Security attributes added to window.open.

The noopener,noreferrer attributes are now correctly applied, preventing tabnapping vulnerabilities.

apps/server/src/routes/updates/routes/pull.ts (3)

62-69: Past review issue resolved: URL validation prevents command injection.

The isValidGitUrl check properly validates the upstream URL before using it in shell commands, mitigating command injection risks.


75-76: Past review issue resolved: Random remote name prevents concurrency conflicts.

Using crypto.randomBytes for the temp remote name ensures unique identifiers even with concurrent pull requests.


146-156: Past review issue resolved: Cleanup in finally block ensures temp remote removal.

The temporary remote is always cleaned up regardless of success or failure, preventing stale remote entries.

apps/ui/src/store/app-store.ts (2)

768-770: Past review issue resolved: setAutoUpdate return type correctly declares Promise<void>.

The type declaration now matches the async implementation, aligning with the pattern used by setAutoLoadClaudeMd and setEnableSandboxMode.


10-12: LGTM! Auto-update state is properly integrated into the app store.

The implementation correctly:

  • Imports and uses shared types from @automaker/types
  • Initializes with sensible defaults
  • Persists settings via partialize
  • Syncs changes to the server

Also applies to: 488-490, 945-945, 1586-1594, 2751-2751

apps/ui/src/lib/electron.ts (1)

629-673: LGTM!

The updates API surface is well-structured with consistent typing. The optional updates property and nullable result fields properly handle cases where the API may not be available or updates cannot be fetched.

apps/ui/src/components/updates/update-notifier.tsx (1)

42-49: Well-designed DI pattern for testability.

The optional callback props (onUpdateAvailable, onUpdateInstalled) and hook-based polling make this component easily testable and extensible.

apps/server/src/routes/updates/routes/check.ts (2)

95-113: Good use of git merge-base --is-ancestor for accurate update detection.

This correctly handles the case where local is ahead of remote (avoiding false positive update notifications), which was mentioned as a fix in the PR objectives.


146-156: Good cleanup pattern with try/finally.

The temporary remote is always removed regardless of success or failure, preventing orphaned remotes from accumulating.

apps/server/src/routes/updates/common.ts (2)

133-145: Good URL validation with shell metacharacter blocking.

The validation correctly allows standard Git protocols while blocking characters that could enable command injection (;, backticks, |, &, $, etc.).


23-52: Cross-platform PATH extension is well-implemented.

Handles Windows vs Unix path separators and includes common Git installation locations for both platforms, ensuring Git is discoverable in Electron environments where PATH may be restricted.

apps/ui/src/lib/http-api-client.ts (1)

1090-1138: LGTM!

The Updates API integration follows the established patterns in the HTTP client. The endpoint methods (check, pull, info) correctly map to the server routes with appropriate HTTP methods and return types.

apps/ui/src/store/updates-store.ts (4)

285-287: Fire-and-forget fetchInfo() call after pull.

The fetchInfo() call after a successful pull is not awaited. This is likely intentional to avoid blocking the pullUpdates return, but if fetchInfo fails, the error won't be surfaced to the caller.

This is acceptable since the pull already succeeded and the info refresh is supplementary.


166-173: Clean DI pattern with mutable references.

The mutable _apiClient and _eventEmitter references allow runtime dependency swapping via setApiClient/setEventEmitter, which is useful for testing scenarios.


211-255: Good guard against concurrent update checks.

The isChecking || isPulling guard prevents race conditions from multiple simultaneous check operations.


316-320: Store instantiation is safe for module load time.

The default store is created when the module loads, which is standard for Zustand singleton stores. getElectronAPI() is not called at import time—it's invoked only when the API client's methods (getInfo(), checkForUpdates(), pullUpdates()) are actually executed, at which point the SSR guard in getElectronAPI() will properly reject any calls during server-side rendering. However, note that createDefaultEventEmitter() does use window.dispatchEvent() at module initialization, which could cause issues in SSR contexts if this file is imported on the server.

coderabbitai bot added a commit that referenced this pull request Dec 28, 2025
Docstrings generation was requested by @amoscicki.

* #290 (comment)

The following files were modified:

* `apps/server/src/routes/updates/common.ts`
* `apps/server/src/routes/updates/index.ts`
* `apps/server/src/routes/updates/routes/check.ts`
* `apps/server/src/routes/updates/routes/info.ts`
* `apps/server/src/routes/updates/routes/pull.ts`
* `apps/ui/src/components/updates/update-notifier.tsx`
* `apps/ui/src/components/views/settings-view.tsx`
* `apps/ui/src/components/views/settings-view/updates/updates-section.tsx`
* `apps/ui/src/hooks/use-settings-migration.ts`
* `apps/ui/src/hooks/use-update-polling.ts`
* `apps/ui/src/lib/utils.ts`
* `apps/ui/src/routes/__root.tsx`
@amoscicki
Copy link
Author

@gemini-code-assist Please recheck the following comments and resolve if addressed:

  1. common.ts:78 (getAutomakerRoot) - Now uses robust traversal to find package.json with name 'automaker' instead of fixed relative paths

  2. pull.ts:65 (tempRemoteName) - Already uses crypto.randomBytes(8).toString('hex') for random remote names

Also asking @coderabbitai to recheck:

  1. updates-section.tsx:249 - Fixed with noopener,noreferrer

  2. app-store.ts - setAutoUpdate return type changed to Promise<void>

@gemini-code-assist
Copy link
Contributor

Hello @amoscicki, thanks for the update!

I've reviewed the points you raised:

  1. common.ts:78 (getAutomakerRoot): I can confirm that the getAutomakerRoot function in apps/server/src/routes/updates/common.ts now correctly implements a robust traversal to find the package.json with the name 'automaker'. This addresses the concern regarding fixed relative paths.

  2. pull.ts:65 (tempRemoteName): I've verified that the tempRemoteName in apps/server/src/routes/updates/routes/pull.ts (specifically line 77 in the current version) uses crypto.randomBytes(8).toString('hex') to generate a random remote name, which resolves the previous comment.

Points 3 and 4 are addressed to @coderabbitai, so I will not comment on those.

@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== 1. Checking common.ts:78 (getAutomakerRoot) ==="
cat -n apps/server/src/routes/updates/common.ts | sed -n '65,95p'

echo ""
echo "=== 2. Checking pull.ts:65 (tempRemoteName) ==="
cat -n apps/server/src/routes/updates/routes/pull.ts | sed -n '55,75p'

echo ""
echo "=== 3. Checking updates-section.tsx:249 (noopener,noreferrer) ==="
cat -n apps/ui/src/components/views/settings-view/updates/updates-section.tsx | sed -n '240,260p'

echo ""
echo "=== 4. Checking app-store.ts (setAutoUpdate return type) ==="
# Find the setAutoUpdate function definition
rg -n -A 10 "setAutoUpdate.*=.*async" apps/ui/src/store/app-store.ts

Length of output: 3440


🏁 Script executed:

#!/bin/bash

echo "=== Looking for tempRemoteName around line 65 in pull.ts ==="
cat -n apps/server/src/routes/updates/routes/pull.ts | sed -n '75,85p'

echo ""
echo "=== 4. Checking app-store.ts (setAutoUpdate signature and implementation) ==="
# Look for the type definition
rg -n -B 2 -A 2 "setAutoUpdate.*Promise" apps/ui/src/store/app-store.ts

echo ""
echo "=== Also checking the implementation ==="
rg -n -B 3 -A 15 "setAutoUpdate:.*async.*settings" apps/ui/src/store/app-store.ts

Length of output: 1529


@amoscicki I've rechecked all 4 items and can confirm they have been properly addressed:

  1. common.ts:78 (getAutomakerRoot) - Verified at lines 72-95. The function now uses a while loop to traverse up from the current directory, checking each package.json for name === 'automaker' instead of using fixed relative paths. This is indeed a robust traversal approach.

  2. pull.ts:76 (tempRemoteName) - Verified at line 76. The code uses:

    const tempRemoteName = `automaker-update-pull-${crypto.randomBytes(8).toString('hex')}`;

    This generates a cryptographically random remote name.

  3. updates-section.tsx:248 - Verified at line 248. The window.open call includes the security attributes:

    window.open(url, '_blank', 'noopener,noreferrer');
  4. app-store.ts - Verified at lines 769 and 1587-1594. The type signature shows Promise<void> return type:

    setAutoUpdate: (settings: Partial<AutoUpdateSettings>) => Promise<void>;

    And the implementation is properly async, returning a Promise.

All concerns have been successfully resolved! 🎉

Copy link

@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: 0

🧹 Nitpick comments (1)
apps/server/src/routes/updates/routes/check.ts (1)

120-137: Consider clarifying the success: true response on fetch failure.

The handler returns success: true even when the upstream fetch fails (lines 124-136), which is a graceful degradation allowing the UI to show local version info. However, this semantic choice (success refers to the endpoint working, not the fetch succeeding) could be confusing. The error field in the result clarifies this, but you might consider either:

  1. Adding a comment explaining this design choice, or
  2. Adding a top-level fetchSuccess: boolean field to make the distinction explicit

This is a minor readability improvement and the current implementation is functional.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cbb63f0 and 4a4463f.

📒 Files selected for processing (3)
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/pull.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/server/src/routes/updates/common.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/server/src/routes/updates/routes/pull.ts
  • apps/server/src/routes/updates/routes/check.ts
apps/server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use createEventEmitter() from lib/events.ts for all server operations to emit events that stream to frontend via WebSocket

Files:

  • apps/server/src/routes/updates/routes/pull.ts
  • apps/server/src/routes/updates/routes/check.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/server/src/routes/updates/routes/pull.ts
  • apps/server/src/routes/updates/routes/check.ts
🧬 Code graph analysis (1)
apps/server/src/routes/updates/routes/check.ts (3)
apps/server/src/routes/updates/common.ts (9)
  • getAutomakerRoot (73-97)
  • isGitAvailable (102-109)
  • isGitRepo (114-121)
  • isValidGitUrl (151-163)
  • getCurrentCommit (126-129)
  • getShortCommit (134-137)
  • withTempGitRemote (169-191)
  • execAsync (15-15)
  • execEnv (59-62)
libs/types/src/index.ts (1)
  • UpdateCheckResult (114-114)
libs/types/src/updates.ts (1)
  • UpdateCheckResult (48-72)
🔇 Additional comments (5)
apps/server/src/routes/updates/routes/pull.ts (3)

25-69: Well-structured validation flow with proper security checks.

The handler correctly validates prerequisites in order: git availability, repository status, local changes check, and URL validation before any git operations. The isValidGitUrl check at line 63 addresses the previously-raised command injection concern.


71-107: Clean implementation using the shared withTempGitRemote helper.

The code now correctly uses the centralized helper that handles random remote name generation and guaranteed cleanup in a finally block. This addresses the earlier refactoring suggestion about code reuse between check and pull handlers.


113-139: Good error differentiation for actionable user feedback.

The handler correctly distinguishes between fast-forward failures and merge conflicts, providing specific guidance for each case. This helps users understand the required manual intervention.

apps/server/src/routes/updates/routes/check.ts (2)

24-59: Consistent validation pattern with pull handler.

The check handler follows the same defensive validation sequence as the pull handler, ensuring git availability, repository status, and URL validation before any network operations.


87-103: Correct use of git merge-base --is-ancestor for update detection.

The logic properly handles all three cases:

  • Local equals remote → no update (line 89 condition)
  • Local is ancestor of remote → update available (exit code 0)
  • Local has diverged or is ahead → no update (exit code non-zero caught at line 98)

This aligns well with the --ff-only merge strategy used in the pull handler.

Copy link

@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: 3

🧹 Nitpick comments (4)
apps/ui/src/hooks/use-update-polling.ts (1)

79-84: Consider adding error handling for the immediate check.

The immediate stableOnCheck() call on line 80 fires without awaiting or catching errors. While the store's checkForUpdates likely handles errors internally, an unhandled rejection could occur if the underlying API throws unexpectedly.

🔎 Suggested improvement
     // Check immediately on enable
-    stableOnCheck();
+    stableOnCheck().catch(() => {
+      // Errors are handled in the store
+    });
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)

209-227: Input validation works but may cause UX confusion.

The validation on lines 216-219 silently ignores invalid values (outside 1-60 range or non-numeric). While this prevents invalid state, users might be confused when typing doesn't update the field. Consider showing validation feedback or allowing the intermediate state.

🔎 Optional improvement for better UX
           <Input
             id="check-interval"
             type="number"
             min={1}
             max={60}
             value={autoUpdate.checkIntervalMinutes}
             onChange={(e) => {
               const value = parseInt(e.target.value, 10);
-              if (!isNaN(value) && value >= 1 && value <= 60) {
-                onAutoUpdateChange({ checkIntervalMinutes: value });
-              }
+              if (!isNaN(value)) {
+                // Clamp to valid range
+                const clamped = Math.min(60, Math.max(1, value));
+                onAutoUpdateChange({ checkIntervalMinutes: clamped });
+              }
             }}
             className="w-32"
             disabled={!autoUpdate.enabled}
           />
apps/ui/src/components/updates/update-notifier.tsx (1)

133-137: Redundant assignment in "Later" onClick handler.

Line 135 sets shownToastForCommitRef.current = remoteVersionShort, but this value was already set on line 107. The assignment is redundant since clicking "Later" dismisses the toast but doesn't change the deduplication tracking.

🔎 Simplify the handler
       cancel: {
         label: 'Later',
         onClick: () => {
-          // Dismiss toast - won't show again for this version until a new version appears
-          shownToastForCommitRef.current = remoteVersionShort;
+          // Toast dismissed - won't show again for this version
         },
       },
apps/ui/src/lib/http-api-client.ts (1)

1090-1137: Consider importing shared types from @automaker/types to avoid drift.

The inline return types for updates.info() include isGitRepo and gitAvailable properties that aren't present in the shared UpdateInfo type from @automaker/types, which uses mechanismInfo?: Record<string, unknown> for mechanism-specific data instead. This creates potential for type drift between the API client and consumers like updates-store.ts that import from the shared package.

Per coding guidelines, prefer importing from shared packages. While the current pattern is consistent with other APIs in this file, using the shared types would improve maintainability.

🔎 Suggested refactor using shared types
+import type {
+  UpdateInfo,
+  UpdateCheckResult,
+  UpdatePullResult,
+  UpdateApiResponse,
+} from '@automaker/types';

  // Updates API
  updates = {
-   check: (): Promise<{
-     success: boolean;
-     result?: {
-       updateAvailable: boolean;
-       localVersion: string;
-       ...
-     };
-     error?: string;
-   }> => this.get('/api/updates/check'),
+   check: (): Promise<UpdateApiResponse<UpdateCheckResult>> => 
+     this.get('/api/updates/check'),

-   pull: (): Promise<{
-     success: boolean;
-     result?: {
-       success: boolean;
-       ...
-     };
-     error?: string;
-   }> => this.post('/api/updates/pull', {}),
+   pull: (): Promise<UpdateApiResponse<UpdatePullResult>> => 
+     this.post('/api/updates/pull', {}),

-   info: (): Promise<{
-     success: boolean;
-     result?: {
-       installPath: string;
-       ...
-     };
-     error?: string;
-   }> => this.get('/api/updates/info'),
+   info: (): Promise<UpdateApiResponse<UpdateInfo>> => 
+     this.get('/api/updates/info'),
  };

Note: Ensure the server response shape matches UpdateInfo (using mechanismInfo for git-specific fields like isGitRepo and gitAvailable), or update the shared type accordingly.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a4463f and 84b7d28.

📒 Files selected for processing (26)
  • README.md
  • apps/server/src/index.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/info.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/updates/index.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/ui/src/lib/electron.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/routes/__root.tsx
  • apps/ui/src/store/app-store.ts
  • apps/ui/src/store/updates-store.ts
  • apps/ui/vite.config.mts
  • docs/auto-updates.md
  • libs/types/src/index.ts
  • libs/types/src/settings.ts
  • libs/types/src/updates.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/server/src/routes/updates/index.ts
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/lib/electron.ts
  • libs/types/src/settings.ts
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/components/updates/index.ts
  • apps/ui/src/store/app-store.ts
  • apps/server/src/index.ts
  • libs/types/src/index.ts
  • docs/auto-updates.md
  • apps/ui/src/routes/__root.tsx
  • apps/ui/vite.config.mts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/server/src/routes/updates/common.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/server/src/routes/updates/routes/info.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/store/updates-store.ts
  • apps/ui/src/lib/http-api-client.ts
  • libs/types/src/updates.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/server/src/routes/updates/common.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/server/src/routes/updates/routes/info.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/store/updates-store.ts
  • apps/ui/src/lib/http-api-client.ts
  • libs/types/src/updates.ts
apps/server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use createEventEmitter() from lib/events.ts for all server operations to emit events that stream to frontend via WebSocket

Files:

  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/routes/info.ts
🧠 Learnings (2)
📚 Learning: 2025-12-30T01:02:07.114Z
Learnt from: illia1f
Repo: AutoMaker-Org/automaker PR: 324
File: apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx:122-131
Timestamp: 2025-12-30T01:02:07.114Z
Learning: Tailwind CSS v4 uses postfix syntax for the important modifier: append ! at the end of the utility class (e.g., backdrop-blur-[0px]! or hover:bg-red-500!). The older v3 style used a prefix (!) at the start (e.g., !backdrop-blur-[0px]); prefer the postfix form for consistency across TSX files.

Applied to files:

  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/components/updates/update-notifier.tsx
📚 Learning: 2025-12-28T05:07:48.147Z
Learnt from: CR
Repo: AutoMaker-Org/automaker PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T05:07:48.147Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always import from shared packages (automaker/*), never from old relative paths

Applied to files:

  • apps/server/src/routes/updates/common.ts
🧬 Code graph analysis (5)
apps/ui/src/hooks/use-update-polling.ts (1)
apps/ui/src/store/updates-store.ts (2)
  • useUpdatesStore (320-320)
  • checkForUpdates (62-68)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (5)
libs/types/src/index.ts (1)
  • AutoUpdateSettings (65-65)
libs/types/src/settings.ts (1)
  • AutoUpdateSettings (444-451)
apps/ui/src/store/updates-store.ts (3)
  • useUpdatesStore (320-320)
  • checkForUpdates (62-68)
  • pullUpdates (70-76)
apps/ui/src/lib/utils.ts (2)
  • cn (5-7)
  • getRepoDisplayName (72-75)
apps/ui/src/components/ui/button.tsx (1)
  • Button (108-108)
apps/server/src/routes/updates/common.ts (2)
apps/server/src/routes/common.ts (1)
  • createLogError (34-38)
apps/server/src/routes/github/routes/common.ts (1)
  • extendedPath (11-19)
apps/ui/src/store/updates-store.ts (3)
libs/types/src/updates.ts (4)
  • UpdateApiResponse (103-107)
  • UpdateInfo (13-43)
  • UpdateCheckResult (48-72)
  • UpdatePullResult (77-98)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (752-761)
apps/ui/src/lib/http-api-client.ts (1)
  • get (174-178)
libs/types/src/updates.ts (1)
libs/types/src/index.ts (4)
  • UpdateInfo (122-122)
  • UpdateCheckResult (123-123)
  • UpdatePullResult (124-124)
  • UpdateApiResponse (125-125)
🔇 Additional comments (25)
apps/ui/src/hooks/use-settings-migration.ts (1)

235-235: LGTM! Auto-update settings integration looks good.

The addition of autoUpdate to the sync payload follows the established pattern for syncing global settings to the server.

apps/ui/src/components/views/settings-view/config/navigation.ts (1)

12-12: LGTM! Updates navigation item added correctly.

The new navigation entry follows the established pattern and properly integrates the RefreshCw icon.

Also applies to: 32-32

apps/server/src/routes/updates/common.ts (4)

1-62: LGTM! Imports and PATH configuration are well-structured.

The extended PATH handling comprehensively covers Windows, macOS, and Linux package manager locations, ensuring git availability across platforms.


73-97: LGTM! Robust root directory resolution.

The upward traversal looking for package.json with name === 'automaker' is a solid approach that addresses the fragility concerns from previous reviews.

Based on learnings, this implementation successfully resolves the fixed relative path issue.


102-145: LGTM! Git utility functions are well-implemented.

All git commands use safe parameterization and the extended environment for cross-platform git discovery.


151-163: LGTM! URL validation provides strong injection protection.

The validation correctly blocks shell metacharacters while allowing standard git protocols (HTTPS, SSH, git).

Based on learnings, this addresses the command injection concerns from previous reviews.

apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts (1)

12-12: LGTM! View ID type extension is correct.

The addition of the 'updates' view ID properly extends the type union to support the new Updates settings section.

README.md (1)

56-56: LGTM! Documentation additions are clear and well-structured.

The Auto-Updates documentation is properly integrated into the TOC and Learn More sections, with a concise feature summary and link to detailed documentation.

Also applies to: 536-536, 570-578

apps/server/src/routes/updates/routes/pull.ts (1)

1-145: LGTM!

The implementation is well-structured with proper security measures:

  • URL validation via isValidGitUrl prevents command injection
  • withTempGitRemote helper centralizes temporary remote management with proper cleanup
  • Comprehensive error handling for git-specific scenarios (non-fast-forward, merge conflicts)
  • Proper HTTP status codes for different error conditions
apps/server/src/routes/updates/routes/info.ts (1)

1-125: LGTM!

Clean implementation with proper handling of edge cases:

  • Git unavailable scenario returns partial info with gitAvailable: false
  • Non-repo scenario returns info with isGitRepo: false
  • Full repo scenario returns complete version, branch, and local changes info
  • Correctly uses DEFAULT_AUTO_UPDATE_SETTINGS from shared types to avoid duplication
apps/ui/src/hooks/use-update-polling.ts (2)

59-63: Ref pattern for callback stabilization is well implemented.

The use of useRef to store the current check function and useCallback to create a stable wrapper prevents unnecessary interval resets when the check function reference changes. This is a good pattern for avoiding stale closure issues while maintaining stable dependencies.


67-92: LGTM - Interval management is correct.

The effect properly:

  • Clears existing intervals before setting new ones
  • Returns a cleanup function for unmount
  • Depends on the right values (isEnabled, intervalMinutes, stableOnCheck)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (3)

61-74: Store access pattern after async operation is correct.

Using useUpdatesStore.getState() after await checkForUpdates() ensures you read the latest state values rather than stale closure values. This is the correct pattern for accessing Zustand store state after async operations.


243-254: Security fix confirmed - window.open includes noopener,noreferrer.

The third parameter 'noopener,noreferrer' properly prevents tabnapping attacks when opening external URLs. This addresses the previously flagged security concern.


85-100: Toast with duration: Infinity is appropriate for important actions.

Using infinite duration for the "Update installed" toast ensures users don't miss the restart prompt. The action/cancel buttons provide clear options. Good UX pattern for critical notifications requiring user action.

apps/server/src/routes/updates/routes/check.ts (2)

87-103: Update detection logic using git merge-base --is-ancestor is correct.

The logic properly handles all three scenarios:

  • Same version: localVersion === remoteVersion → no update
  • Local behind remote: merge-base --is-ancestor succeeds (exit 0) → update available
  • Local ahead or diverged: command fails (exit 1) → no update (safe default)

120-137: Graceful degradation when upstream fetch fails.

Returning success: true with updateAvailable: false and an error message allows the UI to show "up to date" with context about the fetch failure, rather than breaking the entire check flow. Good error handling pattern.

libs/types/src/updates.ts (1)

1-107: LGTM - Well-designed type definitions.

The interfaces are:

  • Thoroughly documented with JSDoc comments
  • Abstract enough to support multiple update mechanisms (git, release, custom)
  • Using proper generic pattern for UpdateApiResponse<T>
  • Using Record<string, unknown> for mechanismInfo provides flexibility without using any
apps/ui/src/components/updates/update-notifier.tsx (2)

148-166: LGTM - Cleanup effects are properly structured.

Two separate effects handle:

  1. Unmount cleanup (lines 149-155): Dismisses any active toast
  2. State reset (lines 158-166): Clears refs and dismisses toast when update is no longer available

Both are correctly implemented with appropriate dependencies.


42-49: Component follows good patterns for headless notification management.

  • Renders null (no visible UI)
  • Uses refs for deduplication to prevent toast spam
  • DI-friendly via optional callback props
  • Starts polling via useUpdatePolling() hook
apps/ui/src/store/updates-store.ts (5)

10-44: Well-structured interfaces for dependency injection.

The interface definitions for IUpdatesApiClient and IUpdateEventEmitter are clean and properly documented. Using types from @automaker/types aligns with coding guidelines.


50-86: LGTM!

The default implementations are well-designed:

  • Defensive checks for API availability before calling methods
  • SSR-safe since getElectronAPI() is deferred to method invocation
  • Standard CustomEvent pattern for cross-component communication

92-154: Clean state design with comprehensive coverage.

The state interface properly separates concerns with loading states for each operation, error tracking, and timestamps. The initial state correctly initializes all fields.


211-255: Good dual-level error handling.

The implementation correctly handles both API response errors (response.error) and result-level errors (result.error), with consistent timestamp updates even on failure. This ensures robust error reporting to the UI.


316-330: LGTM!

The default store instance and selectors follow standard Zustand patterns. The combined selectIsLoading selector is a useful convenience for UI components.

amoscicki and others added 11 commits January 1, 2026 15:52
- Fix false positive 'update available' when local is ahead of remote
  Uses git merge-base --is-ancestor to properly check ancestry
- Fix UI not refreshing after update from toast notification
  Emit custom event when update pulled, UpdatesSection listens and refetches

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

Co-Authored-By: Claude <[email protected]>
- Rename commit-related fields to version-related (localVersion, remoteVersion, etc.)
- Add centralized updates-store with DI support (IUpdatesApiClient, IUpdateEventEmitter)
- Separate concerns: store (state), polling hook (when), notifier (toasts), section (UI)
- Add UpdateNotifier component for toast notifications
- Make updateType optional for backwards compatibility

🤖 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]>
- Add detailed docs/auto-updates.md with architecture and DI guide
- Update README with simplified section and link to full docs
- Include examples for swapping update mechanism

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add autoUpdate to syncSettingsToServer list
- Make setAutoUpdate call syncSettingsToServer after state change

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Addresses the following review comments:

Critical (security):
- #2649468854: Add URL validation in check.ts
- #2649468856: Add URL validation in pull.ts
- #2649468858: Strengthen isValidGitUrl to block shell metacharacters

Medium (robustness):
- #2649468862: Use random temp remote name in check.ts
- #2649468863: Refactor check.ts to use try...finally for cleanup
- #2649468864: Use DEFAULT_AUTO_UPDATE_SETTINGS in info.ts
- #2649468865: Use random temp remote name in pull.ts
- #2649468866: Refactor pull.ts to use try...finally for cleanup
- #2649468868: Extract getRepoDisplayName to shared utility
- #2649468870: Fix docs default interval (5->15 minutes)

Additional fixes from CodeRabbit:
- Stabilize onCheck callback in use-update-polling.ts
- Guard process.env.HOME in common.ts
- Fix markdown lint issues in docs
- Update "Later" button behavior comment
- Remove unused currentBranch variable in pull.ts

Skipped (acceptable as-is):
- #2649468861: Fragile project root resolution

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
- Add noopener,noreferrer to window.open for security
- Fix setAutoUpdate return type to Promise<void>

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Instead of using a fixed relative path that breaks if the file moves,
now traverse up the directory tree looking for the monorepo's package.json
with name "automaker".

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Centralizes the temp remote add/remove pattern into a shared helper
function in common.ts, used by both check.ts and pull.ts.

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

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

@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: 7

♻️ Duplicate comments (1)
apps/server/src/routes/updates/common.ts (1)

169-176: Validate sourceUrl before using it in shell commands.

The function should call isValidGitUrl(sourceUrl) before interpolating the URL into the git command. Even though the URL is wrapped in quotes, validating it provides defense-in-depth against command injection—especially since isValidGitUrl exists specifically for this purpose.

🔎 Proposed fix
 export async function withTempGitRemote<T>(
   installPath: string,
   sourceUrl: string,
   callback: (tempRemoteName: string) => Promise<T>
 ): Promise<T> {
+  if (!isValidGitUrl(sourceUrl)) {
+    throw new Error(`Invalid git URL: ${sourceUrl}`);
+  }
+
   const tempRemoteName = `automaker-temp-remote-${crypto.randomBytes(8).toString('hex')}`;
🧹 Nitpick comments (2)
apps/ui/src/lib/utils.ts (1)

67-75: Enhance regex to handle .git suffix in GitHub URLs.

The regex pattern may not correctly extract repository names from URLs ending with .git (e.g., https://github.com/owner/repo.git). The character class [^/.] excludes dots, which would stop matching at repo instead of repo.git.

🔎 Proposed regex improvement
 export function getRepoDisplayName(url: string): string {
-  const match = url.match(/github\.com[/:]([^/]+\/[^/.]+)/);
+  const match = url.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?(?:[/?#]|$)/);
   return match ? match[1] : 'upstream';
 }

This pattern:

  • Captures owner/repo including dots in the repo name
  • Optionally matches and discards .git suffix
  • Handles trailing slashes, query params, or end of string
apps/ui/src/store/updates-store.ts (1)

285-286: Consider awaiting fetchInfo() to ensure consistent state.

The fetchInfo() call is fire-and-forget, which means:

  1. If it fails, the info state may remain stale after a successful pull
  2. Any fetchInfo error would overwrite the success state in the store (since the error was already cleared at line 261)
  3. Users might see an error message after a successful update
🔎 Recommended fix
         // Refresh info to get new commit
-        get().fetchInfo();
+        await get().fetchInfo();

Alternatively, if non-blocking behavior is intentional, add a comment explaining the rationale and consider handling fetchInfo errors separately to avoid overwriting the success state.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84b7d28 and 6ce4ec2.

📒 Files selected for processing (26)
  • README.md
  • apps/server/src/index.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/routes/info.ts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/updates/index.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • apps/ui/src/lib/electron.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/routes/__root.tsx
  • apps/ui/src/store/app-store.ts
  • apps/ui/src/store/updates-store.ts
  • apps/ui/vite.config.mts
  • docs/auto-updates.md
  • libs/types/src/index.ts
  • libs/types/src/settings.ts
  • libs/types/src/updates.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • apps/ui/src/components/views/settings-view/config/navigation.ts
  • apps/ui/src/components/updates/index.ts
  • apps/server/src/index.ts
  • apps/ui/src/hooks/use-update-polling.ts
  • libs/types/src/index.ts
  • apps/server/src/routes/updates/routes/info.ts
  • apps/ui/vite.config.mts
  • apps/server/src/routes/updates/routes/pull.ts
  • apps/ui/src/components/views/settings-view/hooks/use-settings-view.ts
  • apps/ui/src/hooks/use-settings-migration.ts
  • apps/ui/src/lib/electron.ts
  • libs/types/src/updates.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/routes/__root.tsx
  • apps/server/src/routes/updates/routes/check.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/store/updates-store.ts
  • libs/types/src/settings.ts
  • apps/ui/src/store/app-store.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/lib/utils.ts
  • apps/ui/src/routes/__root.tsx
  • apps/server/src/routes/updates/routes/check.ts
  • apps/ui/src/lib/http-api-client.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
  • apps/ui/src/store/updates-store.ts
  • libs/types/src/settings.ts
  • apps/ui/src/store/app-store.ts
apps/server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use createEventEmitter() from lib/events.ts for all server operations to emit events that stream to frontend via WebSocket

Files:

  • apps/server/src/routes/updates/routes/check.ts
  • apps/server/src/routes/updates/common.ts
  • apps/server/src/routes/updates/index.ts
🧠 Learnings (2)
📚 Learning: 2025-12-30T01:02:07.114Z
Learnt from: illia1f
Repo: AutoMaker-Org/automaker PR: 324
File: apps/ui/src/components/views/board-view/components/kanban-card/kanban-card.tsx:122-131
Timestamp: 2025-12-30T01:02:07.114Z
Learning: Tailwind CSS v4 uses postfix syntax for the important modifier: append ! at the end of the utility class (e.g., backdrop-blur-[0px]! or hover:bg-red-500!). The older v3 style used a prefix (!) at the start (e.g., !backdrop-blur-[0px]); prefer the postfix form for consistency across TSX files.

Applied to files:

  • apps/ui/src/components/views/settings-view/updates/updates-section.tsx
  • apps/ui/src/routes/__root.tsx
  • apps/ui/src/components/updates/update-notifier.tsx
  • apps/ui/src/components/views/settings-view.tsx
📚 Learning: 2025-12-28T05:07:48.147Z
Learnt from: CR
Repo: AutoMaker-Org/automaker PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T05:07:48.147Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always import from shared packages (automaker/*), never from old relative paths

Applied to files:

  • apps/server/src/routes/updates/common.ts
🧬 Code graph analysis (7)
apps/ui/src/routes/__root.tsx (2)
apps/ui/src/components/updates/update-notifier.tsx (1)
  • UpdateNotifier (42-170)
apps/ui/src/components/updates/index.ts (1)
  • UpdateNotifier (7-7)
apps/server/src/routes/updates/routes/check.ts (2)
apps/server/src/routes/updates/common.ts (10)
  • getAutomakerRoot (73-97)
  • isGitAvailable (102-109)
  • isGitRepo (114-121)
  • isValidGitUrl (151-163)
  • getCurrentCommit (126-129)
  • getShortCommit (134-137)
  • withTempGitRemote (169-191)
  • execAsync (15-15)
  • execEnv (59-62)
  • logError (19-19)
libs/types/src/updates.ts (1)
  • UpdateCheckResult (48-72)
apps/server/src/routes/updates/common.ts (3)
apps/server/src/routes/common.ts (1)
  • createLogError (34-38)
apps/server/src/routes/github/routes/common.ts (1)
  • extendedPath (11-19)
apps/ui/scripts/update-version.mjs (1)
  • packageJsonPath (33-33)
apps/ui/src/components/views/settings-view.tsx (1)
apps/ui/src/components/views/settings-view/updates/updates-section.tsx (1)
  • UpdatesSection (39-295)
apps/ui/src/store/updates-store.ts (3)
libs/types/src/updates.ts (4)
  • UpdateApiResponse (103-107)
  • UpdateInfo (13-43)
  • UpdateCheckResult (48-72)
  • UpdatePullResult (77-98)
apps/ui/src/lib/electron.ts (1)
  • getElectronAPI (773-782)
apps/ui/src/lib/http-api-client.ts (1)
  • get (589-611)
libs/types/src/settings.ts (1)
libs/types/src/index.ts (2)
  • AutoUpdateSettings (86-86)
  • DEFAULT_AUTO_UPDATE_SETTINGS (93-93)
apps/ui/src/store/app-store.ts (3)
libs/types/src/index.ts (2)
  • AutoUpdateSettings (86-86)
  • DEFAULT_AUTO_UPDATE_SETTINGS (93-93)
libs/types/src/settings.ts (2)
  • AutoUpdateSettings (513-520)
  • DEFAULT_AUTO_UPDATE_SETTINGS (523-527)
apps/ui/src/hooks/use-settings-migration.ts (1)
  • syncSettingsToServer (198-249)
🪛 Biome (2.1.2)
apps/ui/src/routes/__root.tsx

[error] 23-24: Expected a statement but instead found '======='.

Expected a statement here.

(parse)


[error] 61-62: Expected a statement but instead found '======='.

Expected a statement here.

(parse)

libs/types/src/settings.ts

[error] 564-564: Expected an expression but instead found '==='.

Expected an expression here.

(parse)


[error] 564-564: Expected an expression but instead found '='.

Expected an expression here.

(parse)


[error] 565-565: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)

apps/ui/src/store/app-store.ts

[error] 502-502: Expected a property, or a signature but instead found '==='.

Expected a property, or a signature here.

(parse)


[error] 993-993: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '==='.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)


[error] 993-993: Expected an expression but instead found '==='.

Expected an expression here.

(parse)


[error] 993-993: Expected an expression but instead found '='.

Expected an expression here.

(parse)


[error] 993-993: Expected a semicolon or an implicit semicolon after a statement, but found none

An explicit or implicit semicolon is expected here...

...Which is required to end this statement

(parse)


[error] 2953-2953: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '==='.

Expected a property, a shorthand property, a getter, a setter, or a method here.

(parse)


[error] 2953-2953: Expected an expression but instead found '==='.

Expected an expression here.

(parse)


[error] 2953-2953: Expected an expression but instead found '='.

Expected an expression here.

(parse)


[error] 2914-2953: Invalid assignment to { // Project management projects: state.projects, currentProject: state.currentProject, trashedProjects: state.trashedProjects, projectHistory: state.projectHistory, projectHistoryIndex: state.projectHistoryIndex, // Features - cached locally for faster hydration (authoritative source is server) features: state.features, // UI state currentView: state.currentView, theme: state.theme, sidebarOpen: state.sidebarOpen, chatHistoryOpen: state.chatHistoryOpen, kanbanCardDetailLevel: state.kanbanCardDetailLevel, boardViewMode: state.boardViewMode, // Settings apiKeys: state.apiKeys, maxConcurrency: state.maxConcurrency, // Note: autoModeByProject is intentionally NOT persisted // Auto-mode should always default to OFF on app refresh defaultSkipTests: state.defaultSkipTests, enableDependencyBlocking: state.enableDependencyBlocking, useWorktrees: state.useWorktrees, currentWorktreeByProject: state.currentWorktreeByProject, worktreesByProject: state.worktreesByProject, showProfilesOnly: state.showProfilesOnly, keyboardShortcuts: state.keyboardShortcuts, muteDoneSound: state.muteDoneSound, enhancementModel: state.enhancementModel, validationModel: state.validationModel, autoLoadClaudeMd: state.autoLoadClaudeMd, enableSandboxMode: state.enableSandboxMode, // MCP settings mcpServers: state.mcpServers, mcpAutoApproveTools: state.mcpAutoApproveTools, mcpUnrestrictedTools: state.mcpUnrestrictedTools, // Prompt customization promptCustomization: state.promptCustomization, ======

This expression cannot be assigned to

(parse)


[error] 2953-2953: expected ) but instead found :

Remove :

(parse)

🔇 Additional comments (6)
apps/ui/src/lib/http-api-client.ts (1)

1574-1621: LGTM! Updates API surface is well-structured.

The new updates API object follows the established pattern in this file, with properly typed endpoints for check, pull, and info operations. The inline type definitions are comprehensive and the implementation uses the existing HTTP helper methods consistently.

README.md (1)

56-56: LGTM! Clear and well-organized documentation.

The Auto-Updates documentation is appropriately placed in the table of contents and includes a helpful summary with links to detailed documentation. The formatting is consistent with the rest of the README.

Also applies to: 634-634, 668-676

apps/ui/src/components/views/settings-view.tsx (1)

21-21: LGTM! Clean integration of the Updates section

The UpdatesSection is properly imported and integrated into the settings view. Props are correctly passed from the app store, and the routing is set up appropriately.

Also applies to: 61-62, 185-186

docs/auto-updates.md (1)

1-304: Excellent documentation! Comprehensive and well-structured

The auto-updates documentation is thorough and covers:

  • Feature overview and configuration
  • Architecture with clear component diagrams
  • Dependency injection patterns for extensibility
  • Type definitions and examples
  • Troubleshooting guide

The default check interval has been correctly documented as 15 minutes (matching the code), per the resolved past review comment.

apps/ui/src/store/app-store.ts (1)

790-791: Async implementation looks correct

The setAutoUpdate action is properly declared as returning Promise<void> and the implementation correctly syncs settings to the server. This matches the pattern used by other async settings actions like setAutoLoadClaudeMd and addresses the concern raised in the past review comments.

Also applies to: 1659-1667

apps/server/src/routes/updates/routes/check.ts (1)

1-143: LGTM! Solid implementation with security best practices

The update check endpoint is well-implemented with proper security and error handling:

  • Command injection prevented: isValidGitUrl validation (lines 53-59) blocks malicious URLs before shell execution
  • Cleanup guaranteed: withTempGitRemote helper ensures temporary remotes are always removed via try-finally
  • Proper version comparison: Uses git merge-base --is-ancestor to correctly determine if an update is available
  • Graceful error handling: Fetch failures return success: true with an error message in the result, while global failures return success: false

All concerns from previous reviews have been successfully addressed.

…rkers

- Add isValidGitUrl check inside withTempGitRemote for extra security
- Remove leftover ======= conflict markers from rebase

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

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

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
libs/types/src/settings.ts (1)

557-564: 🚨 Critical: Duplicate enableSandboxMode property in object literal

Lines 557 and 563 both define enableSandboxMode in the DEFAULT_GLOBAL_SETTINGS object. In JavaScript/TypeScript, the last duplicate key wins, so Line 563 (true) will override Line 557 (false). This is a leftover merge conflict artifact.

Remove the duplicate at Line 563:

🔎 Proposed fix
   mcpAutoApproveTools: true,
   mcpUnrestrictedTools: true,
-  enableSandboxMode: true,
   autoUpdate: DEFAULT_AUTO_UPDATE_SETTINGS,
 };
apps/ui/src/store/app-store.ts (1)

985-991: 🚨 Critical: Duplicate enableSandboxMode property in initialState

Lines 985 and 990 both define enableSandboxMode in the initialState object. The second definition (Line 990: true) will override the first (Line 985: false). This is a leftover merge conflict artifact.

Remove the duplicate at Line 990:

🔎 Proposed fix
   mcpAutoApproveTools: true, // Default to enabled - bypass permission prompts for MCP tools
   mcpUnrestrictedTools: true, // Default to enabled - don't filter allowedTools when MCP enabled
   promptCustomization: {}, // Empty by default - all prompts use built-in defaults
-  enableSandboxMode: true, // Default to enabled for security (can be disabled if issues occur)
   autoUpdate: DEFAULT_AUTO_UPDATE_SETTINGS, // Default auto-update settings
🧹 Nitpick comments (1)
apps/server/src/routes/updates/common.ts (1)

21-62: Consider consolidating PATH extension logic.

The extended PATH configuration here is more comprehensive than the similar logic in apps/server/src/routes/github/routes/common.ts. Consider moving this enhanced version to a shared location (e.g., apps/server/src/routes/common.ts) to eliminate duplication and ensure consistent PATH handling across all server routes.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ce4ec2 and 3e47a0d.

📒 Files selected for processing (5)
  • apps/server/src/routes/updates/common.ts
  • apps/ui/src/routes/__root.tsx
  • apps/ui/src/store/app-store.ts
  • libs/types/src/index.ts
  • libs/types/src/settings.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/ui/src/routes/__root.tsx
  • libs/types/src/index.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always import from shared packages (@automaker/*), never from old relative paths

Files:

  • libs/types/src/settings.ts
  • apps/ui/src/store/app-store.ts
  • apps/server/src/routes/updates/common.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use resolveModelString() from @automaker/model-resolver to convert model aliases (haiku, sonnet, opus) to full model names

Files:

  • libs/types/src/settings.ts
  • apps/ui/src/store/app-store.ts
  • apps/server/src/routes/updates/common.ts
apps/server/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use createEventEmitter() from lib/events.ts for all server operations to emit events that stream to frontend via WebSocket

Files:

  • apps/server/src/routes/updates/common.ts
🧠 Learnings (1)
📚 Learning: 2025-12-28T05:07:48.147Z
Learnt from: CR
Repo: AutoMaker-Org/automaker PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-28T05:07:48.147Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always import from shared packages (automaker/*), never from old relative paths

Applied to files:

  • apps/server/src/routes/updates/common.ts
🧬 Code graph analysis (2)
libs/types/src/settings.ts (1)
libs/types/src/index.ts (2)
  • AutoUpdateSettings (86-86)
  • DEFAULT_AUTO_UPDATE_SETTINGS (93-93)
apps/server/src/routes/updates/common.ts (3)
apps/server/src/routes/common.ts (1)
  • createLogError (34-38)
apps/server/src/routes/github/routes/common.ts (1)
  • extendedPath (11-19)
apps/ui/scripts/update-version.mjs (1)
  • packageJsonPath (33-33)
🔇 Additional comments (12)
libs/types/src/settings.ts (2)

507-526: LGTM! Well-structured auto-update configuration.

The AutoUpdateSettings interface and defaults are clearly documented and provide sensible values for the auto-update feature.


368-370: LGTM! Proper integration into GlobalSettings.

The autoUpdate field is correctly typed and documented.

apps/ui/src/store/app-store.ts (5)

15-17: LGTM! Correct imports from shared package.

Imports follow the coding guideline to use @automaker/* packages instead of relative paths.


500-501: LGTM! Proper addition to AppState.

The autoUpdate field is correctly typed and documented.


788-789: LGTM! Return type correctly matches async implementation.

The setAutoUpdate signature properly declares Promise<void> return type, consistent with its async implementation and other async settings actions like setAutoLoadClaudeMd.


1656-1664: LGTM! Correct async implementation with server sync.

The implementation properly merges settings into state and persists changes to the server, following the same pattern as other async settings actions.


2949-2949: LGTM! Proper persistence configuration.

The autoUpdate state is correctly included in the persisted state via partialize.

apps/server/src/routes/updates/common.ts (5)

1-19: LGTM: Proper imports and setup.

The imports from @automaker/utils follow the coding guidelines, and the re-export pattern from the shared server utilities (../common.js) is appropriate for intra-app code sharing.


68-97: Excellent: Robust root discovery implementation.

The upward traversal approach successfully addresses the previous fragility concerns. By checking each package.json for name === 'automaker', the function correctly identifies the monorepo root regardless of where this file is located within the directory structure.


99-145: LGTM: Well-structured Git utility functions.

The Git helper functions are properly implemented with consistent error handling and appropriate use of execEnv for extended PATH support. The boolean checks gracefully return false on errors, while data-fetching functions allow errors to propagate.


147-163: Excellent: Defense-in-depth URL validation.

The isValidGitUrl implementation successfully addresses previous security concerns by validating both protocol schemes and blocking shell metacharacters. This provides robust protection against command injection vulnerabilities.


165-196: Excellent: Comprehensive temp remote management.

This implementation successfully addresses all previous concerns:

  • ✅ Validates the URL at entry (line 175) for defense-in-depth security
  • ✅ Uses crypto.randomBytes(8) for collision-resistant random remote names (line 179)
  • ✅ Ensures cleanup via finally block with graceful error handling

The pattern centralizes temporary remote lifecycle management effectively.

…omise

- Add EventEmitter parameter to createUpdatesRoutes for future progress streaming
- Pass events to pull handler (available for WebSocket progress updates)
- Await fetchInfo() after pull to ensure state is updated before emitting events

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

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant