{
  "markdown": "<div align=\"center\">\n\n# Trazum\n\n### Most of your LLM bill is not the prompt. Trazum finds where it is.\n\n**A deterministic cost analyser for prompts and usage logs.** Offline, free,\nsame answer every time. It reports sixteen findings priced in dollars per\nmonth: caching you are not getting, a model tier you may not need, a schema you\npay to describe on every call. Shortening the prompt is one of them, and it is\nrarely the biggest.\n\n[![CI](https://github.com/Davmunrey/Trazum/actions/workflows/ci.yml/badge.svg)](https://github.com/Davmunrey/Trazum/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/Davmunrey/Trazum/actions/workflows/security.yml/badge.svg)](https://github.com/Davmunrey/Trazum/actions/workflows/security.yml)\n[![MIT licence](https://img.shields.io/badge/licence-MIT-blue.svg)](LICENSE)\n[![Node](https://img.shields.io/badge/node-%E2%89%A520-3c873a.svg)](package.json)\n[![Runtime dependencies](https://img.shields.io/badge/runtime%20dependencies-0-2f855a.svg)](#layout)\n\n</div>\n\n## Your agents spend money in a loop. This prices the call before it happens.\n\nOne agent costs what it costs. A fleet of them spends in a loop nobody is\nwatching per iteration, and the bill arrives a month later as one number with\nno per-decision detail inside it.\n\nTrazum installs into that loop. The MCP server's first tool is `spend_guard`,\nand it is the only one here whose trigger is not a sentence somebody types:\n\n```\n\"May I spend this?\"  ->  yes, no, or cannot-tell.\n```\n\nA refusal carries the cheaper ways to make the same call, each priced for that\ncall and each naming what it assumes. The ceilings come from your\n`trazum.config.json`; the spend so far comes from the usage log your host\nalready writes. Nothing is called and nothing is spent to answer.\n\n```bash\nclaude plugin marketplace add Davmunrey/Trazum\nclaude plugin install trazum@trazum\n```\n\nThat one line brings the skill and the MCP server. For any other MCP client,\n`npx -y @trazum/mcp` over stdio does the same.\n\n**[Or run it right now, without installing anything: the Playground](https://trazum.vercel.app/?tab=playground)**\n\nThat link opens the CLI's pure subset running in the page, against sample files\nalready loaded, through the same `@trazum/core` functions the terminal runs.\nNothing you paste leaves your browser.\n\n## The argument, in one screenshot\n\n<div align=\"center\">\n\n<img src=\"docs/assets/demo.svg\" alt=\"trazum optimize on a wordy support prompt: 238 tokens down to 142 (-40.3%), $24.00/month saved by the rules, and an advisory pointing at $528.40/month, 22 times more\" width=\"760\">\n\n*Real output, transcribed. Read the last two lines: the rules recovered $24.00\na month, and the advisory above them is worth $528.40, **22 times more**. That\ngap is the entire argument for this tool.*\n\n</div>\n\n<div align=\"center\">\n\n<img src=\"docs/assets/boundary.svg\" alt=\"What goes in: a prompt or a usage log. What is computed on your machine, offline: @trazum/core with zero dependencies, plus the CLI, the MCP server, the web app and the Action. What leaves: a receipt, a report, a gate verdict. What never crosses the line: the prompt text, the model's answer, file paths, branch names and credentials.\" width=\"880\">\n\n*Generated by `npm run draw:architecture`, not drawn. `architecture-image.test.js`\nfails the build if a package exists that the picture does not show, if the core\ntakes a dependency while the picture says it has none, or if a third module is\nallowed to reach a network without the picture saying so.*\n\n</div>\n\n**The prompt is the part everyone looks at, and usually the cheap part.** In the\nrun above, forty percent of the text came out and it moved 3.5% of the bill.\nWhat moved the rest was a question nobody was asking: does this task need the\nmodel it is running on?\n\n**Every figure has a receipt.** Sixteen advisories, each priced per month and\nreproducible on a single file: caching you are not getting, work that could go\nthrough the Batch API, a schema costing tokens on every call to describe a shape\nthe request could carry as a parameter. Underneath them, twelve deterministic\nrules that shorten the text itself: same input, same output, free, offline, and\nnever touching code, URLs, email addresses or placeholders. On top, an **optional LLM pass** for\nthe compression rules cannot do, through whichever provider you configure, which\nnever runs unless you ask.\n\n```\n                      ┌──────────────┐\n                      │ @trazum/core │   the library: rules, tokens, pricing\n                      └──────┬───────┘   zero dependencies, browser-safe\n    ┌────────────┬─────────────┼─────────────┬─────────────┐\n @trazum/cli  @trazum/mcp  trazum-vscode  @trazum/web    action/\n 48 commands   MCP server    the editor      Next.js    comments on\n              for your agents  status bar               pull requests\n\n              @trazum/tokenizer-openai   optional: exact counts for OpenAI\n                    install it or do not, nothing else changes\n```\n\n## Contents\n\n- [What it actually does](#what-it-actually-does) — the five things, and what it refuses to touch\n- [The 48 commands](#the-48-commands): the whole surface, one line each\n- [Getting started](#getting-started) — CLI, web, the GitHub Action, pre-commit\n- [The first five minutes](#the-first-five-minutes-trazum-init) — `init`, and the four things it refuses to write\n- [Building on the format](docs/commands.md#building-on-the-format-trazum-conform) — the contracts, the guarantees, and the doctrine\n- [More than one machine](docs/commands.md#more-than-one-machine-trazum-rollup) — several people's documents, one bill, every gap preserved\n- [Did anything stop running](docs/commands.md#did-anything-stop-running-trazum-pulse) — the outside view of a scheduled job\n- [Which few-shot examples earn their tokens](docs/commands.md#which-few-shot-examples-earn-their-tokens-trazum-prune) — measured, and it asks before spending\n- [An MCP server for your agents](docs/commands.md#an-mcp-server-so-an-agent-can-budget-its-own-prompts) — budget a prompt before sending it\n- [Languages](#languages) — what the dictionaries cover, and what they deliberately do not\n- [Connecting your own LLM](#connecting-your-own-llm) — one wire format, four native providers, and the SSRF rules\n- [Every model you pay for by the token](#every-model-you-pay-for-by-the-token) — pricing across 7 providers, live via OpenRouter\n- [Token counting](#token-counting) — the estimator, and the error band it prints\n- [Limitations, stated plainly](#limitations-stated-plainly) — read this one\n- [Running it on a schedule](docs/running.md) — cron, systemd, Actions, and where the answer runs out\n- [Everything else](docs/README.md) — the documentation index, arranged by whether you are\n  choosing this, using it, extending it or maintaining it\n- [Layout](#layout) · [Updating prices](#updating-prices) · [Privacy](#analytics-and-privacy) · [Roadmap](#roadmap-and-contributing)\n\n---\n\n## What it actually does\n\n**1. Tells you where the money actually is.** This is the part worth reading\nfirst, because it is where the numbers are. Every advisory is priced per month\nagainst your own call volume, and none of them is about making the text shorter:\n\n| Advisory | Why it matters |\n|---|---|\n| Prompt caching | Reading from cache costs 10% of input. The saving is computed over the **real stable prefix**: in a template with `{{placeholders}}`, only what precedes the first one is cached — not the whole prompt. |\n| Reorder the template | Stable instructions sitting *after* the first variable placeholder never cache today. Trazum prices moving them in front — and with `--reorder`, [does it](docs/commands.md#reordering-for-the-cache---reorder). |\n| Batch API | 50% off input and output when the work tolerates latency. |\n| Cheaper model | Complexity heuristic: if the task looks simple, what dropping a tier would save. |\n| Output-dominated cost | If you pay more for the answer than for the prompt, shortening the prompt has a ceiling. |\n| Promotional pricing | Warns when you are budgeting with an introductory price that expires. |\n| Context window | If the prompt does not fit, the call is going to fail. |\n| Contradictory instructions | \"Answer in English\" three paragraphs above \"reply in the customer's own language\". The model has to pick one, and which one can change between calls — a correctness problem that also costs tokens twice. |\n| Redundant examples | Few-shot examples that are near-copies of an earlier one, and what they cost per month. |\n| Output format stated twice | A schema shown in a code block and then walked again in prose. The block is the version worth keeping. |\n| Schema the request could carry | A schema block introduced by \"Output format:\" is paid for in input tokens on every call. Every major API now takes a response schema as a *parameter* — and moving it there is both cheaper and stricter. See below. |\n\nThe last four are **advisory only**. A contradiction has a right answer that only\nthe author knows, and an example that looks redundant may be demonstrating a\nboundary case on purpose. Trazum points; it does not cut.\n\n#### The one finding that is not a trade-off\n\nMost of what Trazum reports is a choice: shorter against clearer, cheaper against\nmore capable. Moving an output schema out of the prompt is neither.\n\n```\n→ The output schema could travel in the request instead of the prompt\n  A schema block introduced by \"output format\" defines `category`, `reply`,\n  `escalate_to_human`, `confidence`, costing about 62 tokens on every call.\n```\n\nThose tokens are paid on **every call** to have the model read a shape and be\nasked, politely, to match it. `output_config.format`, `response_format`,\n`responseSchema` — whatever your provider calls it — takes the same shape as a\nrequest parameter, where the decoder is constrained rather than persuaded. Cheaper\n*and* stricter.\n\n**Trazum reports it and never does it**, because it is not a change to the prompt:\nit is a change to the code that sends the prompt. A rule that deleted the schema\nwould leave a prompt asking for a shape it no longer describes, sent by a client\nnobody updated — strictly worse than what it started from.\n\n**The one way this could do harm, and what stops it.** `Output format: {...}` is\na contract and moving it is free; `Input: {...}` inside a few-shot example is\n*data the prompt needs*, and moving it breaks the prompt. So nothing is guessed:\na block counts only when a phrase from the output-cue dictionary appears\nimmediately before it, in one of the seven languages the rules cover. No phrase,\nno finding — a false negative, which is the right direction to be wrong in.\n\nThe example detector finds near-copies — the way few-shot blocks actually grow.\nIt deliberately does not flag *paraphrases*: that case needs a model, and is on\nthe roadmap for the LLM pass.\n\n**2. Then it trims the prompt itself.** Twelve deterministic rules: courtesy,\nfiller, verbose phrasing, duplicated paragraphs, decorative separators, shouting\nin capitals. Two levels — `safe` (no semantic risk) and `aggressive` (read the\ndiff). This is the smallest number on the page more often than not, and it is\nreported that way rather than dressed up.\n\n**3. And never touches what would break the prompt.** Code fences, indented code\nblocks, inline code, URLs, email addresses, template placeholders (`{{x}}`,\n`${x}`, `{x}`, `{% %}`) and XML/HTML tags are isolated before any rule runs. If a rule ever did make one of those\ndisappear, that rule is discarded and the rest carry on.\n\n**Reviewing an aggressive run.** Every rule reports what it actually changed,\nso the level that saves the most is judged rule by rule rather than as one wall\nof diff — and a single rule you disagree with comes off with `--disable`:\n\n```\n  [aggressive] Intensifiers (3×, ~6 tokens)\n      VERY → —\n      extremely → —\n      quite → —\n  [aggressive] Self-verification instructions (1×, ~17 tokens)\n      You should double-check your answer before re… → —\n```\n\n**4. Optionally, runs it past an LLM.** The result is only accepted if it is\nshorter and leaves protected content byte-identical. Otherwise the deterministic\nversion stands. It never returns something worse than where it started. With\n`--suggest` it proposes phrases one at a time — `You should always make sure to →\nAlways` — each checked against your prompt before you see it, so eight surviving\nout of ten is a useful morning rather than a rewrite to read end to end.\n\n**5. Answers the questions that come before \"shorten this\".** Trimming one file\nis the smallest thing here. `optimize` is one of 48 commands — [the table\nabove](#the-48-commands) names what each answers — because knowing a prompt\nis wasteful is not the same as knowing *which* prompt, *whose* change made it so,\nor whether the shorter version still works.\n\n`check`, `diff`, `rank` and `blame` all take `--markdown-out`, so the answer can\nland in a pull request comment rather than a terminal nobody is looking at.\n\n**It gates in whatever CI you already run.** One binary, two exit codes, and\n[worked recipes for GitLab CI, Jenkins, CircleCI and a pre-commit hook](docs/ci.md)\n— no vendor plugin, because each one would be a second code path that drifts\nfrom the exit codes it is supposed to relay.\n\n---\n\n## The 48 commands\n\n| Command | What it answers |\n|---|---|\n| [`trazum init`](#the-first-five-minutes-trazum-init) | What is in this repository, and what is the one thing worth fixing? *The first command to run.* |\n| [`trazum optimize`](#cli) | What can come out of this prompt, and what is that worth a month? |\n| [`trazum check`](#cli) | Does this prompt fit its token budget, and has the repository drifted past its recorded baseline? *Exits 1 when either fails — this is the CI gate.* |\n| [`trazum baseline`](docs/commands.md#the-ci-gate-a-budget-is-a-ceiling-a-baseline-is-a-gate) | What does this repository's prompts cost right now? *Records it, to commit.* |\n| [`trazum diff`](docs/commands.md#did-this-edit-make-it-worse) | What did this edit cost? |\n| [`trazum rank`](docs/commands.md#which-prompt-to-fix-first-trazum-rank) | Of these forty prompts, which is worth an afternoon? |\n| [`trazum doctor`](docs/commands.md#the-whole-workspace-at-once-trazum-doctor) | What is wrong across the whole workspace? |\n| [`trazum prune`](docs/commands.md#which-few-shot-examples-earn-their-tokens-trazum-prune) | Which few-shot examples earn their tokens? Measured, and it asks before spending. |\n| [`trazum blame`](docs/commands.md#who-made-this-prompt-expensive-trazum-blame) | Who made this prompt expensive, and when? |\n| [`trazum eval`](docs/commands.md#does-the-shorter-prompt-still-work) | Does the shorter prompt still do the job? |\n| [`trazum where`](docs/commands.md#prompts-where-they-actually-live) | Which prompts are hiding inside my source files? |\n| [`trazum models`](#every-model-you-pay-for-by-the-token) | What does each model cost, and what is its cache minimum? |\n| [`trazum profile`](#where-the-money-actually-went-trazum-profile) | Where did the money actually go? *Reads a usage log, not a prompt.* |\n| [`trazum route`](docs/commands.md#is-the-cheaper-model-good-enough-trazum-route) | Is the cheaper model good enough? *Measured, and it asks before spending.* |\n| [`trazum plan`](docs/commands.md#the-plan-trazum-plan) | Of everything the log shows, what do I do first, and what is each move worth? |\n| [`trazum verify`](docs/commands.md#did-it-work-trazum-verify) | Did the plan's savings actually arrive? *Three outcomes, never two.* |\n| [`trazum history`](docs/commands.md#the-long-run-trazum-history) | What have twenty reports been saying that no two of them could? *Shapes, never forecasts.* |\n| [`trazum connect`](docs/commands.md#your-bill-without-the-export-trazum-connect) | What did the provider actually bill me? *Read from their API, nothing exported by hand.* |\n| [`trazum store`](docs/commands.md#keeping-it-trazum-store) | What have I measured and kept? *Aggregates only — no prompt text, ever.* |\n| [`trazum watch`](docs/commands.md#the-afternoon-it-happened-trazum-watch) | Has anything crossed a budget? *Measured crossings only — never a forecast.* |\n| [`trazum serve`](docs/commands.md#before-the-call-is-sent-trazum-serve) | What will this call cost, and is there budget? *Answered in milliseconds, halves kept apart.* |\n| [`trazum gateway`](docs/commands.md#in-the-path-of-the-call-trazum-gateway) | Can it stop the call instead of advising against it? *Refuses; never substitutes.* |\n| [`trazum ladder`](docs/commands.md#is-the-ladder-saving-money-or-is-it-a-bill-trazum-ladder) | Is cheap-first-escalate-on-failure saving money, or costing it? *Break-even rate, stated.* |\n| [`trazum experiment`](docs/commands.md#two-arms-on-real-traffic-trazum-experiment) | Which of two arms is better on real traffic? *A winner only when there is one.* |\n| [`trazum quality`](docs/commands.md#the-gate-that-fails-a-build-for-quality-trazum-quality) | Did that prompt change quietly make the product worse? *Refuses to blame what it cannot attribute.* |\n| [`trazum semantic`](docs/commands.md#the-findings-a-dictionary-cannot-see-trazum-semantic) | Does this prompt say the same thing twice, or contradict itself? *The model proposes; the checker disposes.* |\n| [`trazum owners`](docs/commands.md#whose-money-trazum-owners) | Whose budget does this land on? *The unallocated is never spread.* |\n| [`trazum commitment`](docs/commands.md#should-you-sign-that-commitment-trazum-commitment) | What would that committed-use deal have been worth? *On measured months, both directions priced.* |\n| [`trazum report`](docs/commands.md#the-year-from-what-was-already-written-down-trazum-report) | What did the year actually look like? *No new data, and it lists its own blind spots.* |\n| [`trazum schema`](docs/commands.md#building-on-the-format-trazum-conform) | Which fields must a document of this format carry? *A JSON Schema, for validators that are not Trazum.* |\n| [`trazum conform`](docs/commands.md#building-on-the-format-trazum-conform) | Does the document my tool emits conform, and what will it not be able to answer? |\n| [`trazum rollup`](docs/commands.md#more-than-one-machine-trazum-rollup) | Four of us measured four things — what is the total, and what did merging lose? *A format and a merge, not a service.* |\n| [`trazum pulse`](docs/commands.md#did-anything-stop-running-trazum-pulse) | Did the things that are supposed to run, run? *Runs nothing itself — your CI is the thing that notices.* |\n| [`trazum position`](docs/commands.md#where-the-month-stands-trazum-position) | Where does the month stand against every ceiling? *Measured, denominators attached, no forecast anywhere.* |\n| [`trazum receipt`](docs/commands.md#a-figure-that-still-answers-elsewhere-trazum-receipt) | What did this cost, in a form that still answers when read somewhere else? *Counts, and the money split so it can be added up rather than believed. No prompt text, no answers, no paths: there is no field for them.* |\n| [`trazum from-claude-code`](docs/commands.md#the-agents-own-bill-trazum-from-claude-code) | What did my Claude Code sessions cost? *Reads the transcripts already on disk — the numbers only, never the words.* |\n| [`trazum from-otel`](docs/commands.md#the-universal-cost-lens-trazum-from-otel) | What did the LLM calls in my OpenTelemetry export cost? *Reads the GenAI spans any exporter already emits — the counts only, never the prompts.* |\n| [`trazum from-litellm`](docs/commands.md#the-gateway-everybody-already-runs-trazum-from-litellm) | What did the calls my LiteLLM proxy logged cost? *Reads the spend log the gateway already writes — the counts only, never the prompts, keys or addresses on the same row.* |\n| [`trazum reconcile`](docs/commands.md#what-you-were-actually-billed-trazum-reconcile) | Does what Trazum computed match what the provider charged? *Sets the two figures beside each other, never merges them, and leaves the unexplained remainder standing on its own.* |\n| [`trazum from-anthropic`](docs/commands.md#what-the-provider-itself-says-trazum-from-anthropic) | What does the provider itself say my organisation used? *Reads the usage report your own admin key fetched; Trazum never holds the credential, refuses to price a batch row at a standard rate, and labels by workspace only from a mapping you write.* |\n| [`trazum from-helicone`](docs/commands.md#the-proxy-that-kept-every-request-trazum-from-helicone) | What did the requests my Helicone proxy kept cost? *Prices the model that answered, not the one that was asked for, and counts the substitutions.* |\n| [`trazum from-langsmith`](docs/commands.md#the-tree-that-is-not-a-list-trazum-from-langsmith) | What did the model calls in my LangSmith traces cost? *Only the llm runs, because a trace is a tree and summing it bills the same tokens twice — and it refuses to price a call by the client class that made it.* |\n| [`trazum switch`](docs/commands.md#when-does-the-switch-pay-trazum-switch) | Should we move this traffic, and when does moving pay? *Measured delta, declared migration cost, break-even as division on the past — and the required evaluation itself priced.* |\n| [`trazum ownrate`](docs/commands.md#the-model-you-run-yourself-trazum-ownrate) | What does my self-hosted model cost per million tokens? *Your GPU rate over your measured throughput — derived from your declaration, never guessed.* |\n| [`trazum bench`](docs/commands.md#this-machine-measured-trazum-bench) | How fast is Trazum here, and on what? *One shot per workload, no judgement — run it before and after a change.* |\n| [`trazum write`](docs/commands.md#you-describe-it-it-asks-trazum-write) | What should this prompt say, and what will it cost before I ever send it? *Asks; nothing is generated.* |\n| [`trazum rules`](#what-it-actually-does) | Which rules exist, and what does each one do? |\n| [`trazum feedback`](docs/commands.md#telling-us-something-trazum-feedback) | Where do I report this, and what will you ask me for? *Sends nothing.* |\n\n## Getting started\n\n```bash\nnpx @trazum/cli init\n```\n\nNo install, no key, no network. It reads what is already here — your prompts,\nwhich provider your code calls, a usage log if one is lying around — writes a\nconfig out of what it can actually justify, and prints the single most valuable\nthing it found. See [the first five minutes](#the-first-five-minutes-trazum-init).\n\nOr start from one file:\n\n```bash\nnpx @trazum/cli optimize your-prompt.txt --cost\n```\n\nEither way, keep it around:\n\n```bash\nnpm install -g @trazum/cli     # the terminal\nnpm install @trazum/core       # the library\nnpm install @trazum/mcp        # the MCP server, for an agent\nnpm install @trazum/tokenizer-openai  # optional: exact counts for OpenAI models\n```\n\nOr hand the whole thing to Claude Code as a plugin — the `trazum` skill plus\nthe MCP server, installed together, nothing else to configure:\n\n```bash\nclaude plugin marketplace add Davmunrey/Trazum\nclaude plugin install trazum@trazum\n```\n\nThe plugin's skill is [the same document](plugin/skills/trazum/SKILL.md) this\nrepository's own agents work from, derived by\n`scripts/build-plugin-skill.mjs` with only the invocation changed — a test\nfails the build if the two drift apart in any other way.\n\n<details>\n<summary>From source, if you are working on Trazum itself</summary>\n\n```bash\nnpm install\nnpm run build      # core + cli\nnpm test           # every suite: core, CLI, web, Action\nnpm run verify     # the above plus typecheck and the web build\n```\n\n</details>\n\n<sub>The test count used to be written here as a number. It said 580 while the real\nfigure had reached 798, because nothing checked it — so it now says what the command\ncovers instead. A number nobody maintains is worse than no number.</sub>\n\n### The first five minutes: `trazum init`\n\n```bash\nnpx @trazum/cli init\n```\n\n```\nWhat is here\n\n  Running inside a terminal.\n  1 prompt file found.\n  Usage log found: usage.jsonl.\n\nWhat the config would say\n  + usage.model  100% of the measured bill went to claude-opus-5\n  + usage.callsPerMonth  240 calls over 30 days, stated as 240 a month\n  + usage.avgOutputTokens  96000 output tokens over 240 calls averages 400\n  · usage.cacheHitRate  this log has no cache columns at all, which is not the same as a hit rate of zero\n  · usage.batchEligible  whether the work can wait for a batch window is a product decision, and no log records it\n  · labels  1 label in the log, and nothing here proves which prompt file sends which\n  · spend.maxUsd  a budget is a policy, so it is yours to set — the measured figure is $38.40 over 30 days\n\nThe most valuable thing found\n  240 calls labelled \"classify\" went to Claude Opus 5 over 30 days.\n  They cost $38.40.\n  The same work fits Claude Sonnet 5, which is cheaper per token.\n  The Batch API halves both halves of the bill, for work that can wait.\n  Together: $30.72 over the same 30 days.\n```\n\nIt is a **detection, not a wizard**. Nothing is asked. Each line above is\nsomething that was found — a prompt, a provider named in your code, a log —\nor a key it declined **with what would settle it**. `--dry-run` prints the\nconfig and writes nothing; `--yes` replaces one that is already there;\nwithout it an existing config is left alone.\n\n**Every key it writes carries the arithmetic that justified it.** A generated\nconfig full of guessed thresholds is one nobody trusts and everybody deletes,\nand it is worse than an empty one, because six weeks later it reads as a\ndecision somebody made.\n\nFour things it refuses to write, and they are the interesting four:\n\n- **A budget.** A log says what your traffic *was*; a budget says what it *may\n  cost*, which no log can answer. \"The measured month plus twenty per cent\"\n  would be this tool inventing a threshold and then grading you against it. So\n  the measured figure is handed over and the limit stays yours.\n- **A monthly rate from a short window.** Twenty-eight days minimum, so every\n  weekday appears the same number of times. Four days multiplied by seven is a\n  forecast wearing a measurement's clothes.\n- **A cache hit rate from a log with no cache columns.** Not recorded is not\n  not-happened. Writing `0` there would tell every later caching advisory that\n  caching is doing nothing — a finding invented out of a missing field.\n- **`batchEligible`, in either direction.** Whether the work tolerates a batch\n  window is a product decision, and no log records it. `false` would quietly\n  delete the batch lever from every report; `true` would sell a saving on\n  latency nobody agreed to give up.\n\nIt also declines a model when your code names a *provider* and no model. `where`\nprints a provider's default because a reader can see it is a guess; a config\nfile cannot.\n\nNo usage anywhere? It says so, and points at\n[docs/usage-logs.md](docs/usage-logs.md) — Anthropic, OpenAI, the Vercel AI SDK\nand an OTel collector, with records you can copy.\n\n`trazum init --json` is the same proposal as data, including every declined key\nand its reason — [contracted in docs/json-output.md](docs/json-output.md#the-first-run-document).\nIt writes nothing.\n\n### CLI\n\n```bash\nnode packages/cli/dist/index.js optimize prompt.txt --calls 50000 --diff\n```\n\n```\nInput tokens\n  190 → 137   -27.9% (estimated, ±6%)\n\nRules applied\n  [safe] Repeated paragraphs (1×, ~19 tokens)\n  [safe] Wordy phrasing (1×, ~3 tokens)\n  [safe] Politeness formulas (4×, ~19 tokens)\n  [safe] Filler and throat-clearing (2×, ~11 tokens)\n\nCost with Claude Opus 5\n  50,000 calls/month · 300 output tokens per call\n  $422.50 → $409.25   saving $13.25/month (3.1%)\n\nBeyond shortening the prompt\n  → This task may not need Claude Opus 5 ~$327.40/month\n  → If the work tolerates latency, use the Batch API ~$204.62/month\n```\n\nEvery other command, each with its own chapter in [the command\nreference](docs/commands.md):\n\n```bash\ntrazum doctor                        # survey the whole workspace\ntrazum plan usage.jsonl              # the findings as a ranked plan\ntrazum verify plan.json --against new.jsonl   # did it work?\ntrazum history reports/              # the long run, from stored reports\ntrazum connect anthropic             # your bill, read from the provider\ntrazum store                         # what is kept, and what a prune takes\ntrazum watch --once                  # did anything cross, this afternoon\ntrazum serve                         # answer before the call is sent\ntrazum rollup a.json b.json          # several people's bills, one roll-up\ntrazum profile usage.jsonl --html-out report.html   # the report somebody forwards\ntrazum pulse --max-stale-hours 36    # did anything stop running?\ntrazum bench                         # how fast is Trazum on this machine\ntrazum rank prompts/                 # which one to fix first\ntrazum blame prompts/system.txt      # who made it expensive, and when\ntrazum diff old.txt new.txt          # what this edit cost\ntrazum check prompts/ --max-tokens 2000\ntrazum eval prompts/system.txt --cases cases.json\ntrazum where src/agent.ts            # which provider this actually calls\ntrazum models                        # pricing table and cache minimums\ntrazum rules                         # what each rule does, and its id\ntrazum --help\n```\n\nWhen redirected it writes only the optimised prompt, so it pipes cleanly:\n\n```bash\ncat prompt.md | node packages/cli/dist/index.js optimize - > prompt.optimised.md\n```\n\nTo install it as a `trazum` command:\n\n```bash\nnpm link -w @trazum/cli\n```\n\n**Token budgets in CI.** `trazum check` exits 1 when the prompt busts its\nbudget, so a template that grows unchecked breaks the build instead of the bill:\n\n```bash\ntrazum check prompts/system.txt --max-tokens 2000\n# FAILED 2,481 tokens busts the budget of 2,000.\n#   Optimised with \"trazum optimize --level safe\" it would land at ~1,913 tokens and fit.\n```\n\n**Before it reaches CI: a pre-commit hook.**\n\n```bash\nln -s ../../scripts/pre-commit .git/hooks/pre-commit\n```\n\n```\ntrazum: these prompts are over their token budget:\n  prompts/system.txt\n\n  trazum doctor .          shows how far over, and what it costs\n  Shorten them, raise the budget in trazum.config.json, or commit with --no-verify.\n```\n\n**It blocks only on prompts your commit actually touches.** A hook that refuses a\ncommit over a *different* prompt somebody else committed last month is one people\nlearn to pass `--no-verify` to — and then it is worse than no hook at all.\n`TRAZUM_HOOK=0` disables it; nothing staged, no Trazum installed, no prompts or an\nunreadable config each say so once and exit 0. One real limitation: it reads the\nworking tree, not the staged blobs, so it judges a prompt's newest edit even when\nan older version is what is staged.\n\nIn GitHub Actions, use the packaged action — nothing to install:\n\n```yaml\n- uses: actions/checkout@v7\n- uses: Davmunrey/Trazum@2cc44e4ea24f186dd8c9804f153eb271d21c9a6e  # 2.3.0\n  with:\n    target: prompts/system.txt\n    max-tokens: 2000\n```\n\n**One-click fixes, as suggestions.** `suggest-fixes: true` posts the optimised\nprompt as a GitHub *suggested change*, which a reviewer applies with one button:\n\n```yaml\npermissions:\n  contents: read\n  pull-requests: write\nwith:\n  target: prompts/\n  suggest-fixes: true\n  github-token: ${{ secrets.GITHUB_TOKEN }}\n```\n\n**A suggestion, not a commit, and that is deliberate.** Committing the fix would\nneed `contents: write`; a suggestion lands in the same place with the same one\nclick on the `pull-requests: write` the comment mode already uses, and you stay\nthe one who commits. Two limits, both real: it uses the **safe** level only — a\none-click apply is not the moment for a diff that wants reading — and a\nsuggestion can only anchor to lines in the pull request's diff, so a PR that\nedits three lines of a forty-line prompt gets a notice explaining why there is\nno suggestion rather than a partial rewrite.\n\n**Pinned to a commit SHA, not a tag** — the same rule\n[SECURITY.md](SECURITY.md) states and `security.test.js` enforces on every\nthird-party action in this repository. A tag is a mutable pointer: whoever can\nmove `v1` can change what runs in your workflow with your token. The `# 1.0.0`\ncomment names the version at that commit, and is what Dependabot reads to offer\nyou the bump.\n\n**The report lands in the run summary automatically** — every run, pass or fail,\nwith no token and no permissions. To also post it as a pull request comment that\nreplaces its own previous one:\n\n```yaml\npermissions:\n  contents: read\n  pull-requests: write     # the action cannot grant itself this\n\nsteps:\n  - uses: actions/checkout@v7\n  - uses: Davmunrey/Trazum@2cc44e4ea24f186dd8c9804f153eb271d21c9a6e  # 2.3.0\n    with:\n      target: prompts/            # a directory uses trazum.config.json budgets\n      comment: true\n      github-token: ${{ secrets.GITHUB_TOKEN }}\n```\n\n**Commenting can never fail your build.** No pull request, comments disabled, or\na read-only token — each prints a notice and carries on, because the report has\nalready reached the run summary. That matters on **pull requests from forks**,\nwhere `GITHUB_TOKEN` is read-only by design and the comment simply will not post.\n\nIf you go looking for a way around that, the answer you will find is\n`pull_request_target`. **Don't.** It runs with a writable token against the base\nrepository while checking out code the contributor controls, which turns \"we\nwanted to comment on a PR\" into arbitrary code execution with your secrets. The\nrun summary is there precisely so you do not need it. Trazum asserts in CI that\nit uses `pull_request_target` nowhere.\n\nA passing report is collapsed; a failing one is not. A green table that stays\ngreen on every push is the thing you learn to skip — and then you skip the red\none too.\n\n**The spend gate, packaged.** The same action gates the bill itself when handed\na usage log instead of prompts — mutually exclusive with `target`, because one\nrun gates tokens before the money is spent or the spend itself, and saying\nwhich is the caller's job:\n\n```yaml\n- uses: Davmunrey/Trazum@2cc44e4ea24f186dd8c9804f153eb271d21c9a6e  # 2.3.0\n  with:\n    usage-log: logs/yesterday.jsonl\n    max-usd: '50'            # exit 1 over budget — no period assumed\n    # against: logs/day-before.jsonl\n    # max-growth-usd: '10'\n    # label: chat            # one workload's budget\n    # since: '2026-08-11'    # one period's — until includes its whole day\n    # until: '2026-08-17'\n```\n\nThe profile report lands in the run summary either way, and a failing gate\nstill writes it — a red build with no report is a mystery, and mysteries get\ndeleted from pipelines.\n\n**The report leaves the terminal in three shapes.** `--markdown-out` for a CI\nsummary or a PR comment, `--csv-out` for whoever signs off the bill (one row\nper workload and model, no total row, empty cells where dollars are unknown),\nand `--json` for anything built on top — documented field by field in\n[docs/json-output.md](docs/json-output.md), with a `schemaVersion` and a test\nthat fails if the two ever disagree. Point `profile` at a **directory** and a\nmonth of rotated logs is read in name order as one bill.\n\nOr by hand, if you already have the repo checked out:\n\n```yaml\n- run: npm ci && npm run build\n- run: node packages/cli/dist/index.js check prompts/system.txt --max-tokens 2000\n```\n\n### The rest of the commands, in their own book\n\n`optimize`, `check` and `init` above are the front door. Every other\ncommand has its own chapter — same prose, same worked examples, one page —\nin **[the command reference](docs/commands.md)**: the\nmeasured multiplication (`--from-log`), the cache reorder, the CI baseline,\nthe fleet, the plan and its verification, the provider pull, the gateway,\nthe evaluations that spend money and say so first, and everything else the\n[table above](#the-48-commands) links to.\n\n`trazum --version` prints the version on its own, and works when your config is\nbroken — which is exactly when somebody is asking.\n\n### Web\n\n<div align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"docs/assets/web-dark.png\">\n    <img src=\"docs/assets/web-light.png\" alt=\"The Trazum web app: the grouped rail on the left with direct GitHub, npm and documentation links; a wordy support prompt in the middle; and on the right a result panel reading minus 29.8 percent, 258 to 181 input tokens, and $3.85 a month saved.\" width=\"820\">\n  </picture>\n</div>\n\n```bash\nnpm run build:web\nnpm run dev:web        # http://localhost:3000\n```\n\nAn interface for pasting a prompt, tuning the usage scenario, and reading the\nword-by-word diff, the saving and the advisories. Includes optimisation history\nstored only in the browser — nothing leaves your machine.\n\n**Reordering for the cache is available here too**, behind a checkbox rather than\na level, with the same warning the CLI prints and the same refusals reported. It\nis the largest saving Trazum can make, and it should not need a terminal to find.\n\n**And there is a Compare tab.** Two versions of a prompt, and what the edit did:\nthe token delta, what it costs per month, and which advisories and rules it\nintroduced or resolved. Every figure is `after - before`, so **positive means\nworse** — the opposite of the rest of Trazum — and the page says so above the\nnumbers rather than beside them, because a reader arriving from Optimise has the\nopposite convention already loaded.\n\n`Compare what the rules would leave` is off by default and the default is the\ninteresting half: your edit changed the text as written, so the text as written is\nwhat you are being asked about. Trimming both sides first hides a prompt that\ndoubled in length and happened to double in courtesy.\n\nThe usage scenario is shared between the two tabs. Setting 50,000 calls on one and\nreading 10,000 on the other would make their answers incomparable while looking\nlike they were about the same workload.\n\n**So are phrase-level rewrites.** Two switches: one asks the model for\nsuggestions, the second takes them. They are listed above the saving, one line\neach — `You should always make sure to → Always  ~4 ×2` — with a count of how\nmany the checks threw out, because \"four did not survive\" is the useful fact and\nwhich four is noise unless you are debugging the model. Nothing is applied unless\nthe second switch is on, and turning the first one off clears it.\n\n**And a \"Your bill\" tab**, which is [`trazum profile`](#where-the-money-actually-went-trazum-profile)\nin the browser: drop or paste a usage log and read where the money went — the\nspend split, whether caching paid for itself, the levers that would actually\nmove the bill, conversation growth, and the answers that were cut off\nmid-generation. The log is parsed entirely in the page against the bundled\npricing catalogue. **Nothing is uploaded**: there is no fetch in that\ncomponent, a test fails if one appears, and the only analytics event carries\ntwo booleans. A usage log names your workloads, spend and conversation counts —\nexactly the file nobody should have to hand to a server to see a report on it.\n\nThe drop zone reads more than logs. A **Claude Code project folder** —\n`~/.claude/projects` as it sits on disk — prices every transcript in the page,\nlabelled by project, with the counts crossing and never the words. An\n**OpenTelemetry export** prices its GenAI spans the same way. And a **price\ncard** — an OpenRouter `/models` response, or the same overlay JSON a\n`--pricing` file holds — widens the catalogue every figure in the tab prices\nwith, so a model the bundled snapshot has never met (your Qwen, your\nself-hosted rate from [`trazum ownrate`](docs/commands.md#the-model-you-run-yourself-trazum-ownrate))\ngets the same exact arithmetic, still without a single request leaving the page.\n\n**Under the report, the rest of the loop.** The ranked plan — each action with\nits money as a projection *or* a measured stake and never both, the typed\nassumption it rests on, and the command that would check that assumption — and\nbelow it, *Did it work?*. **Save plan.json** writes byte-for-byte what\n`trazum plan -o` writes, so a plan made in a tab can be committed, gated on in\nCI, and opened back here later. Opening a saved plan turns the log in the tab\ninto the check on it: three outcomes, never two, with the three cannot-tell\nreasons kept distinct. Saved as a file rather than offered as a link, because a\nlink would mean this page storing somebody's bill somewhere — an access-control\nquestion nobody has designed. [The plan format is documented](docs/plan-format.md).\n\n**A guided tour walks the public tabs** — Optimise, Write, Compare, Bill and\nthe Playground — ringing each panel in place with a sentence on what it\nanswers. It never auto-plays: a first visit is offered it once, and the compass\nin the rail starts it any time after. The **Playground** tab is the CLI itself\nin the page — 13 commands that spend nothing and touch no network, over\nsample files already loaded, through the same `@trazum/core` functions the\nterminal runs, so `trazum profile usage.jsonl` can be tried before anything is\ninstalled, against data that never existed outside the browser.\n\nThe HTTP API behind it is public and small:\n\n```bash\n# Metadata: models, and whether an LLM is configured on the server\ncurl https://your-deployment/api/optimize\n\n# Optimise\ncurl -X POST https://your-deployment/api/optimize \\\n  -H 'content-type: application/json' \\\n  -d '{\n    \"prompt\": \"Please, in order to help me, analyse {{x}}. Thanks!\",\n    \"level\": \"safe\",\n    \"locale\": \"en\",\n    \"reorder\": false,\n    \"suggest\": false,\n    \"applySuggestions\": false,\n    \"usage\": { \"model\": \"claude-opus-5\", \"callsPerMonth\": 20000, \"avgOutputTokens\": 300 }\n  }'\n```\n\n`reorder`, `suggest` and `applySuggestions` are honoured only on a literal `true`\n— the body is untrusted, and a truthy check would let `\"false\"` rearrange\nsomebody's prompt. With `reorder`, the response carries what moved and what was\ndeclined, and `original` stays the text you sent so a diff shows the move. With\n`suggest`, it carries every proposal that survived the checks and everything\nrejected and why — present even when the model proposed nothing, so \"nothing was\nfound\" is distinguishable from \"you did not ask\". `applySuggestions` without\n`suggest` is a **`400`**, not a no-op, refused before any call to the model.\n\n```bash\n# Compare two versions: what did this edit cost?\ncurl -X POST https://your-deployment/api/compare \\\n  -H 'content-type: application/json' \\\n  -d '{\n    \"before\": \"Classify {{x}}. Answer with the category only.\",\n    \"after\": \"Please kindly classify {{x}}. Thank you!\",\n    \"optimizeBoth\": false,\n    \"usage\": { \"model\": \"claude-opus-5\", \"callsPerMonth\": 50000 }\n  }'\n```\n\n`POST /api/compare` returns every figure as `after - before`, so **positive\nmeans worse**. Both endpoints are rate limited (30/min per IP), with a bucket\neach. And `/api/optimize` will not fetch an LLM endpoint a caller names: a\nrequest may only **select** one from `TRAZUM_ALLOWED_LLM_ENDPOINTS`, empty by\ndefault — stricter than filtering the URL, because a hostname an attacker\nregistered can resolve wherever they like. See [SECURITY.md](SECURITY.md).\n\n### Signing in (optional)\n\nOff by default, and a deployment that leaves it off is the tool this README has\nbeen describing all along: paste a prompt, get an answer, nothing remembered.\n\nSet three variables and the sidebar grows a **Sign in** button at its foot:\n\n```sh\nTRAZUM_GITHUB_CLIENT_ID=Iv1.xxxx\nTRAZUM_GITHUB_CLIENT_SECRET=xxxx\nTRAZUM_PUBLIC_URL=https://trazum.example\n```\n\nA fourth, `TRAZUM_DATABASE_URL`, points it at any Postgres so sign-in survives a\nrestart; without it sessions live in memory and the account menu says\n\"temporary session\". Trazum asks GitHub for `read:user` and nothing else, **never stores\nthe access token**, and stores session cookies only as their SHA-256.\nMisconfigure any of it and sign-in simply stays off, with `/api/auth/*`\nanswering 503 naming the variable to set.\n\nSigned in, a **Library** tab appears: prompts you saved and every version of\neach, append-only, token counts recomputed on read rather than stored. On the\nCompare tab, **Create share link** publishes a comparison at `/c/<token>` for\nanyone holding the URL — expiring after thirty days by default, revocable, kept\nout of search engines, and saying what it publishes *before* the button. Every\nshare link doubles as a **README badge** at `/badge/<token>.svg`, recomputed on\nevery load, with no script and no prompt text. Set `TRAZUM_ADMINS` and `/admin`\ntotals what every prompt on the deployment adds up to — names and token counts,\nnever anybody's prompt text, and deliberately not a spend report.\n\n[docs/accounts.md](docs/accounts.md) has the setup, the schema, every security\ndecision and why, the limits, and an explicit list of what is **not** covered.\n\n### Deploying to Vercel\n\nThe repo is an npm workspaces monorepo; Vercel handles it with no special\nconfiguration:\n\n1. Import the repository in Vercel.\n2. **Root Directory**: `apps/web`. The rest — installing from the workspace\n   root, building `@trazum/core` via `prebuild` — is automatic.\n3. Optional variables: `TRAZUM_LLM_*` to offer the LLM pass without users\n   supplying keys, `NEXT_PUBLIC_POSTHOG_KEY` for analytics, `TRAZUM_GITHUB_*`\n   and `TRAZUM_PUBLIC_URL` for sign-in.\n\nVercel runs more than one instance, so if you enable sign-in there, set\n`TRAZUM_DATABASE_URL` as well. Without it each instance keeps its own sessions\nin memory and a browser is signed in against one and signed out against the\nnext.\n\n### Library\n\n```ts\nimport { optimize, refineWithLlm, openAiCompatible } from '@trazum/core';\n\nconst result = optimize(prompt, {\n  level: 'safe',\n  locale: 'en',\n  usage: {\n    model: 'claude-opus-5',\n    callsPerMonth: 50_000,\n    avgOutputTokens: 500,\n    cacheHitRate: 0.9,\n    batchEligible: false,\n  },\n});\n\nconsole.log(result.optimized);\nconsole.log(result.savings.monthlySavingsUsd);\n```\n\n`reorderForCache` is the API behind `--reorder`. It returns the original text\nunchanged when nothing can safely move, and always reports what it declined and\nwhy — a saving Trazum chose not to take is one the caller cannot evaluate:\n\n```ts\nimport { reorderForCache } from '@trazum/core';\n\nconst r = reorderForCache(prompt, { minPrefixTokens: 1024 });  // the model's minimum\n\nr.text;                 // the rearrangement, or `prompt` byte-for-byte\nr.tokensMoved;          // moved out of paid-every-call into the prefix\nr.prefixTokensBefore;   // 14\nr.prefixTokensAfter;    // 1174\nr.declined;             // [{ reason: 'backward-reference', phrase: 'above', text }]\n```\n\n`minPrefixTokens` is a bar on the **resulting prefix**, not on the amount moved.\nA prefix below the model's minimum caches nothing at all, so a rearrangement that\ndoes not clear it buys nothing — but a head that already clears it gains from any\nblock that joins it, however small.\n\n`comparePrompts` is the API behind `trazum diff`. Note the sign: everything it\nreturns is `after - before`, so **positive means worse** — the opposite of\n`result.savings`, and the reason it lives in its own module.\n\n```ts\nimport { comparePrompts, formatSignedUsd } from '@trazum/core';\n\nconst change = comparePrompts(oldPrompt, newPrompt, { usage });\n\nchange.tokenDelta;                      //  +37   (grew)\nformatSignedUsd(change.monthlyDeltaUsd) //  \"+$9.25\"\nchange.advisories.appeared;             //  ['contradictory-instructions']\nchange.rules.noLongerFiring;            //  what the edit cleaned up\n```\n\n**Two entry points.** `@trazum/core` is browser-safe and imports no Node\nbuiltins — that is enforced by a test that walks the import graph, not by\nconvention, because the web app bundles it and one `node:fs` import anywhere in\nthat graph fails the build. Anything that reads the filesystem lives on\n`@trazum/core/node`:\n\n```ts\nimport { loadConfig, walkPrompts } from '@trazum/core/node';\n\nconst { config, path } = await loadConfig();   // null path = none found\nconst { files, truncated } = await walkPrompts('prompts/');\n```\n\n`parseConfig` and `budgetFor` are pure functions of their arguments, so they sit\non both.\n\n---\n\n## Languages\n\nReports are available in **English and Spanish** (`--locale es`, or the\nbrowser's language on the web). A locale changes the report, never the\noptimisation: same optimised text, same token counts, same advisory ids in\neither language. The full story — what is translated, what is deliberately\nnot, and how the report and the prompt each pick their language — is in\n[the command reference](docs/commands.md#languages).\n\n## Connecting your own LLM\n\nThe optional LLM pass (`--suggest`, `eval`, `route`) speaks to whichever\nOpenAI-compatible or native endpoint you configure by environment — vLLM,\nOllama, OpenRouter, Anthropic, Gemini, Bedrock and Vertex included, keys\nnever stored. Configuration, refusals and the `--pricing-live` overlay are\nin [the command reference](docs/commands.md#connecting-your-own-llm).\n## Every model you pay for by the token\n\nTrazum prices Anthropic, OpenAI, Google, Moonshot, DeepSeek, xAI and Mistral:\n\n**Every report says how old the prices are** — the date the table was checked and\nhow many days ago that was — because every dollar figure descends from that list,\nand a date on its own makes you subtract against today to learn whether to trust\nit. Past 45 days the report says so in a sentence, rather than leaving the reader\nto decide what \"old\" means.\n\nThe 7 providers publish independently, so each carries its own review date and\n`trazum models` prints them; the headline figure above is the oldest of the seven,\nbecause the question \"how old is this table\" is about its worst part.\n\n```bash\ntrazum optimize prompt.txt --model gpt-5 --calls 50000\ntrazum optimize prompt.txt --model kimi-k2\ntrazum models                      # the whole table, with each provider's terms\n```\n\nEverything that reads the prompt is provider-agnostic already — the rules, the\nprotection pass, `--reorder`, the contradiction and example detectors all operate\non text. What differs is the money, and **that is not one set of numbers**:\n\n| | |\n|---|---|\n| Cache read | 10% of input on Anthropic, OpenAI and Moonshot; **25%** on xAI. Two providers changed it **between generations**: DeepSeek V4 reads at about **3%** where V3 read at 10%, and Google's 3.6 Flash reads at 10% where the retired 2.5 models read at 25% |\n| Cache write | 125% of input on Anthropic; 100% elsewhere |\n| Cache minimum | **Per model, not per provider.** Anthropic alone spans 512 to 4,096; 1,024 on OpenAI, Moonshot, DeepSeek, xAI and Gemini Flash; 2,048 on the retired Gemini Pro |\n| How caching starts | You mark the prefix on Anthropic and Google; it is **automatic** on OpenAI, Moonshot, DeepSeek and xAI |\n| Batch API | 50% on Anthropic, OpenAI, Google and Mistral; **none at all** on Moonshot, DeepSeek and xAI |\n| Prompt caching | **None at all** on Mistral |\n\n**The cache minimum is the row to read twice, and it used to be wrong here.** This\ntable said *\"512 on Anthropic\"* flatly. Anthropic's floor is a property of the\nmodel: 512 on Fable 5, Mythos 5 and Opus 5; 1,024 on Opus 4.8, Sonnet 5 and\nSonnet 4.6; 2,048 on Opus 4.7; **4,096 on Opus 4.6 and Haiku 4.5**. A reader on\nHaiku who trusted \"512\" would have built a prefix eight times too short and been\ntold caching would save money that could never arrive — the one direction this\ntool must never be wrong in. `trazum models` prints the real figure per model,\nand every cache advisory has always used it; only this table was wrong.\n\nThose last two rows are why the multipliers had to move onto the model. As global\nconstants they offered a batch discount to providers that do not sell one and a\ncaching saving to a model that has no cache — invented savings, which is the one\nthing this tool must not print. A provider with no batch API now gets no batch\nadvisory, and no discount even if you tick the box: `batchEligible` describes the\nwork, not what the provider sells.\n\n**A cheaper model means a cheaper model, not a different supplier.** The downgrade\nadvisory only ever suggests models from the provider you are already on. Dropping\na tier is a one-line change; switching vendor is a migration, and this advisory is\na keyword heuristic — it has no business recommending that you change supplier.\n\n**Not covered: Cursor, Claude Code, Codex and other subscriptions.** They do not\nbill per token, so \"saves $184/month\" would be false for anyone inside their plan.\nThe honest saving there is context-window and rate-limit headroom, which is a\ndifferent report rather than a row in this table.\n\n### Optimising a prompt that lives in code\n\n```bash\ntrazum optimize src/prompts.ts --prompt support --diff\n```\n\nIt reads the marked prompt and leaves the file alone. **Pointed at an unmarked\nsource file it refuses**, because optimising TypeScript as if it were prose does\nnot produce a worse prompt — it produces broken code, and `-o` would write that\nback over your file. When a file holds several marked prompts it asks which one\nrather than taking the first.\n\nThe model comes from the code too, so a file calling OpenAI is priced against\nOpenAI. `--model` and `trazum.config.json` still win: flags beat config, config\nbeats detection, detection beats a built-in default that has no idea which\nprovider you use.\n\n### Which provider is this prompt even going to?\n\nSince Trazum prices 7 providers, defaulting to Claude became a **wrong\nnumber**: a file calling OpenAI was billed against Claude Opus 5 without comment.\n`trazum where` reads what the code already says.\n\n```bash\ntrazum where src/prompts.ts\n```\n\n```\nRunning inside\n  Claude Code (CLAUDECODE)\n  Claude Code bills by subscription, not by the token. A monthly saving below is\n  arithmetic about tokens, not money you get back — what you gain is context\n  window and rate-limit headroom.\n\nPrompts in src/prompts.ts go to\n  anthropic · Claude Sonnet 5\n    line 2  model-literal: claude-sonnet-5\n    line 1  sdk-import: @anthropic-ai/sdk\n\nPriced as\n  Claude Sonnet 5 (read from the source)\n```\n\n**Every answer names the line it came from.** Four kinds of evidence, strongest\nfirst: `model=` on a `trazum:prompt` marker, a quoted model id, a base URL, an\nSDK import — and **a base URL beats the SDK it was pointed at**, because\nMoonshot, DeepSeek, xAI and Groq are all called through the OpenAI SDK with a\ndifferent `base_url`. **It refuses when a file names two providers**: picking\nsilently is how somebody budgets against the wrong provider for a month.\nDetection sits in the usual layering — a flag beats config, config beats\ndetection, detection beats the built-in default.\n\nWith no file it reports only the host — useful because that is what decides\nwhether a monthly saving is money at all:\n\n| Host | Bills |\n|---|---|\n| Claude Code, Codex, Cursor | subscription — the saving is context and rate-limit headroom, not cash |\n| GitHub Actions, CI | per token |\n| VS Code, plain terminal | unknown, and it says so rather than guessing |\n\n### On a subscription, there is no bill to reduce\n\nInside Claude Code, Codex or Cursor you pay the same whatever your prompt costs.\nA monthly figure there is arithmetic about tokens dressed as money, so Trazum\nstops printing one and reports what is actually scarce:\n\n```\nWhat this buys on Claude Code\n  Claude Code bills by subscription, so there is no bill to reduce and no\n  monthly figure to print.\n\n  1,001 tokens back, every call.\n  Context window: 12.4% → 2.1% of Claude Opus 5's 1,000,000 tokens — room the\n  conversation gets instead.\n  Pass --cost if this prompt is bound for a metered API.\n```\n\nThe context window is the real currency in an agent: every token the system\nprompt holds is one the conversation cannot.\n\n**Advisories whose only pitch is money go too.** \"Use a cheaper model\" is not\nweaker advice on a flat plan — it is not advice. `model-downgrade`, `batch-api`,\n`output-dominated` and `promo-pricing` are dropped; caching, context overflow,\ncontradictions and redundant examples stay, because latency, headroom and\ncorrectness are still real.\n\n**The escape hatch matters.** The host says where *Trazum* runs, not where your\nprompt goes — somebody editing a production prompt inside Cursor wants the\ndollars, and `--cost` gives them back without leaving the editor. `--tokens-only`\nforces the other direction anywhere.\n\n## Where the money actually went: `trazum profile`\n\nThe command the rest of the product orbits: hand it a usage log (or a\ndirectory of rotated ones) and it reads what the provider actually charged —\nthe spend split, whether caching paid for itself, conversation growth, the\nanswers cut off mid-generation, and the levers that would genuinely move\nthe bill, priced from your own calls. It reads counts, never content; a\nsession key groups and is never printed; a model it cannot price is named\nand kept out of the totals rather than silently absorbed.\n\nThe full chapter — the record format, `--against`, `--what-if`, the cache\npost-mortem, the conversation ceiling, every gate — is in\n[the command reference](docs/commands.md#where-the-money-actually-went-trazum-profile).\n\n## Where this fits, said at the front door\n\nEvery `optimize` run closes with the same sentence, because it is the truth about\nwhat the command just did:\n\n```\n  Shortening a prompt is the smallest lever there is: measured on an ordinary\n  support prompt, the rules recover about 1% of a monthly bill. On a metered\n  API the things that move 60% to 80% are which model the call goes to, the\n  Batch API, prompt caching, and what re-sending the conversation costs — and\n  \"trazum profile <usage.jsonl>\" prices all four from what the provider\n  actually charged.\n```\n\nA tool whose first command reports 1% and says nothing about the other 99% has\nnot told you what it knows.\n\n## Token counting\n\nA dependency-free estimator with a **band measured per kind of text** — ±4% on\nCJK, ±6% on Latin prose, ±26% on code and markup, ±33% on tabular numbers —\nmeasured over 47 samples in ten languages, where the worst error in each bucket\nis 3.2%, 5.6%, 25.1% and 32.5%. Language-aware because one English divisor was\n37% wrong on German. `--exact-tokens` settles any doubt\nagainst the provider's free counting endpoint. The measurement story is in\n[the command reference](docs/commands.md#token-counting).\n---\n\n## Limitations, stated plainly\n\n- **Savings are projections, not billing.** They are computed over the scenario\n  you describe (calls/month, output tokens) using the table in\n  `packages/core/src/pricing.ts`. Check that table before budgeting:\n  `PRICING_LAST_REVIEWED` tells you when it was last updated.\n- **Output tokens are held constant in the calculation.** A shorter prompt\n  often produces somewhat shorter answers, but that depends on the task and\n  cannot be promised. The saving shown comes from input only.\n- **The model recommendation is a keyword heuristic**, not a judgement about\n  answer quality. Measure the difference with your own evaluations before\n  dropping a tier in production.\n- **The aggressive level can change nuance.** It removes intensifiers, hedges\n  and self-verification requests. Read the diff before applying it.\n- **Amazon Bedrock and Vertex AI pricing is set by each partner** and is not\n  the pricing in this table.\n\n---\n\n## Layout\n\n```\npackages/core/     dependency-free library (rules, tokens, pricing, LLM)\n  src/segment.ts     isolation of code, URLs, emails, templates and XML\n  src/rules.ts       deterministic rules engine\n  src/phrases.ts     phrase dictionaries (data, multilingual)\n  src/pricing.ts     model and pricing catalogue\n  src/structure.ts   contradictions and repeated few-shot examples\n  src/similarity.ts  shared near-duplicate scoring\n  src/advisories.ts  caching, batch, model and context advisories\n  src/reorder.ts     moving blocks in front of the first placeholder\n  src/compare.ts     comparePrompts — after minus before, positive is worse\n  src/profile.ts     the measurements behind `rank`, and no score\n  src/suggest.ts     phrase-level rewrites, and the checks each one passes\n  src/promptfoo",
  "bytes": 60000,
  "sha": "7683aa3c629c11d3dc4f086991aab1eb7a911ce2a63f0f67e944ba06c3c33f28",
  "repo_slug": "davmunrey/trazum",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_davmunrey_trazum_5dad84e1/readme"
}