Skip to content

chore: code-syncer-update - #357

Merged
shunkakinoki merged 8 commits into
mainfrom
chore/code-syncer-update
Nov 21, 2025
Merged

chore: code-syncer-update#357
shunkakinoki merged 8 commits into
mainfrom
chore/code-syncer-update

Conversation

@shunkakinoki

@shunkakinoki shunkakinoki commented Nov 21, 2025

Copy link
Copy Markdown
Owner
  • Enhanced the clean_extension_list function to accept input from stdin or a file, improving flexibility.
  • Updated remove_unnecessary_extensions and install_extensions functions to directly retrieve VS Code extensions via CLI, ensuring accurate syncing.
  • Added checks for VS Code CLI availability and extension presence, with appropriate logging for better user feedback.
  • Refined the logic for filtering and syncing extensions, ensuring only necessary extensions are retained.

Summary by cubic

Revamps the code-syncer to reliably mirror VS Code settings and extensions to Cursor, Windsurf, and Antigravity. Uses the VS Code CLI, filters blocked extensions, removes extras on targets, and adds clearer logs plus auto-watch.

  • New Features

    • Sync extensions directly from the VS Code CLI; filter proprietary and AI lists.
    • Uninstall extensions on targets that shouldn’t be kept.
    • Resolve editor CLIs (Antigravity, Cursor, Windsurf) and ensure user dirs; use HOME-based paths.
    • Auto-watch settings and keybindings with fswatch; copy updates to all editors.
    • Clear, concise logs for installs, removals, and skipped steps.
  • Bug Fixes

    • Corrected fallback paths for Cursor and Windsurf (Homebrew).
    • Better handling of missing CLIs: skip missing target editors; fail fast if VS Code CLI is unavailable.

Written for commit 958dd23. Summary will update automatically on new commits.

…nagement

- Updated user directory paths to use HOME variable for portability.
- Introduced blocklists for proprietary and AI extensions to prevent sync errors.
- Added helper functions for command resolution and directory management.
- Improved extension installation process with error handling and logging.
- Implemented a watcher for automatic syncing of configuration files on changes.
- Refactored sync logic for clarity and maintainability.
- Standardized indentation and spacing for better readability.
- Enhanced logging for extension syncing and configuration file copying.
- Minor adjustments to ensure consistent command structure and error handling.
- Cleaned up comments for clarity and maintainability.
…ore syncing

- Implemented a new function to identify and uninstall unnecessary extensions from the target editor.
- Enhanced the extension syncing process by ensuring only required extensions are retained.
- Improved logging to provide feedback on the removal of extensions.
- Changed fallback paths for "windsurf" and "cursor" commands to use the Homebrew installation directory.
- Ensured compatibility with Homebrew-managed applications on macOS.
…ommand resolution

- Added "anysphere.pyright" and "anysphere.cursorpyright" to the proprietary extensions list to prevent sync errors.
- Enhanced the command resolution function to return the resolved path of commands, improving compatibility with applications not in the PATH.
- Added checks to ensure the source file exists and is not empty before proceeding with extension removal.
- Improved logging to notify when there are no extensions to sync or when the source list is missing.
- Refined the logic to determine which extensions to keep or remove, ensuring only necessary extensions are retained during the sync process.
- Enhanced the clean_extension_list function to accept input from stdin or a file, improving flexibility.
- Updated remove_unnecessary_extensions and install_extensions functions to directly retrieve VS Code extensions via CLI, ensuring accurate syncing.
- Added checks for VS Code CLI availability and extension presence, with appropriate logging for better user feedback.
- Refined the logic for filtering and syncing extensions, ensuring only necessary extensions are retained.
Copilot AI review requested due to automatic review settings November 21, 2025 07:51
@shunkakinoki shunkakinoki self-assigned this Nov 21, 2025
@shunkakinoki
shunkakinoki enabled auto-merge (squash) November 21, 2025 07:51
@coderabbitai

coderabbitai Bot commented Nov 21, 2025

Copy link
Copy Markdown

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

Summary by CodeRabbit

  • New Features
    • Extended extension and configuration syncing across multiple editors.
    • Added automatic syncing capability with file monitoring support when available.
    • Implemented intelligent extension filtering for improved compatibility management.

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

Walkthrough

Refactors sync.sh to use HOME-based paths, adds extension categorization (PROPRIETARY_EXTENSIONS, AI_EXTENSIONS), introduces CLI resolution and directory helpers, implements filtering/removal and staged installation of extensions, adds config file sync, and adds optional fswatch-based auto-sync.

Changes

Cohort / File(s) Summary
Extension Management & Config Sync
home-manager/services/code-syncer/sync.sh
Rewrote extension sync to use HOME-based paths; added BLOCKLISTs (PROPRIETARY_EXTENSIONS, AI_EXTENSIONS); added helpers: resolve_cli, ensure_dirs, clean_extension_list, remove_unnecessary_extensions, install_extensions, and sync_config_file; changed flow to normalize/filter then install extensions per-target; added VS Code CLI checks, extension count reporting, and optional fswatch watcher; centralized logging and error signaling.

Sequence Diagram(s)

sequenceDiagram
    participant Main as sync.sh (Main)
    participant VSCode as VS Code CLI
    participant Cleaner as Cleaner (filter/remove)
    participant Installer as Installer (install)
    participant Editors as Target Editors
    participant Config as Config Sync
    participant Watcher as fswatch

    Main->>VSCode: resolve_cli & verify
    VSCode-->>Main: CLI present / absent

    Main->>VSCode: count extensions
    VSCode-->>Main: extension list

    Main->>Cleaner: clean_extension_list (remove AI/proprietary)
    Cleaner-->>Main: filtered list

    Main->>Cleaner: remove_unnecessary_extensions
    Cleaner->>Editors: uninstall blocked extensions
    Editors-->>Cleaner: uninstall results

    Main->>Installer: install_extensions (per-editor)
    Installer->>Editors: install each extension
    Editors-->>Installer: install success/fail

    Main->>Config: sync_config_file -> copy settings/keybindings
    Config-->>Editors: files copied (logged)

    Main->>Watcher: check fswatch
    alt fswatch available
        Watcher->>Main: enable auto-sync
    else
        Watcher-->>Main: auto-sync disabled (notice)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to: clean_extension_list, remove_unnecessary_extensions, and install_extensions logic for false positives/negatives and safe uninstalls
  • Verify resolve_cli fallbacks on different platforms and ensure_dirs path handling/escaping
  • Confirm logging/error paths and watcher activation conditions

