Mirofy

io.github.Hasan-Laraib由 GitHub 账号 Hasan-Laraib 发布v0.6.0更新于 Oct 24, 2026

Ask a repository what depends on what. Every answer cites the file, line and commit it came from.

概览

AI 生成的概览

Mirofy 将 git 仓库扫描为证据图,并通过 MCP 回答架构问题,标注文件、行号与提交。

可以做什么
Mirofy 读取 JavaScript、TypeScript、Python、Go、Java、Rust 和 Kotlin 的导入,以及 package.json workspaces、Express/Next 路由和 docker-compose,构建证据图,再通过 MCP 把该模型提供给智能体。服务器提供 11 个工具,包括 callers、impact、find、gaps、assert 和 timeline 等查询,让助手在编辑时询问依赖关系。每个答案都标注其依据的文件、行范围和固定提交,无法读取的文件会记为缺口而非被静默忽略。
适用场景
当助手需要理解仓库架构、追踪依赖或可达性、检查架构规则,或查看两次扫描之间的变化时使用。适合要求答案可追溯到源码的代码审查与重构流程。若仓库语言不在其适配器列表内,则用处有限。
运行要求
作为本地 stdio 进程运行,通常通过 npx mirofy-cli mcp 启动。它读取 ./scan,即先前 map --out ./scan 的输出,路径相对于客户端启动目录;可用 --model 和 --graph 覆盖。需要至少有一次提交的 git 仓库,因为事实都固定到某个修订版本。未声明账户、API 密钥或环境变量。
安装前请注意
它会读取仓库源代码,并在未指定输出目录时把扫描中间文件和 architecture.html 写到目标旁边。它只做只读分析,不修改代码。答案只反映扫描器能读到的内容,因此空结果表示未找到,而非不存在。不涉及凭据或付款。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Mirofy,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

[Mirofy — connect, model, insight]

Diagrams of your system that cite their sources — and say what they could not see.

[mirofy-cli on npm] [MIT licensed] [Zero runtime dependencies] [85 conformance rows proved without a browser] [Output is one HTML file]

[A rendered Mirofy artifact open in a browser: ten services coloured by role, grouped inside an AWS region and a security group, with a legend naming each role]

This is the whole product: one HTML file, open in a browser. Every colour in it is the system’s own vocabulary — backend, database, cloud, security, message bus, external — and nothing else. Colour never marks where an arrow goes, only what a thing is.
Open this exact file ↗ — click any node for its evidence, trace what reaches it, search it, present it.


What it is

Point Mirofy at a repository. It reads the code into an evidence graph, builds a model from that graph, and compiles the model into one HTML file you can open, search, share and check.

Every relationship it draws can answer one question: what is the evidence for this? Each carries the file, the line range and the commit it came from. Where nothing is known, the diagram says so instead of filling the gap.

[scan to model to compile to layout to render]

Run it against this repository and you get this — not a mock-up, and not drawn by hand:

