Imported from okyashgajjar/costwise-mcp (
AGENTS.md). Install upstream withnpx skills add okyashgajjar/costwise-mcp. Copyright stays with the author.
Context for AI Coding Agents
Last updated by big-pickle on 2026-06-10.
V2 Foundation (in progress — branch feat/v2-foundation)
Goal: cut the dominant cost of long single sessions — Anthropic prompt-cache write/read, not model output. Evidence: a single call charged $2.95 where $2.84 was a 5-min cache write of ~455k tokens; output was only ~3.9k. The MCP cannot control when/how the client caches (breakpoints/TTL are client-owned); its only lever is reducing how many tokens ever enter the resident context window (the thing re-cached every turn). Every V2 piece serves that one goal.
Three steps:
- DONE — compact
repo_summary.BuildRepositorySummaryCompact(ks, budget, module)ininternal/retrieval/repository_summary.go: token-budgeted (reusesparseBudget), top modules by symbol count ++N morerollup, dropped the unboundedLayerschain, optionalmoduledrill-down. Tool gainedbudget+moduleparams. LegacyBuildRepositorySummary/Format()untouched. (50-module synthetic: 1192→216 tokens; capped regardless of repo size.) - DONE (code) — 3 cache-reducing tools in
internal/mcpserver/tools.go:remember(repo_path,key,fact)(durable fact →kmemoryUserNote+ per-reposession_facts.json),stash_context(repo_path,content,label?)(park large blob out of window → tiny handle; file-backedinternal/stashat<repoRoot>/.mycli-fts/stash/),recall(repo_path,query,source?,budget?)(query-scoped read of a stash by handle, or facts; hard-capped via step-1 budgeting). Stores wired intoRepoSession(Stash,FactsPath,RememberFact,RecallFacts). Chosen over compact/summarize/forget because the user requires no context drop — stash is lossless (relocates tokens, re-fetchable). All tool outputs stay tiny. New names added toclaude.goallow-list. Tests:internal/stash,internal/session/repo_session_v2_test.go. - DONE —
costwise-sessionskill (session-awareness). Teaches the model to keep the session lean (route large content through stash/recall, remember durable facts, prefer narrow retrieval). Single embedded source of truth:internal/skill/policy.md(go:embed, ~275 tok). Delivered two ways: (a) automatic/cross-IDE viaserver.WithInstructions(skill.Instructions())ininternal/mcpserver/server.go— every MCP client auto-loads it, zero install; (b) native Claude Code SKILL.md viacostwise skill {install,uninstall,print}(cmd/skill.go) writing~/.claude/skills/costwise-session/SKILL.md(or.claude/...with--local).installwrites the skill by default (opt out--no-skill);uninstallremoves it. Other IDEs rely on the instructions field +skill printfor manual placement.internal/skillis standalone (NOT in the Target interface). Tests ininternal/skill/skill_test.go.
V3 Enterprise Capabilities (Completed)
- Semantic Search via Bluge: Replaced SQLite FTS with Bluge inverted index for
search_code, enabling fuzzy matching and BM25 scoring natively. - LSIF Ingestion: Added
.lsifsupport for compiler-verified reference tracing viafind_references. - Zero-Latency CI/CD Caching: GitHub Action integration (
costwise-action) to build and fetchcache.dbfrom remote artifacts. - Shared Team Cache: Remote Postgres adapter supporting
COSTWISE_PG_URLfor shared stash and facts memory. - Policy Engine: Centralized AST-based architecture checking (
costwise-architecture.yaml) enforced by thecostwise validatecommand andvalidate_architectureMCP tool.
LANDMINE: repo_memory/discovery_memory Init with shared os.TempDir() paths (NOT per-repo) — a clobber risk (same class as the shared-index bug). New V2 stores MUST be per-repo (derive from repoRoot like treesitter.NewSymbolDB/cache.NewCache).
Honest limit: these tools can't evict content the client already placed in context; they only help when the model routes new large content through them — which is what the step-3 skill enforces. MCP server-side state persists across tool calls via a process-global per-repo SessionCache (internal/mcpserver/session_cache.go).
CGO Requirement
CGO is mandatory. The project depends on:
github.com/mattn/go-sqlite3—//go:build cgoconstraintgithub.com/smacker/go-tree-sitter— C bindings via cgo
Builds with CGO_ENABLED=0 will fail. Always ensure CGO_ENABLED=1 (which is the default on most systems with a C compiler).
On Ubuntu/Debian: sudo apt install gcc libsqlite3-dev
On macOS: Xcode Command Line Tools (xcode-select --install)
On Windows: MinGW-w64 (mingw-w64)
Build & Test
# Build all packages (CGO must be enabled)
CGO_ENABLED=1 go build ./...
# Run all tests
CGO_ENABLED=1 go test ./...
# Build binary
CGO_ENABLED=1 go build -o costwise ./cmd/costwise/
# Build with version injection (for releases)
go build -ldflags="\
-X github.com/okyashgajjar/costwise-mcp/cmd.version=v1.0.0 \
-X github.com/okyashgajjar/costwise-mcp/cmd.commit=$(git rev-parse --short HEAD) \
-X github.com/okyashgajjar/costwise-mcp/cmd.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
-o costwise ./cmd/costwise/
Default version (no ldflags): dev
Injected version example: v1.0.0 with commit hash and build date
Release Process
- Tag the release:
git tag -a v1.0.0 -m "v1.0.0" - Push the tag:
git push origin v1.0.0 - CI runs test.yml (test+lint), then release.yml builds + publishes artifacts via GoReleaser
- GoReleaser creates GitHub Release with artifacts for all 5 targets
Release artifact verification
./costwise --version
# Expected: costwise v1.0.0
# commit: abc1234
# built: 2026-06-10T00:00:00Z
CGO Cross-Compilation Strategy (.goreleaser.yaml)
Targets: linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64
| Target | CC | Package |
|---|---|---|
| linux/amd64 | gcc |
(native on ubuntu-latest) |
| linux/arm64 | aarch64-linux-gnu-gcc |
gcc-aarch64-linux-gnu |
| darwin/amd64 | zig cc -target x86_64-macos |
zig |
| darwin/arm64 | zig cc -target aarch64-macos |
zig |
| windows/amd64 | x86_64-w64-mingw32-gcc |
gcc-mingw-w64-x86-64 |
CGO_ENABLED=1 set globally. Per-target CC via overrides block.
Local snapshot build
goreleaser release --snapshot --clean
# Artifacts in ./dist/
Local config validation
goreleaser check
Project Structure
/home/mryg/Research-Architectures/CLI/— Go module rootcmd/costwise/main.go— entry pointcmd/install.go— interactive install (detect → prompt → MCP config;--buildto rebuild)cmd/uninstall.go— remove MCP configs from configured clientscmd/doctor.go— diagnostic checks (binary, PATH, MCP configs, startup, repository)cmd/serve.go— MCP stdio servercmd/chat.go— chat modecmd/plan.go— plan modecmd/agent.go— agent modecmd/analyze.go— analyze modeinternal/installer/target.go— Target interface, BinaryPath, GetMcpServerConfig, shared helpersinternal/installer/binary.go— binary installation, verification, PATH checks, ActionableErrorinternal/installer/installer.go— Installer orchestrator with install, uninstall, repair modesinternal/installer/targets/— per-client targets (claude, cursor, opencode, codex, antigravity)internal/doctor/doctor.go— doctor checks (binary, PATH, MCP configs, startup, repository)internal/repository/state.go— 3-state index lifecycleinternal/repository/state_cli.go— CLI prompts/displayinternal/session/repo_session.go— session with NewRepoSession / NewRepoSessionWithoutIndex
Architecture
mycli chat|plan|agent|analyze
└─ DetectRepositoryState() → Unindexed | Stale | Ready
└─ chat/plan: prompt user; agent/analyze: auto-index
└─ NewRepoSession(with or without index)
└─ pipeline: AnswerType → KnowledgeMem → Cache → Retrievers → QualityGate → Compress → LLM
└─ Response Compression if output > 2x budget
└─ Learn() stores results back into KnowledgeMem
Answer Types & Output Budgets
| Type | Max Tokens | Evidence Required |
|---|---|---|
| yes_no | 10 | topScore >= 0.3 |
| location | 25 | topScore >= 0.3 |
| reference | 50 | >= 1 result |
| caller | 50 | >= 1 result |
| overview | 150 | >= 1 result |
| improvement | 200 | >= 3 results |
| feature_suggestion | 200 | >= 3 results |
| architecture_review | 250 | >= 3 results |
| repository_analysis | 300 | >= 3 results |
| explanation | 400 | >= 1 result |
| plan | 500 | >= 1 result |
| agent | dynamic | N/A |
Budgets enforced via API max_tokens, not prompt.
Key Files
internal/repository/state.go— DetectRepositoryState(), 3-state enum, hash comparisoninternal/repository/state_cli.go— PromptIndex(), PromptReindex(), Show*() display funcsinternal/session/repo_session.go— NewRepoSession (indexes), NewRepoSessionWithoutIndex (skips index)internal/kmemory/kmemory.go— session knowledge memoryinternal/answertype/classifier.go— 12 answer types with MaxTokens budgets and pattern matchinginternal/retrieval/pipeline.go— pipeline ordering, quality gates, system prompt builderinternal/retrieval/compress.go— context compression per answer typeinternal/retrieval/compress_response.go— response compression when output > 2x budgetinternal/retrieval/learn.go— auto-learning from resultsinternal/retrieval/repository_summary.go— RepositorySummary builder (modules, files, languages, symbols)internal/retrieval/improvement.go— Improvement struct, Impact/Effort/Confidence rankingcmd/analyze.go— repository analysis command (<300 token output)
Response Compression
After LLM returns, if output_tokens > MaxTokens * 2, a second LLM call compresses with:
"Rewrite answer in shortest possible form. Keep only actionable info. Remove explanations."
Repository Summary (for improvement/analysis types)
When user asks "improve", "analyze", "review architecture", "suggest features":
- Build RepositorySummary from KnowledgeStore (modules, files, languages, symbols)
- Send ONLY summary as context (never raw files)
- Apply hard output budget
- Compress if exceeded
Installer Design
internal/installer/ has three binary resolution modes:
| Mode | Entry Point | When |
|---|---|---|
| Use existing | EnsureBinary() |
Default: copies os.Executable() to ~/.local/bin/costwise |
| Build from source | InstallBinary() |
--build flag: requires Go toolchain + go.mod in parent tree |
| Repair | runRepair() |
--repair: maps to EnsureBinary() or InstallBinary() depending on --build |
EnsureBinary() resolution order:
- Binary already at
DefaultBinaryPath()and verifiable → return it os.Executable()returns a valid path → copy toDefaultBinaryPath(), return itexec.LookPath("costwise")succeeds → copy toDefaultBinaryPath(), return it- None found → return
ActionableErrorsuggesting--build
Key invariants:
- Default install does NOT require a Go module or
go.mod --buildis opt-in (defaultfalse)EnsureBinary()never callsfindGoModRoot()CheckBinary()includesos.Executable()as a candidate- Works from any directory (
/tmp, outside repo, etc.) - Cross-platform:
os.Executable()supported on Windows/macOS/Linux
Invariants
go build ./...must compile with zero errors (GOPATH warning is expected)go test ./...must pass- All 12 answer types integrate into existing pipeline without breaking it
- All 8 existing systems (SharedIndexer, SymbolDB, Auto Router, Query Classifier, retrievers, RepoSession, LRU Cache, Knowledge Store) remain as-is — new code integrates, never replaces.
- Pre-Commit Verification: ALWAYS run
golangci-lint runandgo test ./...locally before committing, pushing, or declaring a task complete. Fix any and all lint errors or failing tests prior to opening a PR. Do not rely on CI to catch these errors.
costwise-session
This project is connected to the costwise MCP server. Its tools keep the session cheap: in long sessions the dominant cost is the prompt cache re-reading everything in context each turn, so keep the window small. Apply this for the rest of the session — no need to be reminded again.
Route large content out of context, don't paste it inline.
- For any large output (file, long command/test output, generated report), call
stash_contextto park it and get a short handle, thenrecall(source=<handle>, query=…)to pull back only the slice you need. - Persist durable facts (a decision, an entrypoint, a gotcha) with
remember; retrieve them later withrecallinstead of re-deriving or re-pasting them.
Prefer narrow retrieval over reading whole files. Reach for a full file read only when a targeted query genuinely can't answer it.
- Pick the tool that fits:
find_symbolto locate,read_symbolto see an implementation body,find_references/find_callersfor usage,search_codefor conceptual questions,get_repository_summaryfor structure.recallreads remembered facts/stashes, not code. For raw regex over files, use the host's own grep. - Default budget unless insufficient — one
largecall can add ~10k uncached tokens.