Linux, Python, vim, git & nix LPvgn Short Stack
Future-proof your skills and escape the tech hamster wheel with Linux, Python, vim & git — now with nix (LPvgn), an AI stack to resist obsolescence. Follow along as I revitalize old school Webmaster skills with next generation AI/SEO techniques porting Jupyter Notebooks to FastHTML / HTMX Web apps using the Pipulate free AI SEO tool.

Cursor Rules Project Workspace Git Solution

I ran into a frustrating issue where Cursor AI's project rules weren't easily version-controlled within my Git repos because of how it handles multi-repo workspaces. After realizing the standard symlink approach wouldn't work with Git, I drew inspiration from my NixOS configuration management: instead of linking *from* my repo *out* to the rules, I put the rules *inside* my main repo and created a symlink *from* the workspace root *into* my repo, successfully getting the rules under version control while ensuring Cursor could still find them.

Understanding AI Assistant Configuration: Getting Started

AI-powered coding assistants, like the ‘Cursor’ tool mentioned in this article, are becoming increasingly popular. They help developers write code faster and more effectively by understanding the context of a software project. Like many complex tools, these assistants often have configuration files – special files that let users customize the AI’s behavior, perhaps by providing specific rules or instructions (referred to here as .mdc files).

Developers rely heavily on version control systems, most commonly ‘Git’, to track changes to their code and configurations over time. This allows them to collaborate, revert mistakes, and maintain a history. A common challenge arises when a tool, like Cursor in this case, stores its configuration files outside the main project folder that Git is tracking. This article tackles that specific problem: how can developers use standard techniques, like ‘symbolic links’ (filesystem shortcuts, often created with ln -s on Linux and macOS), to manage these external configuration files within their Git workflow, ensuring they are properly version-controlled alongside the project code?


The age of AI coding assistants is upon us, and tools like Cursor are changing how we write, debug, and understand code. They offer incredible power, learning from our projects to provide context-aware suggestions and answers. But this exciting new world brings fresh challenges, particularly around something developers hold dear: configuration and version control.

Imagine this scenario: You’re using Cursor, loving how it understands your complex project structure spanning multiple related code repositories (think a backend service, a frontend app, and a shared library). Cursor’s docs suggest opening a parent directory containing all these repos to give the AI broad context. Great!

To fine-tune the AI’s behavior for this specific project group, Cursor uses special rule files (ending in .mdc) tucked away in a .cursor/rules directory located at the root of the folder you opened (what Cursor calls the “project” and VS Code calls the “workspace”).

And here lies the rub. As developers, we live and breathe Git. We want important project configurations – especially rules dictating AI behavior – version-controlled within the relevant repository. We want that history tracked, shareable with collaborators, and intrinsically linked to the codebase it affects. But Cursor’s default setup places these crucial rules outside our individual Git repos, in that shared parent directory. Ugh!

The Obvious Idea (That Doesn’t Quite Work)

If you’re using Linux or macOS, your first thought might be: “Aha! Symlinks!” Symbolic links (ln -s) are like smart shortcuts on your filesystem. Can’t we just put a symlink inside our main repository (say, my-repo/.cursor-rules) that points out to the shared ../.cursor/rules directory where Cursor expects it?

It’s a clever idea, but Git throws a wrench in the works. While Git recognizes symlinks, it doesn’t store the content of the file or directory the link points to. It only stores the path the link points at.

So, if you add that symlink to Git:

  1. Git records that my-repo/.cursor-rules is a symlink pointing to ../.cursor/rules.
  2. The actual .mdc rule files remain outside your repository’s tracked history.
  3. When a collaborator (or you on another machine) clones the repo, they get the symlink, but not the rules themselves. If the external ../.cursor/rules directory doesn’t exist in the same relative location on their machine, the link is broken and useless.

We tested this, and sure enough, Git only tracks the link itself, not the precious rules we want to manage. Back to the drawing board.

The Counter-Intuitive Solution: Flipping the Script

Frustrated, I thought about how I manage my system configuration on NixOS (a Linux distribution known for its rigorous approach to configuration management). My NixOS config files live safely inside a dedicated Git repository in my home directory (~/repos/nixos). But the operating system needs to find them at /etc/nixos.

The solution? /etc/nixos is actually a symlink pointing to my Git repository (ln -s ~/repos/nixos /etc/nixos). The real files are version-controlled, and the system follows a signpost to find them.

