create_sample_lines

create_sample_lines is a skill for Claude Code, Codex from nezolder/civil3d-mcp-roslyn. It costs 36 tokens per session (3,984 once invoked), scanned A, original, MIT.

A tool for creating perpendicular sample lines along an alignment, a path used in a civil-engineering drawing, at chosen stations or intervals.

In plain words
What is it for?
Use it to create sample lines at exact stations, regular intervals, or geometry points and sample selected sources.
Why use it?
It automates repeated cross-section sampling and keeps line widths, names, and source data consistent.

Skill for Claude CodeCodex

Written for no agent in particular: nothing here depends on one.

Good fit Use it to create sample lines at exact stations, regular intervals, or geometry points and sample selected sources.

Compare 6 skills from other repositories ↓
Install with agentmods
npx agentmods add skills/nezolder/civil3d-mcp-roslyn/create_sample_lines
Install

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.

Any agent
npx skills add nezolder/civil3d-mcp-roslyn --skill create_sample_lines
Clone the repo
git clone --depth 1 https://github.com/nezolder/civil3d-mcp-roslyn

Made for: Claude Code, Codex.

Wrote 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.

agentmods badge for create_sample_lines

README.md
[![agentmods](https://agentmods.dev/badge/skills/nezolder/civil3d-mcp-roslyn/create_sample_lines/github.svg)](https://agentmods.dev/skills/nezolder/civil3d-mcp-roslyn/create_sample_lines)
Your own site
<a href="https://agentmods.dev/skills/nezolder/civil3d-mcp-roslyn/create_sample_lines"><img src="https://agentmods.dev/badge/skills/nezolder/civil3d-mcp-roslyn/create_sample_lines/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.

agentmods 80×15 button for create_sample_lines

Your own site · 80×15
<a href="https://agentmods.dev/skills/nezolder/civil3d-mcp-roslyn/create_sample_lines"><img src="https://agentmods.dev/badge/skills/nezolder/civil3d-mcp-roslyn/create_sample_lines.svg" alt="Reviewed on agentmods" width="80" height="20"></a>
Per session 36 Skills are progressive disclosure: only the name and description are preloaded; the body loads when the skill is used.
When invoked 3,984 The whole file, excluding the scripts and references it only reads on demand.
Security scan A 0 findings. A grade says what 26 rules found in the file — not that it is safe.
Origin original No closer match found in the catalogue.
Token cost

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.

ModelPer sessionOnce invoked
Fable 5.1 $0.00036 $0.03984
Opus 5.5 $0.00014 $0.01594
Sonnet 5.5 $0.00007 $0.00797
Haiku 4.5 $0.00004 $0.00398

Measured 16d ago against content hash b3f7865bd58b, method: parsed. Prices are Anthropic first-party input rates as of 2026-10-07, from the pricing page.

Security

Grade A, and why

create_sample_lines 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 16d 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.

skills/sections/create_sample_lines.skill.md · 328 lines

How it starts

The opening of the file, as written. The whole thing — 328 lines — stays where its author put it; the contents beside it link to each section on GitHub.

Code Template

var alignmentHandle = "ALIGNMENT_HANDLE";
var groupName = "SAMPLE_LINE_GROUP_NAME";
var leftWidth = 0.0;
var rightWidth = 0.0;
var stations = new (double station, string name)[] { };
var interval = 0.0;
var rangeStart = double.NaN;
var rangeEnd = double.NaN;
var includeGeometryPoints = false;
var sampledSources = new (string handle, string sectionStyleName)[] { };
var namePrefix = "SL_";
var nameDecimals = 2;
var nameIntegerDigits = 3;
var tolerance = 1e-3;

var isHandle = new Func<string, bool>(value =>
    !string.IsNullOrWhiteSpace(value)
    && System.Text.RegularExpressions.Regex.IsMatch(value, "^[0-9A-Fa-f]{1,16}$"));
var isName = new Func<string, bool>(value =>
    !string.IsNullOrWhiteSpace(value) && value == value.Trim() && value.Length <= 255
    && !value.Any(char.IsControl));
if (!isHandle(alignmentHandle))
    return new { success = false, error = "alignmentHandle must be a hexadecimal handle." };
if (!isName(groupName) || groupName == "SAMPLE_LINE_GROUP_NAME")
    return new { success = false, error = "Configure a trimmed sample line group name." };
if (!double.IsFinite(leftWidth) || !double.IsFinite(rightWidth) || leftWidth <= 0 || rightWidth <= 0
    || leftWidth > 1000 || rightWidth > 1000)
    return new { success = false, error = "leftWidth and rightWidth must be greater than 0 and at most 1000." };
if (!double.IsFinite(interval) || interval < 0 || (interval > 0 && interval < 0.1))
    return new { success = false, error = "interval must be 0 or at least 0.1." };
if (double.IsInfinity(rangeStart) || double.IsInfinity(rangeEnd))
    return new { success = false, error = "rangeStart and rangeEnd must be finite or NaN." };
if (nameDecimals < 0 || nameDecimals > 4 || nameIntegerDigits < 0 || nameIntegerDigits > 6
    || namePrefix == null || namePrefix.Length > 100 || namePrefix.Any(char.IsControl))
    return new { success = false, error = "nameDecimals must be 0..4, nameIntegerDigits 0..6 and namePrefix at most 100 characters." };
if (stations == null || stations.Length > 1000
    || stations.Any(s => !double.IsFinite(s.station) || s.name == null || (s.name != "" && !isName(s.name))))
    return new { success = false, error = "stations must hold at most 1000 finite stations with empty or trimmed names." };
if (sampledSources == null || sampledSources.Length > 20
    || sampledSources.Any(s => !isHandle(s.handle) || s.sectionStyleName == null
        || (s.sectionStyleName != "" && !isName(s.sectionStyleName)))
    || sampledSources.Select(s => s.handle.ToUpperInvariant()).Distinct().Count() != sampledSources.Length)
    return new { success = false, error = "sampledSources must hold at most 20 distinct hexadecimal handles with empty or existing section style names." };

ObjectId? ResolveId(string handle)
{
    try { return Database.GetObjectId(false, new Handle(System.Convert.ToInt64(handle, 16)), 0); }
    catch { return null; }
}

var alignmentId = ResolveId(alignmentHandle);
var alignment = alignmentId.HasValue ? Transaction.GetObject(alignmentId.Value, OpenMode.ForRead) as Alignment : null;
if (alignment == null || alignment.IsErased)
    return new { success = false, error = "alignmentHandle must identify an existing alignment." };
if (alignment.StationEquations.Count != 0)
    return new { success = false, error = "Alignments with station equations are not supported; names and stations would be ambiguous." };

var firstStation = double.IsNaN(rangeStart) ? alignment.StartingStation : rangeStart;
var lastStation = double.IsNaN(rangeEnd) ? alignment.EndingStation : rangeEnd;
var inAlignment = new Func<double, bool>(s =>
    s >= alignment.StartingStation - 1e-6 && s <= alignment.EndingStation + 1e-6);
if (!inAlignment(firstStation) || !inAlignment(lastStation) || lastStation < firstStation)
    return new { success = false, error = "The station range must lie inside the alignment.", alignmentStart = alignment.StartingStation, alignmentEnd = alignment.EndingStation };

string FormatStation(double station)
{
    var text = Math.Abs(station).ToString("F" + nameDecimals, System.Globalization.CultureInfo.InvariantCulture);
    var dot = text.IndexOf('.');
    var integerPart = dot < 0 ? text : text.Substring(0, dot);
    text = integerPart.PadLeft(nameIntegerDigits, '0') + (dot < 0 ? "" : text.Substring(dot));
    return (station < 0 ? "-" : "") + text;
}

// Requested stations keyed to 1 mm; explicit rows win over generated ones.
var planned = new SortedDictionary<long, (double station, string name, string origin)>();
void Plan(double station, string name, string origin)
{
    var key = (long)Math.Round(station / tolerance);
    if (!planned.ContainsKey(key)) planned[key] = (station, name, origin);
}
foreach (var row in stations)
{
    if (!inAlignment(row.station))
        return new { success = false, error = $"Station {row.station} is outside the alignment." };
    Plan(row.station, row.name, "explicit");
}
if (interval > 0)
{
    Plan(firstStation, "", "range");
    for (var k = Math.Ceiling(firstStation / interval - 1e-9); k * interval <= lastStation + 1e-6; k++)
        Plan(Math.Min(k * interval, lastStation), "", "interval");
    Plan(lastStation, "", "range");
}
if (includeGeometryPoints)
{
    foreach (var point in alignment.GetStationSet(StationTypes.GeometryPoint))
        if (point.RawStation >= firstStation - 1e-6 && point.RawStation <= lastStation + 1e-6)
            Plan(point.RawStation, "", "geometry point");
}
if (planned.Count == 0)
    return new { success = false, error = "No stations: give stations, an interval or includeGeometryPoints." };
if (planned.Count > 1000)
    return new { success = false, error = "At most 1000 sample lines can be created in one run.", requested = planned.Count };

// Existing group of this name, its lines and names.
SampleLineGroup group = null;
foreach (ObjectId id in alignment.GetSampleLineGroupIds())
{
    var candidate = Transaction.GetObject(id, OpenMode.ForRead) as SampleLineGroup;
    if (candidate != null && !candidate.IsErased && candidate.Name.Equals(groupName, StringComparison.OrdinalIgnoreCase))
        group = candidate;
}
var existingNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var existingStations = new List<double>();
if (group != null)
{
    foreach (ObjectId id in group.GetSampleLineIds())
    {
        if (Transaction.GetObject(id, OpenMode.ForRead) is SampleLine existing && !existing.IsErased)
        {
            existingNames.Add(existing.Name);
            existingStations.Add(existing.Station);
        }
    }
}

var skippedExisting = new List<object>();
var toCreate = new List<(double station, string name, string origin)>();
foreach (var item in planned.Values)
{
    if (existingStations.Any(s => Math.Abs(s - item.station) <= tolerance))
    {
        skippedExisting.Add(new { item.station, reason = "A sample line already exists at this station in the group." });
        continue;
    }
    toCreate.Add((item.station, item.name != "" ? item.name : namePrefix + FormatStation(item.station), item.origin));
}
var collisions = toCreate.GroupBy(c => c.name, StringComparer.OrdinalIgnoreCase)
    .Where(g => g.Count() > 1 || existingNames.Contains(g.Key)).Select(g => g.Key).ToList();
if (collisions.Count > 0)
    return new { success = false, error = "Sample line names would collide; use more nameDecimals or explicit names.", names = collisions };

// Styles and sources are checked before anything is written.
var sourceStyles = new Dictionary<ObjectId, ObjectId>();
foreach (var source in sampledSources)
{
    var sourceId = ResolveId(source.handle);
    if (!sourceId.HasValue)
        return new { success = false, error = "A sampled source handle does not resolve.", source.handle };
    var styleId = ObjectId.Null;
    if (source.sectionStyleName != "")
    {
        try { styleId = CivilDoc.Styles.SectionStyles[source.sectionStyleName]; }
        catch { styleId = ObjectId.Null; }
        if (styleId.IsNull)
            return new { success = false, error = "The named section style does not exist.", source.sectionStyleName };
    }
    sourceStyles[sourceId.Value] = styleId;
}

var groupCreated = group == null;
if (groupCreated)
{
    var newGroupId = SampleLineGroup.Create(groupName, alignment.ObjectId);
    group = (SampleLineGroup)Transaction.GetObject(newGroupId, OpenMode.ForWrite);
}
else
{
    group.UpgradeOpen();
}

// Any failure below throws, so nothing is kept.
var matchedSources = new HashSet<ObjectId>();
foreach (SectionSource source in group.GetSectionSources())
{
    if (!sourceStyles.TryGetValue(source.SourceId, out var styleId)) continue;
    source.IsSampled = true;
    if (!styleId.IsNull) source.StyleId = styleId;
    matchedSources.Add(source.SourceId);
}
if (matchedSources.Count != sourceStyles.Count)
    throw new InvalidOperationException("Some sampledSources are not available as section sources of this sample line group.");

var created = new List<object>();
foreach (var item in toCreate)
{
    double leftX = 0, leftY = 0, centerX = 0, centerY = 0, rightX = 0, rightY = 0;
    try
    {
        alignment.PointLocation(item.station, -leftWidth, ref leftX, ref leftY);
        alignment.PointLocation(item.station, 0, ref centerX, ref centerY);
        alignment.PointLocation(item.station, rightWidth, ref rightX, ref rightY);
    }
    catch (System.Exception ex)
    {
        throw new InvalidOperationException($"Station {item.station}: the sample line end points could not be located: {ex.Message}", ex);
    }
    var lineId = SampleLine.Create(item.name, group.ObjectId,
        new Point2dCollection { new Point2d(leftX, leftY), new Point2d(rightX, rightY) });
    var line = (SampleLine)Transaction.GetObject(lineId, OpenMode.ForRead);

    // Verify the actual vertices against the requested points; projecting
    // the end points back to a station is not reliable near curves.
    var vertices = line.Vertices.Cast<SampleLineVertex>().ToList();
    var targets = new[] { (label: "left", x: leftX, y: leftY), (label: "center", x: centerX, y: centerY), (label: "right", x: rightX, y: rightY) };
    var sides = new Dictionary<string, string>();
    foreach (var target in targets)
    {
        var matches = vertices.Where(v => Math.Abs(v.Location.X - target.x) <= tolerance && Math.Abs(v.Location.Y - target.y) <= tolerance).ToList();
        if (matches.Count != 1)
            throw new InvalidOperationException($"Sample line '{item.name}': the {target.label} point was found {matches.Count} times among its vertices.");
        sides[target.label] = matches[0].Side.ToString();
    }
    if (vertices.Count > 3 || Math.Abs(line.Station - item.station) > tolerance)
        throw new InvalidOperationException($"Sample line '{item.name}' has {vertices.Count} vertices at station {line.Station}, expected 3 at {item.station}.");

    created.Add(new {
        name = line.Name,
        handle = line.Handle.ToString(),
        station = line.Station,
        item.origin,
        vertexCount = vertices.Count,
        leftSide = sides["left"],
        rightSide = sides["right"]
    });
}

var sources = new List<object>();
foreach (SectionSource source in group.GetSectionSources())
{
    sources.Add(new {
        name = source.SourceName,
        type = source.SourceType.ToString(),
        handle = source.SourceId.IsNull ? null : source.SourceId.Handle.ToString(),
        sampled = source.IsSampled,
        style = source.StyleId.IsNull ? null : (Transaction.GetObject(source.StyleId, OpenMode.ForRead) as Autodesk.Civil.DatabaseServices.Styles.StyleBase)?.Name
    });
}

return new {
    success = true,
    alignment = new { name = alignment.Name, handle = alignment.Handle.ToString() },
    group = new { name = group.Name, handle = group.Handle.ToString(), created = groupCreated, sampleLineCount = group.GetSampleLineIds().Count },
    leftWidth,
    rightWidth,
    createdCount = created.Count,
    created,
    skippedExisting,
    sources
};

Read the full file on GitHub · 328 lines

Changes

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.

  1. 16d ago First seen · 328 lines · 36 tokens per session scan A b3f7865bd58b

Subscribe to this mod's changes

create_sample_lines is a skill published in the GitHub repository nezolder/civil3d-mcp-roslyn (3 stars, last pushed 16d ago), licensed MIT. It adds 36 tokens to every session and 3,984 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-12.

Related

Other skills, from other repositories

shopify-theme-development

Build and customize Shopify themes using Liquid templating, JSON sections, dynamic blocks, and theme app extensions for added functionality.

finsilabs/awesome-ecommerce-skills · 28 tokens

web-section-splitter

Breaks a long-form article into web-optimized sections with subheadings, pull-quote suggestions, image placement markers, and reading-time estimates — ready for a web editor to drop into a CMS.

ur-grue/autopunk-media-skills · 47 tokens

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…

microsoft/ai-agents-for-beginners · 200 tokens

systematic-debugging

Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes.

obra/superpowers · 21 tokens

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…

vercel/next.js · 95 tokens

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.

microsoft/vscode · 53 tokens