Poem

🐇 In directories warm beneath HOME's light,

Extensions pruned and copied through the night,
CLIs found, configs shared with gentle cheer,
A tidy sync — the rabbit hops with glee near!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'chore: code-syncer-update' is vague and generic, using non-descriptive terms that don't convey the specific improvements made to the code-syncer script. Consider using a more specific title like 'chore: refactor code-syncer with extension filtering and auto-sync' to better describe the main changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is well-related to the changeset, providing multiple specific details about the improvements including extension filtering, CLI resolution, settings sync, and fswatch integration.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/code-syncer-update

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.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @shunkakinoki, 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 significantly overhauls the code-syncer script, transforming it into a more robust and intelligent tool for synchronizing VS Code settings and extensions across various editors like Antigravity, Cursor, and Windsurf. The changes focus on improving accuracy, flexibility, and user experience by directly interacting with editor CLIs, implementing extension filtering, and providing clearer operational feedback.

Highlights

  • Improved Extension Sync Logic: The script now directly retrieves VS Code extensions via the CLI for more accurate syncing and introduces blocklists for proprietary and AI-related extensions, ensuring only desired extensions are installed.
  • Enhanced Configuration File Sync: The clean_extension_list function has been made more flexible, accepting input from stdin or a file, and the overall configuration file syncing process has been refined with better logging.
  • Robust Error Handling and User Feedback: New checks for VS Code CLI availability and extension presence have been added, along with more descriptive logging messages to provide clearer user feedback during the sync process.
  • Dynamic CLI Resolution and Home Directory Usage: A new resolve_cli function dynamically finds editor CLI commands, and all base directories now correctly use the $HOME environment variable instead of hardcoded paths, improving portability.
  • Unnecessary Extension Removal: The script now actively identifies and uninstalls extensions from target editors that are not present in the filtered VS Code extension list, streamlining the extension management.
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.

@mesa-dot-dev mesa-dot-dev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Performed full review of 46f486e...cced5b9

Tip

Help

Slash Commands:

  • /review - Request a full code review
  • /review latest - Review only changes since the last review
  • /describe - Generate PR description. This will update the PR body or issue comment depending on your configuration
  • /help - Get help with Mesa commands and configuration options

1 files reviewed | 0 comments | Edit Agent SettingsRead Docs

@mesa-dot-dev

mesa-dot-dev Bot commented Nov 21, 2025

Copy link
Copy Markdown

Mesa Description

TL;DR

Revamped the code-syncer script to reliably mirror VS Code settings and extensions to other editors (Cursor, Windsurf, Antigravity) by directly using the VS Code CLI, filtering extensions, and adding auto-watch functionality.

What changed?

  • home-manager/services/code-syncer/sync.sh:
    • Refactored and enhanced for improved portability using $HOME for base directories.
    • Introduced blocklists for filtering proprietary and AI extensions.
    • Added helper functions for dynamic CLI resolution, directory management, extension list cleaning, and robust installation/removal of extensions.
    • Main logic refactored for clearer execution flow, comprehensive logging, availability checks, and integrated fswatch support.
    • Enhanced error handling and verbosity.

Description generated by Mesa. Update settings

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

Copy link
Copy Markdown
Contributor

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 significantly refactors the code-syncer script, making it more robust and feature-rich. The changes include fetching extension lists directly via CLI, adding blocklists for certain extensions, and improving logging. My review focuses on improving efficiency, robustness, and adherence to shell scripting best practices. I've identified several areas for improvement: optimizing the extension filtering logic, making temporary file handling more secure, centralizing command validation, and fixing a minor output bug. Overall, these are great enhancements to the script's functionality.

Comment on lines +81 to 101
clean_extension_list() {
local input_source="$1" # Can be a file path or "-" for stdin
local clean_file="$2"

if [ -f "$source_path" ]; then
cp "$source_path" "$ANTIGRAVITY_USER_DIR/$file"
cp "$source_path" "$CURSOR_USER_DIR/$file"
cp "$source_path" "$WINDSURF_USER_DIR/$file"
echo "$file synced at $(date)"
# Read from stdin if input_source is "-", otherwise from file
if [ "$input_source" = "-" ]; then
cat >"$clean_file"
else
echo "VSCode $file not found"
cp "$input_source" "$clean_file"
fi

for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
# Remove the line containing the bad extension
sed -i '' "/$bad_ext/d" "$clean_file"
done

for ai_ext in "${AI_EXTENSIONS[@]}"; do
# Remove the line containing the AI extension
sed -i '' "/$ai_ext/d" "$clean_file"
done
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The current implementation of clean_extension_list uses sed in a loop for each blocklisted extension. This is inefficient and can be buggy if extension names contain special regex characters (like '.'). A more efficient and safer approach is to use grep -v -F -f which filters using a list of fixed strings.

Suggested change
clean_extension_list() {
local input_source="$1" # Can be a file path or "-" for stdin
local clean_file="$2"
if [ -f "$source_path" ]; then
cp "$source_path" "$ANTIGRAVITY_USER_DIR/$file"
cp "$source_path" "$CURSOR_USER_DIR/$file"
cp "$source_path" "$WINDSURF_USER_DIR/$file"
echo "$file synced at $(date)"
# Read from stdin if input_source is "-", otherwise from file
if [ "$input_source" = "-" ]; then
cat >"$clean_file"
else
echo "VSCode $file not found"
cp "$input_source" "$clean_file"
fi
for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
# Remove the line containing the bad extension
sed -i '' "/$bad_ext/d" "$clean_file"
done
for ai_ext in "${AI_EXTENSIONS[@]}"; do
# Remove the line containing the AI extension
sed -i '' "/$ai_ext/d" "$clean_file"
done
}
clean_extension_list() {
local input_source="$1" # Can be a file path or "-" for stdin
local clean_file="$2"
local temp_blocklist
temp_blocklist=$(mktemp)
# Ensure temp file is removed on exit
trap 'rm -f "$temp_blocklist"' RETURN
# Combine both blocklists into a single file for grep
printf '%s\n' "${PROPRIETARY_EXTENSIONS[@]}" "${AI_EXTENSIONS[@]}" >"$temp_blocklist"
# Read from stdin if input_source is "-", otherwise from file
if [ "$input_source" = "-" ]; then
# Use grep to filter stdin and write to clean_file
grep -v -F -x -f "$temp_blocklist" >"$clean_file"
else
# Use grep to filter the input file and write to clean_file
grep -v -F -x -f "$temp_blocklist" "$input_source" >"$clean_file"
fi
}

Comment on lines +57 to 71
resolve_cli() {
local cmd=$1
local resolved_path=$(command -v "$cmd" 2>/dev/null)
if [ -n "$resolved_path" ]; then
echo "$resolved_path"
else
echo "Extensions list not found"
# Fallback paths for Mac apps if not in PATH
case $cmd in
"antigravity") echo "/Applications/Antigravity.app/Contents/Resources/app/bin/antigravity" ;;
"windsurf") echo "/opt/homebrew/bin/windsurf" ;;
"cursor") echo "/opt/homebrew/bin/cursor" ;;
*) echo "" ;;
esac
fi
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The resolve_cli function returns a path without verifying if it's valid or executable. This check is then performed at the call sites (lines 111, 218) using [ ! -f ... ], which is not fully robust (e.g., for symlinks) and duplicates logic. It would be better to centralize the validation within resolve_cli and check for executability (-x). This simplifies the calling code to just if [ -z "$cli_cmd" ]; then.

