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 nezolder/civil3d-mcp-roslyn --skill create_alignment_from_pisgit clone --depth 1 https://github.com/nezolder/civil3d-mcp-roslynWrote 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/nezolder/civil3d-mcp-roslyn/create_alignment_from_pis)<a href="https://agentmods.dev/skills/nezolder/civil3d-mcp-roslyn/create_alignment_from_pis"><img src="https://agentmods.dev/badge/skills/nezolder/civil3d-mcp-roslyn/create_alignment_from_pis/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/nezolder/civil3d-mcp-roslyn/create_alignment_from_pis"><img src="https://agentmods.dev/badge/skills/nezolder/civil3d-mcp-roslyn/create_alignment_from_pis.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.03362 |
| Opus 5.5 | $0.00014 | $0.01345 |
| Sonnet 5.5 | $0.00007 | $0.00672 |
| Haiku 4.5 | $0.00003 | $0.00336 |
Grade A, and why
create_alignment_from_pis 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 19d 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.
How it starts
The opening of the file, as written. The whole thing — 262 lines — stays where its author put it; the contents beside it link to each section on GitHub.
Code Template
var alignmentName = "NEW_ALIGNMENT_NAME";
var alignmentStyleName = "ALIGNMENT_STYLE_NAME";
var alignmentLabelSetName = "ALIGNMENT_LABEL_SET_NAME";
var pis = new (double x, double y, double radius, double spiralIn, double spiralOut)[] { };
var startStation = 0.0;
var layerName = "";
var tolerance = 1e-6;
var isName = new Func<string, bool>(value =>
!string.IsNullOrWhiteSpace(value) && value == value.Trim() && value.Length <= 255
&& !value.Any(char.IsControl));
if (!isName(alignmentName) || alignmentName == "NEW_ALIGNMENT_NAME"
|| !isName(alignmentStyleName) || alignmentStyleName == "ALIGNMENT_STYLE_NAME"
|| !isName(alignmentLabelSetName) || alignmentLabelSetName == "ALIGNMENT_LABEL_SET_NAME")
{
return new { success = false, error = "Configure the new alignment name and existing style and label-set names." };
}
if (layerName != "" && !isName(layerName))
{
return new { success = false, error = "layerName must be empty or a trimmed layer name without control characters." };
}
if (!double.IsFinite(startStation) || Math.Abs(startStation) > 1e9)
{
return new { success = false, error = "startStation must be finite." };
}
if (pis == null || pis.Length < 2 || pis.Length > 200)
{
return new { success = false, error = "pis must contain 2 to 200 rows." };
}
// Per-PI checks: finite values, curve parameters only at interior PIs.
for (var i = 0; i < pis.Length; i++)
{
var pi = pis[i];
if (!double.IsFinite(pi.x) || !double.IsFinite(pi.y) || !double.IsFinite(pi.radius)
|| !double.IsFinite(pi.spiralIn) || !double.IsFinite(pi.spiralOut))
return new { success = false, error = $"PI {i}: all values must be finite." };
if (pi.radius < 0 || pi.radius > 1e6 || pi.spiralIn < 0 || pi.spiralOut < 0 || pi.spiralIn > 1e4 || pi.spiralOut > 1e4)
return new { success = false, error = $"PI {i}: radius must be 0..1e6 and spiral lengths 0..1e4." };
var interior = i > 0 && i < pis.Length - 1;
if (!interior && (pi.radius != 0 || pi.spiralIn != 0 || pi.spiralOut != 0))
return new { success = false, error = $"PI {i}: the start and end point take radius and spiral lengths of 0." };
if (pi.radius == 0 && (pi.spiralIn != 0 || pi.spiralOut != 0))
return new { success = false, error = $"PI {i}: spirals need a radius greater than 0." };
if ((pi.spiralIn > 0) != (pi.spiralOut > 0))
return new { success = false, error = $"PI {i}: give both spiral lengths (clothoid-arc-clothoid) or neither (simple arc)." };
if (i > 0 && Math.Sqrt(Math.Pow(pi.x - pis[i - 1].x, 2) + Math.Pow(pi.y - pis[i - 1].y, 2)) <= tolerance)
return new { success = false, error = $"PI {i} coincides with PI {i - 1}." };
}
// Tangent lengths each curve takes from its two neighbouring segments.
// Clothoid shift p and offset k use the usual series approximations.
double SpiralShift(double length, double radius) =>
length * length / (24 * radius) - Math.Pow(length, 4) / (2688 * Math.Pow(radius, 3));
double SpiralOffset(double length, double radius) =>
length / 2 - Math.Pow(length, 3) / (240 * radius * radius);
var segmentLengths = new double[pis.Length - 1];
for (var i = 0; i < segmentLengths.Length; i++)
segmentLengths[i] = Math.Sqrt(Math.Pow(pis[i + 1].x - pis[i].x, 2) + Math.Pow(pis[i + 1].y - pis[i].y, 2));
var tangentIn = new double[pis.Length];
var tangentOut = new double[pis.Length];
var deflections = new double[pis.Length];
var turnsLeft = new bool[pis.Length];
for (var i = 1; i < pis.Length - 1; i++)
{
double ax = pis[i].x - pis[i - 1].x, ay = pis[i].y - pis[i - 1].y;
double bx = pis[i + 1].x - pis[i].x, by = pis[i + 1].y - pis[i].y;
var cross = ax * by - ay * bx;
var delta = Math.Atan2(Math.Abs(cross), ax * bx + ay * by);
deflections[i] = delta;
turnsLeft[i] = cross > 0;
var pi = pis[i];
if (pi.radius == 0) continue;
if (delta < 1e-6 || delta > Math.PI - 1e-6)
return new { success = false, error = $"PI {i}: the deflection angle must be between 0 and 180 degrees to hold a curve.", deflectionDeg = delta * 180 / Math.PI };
if (pi.spiralIn == 0)
{
tangentIn[i] = tangentOut[i] = pi.radius * Math.Tan(delta / 2);
continue;
}
var spiralAngle = (pi.spiralIn + pi.spiralOut) / (2 * pi.radius);
if (spiralAngle >= delta)
return new { success = false, error = $"PI {i}: the spirals turn more than the deflection angle; shorten them or increase the radius.", deflectionDeg = delta * 180 / Math.PI, spiralDeg = spiralAngle * 180 / Math.PI };
double p1 = SpiralShift(pi.spiralIn, pi.radius), p2 = SpiralShift(pi.spiralOut, pi.radius);
tangentIn[i] = (pi.radius + p2) / Math.Sin(delta) - (pi.radius + p1) / Math.Tan(delta) + SpiralOffset(pi.spiralIn, pi.radius);
tangentOut[i] = (pi.radius + p1) / Math.Sin(delta) - (pi.radius + p2) / Math.Tan(delta) + SpiralOffset(pi.spiralOut, pi.radius);
}
for (var i = 0; i < segmentLengths.Length; i++)
{
var needed = tangentOut[i] + tangentIn[i + 1];
if (needed > segmentLengths[i] + 1e-3)
return new {
success = false,
error = $"Segment PI {i} - PI {i + 1} is too short for its curves; reduce the radius or spiral lengths.",
segmentLength = segmentLengths[i],
neededApprox = needed
};
}
foreach (ObjectId existingId in CivilDoc.GetAlignmentIds())
{
var existing = Transaction.GetObject(existingId, OpenMode.ForRead) as Alignment;
if (existing != null && existing.Name.Equals(alignmentName, StringComparison.OrdinalIgnoreCase))
return new { success = false, error = "An alignment with this name already exists.", alignmentName };
}
ObjectId styleId;
ObjectId labelSetId;
try
{
styleId = CivilDoc.Styles.AlignmentStyles[alignmentStyleName];
labelSetId = CivilDoc.Styles.LabelSetStyles.AlignmentLabelSetStyles[alignmentLabelSetName];
}
catch
{
return new { success = false, error = "The named alignment style or label set does not exist.", alignmentStyleName, alignmentLabelSetName };
}
if (styleId.IsNull || labelSetId.IsNull)
{
return new { success = false, error = "The named alignment style or label set is invalid.", alignmentStyleName, alignmentLabelSetName };
}
var layerId = Database.Clayer;
if (layerName != "")
{
var layers = (LayerTable)Transaction.GetObject(Database.LayerTableId, OpenMode.ForRead);
if (!layers.Has(layerName))
return new { success = false, error = "The named layer does not exist.", layerName };
layerId = layers[layerName];
}
var alignmentId = Alignment.Create(CivilDoc, alignmentName, ObjectId.Null, layerId, styleId, labelSetId);
if (alignmentId.IsNull)
{
throw new InvalidOperationException("Alignment creation returned an invalid object ID.");
}
var alignment = (Alignment)Transaction.GetObject(alignmentId, OpenMode.ForWrite);
// Fixed tangents PI to PI, then a free curve or SCS group between each pair
// of tangents. Any failure throws, so nothing is kept.
var lineIds = new List<int>();
for (var i = 0; i < pis.Length - 1; i++)
{
var line = alignment.Entities.AddFixedLine(new Point3d(pis[i].x, pis[i].y, 0), new Point3d(pis[i + 1].x, pis[i + 1].y, 0));
lineIds.Add(line.EntityId);
}
var piRows = new List<object>();
for (var i = 1; i < pis.Length - 1; i++)
{
var pi = pis[i];
var kind = pi.radius == 0 ? "angle point" : pi.spiralIn == 0 ? "arc" : "spiral-arc-spiral";
try
{
if (pi.radius > 0 && pi.spiralIn == 0)
alignment.Entities.AddFreeCurve(lineIds[i - 1], lineIds[i], pi.radius, CurveParamType.Radius, false, CurveType.Compound);
else if (pi.radius > 0)
alignment.Entities.AddFreeSCS(lineIds[i - 1], lineIds[i], pi.spiralIn, pi.spiralOut, SpiralParamType.Length, pi.radius, false, SpiralType.Clothoid);
}
catch (System.Exception ex)
{
throw new InvalidOperationException($"PI {i}: Civil 3D could not add the {kind}: {ex.Message}", ex);
}
piRows.Add(new {
index = i,
kind,
deflectionDeg = deflections[i] * 180 / Math.PI,
turn = turnsLeft[i] ? "left" : "right",
pi.radius,
pi.spiralIn,
pi.spiralOut
});
}
alignment.ReferencePointStation = startStation;
if (Math.Abs(alignment.StartingStation - startStation) > 1e-6)
{
throw new InvalidOperationException("The alignment start station could not be set.");
}
// Created geometry in station order.
var geometry = new List<object>();
for (var order = 0; order < alignment.Entities.Count; order++)
{
var entity = alignment.Entities.GetEntityByOrder(order);
for (var child = 0; child < entity.SubEntityCount; child++)
{
var sub = entity[child];
geometry.Add(new {
type = sub.SubEntityType.ToString(),
startStation = sub.StartStation,
endStation = sub.EndStation,
length = sub.Length,
radius = sub is AlignmentSubEntityArc arc ? arc.Radius : (double?)null,
spiralA = sub is AlignmentSubEntitySpiral spiral ? spiral.A : (double?)null
});
}
}
return new {
success = true,
name = alignment.Name,
handle = alignment.Handle.ToString(),
style = alignment.StyleName,
layer = alignment.Layer,
startStation = alignment.StartingStation,
endStation = alignment.EndingStation,
length = alignment.Length,
entityCount = alignment.Entities.Count,
pis = piRows,
geometry
};
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.
- 19d ago First seen · 262 lines · 34 tokens per session scan A bfec668c9f96
create_alignment_from_pis is a skill published in the GitHub repository nezolder/civil3d-mcp-roslyn (3 stars, last pushed 16d ago), licensed MIT. It adds 34 tokens to every session and 3,362 once invoked, about $0.0001 per session on Opus 5.5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-10-09.
Other skills, from other repositories
local-ai-agents
Build local-first AI agents that run entirely on a developer workstation with Microsoft Foundry Local and Qwen function-calling models. Covers Small Language Models (SLMs), the OpenAI-compatible local endpoint, sandboxed local tools, local RAG with Chroma, local MCP servers, hybrid cloud/local routing, and the…
systematic-debugging
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes.
next-cache-components-adoption
Turn on Cache Components in a Next.js app and resolve the blocking routes it surfaces. Use when the user wants to enable, adopt, or migrate to Cache Components, flip the cacheComponents flag, work through a flood of blocking-prerender / instant validation errors, run the cache-components-instant-false codemod, or…
chat-pet-sprite-creation
Use when creating or changing VS Code chat pet sprite art, sprite sheets, state animations, eye treatments, Stable/Insiders variants, or pet transitions under src/vs/workbench/contrib/chat/browser/widget/media/chatPet.
cpu-profile-analysis
Analyze V8/Chrome CPU profiles (.cpuprofile) and DevTools trace files (Trace-.json). Use when: profiling performance, investigating slow functions, comparing code paths, finding bottlenecks, analyzing timeToRequest, understanding call trees from sampling profiler data, analyzing layout/paint/rendering, investigating…
insight-error-page
Write or audit an insight-kind error page for the Next.js dev overlay. Use when creating a new errors/ .mdx page, auditing an existing one, or checking that a page matches the framework fix cards. Covers page structure, title alignment, FixCard cards with Copy prompt button, code snippets, terminology verification…