Lightbulb moment! Why can’t we do the same for Cursor?

Instead of putting a symlink in the repo pointing out, we do the opposite:

  1. Put the Real Files IN the Repo: Create (or move) the .cursor/rules directory inside the primary repository you want these rules associated with (e.g., ~/repos/pipulate/.cursor/rules). Add this .cursor directory to Git and commit it. Now your rules are properly version-controlled!
  2. Put the Symlink Where the App Looks: Go to the parent directory you open in Cursor (~/repos). Create a symlink named .cursor that points into your repository where the real files now live (ln -s ~/repos/pipulate/.cursor ~/repos/.cursor).

Now, when Cursor opens the ~/repos workspace and looks for ./.cursor/rules, it finds the .cursor symlink, follows it to ~/repos/pipulate/.cursor, and finds the rules subdirectory containing your version-controlled files. Problem solved!

Making It Stick (The NixOS Way)

On Linux and macOS, running the ln -s command once might be enough if your workspace structure is stable. However, on NixOS, we strive for deterministic configuration. We can ensure this symlink is always correctly set up using the system configuration file (configuration.nix).

By adding a small script to system.activationScripts, we tell NixOS to automatically create this symlink every time the system configuration is rebuilt:

# In your configuration.nix
system.activationScripts.linkCursorRules = {
  text = ''
    # Ensure the target directory structure exists inside the repo
    mkdir -p /home/mike/repos/pipulate/.cursor/rules
    chown -R mike:users /home/mike/repos/pipulate/.cursor # Use your username/group
    chmod -R 755 /home/mike/repos/pipulate/.cursor

    # Remove any old symlink at the workspace level first
    [ -L "/home/mike/repos/.cursor" ] && rm -f "/home/mike/repos/.cursor"

    # Create the symlink pointing from the workspace root into the repo
    ln -sf /home/mike/repos/pipulate/.cursor /home/mike/repos/.cursor
    chown -h mike:users /home/mike/repos/.cursor # Set link ownership
  '';
  deps = []; # No dependencies needed for this simple link
};

(Remember to replace /home/mike/repos/pipulate and mike:users with your actual paths and username/group).

After adding this and rebuilding (sudo nixos-rebuild switch), the symlink will be reliably managed by the system.

(For those not on NixOS, you might need some initial manual commands to move existing rules and set up the structure before creating the symlink, but the core ln -s command is the key.)

Did It Work? The Moment of Truth

The final test was simple:

  1. Edit a rule file directly inside the Git repository using a text editor (nvim ~/repos/pipulate/.cursor/rules/some-rule.mdc).
  2. Open Cursor with ~/repos as the workspace root.
  3. Navigate to Cursor’s UI for managing rules.
  4. Success! The UI showed the exact content I had just edited, confirming Cursor was following the symlink and reading the version-controlled file.

Conclusion: Taking Control

This “reverse symlink” trick is a powerful pattern, not just for Cursor or NixOS users. Anyone on Linux or macOS facing tools that store configuration outside project directories can potentially use this method to bring that config back under Git’s watchful eye.

As AI assistants become more integrated into our workflows, managing their configuration effectively becomes paramount. By understanding how tools interact with the filesystem and leveraging simple Unix concepts like symlinks, we can maintain control, keep our configurations version-controlled, and ensure our development environments remain sane and reproducible.

What About .cursorrules?

Earlier on this very day I tried to eliminate the individual .mdc rules by making a single master .cursorrules file only to realize that this approach had been deprecated. And so this article where I got the out-of-repo-scope .cursor/rules folder into repo scope and under my version control (it appears Cursor runs their own). And that’s good. But now I’m going to have to figure out what to do. I do believe it will be deconstructing it and distributing over the .mdc files better per the Cursor rules best practices.

Me: I just learned .cursorrules has been deprecated per @https://docs.cursor.com/context/rules#cursorrules-legacy but it’s where all my hard work went recently. I need to preserve and distribute that work over the appropriate files. Should @htmx-chain-reactions.mdc , @placeholder-step-pattern.mdc and @wet-workflows.mdc still be 3 separate files? Should I work @.cursorrules content into them? Or should I take some more systematic approach now? I’m willing to go in whatever direction you think would be most useful to you and future AI code assistants.

