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 jdmorag97-rgb/DDC_Skills_for_AI_Agents_in_Construction --skill xml-readergit clone --depth 1 https://github.com/jdmorag97-rgb/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/jdmorag97-rgb/ddc_skills_for_ai_agents_in_construction/xml-reader)<a href="https://agentmods.dev/skills/jdmorag97-rgb/ddc_skills_for_ai_agents_in_construction/xml-reader"><img src="https://agentmods.dev/badge/skills/jdmorag97-rgb/ddc_skills_for_ai_agents_in_construction/xml-reader/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/jdmorag97-rgb/ddc_skills_for_ai_agents_in_construction/xml-reader"><img src="https://agentmods.dev/badge/skills/jdmorag97-rgb/ddc_skills_for_ai_agents_in_construction/xml-reader.svg" alt="Reviewed on agentmods" width="80" height="20"></a>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.00034 | $0.01874 |
| Opus 5 | $0.00017 | $0.00937 |
| Sonnet 5 | $0.00007 | $0.00375 |
| Haiku 4.5 | $0.00003 | $0.00187 |
Grade A, and why
xml-reader scanned grade A with 0 findings 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 7d 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.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
This is a copy
100% identical to xml-reader — 0 lines differ, which has more behind it and is treated as the original. This page carries a canonical link to it rather than competing with it.
How it starts
The opening of the file, as written. The whole thing — 261 lines — stays where its author put it; the contents beside it link to each section on GitHub.
XML Reader for Construction Data
Overview
XML is used in construction for P6 schedules (XER), IFC-XML, COBie-XML, and buildingSMART Data Dictionary exports. This skill parses XML and converts to structured DataFrames.
Python Implementation
import xml.etree.ElementTree as ET
import pandas as pd
from typing import Dict, Any, List, Optional, Union
from dataclasses import dataclass
from pathlib import Path
import re
@dataclass
class XMLElement:
"""Parsed XML element."""
tag: str
attributes: Dict[str, str]
text: Optional[str]
children: List['XMLElement']
class ConstructionXMLReader:
"""Parse XML from construction systems."""
def __init__(self):
self.namespaces: Dict[str, str] = {}
def parse_file(self, file_path: str) -> ET.Element:
"""Parse XML file and return root element."""
tree = ET.parse(file_path)
root = tree.getroot()
# Extract namespaces
self._extract_namespaces(root)
return root
def parse_string(self, xml_string: str) -> ET.Element:
"""Parse XML from string."""
root = ET.fromstring(xml_string)
self._extract_namespaces(root)
return root
def _extract_namespaces(self, root: ET.Element):
"""Extract namespace mappings."""
# Find namespace declarations
for attr, value in root.attrib.items():
if attr.startswith('{'):
ns = attr[1:attr.index('}')]
self.namespaces[root.tag.split('}')[0][1:]] = ns
def find_elements(self, root: ET.Element,
tag: str,
namespace: str = None) -> List[ET.Element]:
"""Find all elements with given tag."""
if namespace:
tag = f"{{{namespace}}}{tag}"
return root.findall(f".//{tag}")
def element_to_dict(self, element: ET.Element,
include_children: bool = True) -> Dict[str, Any]:
"""Convert element to dictionary."""
result = {
'_tag': element.tag.split('}')[-1] if '}' in element.tag else element.tag,
'_text': element.text.strip() if element.text else None,
**element.attrib
}
if include_children:
for child in element:
child_tag = child.tag.split('}')[-1] if '}' in child.tag else child.tag
if child_tag in result:
# Multiple children with same tag - make list
if not isinstance(result[child_tag], list):
result[child_tag] = [result[child_tag]]
result[child_tag].append(self.element_to_dict(child))
else:
result[child_tag] = self.element_to_dict(child)
return result
def elements_to_dataframe(self, elements: List[ET.Element]) -> pd.DataFrame:
"""Convert list of elements to DataFrame."""
records = []
for elem in elements:
record = {'_tag': elem.tag.split('}')[-1]}
record.update(elem.attrib)
# Get direct text content
if elem.text and elem.text.strip():
record['_text'] = elem.text.strip()
# Get child values
for child in elem:
child_tag = child.tag.split('}')[-1]
if child.text and child.text.strip():
record[child_tag] = child.text.strip()
# Also get child attributes
for attr, val in child.attrib.items():
record[f"{child_tag}_{attr}"] = val
records.append(record)
return pd.DataFrame(records)
def flatten_xml(self, root: ET.Element,
target_tag: str = None) -> pd.DataFrame:
"""Flatten XML to DataFrame."""
if target_tag:
elements = self.find_elements(root, target_tag)
else:
elements = list(root)
return self.elements_to_dataframe(elements)
class P6XMLReader(ConstructionXMLReader):
"""Reader for Primavera P6 XML exports."""
def parse_activities(self, root: ET.Element) -> pd.DataFrame:
"""Parse activities from P6 XML."""
activities = self.find_elements(root, 'Activity')
return self.elements_to_dataframe(activities)
def parse_resources(self, root: ET.Element) -> pd.DataFrame:
"""Parse resources from P6 XML."""
resources = self.find_elements(root, 'Resource')
return self.elements_to_dataframe(resources)
def parse_wbs(self, root: ET.Element) -> pd.DataFrame:
"""Parse WBS from P6 XML."""
wbs = self.find_elements(root, 'WBS')
return self.elements_to_dataframe(wbs)
def parse_full_schedule(self, file_path: str) -> Dict[str, pd.DataFrame]:
"""Parse complete P6 schedule."""
root = self.parse_file(file_path)
return {
'activities': self.parse_activities(root),
'resources': self.parse_resources(root),
'wbs': self.parse_wbs(root)
}
class IFCXMLReader(ConstructionXMLReader):
"""Reader for IFC-XML files."""
def parse_entities(self, root: ET.Element) -> pd.DataFrame:
"""Parse IFC entities."""
# Find all Ifc* elements
all_entities = []
for elem in root.iter():
if elem.tag.startswith('Ifc'):
all_entities.append(elem)
return self.elements_to_dataframe(all_entities)
def get_entity_types(self, root: ET.Element) -> Dict[str, int]:
"""Count entity types."""
counts = {}
for elem in root.iter():
tag = elem.tag
if tag.startswith('Ifc'):
counts[tag] = counts.get(tag, 0) + 1
return counts
class COBieXMLReader(ConstructionXMLReader):
"""Reader for COBie XML files."""
COBIE_SHEETS = ['Facility', 'Floor', 'Space', 'Zone', 'Type',
'Component', 'System', 'Assembly', 'Connection',
'Spare', 'Resource', 'Job', 'Document', 'Attribute']
def parse_cobie(self, file_path: str) -> Dict[str, pd.DataFrame]:
"""Parse all COBie sheets."""
root = self.parse_file(file_path)
result = {}
for sheet in self.COBIE_SHEETS:
elements = self.find_elements(root, sheet)
if elements:
result[sheet] = self.elements_to_dataframe(elements)
return result
class BSDDXMLReader(ConstructionXMLReader):
"""Reader for buildingSMART Data Dictionary exports."""
def parse_classifications(self, root: ET.Element) -> pd.DataFrame:
"""Parse classification items."""
items = self.find_elements(root, 'Classification')
return self.elements_to_dataframe(items)
def parse_properties(self, root: ET.Element) -> pd.DataFrame:
"""Parse property definitions."""
props = self.find_elements(root, 'Property')
return self.elements_to_dataframe(props)
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.
- 7d ago First seen · 261 lines · 34 tokens per session scan A b397c69766f9
xml-reader is a skill published in the GitHub repository jdmorag97-rgb/DDC_Skills_for_AI_Agents_in_Construction (2 stars, last pushed 6mo ago), licensed MIT. It adds 34 tokens to every session and 1,874 once invoked, about $0.0002 per session on Opus 5. A static security scan graded it A with 0 findings. It is 100% identical to xml-reader, differing in 0 lines, and is treated as a copy.
Other skills, from other repositories
create-pr
Creates a GitHub PR with a Linear-ticket-prefixed title and a decision-led, narrative description for prisma-next. Use when the user wants to create a pull request, open a PR, or submit changes for review.
schema-exploration
Lists tables, describes columns and data types, identifies foreign key relationships, and maps entity relationships in a database. Use when the user asks about database schema, table structure, column types, what tables exist, ERD, foreign keys, or how entities relate.
ha-data-stores
Map of Hope Agent's local data stores and safe read-only query workflow. Use when the user asks where Hope Agent stores data, wants to inspect sessions/messages/memory/logs/background jobs/knowledge indexes/settings, asks the model to query local app data, or debugging requires checking persisted state. Trigger…
supabase
Supabase / PostgREST Row-Level-Security playbook — pull the anon (or leaked servicerole) key out of the frontend JS, map tables from the auto-generated OpenAPI spec, test anonymous RLS READ disclosures (PII/secret leaks), and anonymous RLS WRITE abuse (insert/update/delete — e.g. forging…
nornicdb-cypher-queries
Pick fast, predictable Cypher query shapes in NornicDB — point lookups, batch retrieval, pagination, search, traversal, batched UNWIND/MERGE writes, cleanup, multi-tenant isolation. Use when writing or reviewing Cypher whose latency or throughput matters; maps user intent to the executor's hot-path query templates.
dsql
Build with Aurora DSQL — manage schemas, execute queries, handle migrations, diagnose query plans, diagnose cluster performance, load data, and develop applications with a serverless, distributed SQL database. Covers IAM auth, multi-tenant patterns, MySQL-to-DSQL and PostgreSQL-to-DSQL schema conversion, foreign key…