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 amusleh-spotware-com/terse-sharp --skill assetsgit clone --depth 1 https://github.com/amusleh-spotware-com/terse-sharpWrote 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/amusleh-spotware-com/terse-sharp/assets)<a href="https://agentmods.dev/skills/amusleh-spotware-com/terse-sharp/assets"><img src="https://agentmods.dev/badge/skills/amusleh-spotware-com/terse-sharp/assets/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/amusleh-spotware-com/terse-sharp/assets"><img src="https://agentmods.dev/badge/skills/amusleh-spotware-com/terse-sharp/assets.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.00112 | $0.28154 |
| Opus 5.5 | $0.00045 | $0.11262 |
| Sonnet 5.5 | $0.00022 | $0.05631 |
| Haiku 4.5 | $0.00011 | $0.02815 |
Grade A, and why
terse-sharp 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 today.
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 — 1,211 lines — stays where its author put it; the contents beside it link to each section on GitHub.
TerseSharp
TerseSharp answers C# and XAML questions semantically, from a Roslyn workspace that is already
loaded. Reading a .cs file whole, or grepping for a type name, costs 10-30x more tokens and returns
matches that are not references.
Route every question by its target
Every C#/.NET question has a tool, and the table below names it. Read the left column, take the
tool on the right, call it. It holds for .cs, .razor, .cshtml, .csproj, .props, .targets,
.sln/.slnx/.slnf, .xaml, .axaml, .paml, .resx and .resw, and for every question about
C# symbols, references, diagnostics, builds, tests or the working tree.
The whole surface — one row per job
Read the Job column for what you want, the Instead of column for the built-in it retires, and call what is in Use. Every tool the server advertises is in this table exactly once. What each tool returns, its parameters and its defaults live in the tool's own advertised description — your client already carries those, so this table is the job-to-tool map and nothing else.
| Job | Instead of | Use |
|---|---|---|
| Workspace | — | workspace_status |
| Workspace | Bash: terse doctor |
workspace_status(verbose: true) |
| Workspace | running a command to find out whether the guard allows it | workspace_status(guards: ["<command>", ...]) — each verdict and replacing call, executing nothing |
| Workspace | what one tool's schema costs | workspace_status(tools: true) — one line per tool, tokens descending |
| Workspace | globbing for *.sln |
load_workspace(path, discover: true) |
| Workspace | — | load_workspace |
| Workspace | — | list_workspaces |
| Workspace | — | unload_workspace(path) |
| Workspace | — | list_projects(filter) |
| Workspace | one project_properties call per project |
list_projects(properties: "IsTestProject,TargetFramework") |
| Workspace | reading a .csproj to learn whether an edit is gated |
list_projects(path: "src/Foo.cs") |
| Navigate | Read a .cs file |
get_file_outline(path) |
| Navigate | read_text a .cs file no project compiles |
get_file_outline(path) — parsed from its own text, tagged HEURISTIC |
| Navigate | Read several .cs files |
get_file_outline(paths: [...]) |
| Navigate | outlining a 45-member file to find five members | get_file_outline(path, contains: "Total") |
| Navigate | a type wider than the 40-member cap | get_file_outline(path) — answers the grouped member NAMES per accessibility, which is what contains= (members, fields and constants alike) or a symbolIds= batch is chosen from |
| Navigate | read_text a whole .cs file |
it already answers the outline; verbose: true or a line range for the text — and a verbose whole-file read prices itself against the outline it skipped |
| Navigate | Read a whole class's source |
get_symbol_source(symbolId, verbose: true) on a type id — the default answers its member outline |
| Navigate | Read to see one method |
get_symbol_source(symbolId) |
| Navigate | Read to see several methods |
get_symbol_source(symbolIds: [...]) |
| Navigate | one get_type_outline call per type |
get_type_outline(symbolIds: [...]) |
| Navigate | Read to learn a class's API |
get_type_outline(symbolId) |
| Navigate | — | get_symbol(symbolId) |
| Navigate | one get_symbol call per symbol |
get_symbol(symbolIds: [...]) |
| Navigate | a name an outline printed that answers SaturatedName or AmbiguousSymbol |
get_symbol_source(symbolId, path: "src/Trading/OrderService.cs") — get_symbol, get_type_outline, replace_symbol and replace_symbol_body take path= too |
| Navigate | Grep for a type or member name |
search_symbols(query) or (queries: [...]) — CamelHump works, OSvc finds OrderService |
| Navigate | asking the model whether a framework or NuGet member exists | search_symbols · get_type_outline · get_symbol · get_symbol_source — an exact type name no source declares falls back to the referenced assemblies |
| Navigate | a name the tests declare dozens of times | search_symbols(query, scope: "src") |
| Navigate | a common name that buries the one declaration you meant | search_symbols(query, path: "src/Trading/OrderBook.cs") |
| Navigate | Grep to find callers |
find_usages(symbolId) |
| Navigate | Grep for implementers |
find_implementations(symbolId) |
| Navigate | the outline → source → usages chain, when learning what a symbol IS | get_symbol(symbolId, usages: true) — ONE call |
| Navigate | judging a rename before doing it | find_usages(symbolId, impact: true) |
| Navigate | searching for the tests a change can break | find_usages(symbolId, impact: true, tests: true) |
| What grep cannot reach | "where is IFoo registered?" |
find_registrations(query) |
| What grep cannot reach | "what endpoints exist?" | list_endpoints() |
| Files | "find the file called X" | find_files(name: "orderrouter") |
| Files | ls a directory, or list an archive, outside the workspace |
find_files(root: "C:/logs"); read_text(path: "C:/p/x.nupkg!/a.xml") |
| Files | one find_files call per glob |
find_files(globs: [...], exclude: "tests/**") |
| Files | Glob / ls |
find_files(glob) |
| Files | globbing a whole tree to learn its shape | find_files(glob, depth: 2) |
| Files | ls -l / Get-Item for a size or a timestamp |
find_files(glob, stamps: true) |
| Files | Bash: git ls-files |
find_files(glob, tracked: true) |
| Files | Grep in non-code files |
search_text(query) / search_regex(query) |
| Files | grep -n -e A -e B -e C / one search per literal |
search_text(queries: ["I175", "I176", "I177"]) — records tagged q1..qN |
| Files | a search that keeps hitting a folder you do not want | search_text(query, exclude: ".research/**") |
| Files | Grep -C3 / a search then a read |
search_text(query, context: 3) |
| Files | a text hit, then "which declaration is that line in?" | search_text(query, containers: true) / search_regex(query, containers: true) — each hit becomes an id get_symbol_source takes |
| Files | grep -w |
search_text(query, word: true) — search_regex answers it with \b |
| Edits | change a declaration's attributes — a tool [Description], an [Obsolete] — without re-sending it |
edit_text(path, force: true, oldText: "<short unique fragment>") — compile-gated; allowErrors: true skips it |
| Files | grep -o |
search_regex(query, matchesOnly: true) — compose with unique: true; search_text refuses it |
| Files | grep -c / "is X in these files at all?" |
search_text(query, countOnly: true) |
| Files | grep -r in a log folder outside the repo |
search_text(query, root: "C:/logs") |
| Files | sort | uniq -c over repeated log lines |
search_text(query, unique: true) |
| Files | Bash: git show <ref>:<path> |
read_text(path, ref: "main") · get_file_outline(path, ref: "main") |
| Navigate | the pre-change body of ONE member | get_symbol_source(symbolId, path, ref: "main") — path= required at a ref |
| Files | Read a non-.cs file |
read_text(path) |
| Files | Read several files |
read_text(paths: [...]) |
| Files | tail -n 200 log.txt |
read_text(path, tail: 200) |
| Files | four ranged reads for four anchors in ONE file | read_text(path, ranges: ["42", "101-102"]) |
| Edit text | a read-modify-write on a tree another session is also editing | read_text(path, stamp: true) then edit_text(path, …, ifUnchangedSince: "<that stamp>") |
| Files | wc -c file for a size you want while reading |
read_text(path, bytes: true) |
| Files | guessing what a budgeted document costs before its test runs | read_text(path, tokens: true) |
| Files | a file whose lines are enormous | read_text(path, maxChars: 20000) |
| Files | Read a whole .md to find a section |
read_text(path, headings: true) then read_text(path, section: "## Commands") — a map over 40 sections folds to the levels that fit; verbose: true lists every level |
| Files | reading a whole .md whose content is one long table |
read_text(path, columns: "Finding,Tool") |
| Files | a projection whose first column is PROSE, when you only want the row ids | read_text(path, columns: "Finding", cellChars: 60) |
| Files | Bash: git checkout -- <path> after a bad write |
write_text(path, ref: "HEAD") |
| Files | scratch probes outside every workspace root | write_text(files: [...], force: true) |
| Files | Bash: rm file · Bash: rmdir |
write_text(path, delete: true) — an EMPTY directory is removed too |
| Files | Bash: rm -r folder |
write_text(path, delete: true, recursive: true) — refused when the tree holds a file this workspace compiles, unless force: true; the workspace root and any .git are refused outright |
| Edit text | Edit a .md section |
edit_text(path, section: "## Commands", newText: …) — no oldText needed |
| Edit text | re-reading a file to see what an edit landed | edit_text(path, oldText, newText, context: 2) — POST-edit lines, not a diff |
| Edit text | reading a section out of one file and writing it into another | edit_text(path, section: "## Open", toPath: "other.md") |
| Edit text | anchoring on ### Added to add a changelog entry |
edit_text(path, section: "### Added", occurrence: 1, place: "prepend", newText: …) — read_text takes occurrence= too |
| Edit text | one edit_text row= call per row when closing a whole backlog, plus one more for its changelog line |
edit_text(path, rows: [{row, newText}, ...], toPath: "IMPROVEMENTS-ARCHIVE.md", edits: [{path: "CHANGELOG.md", oldText, newText}]) — edits= entries name OTHER files |
| Edit text | cutting one table row out of one markdown file and appending it to another | edit_text(path, row: "I286", toPath: "IMPROVEMENTS-ARCHIVE.md", newText: "| … |") — matched by its first cell: the row that leads with the id wins over rows that only cite it |
| Edit text | three or more edit_text calls on the same file |
edit_text(path, edits: [{oldText, newText}, …]) |
| Edit text | one edit_text call per file across several files |
edit_text(edits: [{oldText, newText, path}, …]) — an entry carries its own path and force |
| Edit text | one write_text call per new file |
write_text(files: [{path, content, force}, …]) — an entry carries its own force, and every .cs among them shares ONE compile gate |
| Edit text | two entries of one edits= batch addressing occurrence 1 and 2 of the SAME anchor |
edit_text(path, edits: [...]) — ordinals resolve against the ORIGINAL text |
| Edit text | an anchor that deliberately repeats — a table of near-identical rows | edit_text(path, oldText: "| row |", occurrence: 3) |
| Edit text | N sequential calls to replace the SAME anchor N times — three identical [Description] strings in one file |
edit_text(path, oldText, newText, replaceAll: true) — one pass, applied by descending offset |
| Edit text | Edit/Write a non-.cs file |
edit_text · write_text |
| Edit text | re-reading a file because an anchor copied from get_symbol_source did not match |
edit_text handles it — dedented or blank-stripped anchors match |
| Edit text | Write a new .cs file |
write_text(path, content, force: true) |
| Edit text | re-sending a file, a batch or a snippet after a rollback or a landing dryRun |
write_text(retryWith: "r3") · edit_text(retryWith: "r3", usings: ["System.Collections.Immutable"]) — the payload is held |
| Edit text | rewriting a whole .cs file |
write_text(path, content) — compile-gated, no force once a project compiles it; replacing an unrelated file needs overwrite: true |
| Edit code | Edit a .cs file |
replace_symbol_body · replace_symbol · add_member · delete_symbol |
| Edit code | a new body that calls a private helper you have not written yet | replace_symbol(symbolId, declaration, add: [...]) |
| Edit code | a signature change that breaks its callers | replace_symbol(symbolIds: [...], declarations: [...]) — one compile-gated edit across files |
| Edit code | renaming a member and rewriting its body in one edit | replace_symbol(symbolIds: [...], declarations: [...], rename: true) |
| Edit code | adding an enum member | add_member(typeSymbolId: "T:…MyEnum", declaration: "Retry") |
| Edit code | adding a sibling type to an existing file | add_member(path: "Foo.cs", declaration: "public sealed record Bar(int X);") |
| Edit code | placing a member instead of letting it land last | add_member(typeSymbolId, declaration, before: "Submit") — also after:, and position: "first" / "afterFields" / "last"; the anchor takes any spelling of the parameter list |
| Edit code | removing several members | delete_symbol(symbolIds: [...]) — one gated edit; a file left declaring nothing is deleted |
| Edit code | an interface member and every implementation, or an enum value and its use | add_member(typeSymbolIds: [...], declarations: [...]) — ONE gated edit |
| Edit code | find-and-replace a name | rename_symbol(symbolId, newName) — interfaces, overrides, doc crefs and XAML follow |
| Edit code | reverting an edit you regret | undo_last_change |
| Refactor | hand-writing an interface from a class | extract_interface(symbolId) |
| Refactor | cut-and-paste between files, un-nesting a type | move_type_to_file (a file's only type moves the file whole) · move_type_to_namespace (a nested type id lifts it to namespace level in its own file) |
| Refactor | editing a signature and every call site by hand | change_signature(symbolId, …) |
| Projects | editing a .csproj by hand |
project_set_property · project_properties · project_add_reference · project_remove_reference · project_create — untouched lines survive byte for byte |
| Projects | editing PackageReference by hand |
package_list · package_add · package_remove |
| Projects | Bash: dotnet list package --vulnerable |
package_list(vulnerable: true) · package_list(outdated: true) |
| Projects | "which properties does this project really have?" | project_properties(project) — MSBuild's evaluated set |
| Projects | editing a .sln/.slnx by hand |
solution_add_project · solution_remove_project |
| Projects | "which projects does this solution contain?" for a solution that is not loaded | solution_projects(path: …) |
| Git | Bash: git log / git show --stat |
history — git blame stays on the shell |
| Git | git hash-object -w + git update-index --cacheinfo for a partial commit |
write_text(path, content, stage: true) — index only, as git add stages it; ref: "HEAD" unstages |
| Git | Bash: git describe |
history(describe: true) |
| Git | Bash: git tag --list / git tag -l "v*" |
history(tags: true) — creating or deleting a tag stays on the shell |
| Git | Bash: git ls-remote --tags origin |
history(tags: true, remote: true) — every row tagged local=yes|no remote=yes|no |
| Git | Bash: git diff --cached for its hunk text or its declarations |
diff_symbols(staged: true) · diff_text(staged: true) |
| Git | Bash: git diff --cached --name-only / git status --untracked-files=no |
changed_files(staged: true) · changed_files(untracked: false) |
| Git | Bash: git status / git diff --stat / --numstat / --name-only / --name-status |
changed_files — the counts family routes here, not to diff_symbols; a byte-identical repeat with nothing moved replays as UNCHANGED |
| Git | Bash: git diff to decide what to review |
diff_symbols — hunks become symbol ids for get_symbol_source |
| Git | Bash: git diff for the hunk text itself |
diff_text(path: …) — a clipped answer is INCOMPLETE and names the skipLines= that continues it |
| Build and test | Bash: dotnet build / msbuild |
build — a byte-identical repeat with nothing written since answers build UNCHANGED |
| Build and test | Bash: dotnet build -c Release |
build(configuration: "Release") |
| Build and test | Bash: dotnet build -p:Name=Value |
build(properties: ["Name=Value"]) |
| Build and test | Bash: dotnet test / vstest |
run_tests |
| Build and test | one run_tests call per test project |
run_tests(projects: [...]) — concurrent, per-project timeout |
| Build and test | a run past the client's foreground limit | run_tests where the client backgrounds long calls; else run_tests(detach: true), then run_tests(status: "<id>", waitSeconds: 3600) - nothing notifies it |
| Build and test | bounding parallelism inside one test assembly | run_tests(runSettings: ["xUnit.MaxParallelThreads=1"]) |
| Build and test | re-running what broke - a failure stays remembered until a run passes it | rerun_failed |
| Build and test | re-verifying SOME of what broke | rerun_failed(tests: [...], exclude: [...]) |
| Build and test | dotnet test --list-tests |
list_tests(contains) |
| Build and test | Bash: dotnet clean |
clean |
| Analyse | one analyze call per touched file |
analyze(paths: [...]) |
| Analyse | dotnet format whitespace / an IDE inspection |
analyze — compiler + analyzers + dead code, down to info |
| Analyse | running analyze → format → cleanup → analyze at the end of a task |
gate — one verdict line |
| Analyse | an end-of-task sweep buried in findings this task did not author | analyze baseRef="HEAD" · gate baseRef="HEAD" · get_diagnostics baseRef="HEAD" — only changed lines, plus every error; the rest is one count |
| Analyse | dotnet format style / dotnet format analyzers |
cleanup fix=style|analyzers|all |
| Analyse | dotnet format --verify-no-changes |
format verify=true · cleanup verify=true |
| Analyse | one cleanup call per touched file |
cleanup(paths: [...]) |
| Analyse | one format call per touched file |
format(paths: [...]) |
| Analyse | formatting only what you touched | format changed=true · cleanup changed=true |
| Analyse | reading build output for a consumer you broke | get_diagnostics |
| XAML | Read a .xaml file |
xaml_outline(path) |
| XAML | Grep a .xaml file |
xaml_find(query) · xaml_names() · xaml_resources() |
| XAML | hunting a resource through App.xaml |
xaml_resolve(key) |
| XAML | "why does this control look like that" | xaml_styles(typeName) |
| XAML | eyeballing a {Binding} |
xaml_bindings(path, validate: true) |
| XAML | Read a .xaml.cs to see what the markup wires |
xaml_codebehind(path) |
| XAML | "is this element translated" | xaml_localization() |
| XAML | guessing whether the markup is sound | xaml_validate() |
| XAML | Edit a .xaml file |
xaml_set_property · xaml_add_element · xaml_remove_element |
| Localization | Read a .resx/.resw |
resx_get(path, cultures) |
| Localization | Glob for resource files |
resx_files() |
| Localization | Grep a resource key |
resx_find(query) |
| Localization | "is this key still used" | resx_usages(key) |
| Localization | "which strings are untranslated" | resx_validate() |
| Localization | one resx_set call per key |
resx_set(entries: "Key=Value\nOther=Second") — files: [{path, entries}, …] writes up to 10 culture files, and needs no top-level path |
| Localization | Edit a .resx/.resw |
resx_set · resx_remove · resx_rename |
| Razor | Read a .razor or .cshtml file |
razor_outline(path) |
| Razor | "how do I use this component" | razor_component(name) |
| Razor | Grep a tag, directive or route in markup |
razor_find(query, kind) |
| Razor | "is this @bind real" |
razor_bindings(path, validate: true) |
| Razor | Read a .razor.cs |
razor_codebehind(path) |
| Razor | "what breaks at render" | razor_validate() |
| Razor | Edit a .razor file |
razor_set_attribute · razor_add_element · razor_remove_element · razor_set_directive |
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.
- today Changed 3f1ca228c083
- 2d ago Changed · -2 lines faa0d4b10a4e
- 5d ago Changed · -4 lines 736c316265d6
- 8d ago Changed · -6 lines 63f1d76e2670
- 13d ago Changed · -2 lines 076cc940d7d2
- 16d ago Changed · -1 lines b0d9db2afe8b
- 21d ago Changed · -6 lines b55b2e26cf45
- 22d ago Changed · -1 lines aac157e1e0b3
- 23d ago Changed · -8 lines 59fda35460db
- 27d ago First seen · 1,241 lines · 112 tokens per session scan A d12c415e0746
terse-sharp is a skill published in the GitHub repository amusleh-spotware-com/terse-sharp (2 stars, last pushed today), licensed MIT. It adds 112 tokens to every session and 28,154 once invoked, about $0.0004 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-08-31.
Other skills, from other repositories
generate-code-cs
Generate the code from typespec for C#. Parameter: C# SDK repository root location .
author-test
Generate a test given sample. Parameters: C# SDK repository root; Package name: one of Azure.AI.Projects, Azure.AI.Projects.Agents or Azure.AI.Extensions.OpenAI; the sample to use as a starting point for the test.
opentelemetry-net-instrumentation
Provides guidance for implementing OpenTelemetry instrumentation in .NET codebases, covering tracing (Activities/Spans), metrics, logs, naming conventions, error handling, performance, SDK setup, resources, context propagation, and API design best practices.
wpf
Build and modernize WPF applications on .NET with correct XAML, data binding, commands, threading, styling, and Windows desktop migration decisions. USE FOR: working on WPF UI, MVVM, binding, commands, or desktop modernization; migrating WPF from .NET Framework to .NET; integrating newer Windows capabilities into a…
mcp
Build or consume Model Context Protocol (MCP) servers and clients in .NET using the official MCP C# SDK, including stdio, Streamable HTTP, tools, prompts, resources, and capability negotiation. USE FOR: .NET MCP servers or clients; stdio versus HTTP transport choices; tools, resources, prompts, completions, and…
migrate-xunit-to-mstest
Convert .NET tests from xUnit.net v2/v3 to MSTest v4 while preserving VSTest or MTP. Use for replacing xunit packages, Fact/Theory/InlineData/MemberData, assertions, IClassFixture/ICollectionFixture, ITestOutputHelper, TestContext cancellation, traits/Owner, skips, timeouts, and xUnit parallelization. Also use when a…