Skip to content

Instantly share code, notes, and snippets.

@sandrods
Last active April 24, 2025 15:16
Show Gist options
  • Select an option

  • Save sandrods/e238f5563d6f7ec1a5f0def1f3ed7d21 to your computer and use it in GitHub Desktop.

Select an option

Save sandrods/e238f5563d6f7ec1a5f0def1f3ed7d21 to your computer and use it in GitHub Desktop.

Revisions

  1. sandrods revised this gist Apr 24, 2025. 1 changed file with 7 additions and 1 deletion.
    8 changes: 7 additions & 1 deletion 01_focus.md
    Original file line number Diff line number Diff line change
    @@ -39,10 +39,16 @@ Define your focus areas in `.focus/config.yml`:

    ```yaml
    invoices:
    - app/assets/stylesheets/components.css
    - app/components/invoices
    - app/components/ui/picker
    - app/controllers/invoices_controller.rb
    - app/controllers/invoices
    - app/controllers/picker
    - app/models/invoice.rb
    - app/models/invoice_item.rb
    - app/views/invoices
    - spec/models/invoice_spec.rb
    - app/views/picker
    ```
    ### Basic Commands
  2. sandrods revised this gist Apr 24, 2025. 2 changed files with 0 additions and 0 deletions.
    File renamed without changes.
    File renamed without changes.
  3. sandrods revised this gist Apr 24, 2025. 1 changed file with 0 additions and 8 deletions.
    8 changes: 0 additions & 8 deletions focus.md
    Original file line number Diff line number Diff line change
    @@ -31,14 +31,6 @@ When you update your configuration, you can refresh your focus area without losi
    bin/focus invoices --refresh
    ```

    ### 4. Clear Feedback

    The script provides clear status messages:

    - ✅ Successfully linked files
    - ❌ Missing source files
    - ℹ️ Already existing links (in refresh mode)

    ## Usage

    ### Configuration
  4. sandrods revised this gist Apr 24, 2025. 1 changed file with 87 additions and 0 deletions.
    87 changes: 87 additions & 0 deletions focus.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    # Focus: A Developer's Context Switcher

    When working on large codebases, developers often need to switch between different features or areas of the application. The `focus` script helps manage this cognitive load by creating organized workspaces for different contexts.

    ## The Problem

    Working on features that span multiple directories (controllers, models, views, specs) requires constant navigation and context switching. This leads to keeping multiple editor windows open, maintaining mental lists of relevant files, and repeatedly searching through deep directory structures.

    ## The Solution

    The `focus` script creates virtual workspaces using symlinks. It maintains a configuration file where you define groups of related files and directories. When you switch to a focus area, the script creates a dedicated directory with symlinks to all relevant files, preserving their original structure while making them easily accessible in one place.

    ## Features

    ### 1. Easy Setup

    - First-time run creates an example configuration
    - Opens the config file in your editor for immediate customization

    ### 2. Smart Symlink Management

    - Creates directory structure as needed
    - Handles both files and directories
    - Maintains original file structure within the focus directory

    ### 3. Refresh Mode

    When you update your configuration, you can refresh your focus area without losing existing symlinks:

    ```bash
    bin/focus invoices --refresh
    ```

    ### 4. Clear Feedback

    The script provides clear status messages:

    - ✅ Successfully linked files
    - ❌ Missing source files
    - ℹ️ Already existing links (in refresh mode)

    ## Usage

    ### Configuration

    Define your focus areas in `.focus/config.yml`:

    ```yaml
    invoices:
    - app/controllers/invoices_controller.rb
    - app/models/invoice.rb
    - app/views/invoices
    - spec/models/invoice_spec.rb
    ```
    ### Basic Commands
    ```bash
    # First time setup
    bin/focus

    # Switch to a focus area
    bin/focus invoices

    # Update after config changes
    bin/focus invoices --refresh
    ```

    ## Benefits

    1. **Reduced Cognitive Load**: All relevant files for a feature are in one place
    2. **Faster Navigation**: No need to dig through deep directory structures
    3. **Non-Destructive**: Uses symlinks, so original files remain unchanged
    4. **Flexible**: Easy to update and modify focus areas as needed

    ## Installation

    1. Copy the `focus` script to your project's `bin` directory
    2. Make it executable: `chmod +x bin/focus`

    ## Tips

    - Include both implementation and test files in your focus areas
    - Use directories when you need all files in a folder
    - Use `--refresh` when you want to preserve existing links

    The `focus` script is a simple tool that can significantly improve your development workflow by reducing the friction of context switching and keeping related files easily accessible.
  5. sandrods revised this gist Apr 24, 2025. No changes.
  6. sandrods revised this gist Apr 24, 2025. 1 changed file with 21 additions and 8 deletions.
    29 changes: 21 additions & 8 deletions focus
    Original file line number Diff line number Diff line change
    @@ -56,32 +56,45 @@ rescue Psych::SyntaxError => e
    end

    if ARGV.empty?
    puts "Usage: bin/focus [section_name]"
    puts "Usage: bin/focus [section_name] [--refresh]"
    list_available_sets(sets)
    exit 0
    end

    section = ARGV.first
    target_dir = File.join(FOCUS_ROOT, section)
    refresh_mode = ARGV.include?("--refresh")
    section = ARGV.find { |arg| !arg.start_with?("--") }

    unless section
    puts "❌ Please provide a section name"
    list_available_sets(sets)
    exit 1
    end

    unless sets.key?(section)
    puts "❌ Section '#{section}' not found in #{FOCUS_FILE}."
    list_available_sets(sets)
    exit 1
    end

    puts "🔄 Creating symlinks in #{target_dir}/ ..."
    target_dir = File.join(FOCUS_ROOT, section)

    puts "🔄 #{refresh_mode ? "Refreshing" : "Creating"} symlinks in #{target_dir}/ ..."

    FileUtils.rm_rf(target_dir)
    # Only remove existing symlinks if not in refresh mode
    FileUtils.rm_rf(target_dir) unless refresh_mode

    sets[section].each do |relative_path|
    source_path = File.expand_path(relative_path)
    dest_path = File.join(target_dir, relative_path)

    if File.exist?(source_path)
    FileUtils.mkdir_p(File.dirname(dest_path))
    FileUtils.ln_s(source_path, dest_path)
    puts "✅ Linked: #{relative_path}"
    begin
    FileUtils.mkdir_p(File.dirname(dest_path))
    FileUtils.ln_s(source_path, dest_path) unless File.exist?(dest_path)
    puts "✅ Linked: #{relative_path}"
    rescue Errno::EEXIST
    puts "ℹ️ Already exists: #{relative_path}"
    end
    else
    puts "❌ Missing: #{relative_path}"
    end
  7. sandrods created this gist Apr 24, 2025.
    93 changes: 93 additions & 0 deletions focus
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,93 @@
    #!/usr/bin/env ruby
    require "fileutils"
    require "yaml"

    FOCUS_FILE = ".focus/config.yml"
    FOCUS_ROOT = ".focus"
    EDITOR_COMMAND = "cursor" # Change to "code" or whatever

    def list_available_sets(sets)
    puts "ℹ️ Available focus sets:"
    sets.keys.each { |key| puts " - #{key}" }
    end

    def create_example_focus_file
    example = {
    "invoices" => [
    "app/controllers/invoices_controller.rb",
    "app/models/invoice.rb",
    "app/views/invoices",
    "spec/models/invoice_spec.rb"
    ],
    "auth" => [
    "app/controllers/sessions_controller.rb",
    "app/models/user.rb",
    "app/views/sessions",
    "spec/requests/login_spec.rb"
    ]
    }

    FileUtils.mkdir_p(File.dirname(FOCUS_FILE))
    File.write(FOCUS_FILE, example.to_yaml)
    puts "✅ Created #{FOCUS_FILE} with example sections."
    end

    unless File.exist?(FOCUS_FILE)
    puts "⚠️ #{FOCUS_FILE} not found."

    print "Would you like to create a sample one? (Y/n): "
    answer = $stdin.gets.strip.downcase

    if answer.empty? || answer == "y" || answer == "yes"
    create_example_focus_file
    system(EDITOR_COMMAND, FOCUS_FILE)
    exit 0
    else
    puts "🚫 Aborted. Please create #{FOCUS_FILE} manually."
    exit 1
    end
    end

    begin
    sets = YAML.load_file(FOCUS_FILE)
    rescue Psych::SyntaxError => e
    puts "❌ YAML parsing error in #{FOCUS_FILE}:\n#{e.message}"
    exit 1
    end

    if ARGV.empty?
    puts "Usage: bin/focus [section_name]"
    list_available_sets(sets)
    exit 0
    end

    section = ARGV.first
    target_dir = File.join(FOCUS_ROOT, section)

    unless sets.key?(section)
    puts "❌ Section '#{section}' not found in #{FOCUS_FILE}."
    list_available_sets(sets)
    exit 1
    end

    puts "🔄 Creating symlinks in #{target_dir}/ ..."

    FileUtils.rm_rf(target_dir)

    sets[section].each do |relative_path|
    source_path = File.expand_path(relative_path)
    dest_path = File.join(target_dir, relative_path)

    if File.exist?(source_path)
    FileUtils.mkdir_p(File.dirname(dest_path))
    FileUtils.ln_s(source_path, dest_path)
    puts "✅ Linked: #{relative_path}"
    else
    puts "❌ Missing: #{relative_path}"
    end
    end

    puts "\n📂 Focus folder ready: #{target_dir}"
    puts "🚀 Opening with: #{EDITOR_COMMAND} #{target_dir}/"

    system(EDITOR_COMMAND, target_dir)