resolve_cli() {
  local cmd=$1
  local resolved_path

  resolved_path=$(command -v "$cmd" 2>/dev/null)
  if [ -n "$resolved_path" ]; then
    echo "$resolved_path"
    return 0
  fi

  # Fallback paths for Mac apps if not in PATH
  case $cmd in
  "antigravity") resolved_path="/Applications/Antigravity.app/Contents/Resources/app/bin/antigravity" ;;
  "windsurf") resolved_path="/opt/homebrew/bin/windsurf" ;;
  "cursor") resolved_path="/opt/homebrew/bin/cursor" ;;
  *)
    echo ""
    return 1
    ;;
  esac

  if [ -x "$resolved_path" ]; then
    echo "$resolved_path"
  else
    echo ""
  fi
}

Comment on lines +107 to +109
local clean_list="/tmp/${target_name}_extensions_clean.list"
local installed_list="/tmp/${target_name}_extensions_installed.list"
local vscode_list="/tmp/vscode_extensions.list"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This function, and install_extensions, use hardcoded temporary file paths in /tmp. This can lead to race conditions and is generally insecure. It also leads to redundant work, as vscode_list is generated in both functions. Consider using mktemp to create temporary files in install_extensions and pass their paths to this function. This would make the script more robust and efficient. You should also add a trap to ensure temporary files are cleaned up on exit.

Comment on lines +147 to +191
local to_remove=()
while IFS= read -r installed_ext; do
if [ -z "$installed_ext" ]; then
continue
fi

# Skip PROPRIETARY_EXTENSIONS - don't attempt to remove them
local is_proprietary=false
for prop_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
if [ "$installed_ext" = "$prop_ext" ]; then
is_proprietary=true
break
fi
done

if [ "$is_proprietary" = true ]; then
continue
fi

# Check if extension is in VS Code's original list (before filtering)
# If it's in VS Code, we should keep it (it will be synced)
local in_vscode=false
if grep -Fxq "$installed_ext" "$vscode_list" 2>/dev/null; then
in_vscode=true
fi

# If extension is in VS Code, keep it (don't remove)
if [ "$in_vscode" = true ]; then
continue
fi

# Check if extension is in AI_EXTENSIONS (should be removed)
local is_ai=false
for ai_ext in "${AI_EXTENSIONS[@]}"; do
if [ "$installed_ext" = "$ai_ext" ]; then
is_ai=true
break
fi
done

# Remove if it's an AI extension (not in VS Code but installed)
if [ "$is_ai" = true ]; then
to_remove+=("$installed_ext")
fi
done <"$installed_list"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The nested loops to check if an extension is proprietary or an AI extension are inefficient (O(N*M)). For better performance and readability, you can convert the blocklist arrays into associative arrays (requires bash 4+) for near O(1) lookups. This simplifies the logic inside the while loop considerably.

  local to_remove=()

  declare -A proprietary_extensions_set
  for ext in "${PROPRIETARY_EXTENSIONS[@]}"; do proprietary_extensions_set["$ext"]=1; done

  declare -A ai_extensions_set
  for ext in "${AI_EXTENSIONS[@]}"; do ai_extensions_set["$ext"]=1; done

  while IFS= read -r installed_ext; do
    if [ -z "$installed_ext" ]; then
      continue
    fi

    # Skip PROPRIETARY_EXTENSIONS - don't attempt to remove them
    if [[ -v proprietary_extensions_set["$installed_ext"] ]]; then
      continue
    fi

    # If extension is in VS Code, keep it (it will be synced)
    if grep -Fxq "$installed_ext" "$vscode_list" 2>/dev/null; then
      continue
    fi

    # Remove if it's an AI extension (not in VS Code but installed)
    if [[ -v ai_extensions_set["$installed_ext"] ]]; then
      to_remove+=("$installed_ext")
    fi
  done <"$installed_list"

echo "Initial Sync Complete."

# 4. Watcher (Mac only)
if command -v fswatch >/dev/null; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

command -v prints the path of the command to standard output if it's found. This output should be redirected to /dev/null to prevent it from being displayed during script execution. It's also good practice to redirect stderr.

Suggested change
if command -v fswatch >/dev/null; then
if command -v fswatch >/dev/null 2>&1; then

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (9)
home-manager/services/code-syncer/sync.sh (9)

59-59: Declare and assign separately to avoid masking return values (SC2155).

The pattern local var=$(command) masks the return value of command -v. While this is low-risk here, separate the declaration and assignment for safety and consistency.

Apply this diff to improve error-detection clarity:

-  local resolved_path=$(command -v "$cmd" 2>/dev/null)
+  local resolved_path
+  resolved_path=$(command -v "$cmd" 2>/dev/null)

Consider applying the same pattern fix to lines 106, 214, and 248 for consistency across the script.


73-77: Add error handling to ensure_dirs.

The function silently ignores mkdir failures. Consider adding basic error checking for better observability.

