Skip to content

Instantly share code, notes, and snippets.

@netgfx
Last active December 22, 2025 09:45
Show Gist options
  • Select an option

  • Save netgfx/82efc279d6d0f14e401d645e284894a2 to your computer and use it in GitHub Desktop.

Select an option

Save netgfx/82efc279d6d0f14e401d645e284894a2 to your computer and use it in GitHub Desktop.
context tunnel - powershell
<#
.SYNOPSIS
Tunnel Manager for AI Context (Windows Version)
.DESCRIPTION
Creates or removes symbolic links to external folders to allow AI Agents
to access them as local context.
.EXAMPLE
.\tunnel.ps1 link "C:\Projects\ReferenceRepo" "_TEMPLATE"
.\tunnel.ps1 unlink "_TEMPLATE"
#>
param (
[Parameter(Mandatory=$true, Position=0)]
[ValidateSet("link", "unlink")]
[string]$Command,
[Parameter(Position=1)]
[string]$SourcePath,
[Parameter(Position=2)]
[string]$TargetName = "TEMPLATE"
)
# --- Helper for Colors ---
function Log-Info ($msg) { Write-Host "$msg" -ForegroundColor Green }
function Log-Warn ($msg) { Write-Host "$msg" -ForegroundColor Yellow }
function Log-Error ($msg) { Write-Host "$msg" -ForegroundColor Red }
# --- LINK COMMAND ---
if ($Command -eq "link") {
if ([string]::IsNullOrWhiteSpace($SourcePath)) {
Log-Error "Missing source path. Usage: .\tunnel.ps1 link <path> [name]"
exit 1
}
# 1. Validate Source
if (-not (Test-Path -Path $SourcePath)) {
Log-Error "Source directory does not exist: $SourcePath"
exit 1
}
# Get Absolute Path to ensure the link works reliably
$AbsSourcePath = (Resolve-Path $SourcePath).Path
# Check for circular links
$SourceItem = Get-Item -Path $AbsSourcePath -ErrorAction SilentlyContinue
if ($SourceItem.LinkType -eq "SymbolicLink" -or $SourceItem.Attributes -match "ReparsePoint") {
try {
$ResolvedPath = $SourceItem.Target
$CurrentDir = (Get-Location).Path
if ($ResolvedPath -match [regex]::Escape($CurrentDir) -or $ResolvedPath -match [regex]::Escape($TargetName)) {
Log-Error "Creating this link would form a circular dependency."
exit 1
}
}
catch {
Log-Error "Could not resolve link target."
exit 1
}
}
# 2. Check if Target already exists
if (Test-Path -Path $TargetName) {
Log-Error "A file or folder named '$TargetName' already exists here."
exit 1
}
# 3. Create Symlink
try {
New-Item -ItemType SymbolicLink -Path ".\$TargetName" -Target $AbsSourcePath | Out-Null
Log-Info "Linked '$TargetName' -> '$AbsSourcePath'"
}
catch {
Log-Error "Failed to create symlink. Ensure you are running as Administrator or have Developer Mode enabled."
Write-Host $_
exit 1
}
# 4. Handle .gitignore
$GitIgnoreFile = ".gitignore"
if (-not (Test-Path $GitIgnoreFile)) {
New-Item $GitIgnoreFile -ItemType File | Out-Null
Log-Warn "Created .gitignore file."
}
# Check if entry exists
$Content = Get-Content $GitIgnoreFile -ErrorAction SilentlyContinue
if ($Content -contains $TargetName) {
Log-Info "'$TargetName' is already in .gitignore"
}
else {
Add-Content -Path $GitIgnoreFile -Value "$TargetName"
Log-Info "Added '$TargetName' to .gitignore"
}
}
# --- UNLINK COMMAND ---
elseif ($Command -eq "unlink") {
# If user ran "unlink", SourcePath might be empty, so we check if they passed a name in pos 1 or 2
# Logic: if user typed "unlink MyLink", $SourcePath holds "MyLink" because of position.
if (-not [string]::IsNullOrWhiteSpace($SourcePath)) {
$TargetName = $SourcePath
}
# 1. Validate it exists
if (-not (Test-Path -Path $TargetName)) {
Log-Error "'$TargetName' not found."
exit 1
}
# 2. Validate it is a Symlink/ReparsePoint (Safety Check)
$Item = Get-Item -Path $TargetName
if ($Item.LinkType -ne "SymbolicLink" -and $Item.Attributes -notmatch "ReparsePoint") {
Log-Error "CRITICAL: '$TargetName' is a real directory, not a link!"
Log-Error "Aborting to prevent data loss."
exit 1
}
# 3. Remove the link
try {
Remove-Item -Path $TargetName -Force
Log-Info "Unlinked '$TargetName'"
}
catch {
Log-Error "Failed to remove link."
Write-Host $_
exit 1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment