Osnova

io.github.getdomovoi由 GitHub 帳號 getdomovoi 發布v0.12.0更新於 Oct 24, 2026

Deterministic code map for AI coding agents: a tree-sitter call graph with exact file:line over MCP.

已驗證STDIO僅桌面知識與記憶開發者工具

概覽

AI 產生的概覽

Osnova 以 tree-sitter 將程式碼倉庫索引成確定性的符號與呼叫圖,並透過 MCP 提供查詢,讓助理取得精確到 file:line 的定義、呼叫端與測試資訊。

可以做什麼
  • 依關鍵字檢索定義,並依符號分組尋找正規表示式或字面文字比對
  • 列出單一檔案的簽章與行範圍,並提供目錄叢集、樞紐與熱點
  • 追蹤直接或遞迴的呼叫端與被呼叫端,並附上把呼叫綁定到定義的證據
  • 針對問題彙整任務脈絡、某次變更的影響範圍,以及引用某符號的測試檔案
  • 核對宣稱的呼叫點,並列出沒有索引到呼叫端的定義
適用情境
適合助理在倉庫中修改或重構程式碼時使用:需要回答誰呼叫了某個函式、一次變更會影響什麼、哪些測試涵蓋某個符號,而不是靠文字搜尋猜測。對具備深度轉接器的七種語言(TypeScript、JavaScript、Python、Go、Rust、Java、C#)效果最好。
執行需求
需要 Node.js 22.13 或更新版本;以本機 stdio 程序執行,通常透過 npx 或全域安裝啟動。選用的 setup 指令會把 MCP 項目、鉤子與技能寫入 Claude Code、Codex、Cursor、OpenCode、Kilo 或 Pi。不需帳號、API 金鑰或網路連線;OSNOVA_CACHE_DIR 用來指定快取目錄。
安裝前請注意
setup 指令會寫入用戶端設定檔與鉤子;它會先預覽變更,只有加上 --apply 才寫入,並會備份被修改的檔案。選用的搜尋閘門會在本輪尚未執行 osnova 工具時拒絕 grep、rg、find 或搜尋工具呼叫。結果屬啟發式:呼叫端清單為空並不代表沒有呼叫端,未解析的呼叫會以候選身分另外列出。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Osnova,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[osnova. A deterministic code map for AI coding agents. Ten tools over MCP and CLI.]

Osnova

[website getosnova.dev] [npm version] [license Apache-2.0] [ci] [node >=22.13] [M8ven Verified]

A deterministic code map for AI coding agents. Osnova indexes a repository into a symbol and call graph with tree-sitter, then serves it to any MCP client or from the command line. Same input, same output, byte for byte. No embeddings, no telemetry, and no network connection unless you type osnova update-check. Twenty languages, seven of them (TypeScript, JavaScript, Python, Go, Rust, Java, C#) with deep adapters.

[The getosnova.dev film in eight scenes, measured on osnova 0.12.0 indexing its own source: the word osnova; 442 files read into 7,896 symbols and 35,343 edges; one function parsed into its tree-sitter tree; eleven of the calls refreshWorkspace makes, woven as threads; an agent asks who calls refreshWorkspace and osnova_warp answers with exact file and line; two fresh builds give identical hashes; the install command.]

Osnova is the Slavic word for base or foundation. That is the job: give an agent solid ground to stand on before it edits code.

[A coding agent asks osnova a question and gets back exact file and line, the resolution basis and an omission count; osnova reads a local cache that is built and refreshed from the repository by tree-sitter parsing.]

Quick start

Node.js 22.13 or newer. Serve a repository to any MCP client:

sh
npx -y @getdomovoi/osnova mcp --workspace /path/to/repo

After npm install -g @getdomovoi/osnova, one command per kind of agent: claude wires Claude Code (MCP entry, session, prompt and stop hooks, and the skill); agents wires every installed harness that reads AGENTS.md (Codex, OpenCode, Kilo, Pi, Cursor), each with its hooks, plugin or extension, plus one shared skill in ~/.agents/skills/. Each previews its changes; --apply writes them, backing up every file it changes.

sh
osnova setup claude --applyosnova setup agents --apply

Or add the MCP entry by hand; replace osnova with npx -y @getdomovoi/osnova when there is no global install.

json
{ "mcpServers": { "osnova": { "command": "osnova", "args": ["mcp"] } } }
[One agent turn in five steps: prompt, footing, edit, settle, review; footing and settle are answered by osnova from the index, the rest are the agent or the developer acting.]

What you get back

[A terminal recording: osnova warp lists the five resolved callers of click Context.invoke, each with its receiver hint; grep finds twelve .invoke( lines; osnova plumb checks those twelve and reports five confirmed, six name-only matches on other invoke methods and one line inside a docstring.]

osnova warp src/api.ts#refreshWorkspace on this repository, cut to twelve lines (test/docs-readme-warp.test.ts fails when the first two no longer reproduce):

text
function src/api.ts#refreshWorkspace: 83 indexed edgesreach: d1 callers 83 in 14 files (3 dirs); d2 +16 in 2 files; unresolved same-name 10; tests 18d1 calls src/cli/cli.ts#ensureIndex:78 [import-binding]d1 calls src/cli/hook.ts#runHook:191,203,225,240 [import-binding]d1 calls src/mcp/server.ts#createOsnovaMcpServer.refresh:297 [import-binding]d1 calls test/verification-fastpath.test.ts#<module>:37,47,48,51,53,57,58,64,68,74,78,85,110,114,132,145,154,166,171,197 [re-export-binding]  via src/index.ts:65 refreshWorkspace -> src/api.ts (export refreshWorkspace)This does not prove absence of callers or that deletion is safe.unresolved evidence (10); not confirmed relationshipscandidates for refreshWorkspace (2, unverified): src/api.ts#refreshWorkspace, test/mcp-watch.test.ts#refreshWorkspaced1 calls refreshWorkspace test/artifact-format9.test.ts:78,85,104,120,140 [binding-blocked]d1 calls refreshWorkspace scripts/perf.mjs:485,486,491 [import-target-unresolved]

Each confirmed line names the caller, its lines and the evidence that tied the call to this definition: an import binding, a same-file definition, a re-export chain with its hop, or an identified receiver. Calls the index could not tie to a definition are listed apart as unresolved evidence, with the same-name candidates it found and the reason it stopped, so a name match is never mistaken for a caller. The reach line gives exact counts, not scores, and when the list outgrows its budget a capped: line and an omitted: footer count what was left out.

How much of the graph is exact

A call site counts as resolved when the index ties it to one definition through evidence it can name; everything else stays unresolved with a reason. These are the shares on the pinned checkouts under benchmarks/corpora/, recorded in benchmarks/results/resolution-coverage-2026-09-21b.json; the last column leaves out calls through packages outside the repository and calls to builtins, which can never resolve locally, and the reference defines every column.

CorpusLanguagesCall sitesResolvedShareExcluding externals
clickall6593290344.0%62.4%
cobraall4374198045.3%88.9%
gsonall23382847336.2%56.7%
humanizerall30517779825.6%51.3%
pyrightall585783007051.3%74.5%
ripgrepall13387612145.7%71.6%
zodall534172163040.5%73.2%

Per-language rows are in the reference. osnova coverage reports the same numbers for your own repository, per language and per reason.

Resolved is not the same as right, so the call edges are also scored against each language's type checker or compiler. Every call site in six pinned checkouts was sent to the checker for the callee's declarations, and each osnova edge was marked true when the definition it names contains that declaration and false when it does not. Measured at 0.12.0:

CorpusCheckerDecided edgesFalseFalse-edge rateIn-repo sites covered
clickpyright 1.1.414288300%90.4%
zodTypeScript 5.9.32159340.02%78.2%
cobrago/types, Go 1.27.1198000%87.5%
ripgreprust-analyzer 1.98.16066290.48%85.8%
humanizerRoslyn 5.9.0736330.04%62.2%
gsonjavac 27813340.05%72.3%

Java and C# overloads share one symbol in the index, so a call edge names the overload it binds only when the written argument count fits exactly one declaration of the type, its partial parts and its declared base classes; when it fits several or none, the edge keeps the method and names no declaration, and the oracle leaves it out of both rates (2526 edges on gson, 602 on humanizer). 0.11.0 named the last overload declared for every call, recorded a call at the first line of its call expression, and lost the rest of a C# file after a primary constructor or a raw string; it measured gson 2045 false edges (24.75%), humanizer 617 (8.25%) and ripgrep 112 (2.06%). Of the 29 left on ripgrep, 11 are functions declared once per #[cfg] branch, 6 are a test module's function named like its parent's, and 5 are calls to a local closure or parameter named like a function elsewhere. A call whose target has one declaration that fits keeps it even when a base type is outside the index, where an unseen base overload could be the one the compiler binds; this limit is kept because withdrawing those edges cost 10 recall points on humanizer for one fewer false edge. Every false edge is classified by cause in benchmarks/results/type-checker-oracle-2026-09-30.json with the method and its limits. The scripts that produce these numbers are in benchmarks/oracle/.

Grep versus the graph

The reason to keep a call graph instead of running a text search is not speed. It is that the first regex a person types is wrong more often than it looks, and nobody notices. Nine call-site sets in six languages were verified line by line; each cell shows sites found (precision / recall), and the method lists what each side got wrong.

CorpusTargetVerified sitesText searchResolved graph
clickContext.invoke depth 21312 (0.92 / 0.85)12 (1.00 / 0.92)
pyrightgetChildNodes depth 2285 (0.80 / 0.14)28 (1.00 / 1.00)
cobraCommand.Root depth 13030 (1.00 / 1.00)30 (1.00 / 1.00)
cobraCommand.PersistentFlags depth 11213 (0.92 / 1.00)12 (1.00 / 1.00)
humanizerConfigurator.GetFormatter depth 11819 (0.74 / 0.78)18 (1.00 / 1.00)
ripgrepSearcher.line_terminator depth 11232 (0.38 / 1.00)12 (1.00 / 1.00)
ripgrepLineTerminator.as_byte depth 12626 (1.00 / 1.00)26 (1.00 / 1.00)
gsonJsonReader.beginObject depth 11029 (0.34 / 1.00)10 (1.00 / 1.00)
gsonTypeToken.getRawType depth 12743 (0.63 / 1.00)27 (1.00 / 1.00)

The graph never returned a site that was not a call of the target. The record is benchmarks/results/grep-vs-graph-2026-09-21.json.

The ten tools

The names play on the foundation image. The CLI uses the same names without the prefix: osnova ground and osnova_ground are the same query.

ToolMeaningDoes
osnova_groundthe ground you stand onKeyword search: ranked definitions with exact file:line
osnova_threadthe thread you follow through the clothText search: regex or literal matches grouped by symbol
osnova_outlinethe outline of one partSignatures and line spans for one file
osnova_warpthe threads that hold the weaveCall graph: callers and callees, direct or transitive
osnova_groundworkthe groundwork under everythingRepository map: directory clusters, hubs and hotspots
osnova_footingthe footing you build onTask context: definitions, relationships and candidate tests around a question
osnova_settlehow the ground settles after a changeChange impact: the symbols a diff touches and their indexed dependents
osnova_plumbthe plumb line dropped straight throughCheck claims: which listed call sites the index confirms, and which dependents were left out
osnova_teststhe cloth pulled to see what holdsTests: the test files that reference a symbol, or the symbols one test file reaches
osnova_unreferencedthreads left loose at the edgeDefinitions with no indexed caller, each with its leads; candidates, never proof

Every MCP response opens with its index generation, says when the index is partial, and counts what its budget left out; the budgets, and which CLI subcommands carry the generation, are in the reference.

Hooks and clients

One global install serves every repository and every client: one entry in each client's global config, nothing per project, nothing written inside your repository. osnova setup claude and osnova setup agents show the diff and write nothing until --apply; agents skips a harness whose config folder is missing, --only codex,pi narrows it, and a skill or plugin file you edited is kept and reported. --uninstall reverses either family the same way, previewing first. The table lists the per-client form, for one piece at a time. What each hook prints, when it stays quiet, and what the trials measured are in the reference.

ClientHow to wireWhat it adds
Claude Codeosnova setup --apply --client claude-code --hooksMCP entry plus session, prompt and stop hooks; --skill adds the skill, --nudge the opt-in grep nudge, --gate the opt-in search gate
Claude Code, as a plugin/plugin marketplace add getdomovoi/osnova then /plugin install osnova@osnovaThe same MCP entry, hooks and skill, run through npx -y @getdomovoi/osnova@<version>, pinned to the plugin's release, with no global install; updates follow the marketplace
Codexosnova setup --apply --client codex --hooksMCP entry plus the same three hooks; trust them in /hooks or Codex skips them silently; --gate adds the search gate; step by step in Osnova with Codex
Cursorosnova setup --apply --client cursor --hooksMCP entry plus the stop hook as a follow-up message; --gate adds the search gate
OpenCodeosnova setup --apply --client opencode --pluginMCP entry plus a plugin that appends starting points to each message; the tool guidance comes from the MCP instructions; --gate adds a gate plugin. OpenCode 2.x does not load either plugin; its plugin API has no message or tool hook
Kiloosnova setup --apply --client kilo --pluginMCP entry plus the same plugin; --gate adds the gate plugin
Piosnova setup --apply --client pi --pluginMCP entry through pi-mcp-adapter plus an extension that does the same; --gate adds a gate extension

The search gate is opt-in: add --gate to osnova setup claude or osnova setup agents, or to the per-client form. It denies a grep, rg, find or search-tool call that reaches code osnova indexes until an osnova tool, or the osnova CLI, has run in the current turn; the deny names the tools to use instead. A search of data, prose or configuration files, of a path outside the repository, or of a command's output after | always runs. Codex and Pi may not report an osnova call to the gate, so there it denies once per turn until it has seen one, then holds as on the other clients. OSNOVA_GATE=off switches it off for a session.

In CI

The action runs osnova settle --base-ref against the pull request base and lists every indexed dependent of the symbols the pull request changed; the report is indexed structural evidence only, so a missing dependent is not proof that nothing depends on the change.

yaml
- uses: actions/checkout@v4  with:    fetch-depth: 0- uses: getdomovoi/[email protected]  with:    depth: "2"

Inputs and the local form are in the reference.

What osnova does not do

  • No type inference and no dynamic dispatch. Edges come from syntax: direct calls, imports, name references, declared heritage (a written superclass or interface name) and framework routes (a registration whose receiver binds to a listed framework import, with the verb and the literal path written at the site), with lexical binding and receiver hints for TypeScript, JavaScript and Python. Resolution is heuristic and says so.
  • No route table. A routes edge is one registration site tied to one handler; prefixes from mounts, blueprints and controllers are recorded on their own edges and never composed into a full path, a computed path records no path, and an inline closure or a wrapped handler records the route with no target. Express, NestJS, Flask and FastAPI are read; gin, axum, Django, Spring, ASP.NET, Rails and file-based routers are not.
  • That boundary has a measured price. Scored against a type checker on two pinned corpora, the calls osnova does not resolve are mostly calls whose receiver type is never written down: 2945 of 6359 missed sites on zod and 164 of 307 on click are a plain name carrying no annotation, and another 1478 on zod are a call result or a property chain. Only 349 missed sites on zod and 10 on click have a type written at the receiver's declaration, and 248 of those 349 are a single library idiom. Resolving every one of them would move recall from 76.9% to 78.2% on zod and from 90.4% to 90.7% on click, so the boundary costs roughly one recall point rather than ten. The full census is in benchmarks/results/receiver-boundary-census-2026-09-21.json.
  • No semantic search. osnova_ground is fielded lexical ranking over definitions. It is fast, deterministic and explainable, and it will not match a paraphrase.
  • No proof of safety. An empty caller list means the index found no caller, not that none exists.
  • No cost claims. Agent trials so far show correctness parity with and without the graph on small tasks. A benchmark that separates the two is in progress.

How it stays honest

  • Every query refreshes the index from the working tree first, uncommitted edits included, and reports its generation.
  • Every clipped output says how much was clipped. Every ranked list says how many candidates it dropped. Partial indexes say so on every response.
  • Every hit carries a file:line span, a source hash and an index generation, so you can check what the agent cites.
  • Every benchmark result in benchmarks/results/ is frozen with its corpus fingerprint, and rejected experiments stay on record next to accepted ones.
  • The cache verifies a SHA-256 over the structural core before parsing it and hash-checks each source text on read. Nothing is written outside it.

CLI

Every tool is a subcommand: osnova build <root>, osnova warp <symbol>, osnova settle --base-ref <ref>, plus coverage, check, doctor, setup and mcp. The full list is in the reference.

Library

import { buildIndex, ask, callersDetailed, renderMapCard } from "@getdomovoi/osnova". The structured APIs return complete results with omission counts; the text budgets apply to CLI and MCP presentation only. Every export is in the reference.

Contributing

Bug reports, language adapters, benchmark corpora and agent trials are all welcome. Read CONTRIBUTING.md for the gates a change must pass: lint, typecheck, tests, build, perf budgets and package smoke.

sh
pnpm installpnpm lint && pnpm typecheck && pnpm test && pnpm build && pnpm perf

Determinism is the core invariant. A change that makes incremental refresh differ from a full rebuild by one byte is a bug.

License

Apache-2.0

Privacy: PRIVACY.md. Security policy and reporting: SECURITY.md.

來源:README.md,提交 dff046f

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

2
  1. v0.12.0最新Oct 2, 2026
  2. v0.11.0Oct 1, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分