Apply this diff to add error handling:

 ensure_dirs() {
-  mkdir -p "$ANTIGRAVITY_USER_DIR"
-  mkdir -p "$CURSOR_USER_DIR"
-  mkdir -p "$WINDSURF_USER_DIR"
+  mkdir -p "$ANTIGRAVITY_USER_DIR" || echo "⚠️  Failed to create $ANTIGRAVITY_USER_DIR"
+  mkdir -p "$CURSOR_USER_DIR" || echo "⚠️  Failed to create $CURSOR_USER_DIR"
+  mkdir -p "$WINDSURF_USER_DIR" || echo "⚠️  Failed to create $WINDSURF_USER_DIR"
 }

81-101: Improve sed pattern specificity and add error handling.

The function uses partial-line matching (e.g., /github.copilot/d) which could accidentally match github.copilot-chat before the exact pattern is checked. Also, sed failures are silently ignored.

Use exact line matching (^pattern$) and add error handling:

 clean_extension_list() {
   local input_source="$1"
   local clean_file="$2"

-  if [ "$input_source" = "-" ]; then
-    cat >"$clean_file"
+  if [ "$input_source" = "-" ]; then
+    cat >"$clean_file" || { echo "⚠️  Failed to write to $clean_file"; return 1; }
   else
-    cp "$input_source" "$clean_file"
+    cp "$input_source" "$clean_file" || { echo "⚠️  Failed to copy $input_source"; return 1; }
   fi

   for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
-    sed -i '' "/$bad_ext/d" "$clean_file"
+    sed -i '' "/^$bad_ext\$/d" "$clean_file" || { echo "⚠️  Failed to filter $bad_ext"; return 1; }
   done

   for ai_ext in "${AI_EXTENSIONS[@]}"; do
-    sed -i '' "/$ai_ext/d" "$clean_file"
+    sed -i '' "/^$ai_ext\$/d" "$clean_file" || { echo "⚠️  Failed to filter $ai_ext"; return 1; }
   done
 }

106-106: Declare and assign separately to avoid masking return values (SC2155).

The pattern local cli_cmd=$(resolve_cli "$target_name") masks the return value. Separate declaration and assignment for consistency (also flagged on lines 214 and 248).

Apply this diff:

-  local cli_cmd=$(resolve_cli "$target_name")
+  local cli_cmd
+  cli_cmd=$(resolve_cli "$target_name")

138-144: Unused filtered list: clean_extension_list creates $clean_list but the removal logic doesn't use it.

Line 138 calls clean_extension_list "$vscode_list" "$clean_list", but the subsequent removal logic (lines 148–191) only checks if $clean_list is non-empty; it doesn't use $clean_list to filter $installed_list. The logic instead re-implements filtering inline (checking PROPRIETARY_EXTENSIONS and AI_EXTENSIONS per installed extension).

This works, but it's redundant and harder to follow. Consider either:

  • (A) Using the pre-filtered clean_list in the removal logic, or
  • (B) Removing the clean_extension_list call here since it's not leveraged.

Clarify intent by either using $clean_list or removing the redundant call. If the goal is to remove only AI extensions that aren't in VS Code, simplify the logic:

   # Create clean list of what should be synced
   clean_extension_list "$vscode_list" "$clean_list"

-  # Check if clean list has content
-  if [ ! -s "$clean_list" ]; then
-    # No extensions to sync after filtering
-    return
-  fi
-
   # Find extensions to remove (skip PROPRIETARY_EXTENSIONS)
   local to_remove=()
   while IFS= read -r installed_ext; do
     if [ -z "$installed_ext" ]; then
       continue
     fi

     # Skip PROPRIETARY_EXTENSIONS - don't attempt to remove them
     local is_proprietary=false
     for prop_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
       if [ "$installed_ext" = "$prop_ext" ]; then
         is_proprietary=true
         break
       fi
     done

     if [ "$is_proprietary" = true ]; then
       continue
     fi

-    # Check if extension is in VS Code's original list (before filtering)
-    # If it's in VS Code, we should keep it (it will be synced)
-    local in_vscode=false
-    if grep -Fxq "$installed_ext" "$vscode_list" 2>/dev/null; then
-      in_vscode=true
-    fi
-
-    # If extension is in VS Code, keep it (don't remove)
-    if [ "$in_vscode" = true ]; then
-      continue
-    fi
-
     # Check if extension is in AI_EXTENSIONS (should be removed)
     local is_ai=false
     for ai_ext in "${AI_EXTENSIONS[@]}"; do
       if [ "$installed_ext" = "$ai_ext" ]; then
         is_ai=true
         break
       fi
     done

     # Remove if it's an AI extension (not in VS Code but installed)
     if [ "$is_ai" = true ]; then
       to_remove+=("$installed_ext")
     fi
   done <"$installed_list"