Claude: Based on Cursor’s documentation, .cursorrules is now deprecated in favor of Project Rules stored in the .cursor/rules directory. You should definitely migrate your rules to this newer format, but let me suggest how to restructure your content.

Your current MDC files are already well-organized as separate rules focusing on specific patterns, which aligns with Cursor’s recommended best practice: “Split large concepts into multiple, composable rules.”

Here’s what I recommend:

  1. Keep the three separate MDC files in the .cursor/rules directory:
    • htmx-chain-reactions.mdc
    • placeholder-step-pattern.mdc
    • wet-workflows.mdc
  2. Add proper MDC metadata headers to each file to define:
    • Description (for Agent to understand purpose)
    • Globs (to auto-attach when relevant files are referenced)
    • Always apply setting
  3. Extract core principles from your .cursorrules file into a new foundational rule:
    • Create pipulate-architecture.mdc with broader architectural principles

For example, your htmx-chain-reactions.mdc could become:

---
description: Critical HTMX chain reaction pattern for workflow progression
globs: ["pipulate/plugins/*.py"]
alwaysApply: false
---

# chain-reaction-pattern

## Overview
The chain reaction pattern is the core mechanism for step progression...

This approach:

  • Preserves your detailed pattern documentation
  • Makes rules available in the right context (when working with matching files)
  • Follows the new Project Rules structure
  • Allows the AI to selectively include rules based on relevance

You could use the /Generate Cursor Rules command mentioned in the docs to help with formatting your existing content into the new structure.

Additionally, you might want to categorize your rules as either “Always” included, “Auto Attached” based on file patterns, or “Agent Requested” where the AI decides whether to include them based on relevance.

Would you like me to help structure the metadata for any specific rule file?

Me: /Generate Cursor Rules [Apply, Apply, Apply]

And just like that all the work I put into .cursorrules is broken up and distributed back over the individual .mdc files.


AI Analysis

Title/Headline Ideas & Filenames

  • Title: Version Control Cursor AI Rules with Git and Symlinks (Linux/macOS/NixOS) Filename: version-control-cursor-ai-rules-git-symlinks-linux-macos-nixos.md
  • Title: Solving the Cursor AI Configuration Problem: Git, Symlinks, and the NixOS Trick Filename: cursor-ai-config-git-symlinks-nixos-trick.md
  • Title: Keep Your AI Config in Git: A Symlink Strategy for Cursor Rules Filename: ai-config-git-symlink-strategy-cursor-rules.md
  • Title: Wrangling AI Configuration: The “Reverse Symlink” Fix for Cursor and Git Filename: wrangle-ai-config-reverse-symlink-fix-cursor-git.md
  • Title: Deterministic Cursor AI Rules Management on NixOS using Symlinks and Git Filename: deterministic-cursor-ai-rules-nixos-symlinks-git.md

Strengths

  • Clear Problem/Solution: Effectively identifies a real-world developer pain point and presents a clear, practical solution.
  • Addresses Misconception: Explicitly explains why the intuitive symlink approach fails with Git, preventing readers from making the same mistake.
  • Relatable Analogy: The NixOS configuration example provides a strong, successful analogy that clarifies the correct pattern.
  • Actionable Steps: Provides specific commands and code examples (especially for NixOS), making the solution easy to implement.
  • Validation: Includes clear steps on how the author tested and confirmed the solution worked.

Weaknesses

  • NixOS Focus: While aiming for broader appeal, the detailed NixOS implementation might slightly overshadow the general Linux/macOS applicability for some readers, potentially making it feel less relevant if they aren’t NixOS users.
  • Assumed Knowledge: Presumes familiarity with concepts like Git repositories, workspaces (VS Code/Cursor context), and the basic purpose of configuration files without much preamble in the core text.
  • Tool Specificity: The solution is tightly coupled to Cursor’s specific behavior; readers need to extrapolate how the pattern applies to other tools.

AI Opinion

This article offers high value for developers encountering the specific, niche problem of version-controlling Cursor AI’s project-level rules, especially when working with multiple repositories. The explanation of why the common symlink approach fails with Git and the presentation of the “reverse” pattern are clear and insightful for the technically inclined audience. While the NixOS details add depth for users of that OS, the core symlink solution is explained well enough to be useful for Linux and macOS users facing similar configuration challenges with other tools. Its clarity is good for its target audience, though a complete novice might need the introductory context provided separately.

Post #260 of 261 - May 4, 2025