[Mirofy's own architecture, derived from its own source code: twelve packages and the imports between them]

Every box is the same colour here, and that is the point. All twelve of these are the same thing — a package, derived from a manifest — so there is nothing for colour to say, and it says nothing. The picture at the top is colourful because that system genuinely has six kinds in it. A tool that tinted these boxes to look livelier would be inventing a distinction it had not found.
Open the live one ↗ — click any node for the file, line range and commit behind it.

Install

Nothing to install — one command, and a diagram opens:

bash
npx mirofy-cli map .    # YOUR repository, mapped, in one commandnpx mirofy-cli demo     # a finished artifact, to see what this producesnpx mirofy-cli init     # a starter document of your own to editnpx mirofy-cli render architecture architecture.json

Give it to your agent instead — one line, and the skill installs for Claude Code, Cursor, Gemini CLI, Amp and a dozen others:

bash
npx skills add Hasan-Laraib/Mirofy

Then ask: map this repository's architecture. Your agent reads SKILL.md and drives the same CLI.

In Claude Code, the plugin carries both the skill and the MCP server:

/plugin marketplace add Hasan-Laraib/Mirofy/plugin install mirofy@mirofy

Every one of these routes is the same package. The agent never draws the diagram — it runs the CLI you would have run, which is why nothing it reports can drift from what the CLI reports.

map runs the whole pipeline in the directory you point it at — scan, model, compile, layout, render — and writes architecture.html next to your code. map --out <dir> sends the diagram and the intermediates there instead, so nothing lands in your repository; without it the intermediates go to <target>/scan. Naming an output path still wins over both. It works on a repository that declares no workspaces: where there are no packages to draw, it models the source directories and the imports between them.

Or call it from your own code

mirofy-cli/api is the same pipeline as a library — no argv, no process.exit, and no stderr to parse:

js
import { render, validate, map } from 'mirofy-cli/api';
const html = await render('architecture', diagram);   // one HTML stringconst { ok, diagnostics } = await validate('architecture', diagram);const { html, drawn, coveragePath } = await map('.');  // the whole pipeline

A rejected document throws a MirofyRenderError carrying .diagnostics — the renderer's own structured report, each entry with a code, a subject, the evidence behind it and the fixes it supports. validate returns that report instead of throwing, because "this document is invalid" is an answer, and checking a hundred documents should not need a hundred try/catch blocks.

map needs a git repository: every fact is pinned to a revision, so a directory with no commit has nothing for the evidence to cite, and it says so rather than failing further down.

What it reads

JavaScript and TypeScript imports · Python imports · Go imports · Java imports · Rust imports · Kotlin imports · package.json workspaces · Express and Next routes · docker-compose.

That is the whole list, and the list is the point. Everything else is reported, not skipped: coverage.md names every file no adapter opened, grouped by type, and map says so on its way out when the unread files outnumber the read ones. Point it at a Ruby repository and you get an honest empty answer naming every unread .rb file — not a confident small one drawn from the two JavaScript files in an examples/ folder.

Python resolves by file existence, not by convention: relative imports against the importing file's directory, absolute ones against the repository root and any directory that actually holds a package. A specifier that matches two source roots is a gap naming both, because which one wins depends on sys.path, which is configuration and not in the source.

Go resolves against the module path go.mod declares, and decides the standard library the way the toolchain does — a first path segment containing a dot is a domain, and a domain means a module fetched from somewhere. Java builds its index from the package statements files declare, not from directory layout: Maven convention puts com.acme.store under src/main/java/com/acme/store and convention is not always, but the declaration is what the compiler reads.

Rust peels a use from the right until a real file appears, because use crate::a::b::C does not say which of a, b or C is the file. It reads the crate name and the source root from Cargo.toml — including a declared [lib] path, since src/ is only the default — and knows that Cargo compiles every direct child of tests, benches and examples as its own crate.

Kotlin reads its type index from the declarations themselves — class, interface, object, typealias and fun interface among them — rather than from file names, because a Kotlin file need not be named after the type it holds and may declare several. It shares that index with Java: the two compile to one namespace and import each other freely, so an index of one extension reports a real edge to the other as a missing type.

In every one of them, an import that names something inside this repository which is not there is a gap — never a dependency on a published copy of yourself.

npx mirofy-cli guide "show an API request with a cache miss" picks the diagram type for you if you are not sure which one you want.

As a CLI you keep — npm install -g mirofy-cli. The command it installs is mirofy; the package carries the -cli suffix because npm refused the bare name as too close to the existing minify.

From source — no install at all, because there is nothing to install:

bash
git clone https://github.com/Hasan-Laraib/Mirofy.gitnode Mirofy/packages/core/bin/mirofy.mjs demo

That works on a bare checkout with no npm install, because every package here has zero runtime dependencies.

As an agent skill — build the bundle and copy it where your agent looks:

bash
git clone https://github.com/Hasan-Laraib/Mirofy.gitcd Mirofy && npm install && npm run build:skill
cp -r dist/mirofy ~/.claude/skills/      # Claude Codecp -r dist/mirofy ~/.agents/skills/      # Codex CLI, opencode

Then ask: Use mirofy to map this repository's runtime architecture.

The bundle is 2.8 MB and named for the skill inside it — copying packages/core instead installs a skill called core that says in its own frontmatter it is called mirofy, and drags the test suite along with it. Before writing the bundle, build:skill copies it somewhere with no repository around it and renders a diagram: a bundle that only works inside its own checkout is not a bundle.

Nothing is downloaded at runtime and nothing phones home — there is no update check, because a tool that reaches the network to tell you about itself is a tool that reaches the network.

The pipeline, one step at a time

mirofy map is these five steps in order. If you only want the intermediates, mirofy map . out.html --out ./scan writes every one of them — the evidence graph, the model, the view and the positioned document — into that directory.

To run a step on its own, or point one somewhere else, you need a checkout; these are the repository's own npm scripts, not commands the installed package exposes:

bash
npm run scan                    # repository  → evidence graphnpm run model -- --from-graph --graph scan/evidence-graph.jsonnpm run compile                 # model       → a bounded viewnpm run layout                  # view        → positioned documentnode packages/core/bin/mirofy.mjs render architecture scan/diagram.json out.html --repo-root .

Against this repository it records 1,227 facts across 223 files, with 17 gaps it could not read; derives 19 components and 21 relationships — every one citing the file and line it came from — and draws twelve, recording what it left out and why.

Those figures are checked, not remembered — see the numbers on this page below.

Those commands reproduce the diagram at the top of this page. It is checked in under assets/ as documentation; the interactive artifacts are built, never stored.

No repository? Author a JSON document, or convert a Mermaid diagram:

bash
node packages/core/bin/mirofy.mjs import mermaid design.mmdnode packages/core/bin/mirofy.mjs render architecture design.json out.htmlnode packages/core/bin/mirofy.mjs validate architecture design.json --json

What you get

[One drawn edge, opened: the passport shows the relation depends-on, provenance statically-derived, the source line render-workflow.mjs line 3, and the revision it was verified against. Below it, a gap the scanner recorded instead of guessing.]

Open any edge and it tells you why it is on the page: the relation, its provenance class, the file and line it came from, and the commit it was checked against. Underneath is the half most tools leave out — what the scanner could not determine, written down instead of guessed.
That record is real, and taken from this repository. So is the gap.

Three claims about the pictures below, each with the thing that keeps it honest.

[An architecture diagram in the meridian preset]

Colour tells you what a node is, never where an arrow goes. Six presets, light and dark. meridian holds every arrow at graphite so hue is never doing two jobs at once.
Proved by conformance row 4.16 · open this exact artifact ↗

[A sequence diagram with lifelines and messages]

Five diagram types, one schema, one validator. architecture · workflow · sequence · dataflow · lifecycle — the same typed IR behind all of them.
Proved by conformance row 1.1, which renders all five from their baseline fixtures in one pass · open this exact artifact ↗

[The interactive viewer with search, tracing and evidence panels]

One file. No server — and nothing it needs from the network.
The diagram, the evidence, the search and every interaction are in the file. The one thing it ever asks the internet for is a webfont it does not wait for and does not need, and it falls back to your system monospace without it.
Checked on every run by scripts/check-readme-claims.mjs, which fails the build the moment a reference appears that could block first paint or change what the diagram says — and which fails just as loudly if this sentence ever overstates what the artifact actually fetches.

All thirty are live ↗ — five types × six presets, rebuilt from every commit.

The viewer, actually being used

Not mock-ups. Every frame below is a capture of the shipped viewer, driven through real clicks by scripts/build-screenshots.mjs — which fails rather than reuse an old picture if a control is renamed or a panel stops opening, and refuses to save a shot of a feature that did nothing.

[The Node Finder open, a query typed, the list narrowed to two of ten nodes]

Find anything. Typing api narrows ten nodes to two. The capture asserts the list actually shrank — a screenshot of an unfiltered list is not a screenshot of search.

[A Semantic Passport showing a verified source-backed citation with its repository and commit]

Ask a node where it came from. conformance, source-backed, cited to packages/conformance/package.json at a pinned commit. This one is captured from this repository, because the authored example has no citations and a passport with the evidence missing would illustrate the claim by not showing it.

[Authored reachability traced upstream from Worker: six nodes, six links, five hops, everything off the path dimmed]

Follow what reaches what. Upstream of Worker: six nodes, six links, five hops — lit, with everything off the path dimmed. The capture picks the node with the deepest reach, so the picture is of a path and not of one arrow.

[The Semantic Lens comparing system roles across the whole diagram]

Compare roles across the whole system. The Semantic Lens answers provenance and kind for every node at once, rather than one node at a time.


What it does that a diagram tool doesn't

It refuses to guess

A file the scanner cannot parse becomes a recorded gap, never a silent omission. Every fact is labelled with one of six provenance classes, so source-backed and inferred never look alike.

The same rule holds where a decision has to be made that evidence cannot settle. A derived component's kind is package — the scanner knows a manifest exists, not whether something is a "backend". 878 imports of Node builtins are counted and named, not drawn and not dropped in silence. In Python a computed importlib.import_module(name) is a gap with its line, and docstrings are blanked before parsing — a docstring full of example imports would otherwise become edges the code does not have, cited to prose. A citation with no pinned commit to verify against is discarded rather than shown, because a citation nobody can check is worse than none — map reads the commit from your origin remote, or takes --repo-url and --revision when there is no remote to read.

A passport lists at most three sources, because forty-three links is not a passport. It says “Showing 3 of 43 cited sources” when it does, so a bound on the drawing is never mistaken for a claim about the evidence.

It answers questions about your system

bash
mirofy explain callers api     # what points at apimirofy explain impact api      # what is downstream of itmirofy explain find payment    # id, label, kind or metadata matchmirofy explain gaps            # what the scan could not read

Every answer names the unread files that could change it. "Nothing calls PaymentService" is useful if the scanner read everything and reckless if six files failed to parse — so an empty result means not found, never does not exist.

impact answers as reachability and refuses to be more. What is connected is a fact about the graph; whether a change breaks it is a judgement about a running system, and Mirofy has no evidence for that.

Your agent can ask too

The same queries over MCP — 11 tools, the same engine, not a second implementation that could disagree with the CLI. assert and timeline are there too, because "is this change allowed" and "what has been moving here" are questions an agent asks while editing, and one that has to shell out to ask them will not ask at all:

json
{ "mcpServers": { "mirofy": { "command": "npx", "args": ["-y", "mirofy-cli", "mcp"] } } }

Point any MCP client at that. It reads ./scan — whatever map --out ./scan last wrote — relative to the directory the client starts it in; --model and --graph override. No clone, and nothing to install first.

The incompleteness warning is in the prose an agent reads, not only the JSON. Most clients feed the text to the model and drop the rest.

It checks architecture rules — with three outcomes, not two

bash
mirofy assert     # reads architecture-rules.json

pass, fail, and unproven. A rule that found no violation over a scan with unread files has not been shown to hold, so it never counts as passing. Turning a gap into a green check is the one failure this project exists to avoid.

Some gaps are permanent — a dynamic import whose base path is a variable cannot be resolved without guessing. Those can be acknowledged, one path at a time, quoting the gap's reason and carrying a written argument. An acknowledgement written for a dynamic import stops applying the day that file fails to parse instead. And a rule that passes on the strength of one says so:

[ok  ] no-cycles — No violation. 8 unread file(s) are acknowledged as unable       to hide one; this rests on that judgement, not on a complete scan.
2 passed, 0 failed, 0 unproven of 22 of those rule(s) rest on acknowledged gaps, not on evidence.

It tells you what is moving

bash
mirofy timeline                                # cited-file churn, newest firstmirofy drift --base a.json --head b.json       # what two scans say differently

Drift reports changed facts and nothing else — no score, no risk label, no merge recommendation. It runs on every pull request and can never fail one.


The number we would rather not publish

A benchmark asks one question: hand a model a written brief, and how often does the diagram it writes come out usable on the first attempt?

Right now, over eight briefs authored by Claude Code: 2 of 8.

bash
node scripts/benchmark.mjs --author "<your command>" --model "<id>" --keep benchmarks/corpus/minenode scripts/benchmark.mjs --replay benchmarks/corpus/mine

That is not a good number and it is the real one. Three things make it worth printing anyway.

Usable means clean, not accepted. A warning is the diagram telling you it needs a second look, which is exactly what a first-pass rate is supposed to exclude. Two more documents in that set validate with zero errors and are still not counted.

It is measured against a saved corpus, not a fresh one. --keep stores what the model produced; --replay re-runs the tool over those exact documents without calling the model again. Without that split, every re-run changes both the documents and the tool, and any movement can be attributed to either — which is why the rate sat at zero for weeks without anyone being able to say what was wrong. A replay cannot even claim a different author: the model is read from the saved manifest, and --model is refused if it disagrees.

It moves for reasons you can name. The last change to the layout engine took the same eight documents from 0 of 8 to 2 of 8, and total composition errors from 121 to 34, because a diagnostic that said "shorten the label or widen size" was asking an author to rename part of their system to fit a box the renderer had picked. The renderer now widens the box.

If you compare this to a number published elsewhere, check what was measured. A rate for an agent that can call a validator and repair its own output, reviewed by a person at the end, is a different measurement from a blind single-shot model — not a worse one, a different one. Ours is the second kind.


Inside the artifact

  • Node Finder, Semantic Lens, Semantic Radar — search, filter and overview a diagram too large to read at once
  • Route Probe — resolve a directed path and see it traced, hop by hop
  • Semantic Passport — click a node for its type, tags and cited evidence, with a Verified Source Beacon on anything backed by a pinned commit
  • Your code, drawn apart from everyone else’s — a module you wrote is solid, a third-party package is dashed. Not a colour: the colour-blind-safe preset already spends all seven of its hues on the seven semantic roles, so an eighth would be a distinction only some readers could see
  • Guided views, presentation mode, motion governor, and ?embed=1
  • Three exports, copied straight to the clipboard:
    • Export → Share Card — a 1200×630 PNG in the current theme and preset
    • Export → Route Share Card — the exact route a Route Probe resolved
    • Export → Reach Share Card — the set a reachability query returned

A card shows what the reader actually did. None of them claim validation, and none are produced from a query that returned nothing.

bash
npm run gallery      # every type in every preset → preview/index.htmlnpm run build:site   # the same, plus this repo's self-model → site/

The second is what CI publishes to hasan-laraib.github.io/Mirofy on every commit. Nothing is committed: the site is built from the code at the commit it describes, so it cannot go on quietly describing an older one.

Taking it elsewhere

The interactive file is ~720 KB and earns it. None of that survives a README, a pull request or a Notion page, though — all of them strip scripts. So:

bash
# 19 KB standalone SVG: no scripts, no stylesheet needednode packages/core/bin/mirofy.mjs render architecture in.json out.svg --format svg-static
# or open it in an editor you already own — from a checkout; the export# package is not bundled into the published CLInpm run export -- drawio     architecture in.jsonnpm run export -- excalidraw architecture in.json
where it goeshow
README, pull request, Notion, Confluencesvg-static
Figma, Canva, Illustrator, Sketchsvg-static — styling is written as attributes, so it arrives with its colours
diagrams.net · draw.io VS Code extensiondrawio — real shapes and connectors
Excalidraw · Obsidian · VS Codeexcalidraw — bound arrows, movable boxes

The SVG carries its styling twice: in a stylesheet and on the elements. In SVG a stylesheet outranks an attribute, so a browser renders from the CSS, and the attributes speak only where the CSS is ignored — which is exactly what Figma, Canva and Illustrator do. Without them the diagram imports shape-correct and colour-dead.

Both editor exports say exactly what they lost, computed from your document rather than recited as a disclaimer. A diagram you can only edit in the tool that made it is a diagram held hostage.

What is proved

The conformance matrix has 105 rows. 85 are proved without a browser; 19 more need headless Chrome (MIROFY_CHROME), bringing the total to 104.

bash
npm run check    # lint, types, 1,000+ tests, golden parity, conformance, size, audit

Every row names a test, and the title must match character-for-character — a row whose proof file passes while its own test was renamed counts as unproven, never as passing. One row (6.10, deterministic ZIP packaging) is UNPROVEN and counted as such rather than quietly dropped.

Skipped is not passed. Browser rows never count toward the proved total unless a browser actually ran them.

And the numbers on this page are checked too:

bash
node scripts/check-readme-claims.mjs

It counts the matrix, reads the tool list the MCP server serves, renders an artifact to measure it, and re-runs the benchmark. This exists because a review found three numbers here wrong at once — none of them dishonest, all of them true when written and left behind by the repository. A page that argues for checking claims has no business making unchecked ones.

Evidence and provenance

Six classes, never blurred:

classmeans
authoreda human wrote it
source-backedread out of a cited file and line range
statically-derivedcomputed from code without running it
config-derivedread from a manifest — configuration, not code
runtime-observedseen in a real run
inferreda guess, and labelled as one

Source citations verify against a pinned 40-character commit in a real local checkout before they render. A path that does not exist at that revision is an error, not a broken link.

Several repositories can be declared at once, and a citation names which one it belongs to. Verifying against a repository rather than the right one is how a path from a sibling repo passes as evidence for this one.

Packages

packagedoes
scanneradapters that read a repository into facts and gaps
evidenceappend-only evidence graph, query, honest coverage
modelthe system model: stable ids, evidence refs, human overrides
compileview compiler and the planner seam
explaingraph queries, architecture rules, drift, timeline
mcpthe model as agent context
importMermaid into typed documents
exportdraw.io and Excalidraw escape hatches
layoutconstraint layout: intent to coordinates (dev-time)
judgeopt-in: asks a System One model about the gaps, and keeps the receipt
corerenderers, schemas, validators, CLI
viewerthe interactive viewer, built into one template
benchmarkfirst-pass usable rate, over a saved corpus
conformancethe matrix, and the tests every row names

Zero runtime dependencies in every package. The artifact ships nothing but itself.

packages/core/assets/template.html is generated from packages/viewer/. Never edit it directly — edit the source and run npm run build:template. npm run check:template rebuilds from source and fails if the committed file has drifted.

Attribution

Every artifact says what made it. The viewer footer is dismissible — the diagram is yours, and a banner you cannot close is an imposition on someone else's document. Share Cards carry a permanent one, because a card travels without its context and lands where nothing says where it came from.

It names the tool and claims nothing about the diagram, and carries no URL: a link baked into every shared artifact outlives the address it points at.

MIT. packages/core/LICENSE retains, verbatim, the required third-party copyright notice for the imported rendering core; the root LICENSE covers this project's own work.

来源:README.md,提交 9f5fbcb

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.6.0最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分