(This assumes the goal is to remove only AI extensions. If you want to preserve non-proprietary, non-AI extensions that aren't in VS Code, adjust accordingly.)


214-214: Declare and assign separately to avoid masking return values (SC2155).

The pattern local cli_cmd=$(resolve_cli "$target_name") masks the return value. Separate declaration and assignment for consistency.

Apply this diff:

-  local cli_cmd=$(resolve_cli "$target_name")
+  local cli_cmd
+  cli_cmd=$(resolve_cli "$target_name")

244-244: Comment inaccuracy: Clarify what "clean" means.

The comment says "without proprietary MS extensions" but clean_extension_list also removes AI extensions (e.g., Claude Code, CodeRabbit). Update the comment for clarity.

Apply this diff:

-  # create a clean list without proprietary MS extensions
+  # create a clean list without proprietary and AI extensions
   clean_extension_list "$vscode_list" "$clean_list"

248-248: Declare and assign separately to avoid masking return values (SC2155).

The pattern local extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ' || echo "0") masks the return value of the pipeline. Separate declaration and assignment, or use a simpler command like grep -c ''.

Apply this diff to improve clarity:

-  local extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ' || echo "0")
+  local extension_count
+  extension_count=$(grep -c '^..*$' "$clean_list" 2>/dev/null || echo "0")
   extension_count=${extension_count:-0}

Or keep the current approach but separate the assignment for safety.


279-294: Add error handling to sync_config_file.

The function silently ignores cp failures when copying config files to target editors. If a copy fails, users may not notice.

Apply this diff to add error checking:

 sync_config_file() {
   local filename=$1
   local source="$VSCODE_USER_DIR/$filename"

   if [ -f "$source" ]; then
     echo "Copying $filename to all editors..."
     echo "   📋 Source: $source"
     echo "   📤 Destinations:"
     echo "      → $ANTIGRAVITY_USER_DIR/$filename"
-    cp "$source" "$ANTIGRAVITY_USER_DIR/$filename"
+    cp "$source" "$ANTIGRAVITY_USER_DIR/$filename" || echo "   ⚠️  Failed to copy to Antigravity"
     echo "      → $CURSOR_USER_DIR/$filename"
-    cp "$source" "$CURSOR_USER_DIR/$filename"
+    cp "$source" "$CURSOR_USER_DIR/$filename" || echo "   ⚠️  Failed to copy to Cursor"
     echo "      → $WINDSURF_USER_DIR/$filename"
-    cp "$source" "$WINDSURF_USER_DIR/$filename"
+    cp "$source" "$WINDSURF_USER_DIR/$filename" || echo "   ⚠️  Failed to copy to Windsurf"
   fi
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 46f486e and cced5b9.

📒 Files selected for processing (1)
  • home-manager/services/code-syncer/sync.sh (1 hunks)
🧰 Additional context used
🪛 Shellcheck (0.11.0)
home-manager/services/code-syncer/sync.sh

[warning] 14-14: EXTENSIONS_FILE appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 59-59: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 106-106: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 214-214: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 248-248: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 329-329: num appears unused. Verify use (or export if used externally).

(SC2034)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Agent
  • GitHub Check: nix-linux
  • GitHub Check: nix-nixos
  • GitHub Check: nix-darwin
  • GitHub Check: e2e-run (NixOS, ubuntu-latest)
  • GitHub Check: docker-build-push (linux/amd64, amd64, ubuntu-latest)
  • GitHub Check: e2e-run (Ubuntu, ubuntu-latest)
  • GitHub Check: e2e-run (MacOS, macos-latest)
  • GitHub Check: docker-build-push (linux/arm64, arm64, ubuntu-24.04-arm)
🔇 Additional comments (2)
home-manager/services/code-syncer/sync.sh (2)

298-336: Main execution flow is well-orchestrated and user-friendly.

The script follows a clear sequence: validate CLI, report extension count, sync configs, install/filter extensions, and optionally watch for changes. Error handling and logging are appropriate throughout the main flow.

Minor observation: Consider whether /tmp/ file collisions could occur if the script runs concurrently (e.g., in a cron job or multiple shells). If concurrency is possible, consider using unique temp files (e.g., with mktemp or a PID suffix).

Are there scenarios where this script might run concurrently? If so, would you like me to suggest temp file improvements?


14-14: Unused constant: Remove or document EXTENSIONS_FILE.

The constant EXTENSIONS_FILE is defined but never referenced in the script. Either remove it if it's dead code, or document its intended external use.

If this constant is unused, apply this diff to remove it:

 SETTINGS_FILE="settings.json"
 KEYBINDINGS_FILE="keybindings.json"
-EXTENSIONS_FILE="extensions.list"

: If this is intentionally exported or used by external tools, clarify via a comment.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull Request Overview

This PR significantly refactors the VS Code configuration syncing script to improve reliability and flexibility. The script now syncs VS Code settings, keybindings, and extensions to multiple editor forks (Antigravity, Cursor, Windsurf) while filtering out proprietary Microsoft extensions and AI-specific extensions that may not work properly in these forks.

Key Changes:

  • Replaced hardcoded paths with $HOME-based configuration for better portability
  • Added comprehensive blocklists for proprietary and AI extensions that should not be synced
  • Implemented direct CLI-based extension retrieval and management instead of file-based syncing
  • Enhanced logging with emoji indicators and detailed status messages for better user feedback

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

local installed_list="/tmp/${target_name}_extensions_installed.list"
local vscode_list="/tmp/vscode_extensions.list"

if [ -z "$cli_cmd" ] || [ ! -f "$cli_cmd" ]; then

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The check [ ! -f "$cli_cmd" ] will fail if $cli_cmd is a symlink or if the command is in PATH but not a regular file. Use [ ! -x "$cli_cmd" ] instead to check if it's executable, or use command -v to verify the command exists and is executable.

Suggested change
if [ -z "$cli_cmd" ] || [ ! -f "$cli_cmd" ]; then
if [ -z "$cli_cmd" ] || [ ! -x "$cli_cmd" ]; then

Copilot uses AI. Check for mistakes.
clean_extension_list "$vscode_list" "$clean_list"

# Log extensions that will be synced
local extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ' || echo "0")

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The expression $(wc -l <"$clean_list" 2>/dev/null | tr -d ' ' || echo "0") is overly complex. The subsequent line extension_count=${extension_count:-0} will never take effect because the || echo "0" ensures a value is always returned. Simplify to: extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ') and keep the ${extension_count:-0} assignment.

Suggested change
local extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ' || echo "0")
local extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ')

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +89
local input_source="$1" # Can be a file path or "-" for stdin
local clean_file="$2"

if [ -f "$source_path" ]; then
cp "$source_path" "$ANTIGRAVITY_USER_DIR/$file"
cp "$source_path" "$CURSOR_USER_DIR/$file"
cp "$source_path" "$WINDSURF_USER_DIR/$file"
echo "$file synced at $(date)"
# Read from stdin if input_source is "-", otherwise from file
if [ "$input_source" = "-" ]; then
cat >"$clean_file"
else
echo "VSCode $file not found"
cp "$input_source" "$clean_file"

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The input_source parameter is used in a command without proper validation. If it contains special characters or command injection payloads, it could lead to security issues. Consider validating or quoting the parameter, or restrict its usage to known safe values.

Copilot uses AI. Check for mistakes.
echo ""
fi
}

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The function install_extensions lacks documentation explaining its parameters, what it does, and its behavior. Add a comment describing that it syncs VS Code extensions to the target editor after filtering out proprietary and AI extensions.

Suggested change
# Syncs VS Code extensions to the target editor after filtering out proprietary and AI extensions.
#
# Parameters:
# $1 - target_name: The name of the target editor (e.g., "antigravity", "cursor", "windsurf").
#
# Behavior:
# - Removes unnecessary extensions from the target editor.
# - Retrieves the list of extensions installed in VS Code.
# - Filters out proprietary and AI extensions from the list.
# - Installs the filtered extensions to the target editor using its CLI.
# - Logs successes and failures, skips syncing if required CLIs are not found.

Copilot uses AI. Check for mistakes.
Comment on lines +265 to +270
if [ $? -ne 0 ]; then
echo " ❌ Failed: $extension (Likely missing from Open VSX)"
else
# Optional: verify it's actually installed
echo " ✅ Synced: $extension"
fi

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Avoid using bare $? checks in conditional statements. The if statement already captures the exit status, making the subsequent check redundant. Combine into: if ! $cli_cmd --install-extension "$extension" >/dev/null 2>&1; then echo " ❌ Failed: $extension (Likely missing from Open VSX)"; else echo " ✅ Synced: $extension"; fi

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +117
$cli_cmd --list-extensions >"$installed_list" 2>/dev/null
if [ $? -ne 0 ]; then

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Avoid using bare $? checks in conditional statements. The if statement already captures the exit status, making the subsequent check redundant. Remove the if [ $? -ne 0 ]; then return; fi pattern and instead use: if ! $cli_cmd --list-extensions >"$installed_list" 2>/dev/null; then return; fi

Suggested change
$cli_cmd --list-extensions >"$installed_list" 2>/dev/null
if [ $? -ne 0 ]; then
if ! $cli_cmd --list-extensions >"$installed_list" 2>/dev/null; then

Copilot uses AI. Check for mistakes.
Comment on lines +199 to +203
if [ $? -eq 0 ]; then
echo " ✅ Removed: $ext"
else
echo " ⚠️ Failed to remove: $ext"
fi

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Avoid using bare $? checks in conditional statements. The if statement already captures the exit status, making the subsequent check redundant. Combine into: if $cli_cmd --uninstall-extension "$ext" >/dev/null 2>&1; then echo " ✅ Removed: $ext"; else echo " ⚠️ Failed to remove: $ext"; fi

Copilot uses AI. Check for mistakes.
Comment on lines +92 to +100
for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
# Remove the line containing the bad extension
sed -i '' "/$bad_ext/d" "$clean_file"
done

for ai_ext in "${AI_EXTENSIONS[@]}"; do
# Remove the line containing the AI extension
sed -i '' "/$ai_ext/d" "$clean_file"
done

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Running sed in a loop for each extension is inefficient. Instead, build a single sed expression with multiple patterns: sed -i '' -e "/$ext1/d" -e "/$ext2/d" ... "$clean_file" or use a single regex pattern that matches all extensions. This would reduce the number of file I/O operations significantly.

Suggested change
for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
# Remove the line containing the bad extension
sed -i '' "/$bad_ext/d" "$clean_file"
done
for ai_ext in "${AI_EXTENSIONS[@]}"; do
# Remove the line containing the AI extension
sed -i '' "/$ai_ext/d" "$clean_file"
done
# Build sed delete expressions for all extensions
local sed_expr=()
for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
sed_expr+=("-e" "/$bad_ext/d")
done
for ai_ext in "${AI_EXTENSIONS[@]}"; do
sed_expr+=("-e" "/$ai_ext/d")
done
sed -i '' "${sed_expr[@]}" "$clean_file"

Copilot uses AI. Check for mistakes.
Comment on lines +215 to +216
local clean_list="/tmp/${target_name}_extensions_clean.list"
local vscode_list="/tmp/vscode_extensions.list"

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Using predictable filenames in /tmp (e.g., /tmp/${target_name}_extensions_clean.list) can lead to security vulnerabilities such as symlink attacks or race conditions. Use mktemp to create secure temporary files instead: local clean_list=$(mktemp)

Suggested change
local clean_list="/tmp/${target_name}_extensions_clean.list"
local vscode_list="/tmp/vscode_extensions.list"
local clean_list=$(mktemp)
local vscode_list=$(mktemp)

Copilot uses AI. Check for mistakes.
Comment on lines +153 to +160
# Skip PROPRIETARY_EXTENSIONS - don't attempt to remove them
local is_proprietary=false
for prop_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
if [ "$installed_ext" = "$prop_ext" ]; then
is_proprietary=true
break
fi
done

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The nested loop structure checking for proprietary extensions (lines 154-160) and AI extensions (lines 178-185) is inefficient with O(n*m) complexity. Consider using associative arrays (bash 4+) or a more efficient lookup method to improve performance when dealing with large extension lists.

Copilot uses AI. Check for mistakes.
…n syncing

- Adjusted spacing in the clean_extension_list function for consistency.
- Removed unnecessary blank lines in the install_extensions function to enhance readability.
- Improved logging to provide clearer feedback on the number of extensions found for syncing.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

2 issues found across 1 file

Prompt for AI agents (all 2 issues)

Understand the root cause of the following 2 issues and fix them.


<file name="home-manager/services/code-syncer/sync.sh">

<violation number="1" location="home-manager/services/code-syncer/sync.sh:94">
clean_extension_list deletes extensions whenever their ID merely contains a blocklisted substring, so legitimate extensions such as github.copilot-labs are filtered out even though they were not meant to be blocked.</violation>

<violation number="2" location="home-manager/services/code-syncer/sync.sh:169">
remove_unnecessary_extensions compares installed extensions against the unfiltered VS Code list, so extensions that should have been excluded by the blocklist are never removed when they still exist in VS Code.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

# Check if extension is in VS Code's original list (before filtering)
# If it's in VS Code, we should keep it (it will be synced)
local in_vscode=false
if grep -Fxq "$installed_ext" "$vscode_list" 2>/dev/null; then

@cubic-dev-ai cubic-dev-ai Bot Nov 21, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remove_unnecessary_extensions compares installed extensions against the unfiltered VS Code list, so extensions that should have been excluded by the blocklist are never removed when they still exist in VS Code.

Prompt for AI agents
Address the following comment on home-manager/services/code-syncer/sync.sh at line 169:

<comment>remove_unnecessary_extensions compares installed extensions against the unfiltered VS Code list, so extensions that should have been excluded by the blocklist are never removed when they still exist in VS Code.</comment>

<file context>
@@ -1,94 +1,336 @@
+    # Check if extension is in VS Code&#39;s original list (before filtering)
+    # If it&#39;s in VS Code, we should keep it (it will be synced)
+    local in_vscode=false
+    if grep -Fxq &quot;$installed_ext&quot; &quot;$vscode_list&quot; 2&gt;/dev/null; then
+      in_vscode=true
+    fi
</file context>
Suggested change
if grep -Fxq "$installed_ext" "$vscode_list" 2>/dev/null; then
if grep -Fxq "$installed_ext" "$clean_list" 2>/dev/null; then
Fix with Cubic


for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
# Remove the line containing the bad extension
sed -i '' "/$bad_ext/d" "$clean_file"

@cubic-dev-ai cubic-dev-ai Bot Nov 21, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

clean_extension_list deletes extensions whenever their ID merely contains a blocklisted substring, so legitimate extensions such as github.copilot-labs are filtered out even though they were not meant to be blocked.

Prompt for AI agents
Address the following comment on home-manager/services/code-syncer/sync.sh at line 94:

<comment>clean_extension_list deletes extensions whenever their ID merely contains a blocklisted substring, so legitimate extensions such as github.copilot-labs are filtered out even though they were not meant to be blocked.</comment>

<file context>
@@ -1,94 +1,336 @@
+
+  for bad_ext in &quot;${PROPRIETARY_EXTENSIONS[@]}&quot;; do
+    # Remove the line containing the bad extension
+    sed -i &#39;&#39; &quot;/$bad_ext/d&quot; &quot;$clean_file&quot;
+  done
+
</file context>
Suggested change
sed -i '' "/$bad_ext/d" "$clean_file"
sed -i '' "/^${bad_ext//./\.}$/d" "$clean_file"
Fix with Cubic

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (9)
home-manager/services/code-syncer/sync.sh (9)

111-111: Use robust executable check and consolidate validation in resolve_cli().

The check [ ! -f "$cli_cmd" ] is unreliable for symlinks and executables. Previous reviews suggested consolidating validation within resolve_cli() and checking for executability (-x). This simplifies call sites to if [ -z "$cli_cmd" ]; then.


116-117: Avoid bare $? checks; use if ! pattern instead.

This pattern is redundant and less readable. Let the if statement capture exit status directly.

-  $cli_cmd --list-extensions >"$installed_list" 2>/dev/null
-  if [ $? -ne 0 ]; then
+  if ! $cli_cmd --list-extensions >"$installed_list" 2>/dev/null; then

154-160: Optimize extension lookup from O(N*M) to O(1) using associative arrays.

The nested loops checking blocklists have quadratic complexity. For large extension lists, use associative arrays (bash 4+) for near-constant-time lookups, as suggested in previous reviews.

   # Find extensions to remove (skip PROPRIETARY_EXTENSIONS)
   local to_remove=()
+  declare -A proprietary_set ai_set
+  for ext in "${PROPRIETARY_EXTENSIONS[@]}"; do proprietary_set["$ext"]=1; done
+  for ext in "${AI_EXTENSIONS[@]}"; do ai_set["$ext"]=1; done
+
   while IFS= read -r installed_ext; do
     if [ -z "$installed_ext" ]; then
       continue
     fi
 
     # Skip PROPRIETARY_EXTENSIONS - don't attempt to remove them
-    local is_proprietary=false
-    for prop_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
-      if [ "$installed_ext" = "$prop_ext" ]; then
-        is_proprietary=true
-        break
-      fi
-    done
-
-    if [ "$is_proprietary" = true ]; then
+    if [[ -v proprietary_set["$installed_ext"] ]]; then
       continue
     fi
 
     # Check if extension is in VS Code's original list (before filtering)
     # If it's in VS Code, we should keep it (it will be synced)
-    local in_vscode=false
     if grep -Fxq "$installed_ext" "$vscode_list" 2>/dev/null; then
-      in_vscode=true
+      continue
     fi
-
-    # If extension is in VS Code, keep it (don't remove)
-    if [ "$in_vscode" = true ]; then
-      continue
-    fi
 
     # Check if extension is in AI_EXTENSIONS (should be removed)
-    local is_ai=false
-    for ai_ext in "${AI_EXTENSIONS[@]}"; do
-      if [ "$installed_ext" = "$ai_ext" ]; then
-        is_ai=true
-        break
-      fi
-    done
-
-    # Remove if it's an AI extension (not in VS Code but installed)
-    if [ "$is_ai" = true ]; then
+    if [[ -v ai_set["$installed_ext"] ]]; then
       to_remove+=("$installed_ext")
     fi

Also applies to: 180-185


198-199: Avoid bare $? checks; use if ! pattern instead.

Use direct exit status capture for clearer, more idiomatic bash code.

-      $cli_cmd --uninstall-extension "$ext" >/dev/null 2>&1
-      if [ $? -eq 0 ]; then
+      if $cli_cmd --uninstall-extension "$ext" >/dev/null 2>&1; then
         echo "      ✅ Removed: $ext"
       else
         echo "      ⚠️  Failed to remove: $ext"

214-214: Declare and assign separately to detect resolution failures.

SC2155 pattern masks return value from resolve_cli().

-  local cli_cmd=$(resolve_cli "$target_name")
+  local cli_cmd
+  cli_cmd=$(resolve_cli "$target_name")

215-216: Replace hardcoded /tmp paths with mktemp for security.

Use mktemp instead of predictable filenames, as flagged in previous reviews.

-  local clean_list="/tmp/${target_name}_extensions_clean.list"
-  local vscode_list="/tmp/vscode_extensions.list"
+  local clean_list
+  local vscode_list
+  clean_list=$(mktemp)
+  vscode_list=$(mktemp)
+  trap 'rm -f "$clean_list" "$vscode_list"' RETURN

218-218: Use robust executable check instead of [ ! -f ].

The file test is unreliable for symlinks and executables. Previous reviews suggested centralizing validation in resolve_cli().


264-265: Avoid bare $? checks; use if ! pattern instead.

Combine the command and conditional check for clarity.

-        $cli_cmd --install-extension "$extension" >/dev/null 2>&1
-        if [ $? -ne 0 ]; then
+        if ! $cli_cmd --install-extension "$extension" >/dev/null 2>&1; then

327-327: Redirect stderr in command -v check.

Ensure both stdout and stderr are redirected to suppress any command output, consistent with line 302 and previous review feedback.

-if command -v fswatch >/dev/null; then
+if command -v fswatch >/dev/null 2>&1; then
🧹 Nitpick comments (5)
home-manager/services/code-syncer/sync.sh (5)

59-59: Declare and assign separately to detect command failures.

The SC2155 pattern masks the return value of command -v. Separate the declaration and assignment to properly detect errors.

 resolve_cli() {
   local cmd=$1
-  local resolved_path=$(command -v "$cmd" 2>/dev/null)
+  local resolved_path
+  resolved_path=$(command -v "$cmd" 2>/dev/null)

92-100: Use exact-line matching instead of substring patterns in sed.

The sed patterns /$bad_ext/d perform substring matching and can remove unintended lines. For portability (Linux/macOS) and exact matching, use grep -Fxv as suggested in previous reviews.

-  for bad_ext in "${PROPRIETARY_EXTENSIONS[@]}"; do
-    # Remove the line containing the bad extension
-    sed -i '' "/$bad_ext/d" "$clean_file"
-  done
-
-  for ai_ext in "${AI_EXTENSIONS[@]}"; do
-    # Remove the line containing the AI extension
-    sed -i '' "/$ai_ext/d" "$clean_file"
-  done
+  local temp_blocklist
+  temp_blocklist=$(mktemp)
+  trap 'rm -f "$temp_blocklist"' RETURN
+
+  # Combine both blocklists into a single file for grep
+  printf '%s\n' "${PROPRIETARY_EXTENSIONS[@]}" "${AI_EXTENSIONS[@]}" >"$temp_blocklist"
+
+  # Use grep to filter out blocklisted extensions (exact line matching)
+  grep -v -F -x -f "$temp_blocklist" "$clean_file" >"${clean_file}.tmp" && mv "${clean_file}.tmp" "$clean_file"

106-106: Declare and assign separately to detect resolution failures.

SC2155 pattern masks the return value of resolve_cli(). Separate the declaration and assignment.

-  local cli_cmd=$(resolve_cli "$target_name")
+  local cli_cmd
+  cli_cmd=$(resolve_cli "$target_name")

107-109: Replace hardcoded /tmp paths with mktemp for security and atomicity.

Predictable filenames in /tmp create security vulnerabilities (symlink attacks, race conditions). Use mktemp and add cleanup traps. This addresses previous review feedback.

-  local clean_list="/tmp/${target_name}_extensions_clean.list"
-  local installed_list="/tmp/${target_name}_extensions_installed.list"
-  local vscode_list="/tmp/vscode_extensions.list"
+  local clean_list
+  local installed_list
+  local vscode_list
+  clean_list=$(mktemp)
+  installed_list=$(mktemp)
+  vscode_list=$(mktemp)
+  trap 'rm -f "$clean_list" "$installed_list" "$vscode_list"' RETURN

248-249: Simplify extension count expression and remove redundant assignment.

The pattern $(... || echo "0") followed by ${extension_count:-0} is redundant. The fallback in the assignment will never execute. Simplify by separating declare and assign (SC2155) and letting the fallback work correctly.

-  local extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ' || echo "0")
+  local extension_count
+  extension_count=$(wc -l <"$clean_list" 2>/dev/null | tr -d ' ')
   extension_count=${extension_count:-0}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cced5b9 and 958dd23.

📒 Files selected for processing (1)
  • home-manager/services/code-syncer/sync.sh (1 hunks)
🧰 Additional context used
🪛 Shellcheck (0.11.0)
home-manager/services/code-syncer/sync.sh

[warning] 14-14: EXTENSIONS_FILE appears unused. Verify use (or export if used externally).

(SC2034)


[warning] 59-59: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 106-106: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 214-214: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 248-248: Declare and assign separately to avoid masking return values.

(SC2155)


[warning] 329-329: num appears unused. Verify use (or export if used externally).

(SC2034)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: docker-build-push (linux/amd64, amd64, ubuntu-latest)
  • GitHub Check: docker-build-push (linux/arm64, arm64, ubuntu-24.04-arm)
  • GitHub Check: nix-linux
  • GitHub Check: nix-nixos
  • GitHub Check: nix-darwin
  • GitHub Check: e2e-run (MacOS, macos-latest)
  • GitHub Check: e2e-run (NixOS, ubuntu-latest)
  • GitHub Check: e2e-run (Ubuntu, ubuntu-latest)

# Files
SETTINGS_FILE="settings.json"
KEYBINDINGS_FILE="keybindings.json"
EXTENSIONS_FILE="extensions.list"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unused variable: remove or verify external use.

EXTENSIONS_FILE is defined but never referenced in this script. If this is intentionally preserved for external scripts, consider adding a comment. Otherwise, remove it.

🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 14-14: EXTENSIONS_FILE appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In home-manager/services/code-syncer/sync.sh around line 14, the variable
EXTENSIONS_FILE is declared but never used; either delete this unused variable
to clean up the script or make its intent explicit by exporting it and/or adding
a comment that it is consumed by external scripts (e.g., add a one-line comment
describing external usage or change to export EXTENSIONS_FILE so downstream
processes can read it).

# 4. Watcher (Mac only)
if command -v fswatch >/dev/null; then
echo "Watching for changes in VS Code settings..."
fswatch -o "$VSCODE_USER_DIR/$SETTINGS_FILE" "$VSCODE_USER_DIR/$KEYBINDINGS_FILE" | while read num; do

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Address unused variable in fswatch loop.

The num variable appears unused. Either use it explicitly or replace with _ for clarity that the value is intentionally ignored.

-  fswatch -o "$VSCODE_USER_DIR/$SETTINGS_FILE" "$VSCODE_USER_DIR/$KEYBINDINGS_FILE" | while read num; do
+  fswatch -o "$VSCODE_USER_DIR/$SETTINGS_FILE" "$VSCODE_USER_DIR/$KEYBINDINGS_FILE" | while read _; do
📝 Committable suggestion

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

Suggested change
fswatch -o "$VSCODE_USER_DIR/$SETTINGS_FILE" "$VSCODE_USER_DIR/$KEYBINDINGS_FILE" | while read num; do
fswatch -o "$VSCODE_USER_DIR/$SETTINGS_FILE" "$VSCODE_USER_DIR/$KEYBINDINGS_FILE" | while read _; do
🧰 Tools
🪛 Shellcheck (0.11.0)

[warning] 329-329: num appears unused. Verify use (or export if used externally).

(SC2034)

🤖 Prompt for AI Agents
In home-manager/services/code-syncer/sync.sh around line 329, the fswatch loop
reads into an unused variable `num`; change the read to use a placeholder (e.g.,
`read -r _`) or otherwise name it `_` to signal the value is intentionally
ignored (or use `read -r ignored` if clearer), ensuring no functional change to
the loop but removing the unused variable warning.

@shunkakinoki
shunkakinoki merged commit 60f8486 into main Nov 21, 2025
22 checks passed
@shunkakinoki
shunkakinoki deleted the chore/code-syncer-update branch November 21, 2025 08:54
@coderabbitai coderabbitai Bot mentioned this pull request Dec 14, 2025
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.

2 participants