TL;DR: This batch script simplifies Dynamo package management by creating a file-based map that reveals exactly which Dynamo version and package folder each installed Revit version is using.
If you’ve ever opened the Dynamo Packages folder and thought:
“Which one of these is Revit actually using?”
…you’re not alone.
When you have multiple versions of Revit installed, you also have multiple versions of Dynamo - and that usually means multiple package folders. If you install or update packages in the wrong place, best case nothing happens. Worst case, graphs break in ways that are really hard to diagnose.
So the real question becomes:
Which Dynamo package folder belongs to which Revit version?
Why This Is a Problem
Here’s what makes this confusing:
- Revit is versioned by year (2022, 2023, 2024…)
- Dynamo is versioned independently (2.x, 3.x…)
- Each Dynamo version uses its own package folder
- Revit silently decides which Dynamo version (and package folder) gets used
So when you install a package, you’re often guessing:
- Is this for Revit 2023 or 2024?
- Which Dynamo version is actually loading these packages?
- Why does this graph work in one Revit version but not another?
Most of the time, the problem isn’t the graph - it’s the package folder.
The Key Insight: Revit → Dynamo → Package Folder
Revit doesn’t load packages directly.
Instead, the chain looks like this:
Revit Version
↓
Dynamo Version
↓
Dynamo Package Folder
If you don’t know the Dynamo version Revit is using, you don’t really know which package folder matters.
Enter MagicallyMapDynamoVersions.bat
This batch file exists to answer one very practical question:
“Which Dynamo version - and therefore which package folder - does this Revit version use?”
This magic little batch file lives here:
C:\Users<your-user>\AppData\Roaming\Dynamo\Dynamo Revit\MagicallyMapDynamoVersions.bat
When you run MagicallyMapDynamoVersions.bat, it creates a set of empty files whose names encode the mapping between Revit and Dynamo.
@echo off
powershell.exe -ExecutionPolicy Bypass -NoProfile -Command "$scriptPath = Split-Path -Parent '%~f0'; Get-Content '%~f0' | Select-Object -Skip 2 | Out-File -Encoding UTF8 '%TEMP%\MagicallyMapDynamoVersions.ps1'; $content = Get-Content '%TEMP%\MagicallyMapDynamoVersions.ps1' -Raw; $content = $content -replace '\$scriptPath = Split-Path -Parent \$MyInvocation\.MyCommand\.Path', \"`$scriptPath = '$scriptPath'\"; $content | Out-File -Encoding UTF8 '%TEMP%\MagicallyMapDynamoVersions.ps1' -NoNewline; & '%TEMP%\MagicallyMapDynamoVersions.ps1'; Remove-Item '%TEMP%\MagicallyMapDynamoVersions.ps1'"
goto :eof
$mappingUrl = "https://raw.githubusercontent.com/DynamoDS/DynamoPrimerNew/refs/heads/master/a_appendix/host-integration-map.md"
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
Set-Location $scriptPath
# Delete existing mapping files (files containing the arrow character or with .. extension)
Write-Host "Deleting existing mapping files..." -ForegroundColor Yellow
$magicExt = ".."
Get-ChildItem -Path $scriptPath -File | Where-Object { $_.Name -match "➡️|→|->" -or $_.Name -like "*$magicExt" } | Remove-Item -Force
Write-Host "Existing mapping files deleted." -ForegroundColor Green
# Get all folders in the current directory (excluding files)
$existingFolders = Get-ChildItem -Path $scriptPath -Directory | Select-Object -ExpandProperty Name
Write-Host "Found folders: $($existingFolders -join ', ')" -ForegroundColor Gray
# Download the markdown integration map
Write-Host "Downloading integration map from $mappingUrl..." -ForegroundColor Yellow
try {
$markdownContent = Invoke-RestMethod -Uri $mappingUrl -Method Get
Write-Host "Markdown downloaded successfully." -ForegroundColor Green
} catch {
Write-Host "Error downloading markdown: $_" -ForegroundColor Red
exit 1
}
# Parse the markdown to extract Revit -> Dynamo mappings
# Find the Revit section and parse the table
$revitSection = $false
$dynamoToRevitMap = @{}
foreach ($line in $markdownContent -split "`n") {
# Check if we're in the Revit section
if ($line -match "^### Revit") {
$revitSection = $true
continue
}
# Stop at next section
if ($revitSection -and $line -match "^### ") {
break
}
# Parse table rows (skip header and separator lines)
if ($revitSection -and $line -match "^\|" -and $line -notmatch "^\|\s*---") {
# Extract Revit version and Dynamo version from table row
# Format: | 2026.3 | 3.6.0.9395 |
if ($line -match "\|\s*([^\|]+)\s*\|\s*([^\|]+)\s*\|") {
$revitVersion = $matches[1].Trim()
$dynamoFullVersion = $matches[2].Trim()
# Extract major.minor version from Dynamo (e.g., "3.6" from "3.6.0.9395")
if ($dynamoFullVersion -match "^(\d+\.\d+)") {
$dynamoMajorMinor = $matches[1]
# Add Revit version to the list for this Dynamo version
if (-not $dynamoToRevitMap.ContainsKey($dynamoMajorMinor)) {
$dynamoToRevitMap[$dynamoMajorMinor] = @()
}
$dynamoToRevitMap[$dynamoMajorMinor] += $revitVersion
}
}
}
}
# Process each Dynamo version (only if corresponding folder exists)
Write-Host "Creating mapping files..." -ForegroundColor Yellow
foreach ($dynamoVersion in $dynamoToRevitMap.Keys | Sort-Object) {
# Only create mapping file if folder exists for this Dynamo version
if ($existingFolders -notcontains $dynamoVersion) {
Write-Host "Skipping $dynamoVersion (no folder found)" -ForegroundColor DarkGray
continue
}
$revitVersions = $dynamoToRevitMap[$dynamoVersion]
# Group and order: remove duplicates, then sort (newest first)
# Custom sort to handle version numbers properly (e.g., 2026.3 > 2026.2 > 2026.1 > 2026)
$sortedRevitVersions = $revitVersions | Select-Object -Unique | Sort-Object {
# Parse version for proper sorting (e.g., "2026.3" -> [2026, 3])
$parts = $_ -split '\.'
[int]$major = $parts[0]
[int]$minor = if ($parts.Count -gt 1) { [int]$parts[1] } else { 0 }
[int]$patch = if ($parts.Count -gt 2) { [int]$parts[2] } else { 0 }
# Return sortable value (newer versions have higher values)
$major * 10000 + $minor * 100 + $patch
} -Descending
# Join multiple Revit versions with comma and space (e.g., "2026.3, 2026.2, 2026.1")
$revitVersionsString = $sortedRevitVersions -join ", "
# Create filename: "DynamoVersion → RevitVersions.." (comma-separated if multiple)
# Using → (U+2192) instead of emoji for better filename compatibility
$arrow = [char]0x2192 # Rightwards arrow (→)
$magicExt = ".."
$fileName = "$dynamoVersion $arrow $revitVersionsString$magicExt"
$filePath = Join-Path $scriptPath $fileName
# Create empty file with .. extension
New-Item -Path $filePath -ItemType File -Force | Out-Null
Write-Host "Created: $fileName" -ForegroundColor Cyan
}
Write-Host "`nMapping files created successfully!" -ForegroundColor Green
No registry hacks.
No DLL inspection.
No guessing.
Just filenames.
Why This Helps With Packages
Once you know the Dynamo version tied to a Revit install, the package story becomes much clearer.
For example:
- Revit 2023 → Dynamo 2.x →
Dynamo\2.x\packages - Revit 2024 → Dynamo 3.x →
Dynamo\3.x\packages
Now you know:
- Where to install packages
- Which package versions are safe to use
- Why a package shows up in one Revit version but not another
This is especially useful if you:
- Maintain shared package environments
- Support multiple Revit versions
A File-Based Map Is Enough
The clever part of this approach is that it doesn’t try to be clever.
If a file exists, the mapping exists.
That means:
- Any script can check it
- Any tool can rely on it
- Anyone can inspect it just by browsing a folder
It turns “which package folder should I care about?” into a question with a concrete, verifiable answer.
..john
