Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx skills add datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction --skill excel-to-rvtgit clone --depth 1 https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_ConstructionWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/excel-to-rvt)<a href="https://agentmods.dev/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/excel-to-rvt"><img src="https://agentmods.dev/badge/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/excel-to-rvt/github.svg" alt="Measured on agentmods" height="20"></a>Or the 80×15 button, for a site that already has a row of RSS and ATOM ones. Only the verdict fits; the numbers stay here.
<a href="https://agentmods.dev/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/excel-to-rvt"><img src="https://agentmods.dev/badge/skills/datadrivenconstruction/ddc_skills_for_ai_agents_in_construction/excel-to-rvt.svg" alt="Reviewed on agentmods" width="80" height="20"></a>- NVIDIA SkillSpector pass
What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5.1 | $0.00026 | $0.02628 |
| Opus 5 | $0.00013 | $0.01314 |
| Sonnet 5 | $0.00005 | $0.00526 |
| Haiku 4.5 | $0.00003 | $0.00263 |
Grade A, and why
excel-to-rvt scanned grade A with 1 finding against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 12d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Runs shell commandslowCapability
Expected in a hook, worth knowing in a rule or an instructions file.
result = subprocess.run(cmd, capture_output=True, text=True) Copies of this mod
1 near-identical copy found in the catalogue:
- excel-to-rvt — 100% identical, 0 lines differ
How it starts
The opening of the file, as written. The whole thing — 417 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Excel to RVT Import
Note: RVT is the file format. Examples may reference Autodesk® Revit® APIs. Autodesk and Revit are registered trademarks of Autodesk, Inc.
Business Case
Problem Statement
External data (costs, specifications, classifications) lives in Excel but needs to update Revit:
- Cost estimates need to link to model elements
- Classification codes need assignment
- Custom parameters need population
- Manual entry is slow and error-prone
Solution
Automated import of Excel data into Revit using the DDC ImportExcelToRevit tool and Dynamo workflows.
Business Value
- Automation - Batch update thousands of parameters
- Accuracy - Eliminate manual data entry errors
- Sync - Keep external data in sync with model
- Flexibility - Update any writable parameter
Technical Implementation
Methods
- ImportExcelToRevit CLI - Direct command-line update
- Dynamo Script - Visual programming approach
- Revit API - Full programmatic control
ImportExcelToRevit CLI
ImportExcelToRevit.exe <model.rvt> <data.xlsx> [options]
| Option | Description |
|---|---|
-sheet |
Excel sheet name |
-idcol |
Element ID column |
-mapping |
Parameter mapping file |
Python Implementation
import subprocess
import pandas as pd
from pathlib import Path
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass
import json
@dataclass
class ImportResult:
"""Result of Excel import to Revit."""
elements_processed: int
elements_updated: int
elements_failed: int
parameters_updated: int
errors: List[str]
class ExcelToRevitImporter:
"""Import Excel data into Revit models."""
def __init__(self, tool_path: str = "ImportExcelToRevit.exe"):
self.tool_path = Path(tool_path)
def import_data(self, revit_file: str,
excel_file: str,
sheet_name: str = "Elements",
id_column: str = "ElementId",
parameter_mapping: Dict[str, str] = None) -> ImportResult:
"""Import Excel data into Revit."""
# Build command
cmd = [
str(self.tool_path),
revit_file,
excel_file,
"-sheet", sheet_name,
"-idcol", id_column
]
# Add mapping file if provided
if parameter_mapping:
mapping_file = self._create_mapping_file(parameter_mapping)
cmd.extend(["-mapping", mapping_file])
# Execute
result = subprocess.run(cmd, capture_output=True, text=True)
# Parse result (format depends on tool)
return self._parse_result(result)
def _create_mapping_file(self, mapping: Dict[str, str]) -> str:
"""Create temporary mapping file."""
mapping_path = Path("temp_mapping.json")
with open(mapping_path, 'w') as f:
json.dump(mapping, f)
return str(mapping_path)
def _parse_result(self, result: subprocess.CompletedProcess) -> ImportResult:
"""Parse CLI result."""
# This is placeholder - actual parsing depends on tool output
if result.returncode == 0:
return ImportResult(
elements_processed=0,
elements_updated=0,
elements_failed=0,
parameters_updated=0,
errors=[]
)
else:
return ImportResult(
elements_processed=0,
elements_updated=0,
elements_failed=0,
parameters_updated=0,
errors=[result.stderr]
)
class DynamoScriptGenerator:
"""Generate Dynamo scripts for Revit data import."""
def generate_parameter_update_script(self,
mappings: Dict[str, str],
excel_path: str,
output_path: str) -> str:
"""Generate Dynamo Python script for parameter updates."""
mappings_json = json.dumps(mappings)
script = f'''
# Dynamo Python Script - Excel to Revit Parameter Update
# Generated by DDC
import clr
import sys
sys.path.append(r'C:\\Program Files (x86)\\IronPython 2.7\\Lib')
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
clr.AddReference('Microsoft.Office.Interop.Excel')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
import Microsoft.Office.Interop.Excel as Excel
# Configuration
excel_path = r'{excel_path}'
mappings = {mappings_json}
# Open Excel
excel_app = Excel.ApplicationClass()
excel_app.Visible = False
workbook = excel_app.Workbooks.Open(excel_path)
worksheet = workbook.Worksheets[1]
# Get Revit document
doc = DocumentManager.Instance.CurrentDBDocument
# Read Excel data
used_range = worksheet.UsedRange
rows = used_range.Rows.Count
cols = used_range.Columns.Count
# Find column indices
headers = {{}}
for col in range(1, cols + 1):
header = str(worksheet.Cells[1, col].Value2 or '')
headers[header] = col
# Process rows
TransactionManager.Instance.EnsureInTransaction(doc)
updated_count = 0
error_count = 0
for row in range(2, rows + 1):
try:
# Get element ID
element_id_col = headers.get('ElementId', 1)
element_id = int(worksheet.Cells[row, element_id_col].Value2 or 0)
element = doc.GetElement(ElementId(element_id))
if not element:
continue
# Update mapped parameters
for excel_col, revit_param in mappings.items():
if excel_col in headers:
col_idx = headers[excel_col]
value = worksheet.Cells[row, col_idx].Value2
if value is not None:
param = element.LookupParameter(revit_param)
if param and not param.IsReadOnly:
if param.StorageType == StorageType.Double:
param.Set(float(value))
elif param.StorageType == StorageType.Integer:
param.Set(int(value))
elif param.StorageType == StorageType.String:
param.Set(str(value))
updated_count += 1
except Exception as e:
error_count += 1
TransactionManager.Instance.TransactionTaskDone()
# Cleanup
workbook.Close(False)
excel_app.Quit()
OUT = f"Updated: {{updated_count}}, Errors: {{error_count}}"
'''
with open(output_path, 'w') as f:
f.write(script)
return output_path
def generate_schedule_creator(self,
schedule_name: str,
category: str,
fields: List[str],
output_path: str) -> str:
"""Generate script to create Revit schedule from Excel structure."""
fields_json = json.dumps(fields)
script = f'''
# Dynamo Python Script - Create Schedule
# Generated by DDC
import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
doc = DocumentManager.Instance.CurrentDBDocument
fields = {fields_json}
# Get category
category = Category.GetCategory(doc, BuiltInCategory.OST_{category})
TransactionManager.Instance.EnsureInTransaction(doc)
# Create schedule
schedule = ViewSchedule.CreateSchedule(doc, category.Id)
schedule.Name = "{schedule_name}"
# Add fields
definition = schedule.Definition
for field_name in fields:
# Find schedulable field
for sf in definition.GetSchedulableFields():
if sf.GetName(doc) == field_name:
definition.AddField(sf)
break
TransactionManager.Instance.TransactionTaskDone()
OUT = schedule
'''
with open(output_path, 'w') as f:
f.write(script)
return output_path
class ExcelDataValidator:
"""Validate Excel data before Revit import."""
def __init__(self, revit_elements: pd.DataFrame):
"""Initialize with exported Revit elements."""
self.revit_data = revit_elements
self.valid_ids = set(revit_elements['ElementId'].astype(str).tolist())
def validate_import_data(self, import_df: pd.DataFrame,
id_column: str = 'ElementId') -> Dict[str, Any]:
"""Validate import data against Revit export."""
results = {
'valid': True,
'total_rows': len(import_df),
'matching_ids': 0,
'missing_ids': [],
'invalid_ids': [],
'warnings': []
}
import_ids = import_df[id_column].astype(str).tolist()
for import_id in import_ids:
if import_id in self.valid_ids:
results['matching_ids'] += 1
else:
results['invalid_ids'].append(import_id)
if results['invalid_ids']:
results['valid'] = False
results['warnings'].append(
f"{len(results['invalid_ids'])} element IDs not found in Revit model"
)
results['match_rate'] = round(
results['matching_ids'] / results['total_rows'] * 100, 1
) if results['total_rows'] > 0 else 0
return results
def check_parameter_types(self, import_df: pd.DataFrame,
type_definitions: Dict[str, str]) -> List[str]:
"""Check if values match expected parameter types."""
errors = []
for column, expected_type in type_definitions.items():
if column not in import_df.columns:
continue
for idx, value in import_df[column].items():
if pd.isna(value):
continue
if expected_type == 'number':
try:
float(value)
except ValueError:
errors.append(f"Row {idx}: '{column}' should be number, got '{value}'")
elif expected_type == 'integer':
try:
int(value)
except ValueError:
errors.append(f"Row {idx}: '{column}' should be integer, got '{value}'")
return errors
What ships with it
2 files beside SKILL.md in the same directory: the scripts, references and assets a skill reads on demand. Not counted in the per-session cost; read them before you install if any of them is executable.
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 12d ago First seen · 417 lines · 26 tokens per session scan A 4f941dca0adc
excel-to-rvt is a skill published in the GitHub repository datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction (310 stars, last pushed 21d ago), licensed MIT. It adds 26 tokens to every session and 2,628 once invoked, about $0.0001 per session on Opus 5. A static security scan graded it A with 1 finding (runs shell commands). No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other skills, from other repositories
google-apps-script
Build Google Apps Script automation for Sheets and Workspace. Custom menus, triggers (onEdit / time-driven / form submit), dialogs, sidebars, email batches, PDF export, external API. Use whenever the user wants to automate a Google Sheet, build a Sheets menu / sidebar / dialog, hit a Sheets row from email or a…
search
This skill should be used when the user wants to check if a company or job URL is already in their tracker, or list all applications for a company. Triggers on phrases like "have I applied to [company]", "is [company] in tracker", "check [url]", "already applied [url]", "search [company]", "what jobs do I have at…
update-hr
This skill should be used when the user wants to find, add, or update HR contacts (recruiters, talent acquisition, hiring managers) for a company in the Excel job tracker. Triggers on phrases like "find HR for [company]", "add recruiter for [company]", "search LinkedIn for [company] recruiter", "update HR contacts"…
mark-rejected
This skill should be used when the user wants to mark a company as rejected in their Excel job tracker (List.xlsx). Triggers on phrases like "mark [company] as rejected", "reject [company] in the tracker", "[company] rejected me", "strikethrough [company]", "update [company] to rejected", "I got rejected by…
new-job
This skill should be used when the user wants to add a new company or job to their Excel tracker (List.xlsx). Triggers on phrases like "add [company] to tracker", "I applied to [company]", "track [company]", "save [company] for later", "add [company] [role]", "new application at [company]".
thinkcell
Generate, update, and automate think-cell charts and elements in PowerPoint and Excel. Use ANY time the user mentions think-cell, thinkcell, or .ppttc files, or asks to create/update PowerPoint charts following think-cell conventions (waterfall, Mekko, stacked column, Gantt, Harvey ball, scatter/bubble, etc.) …