archview
by LZZLHY
README.md
# ArchView
**给一个仓库画一张「模块之间怎么依赖」的架构图 —— 拓扑全部来自 tree-sitter 静态分析,LLM 只负责给每个节点写一句人话摘要。同一份图通过 MCP 暴露给你 IDE 里的 agent。**
单机、本地、只绑 `127.0.0.1`。默认中文界面。
---
## 📦 直接下载(Windows,不用装 Node、不用 clone)
### **[⬇ ArchView_0.1.0_x64-setup.exe](https://github.com/LZZLHY/archview/releases/latest)** · 35 MB · Windows 10/11 x64
装完双击就有一个**独立窗口**,里面就是网页版那个面板。自带 Node 与 CodeGraph,
装到 `%LOCALAPPDATA%\ArchView`,**不要管理员权限**。
**用法:复制一段话给你的 AI,然后回答几个问题。**
打开软件时面板是空的,上面有一段可复制的提示词(带着这次运行的真实端口与 token)。
把它粘给正在编辑你项目的那个 AI —— Kiro / Claude Code / Cursor / Codex / opencode /
Gemini CLI / Copilot CLI 都行,它会自己认出自己是哪个。然后它会:
1. 连上这个已经跑着的窗口(不会去 clone 或再装一份)
2. 认出自己是哪个宿主,把 MCP 配好(**动你的配置之前会先问你**)
3. 在你的工作区里**递归找出候选项目** —— 一个文件夹里有好几个仓库/子项目是常态,
它会把清单给你看,问你要接哪个(还是全都接)
4. 注册 + 建索引 + 建图(索引不存在时它自己会建,大仓几分钟)
5. 再问你要不要现在写语义摘要(那一步烧 token,所以必须问)
你只需要回答几个问题,然后等窗口里出现图。做法写在
[`CONNECT-FOR-AI.md`](./CONNECT-FOR-AI.md)(软件里那段提示词就指向它),
带真实端口与 token 的实况版在运行时的 `/onboarding.md`。
**完全不用 AI 也能出图。** 首屏是并列的两栏,右边那栏就是这条路:点「选择文件夹…」
弹出目录选择器(带盘符、快捷入口、以及「像是项目 / 已接入」的角标)→ 选中 → 确认 id →
注册 → 点卡片上的「重建数据」。它会自己代跑 `codegraph init` 建索引再建图。
这条路一个 AI 都不需要,因为**拓扑本来就全部来自 tree-sitter 静态分析**(第 3 节铁律 1)——
AI 只是把每个节点的摘要从「由签名合成的兜底句」换成人话。图先出来,摘要以后随时补。
### 三个入口之间可以随便切
| 情况 | 行为 |
|---|---|
| 先 `archview serve` 在浏览器里用,中途想开桌面版 | 桌面版**复用那个正在跑的服务**,不会新起端口。窗口里和浏览器里是同一个进程、同一批数据 |
| 已经开着桌面版,又敲 `archview serve` | 命令行提示「本机已经有一个在跑」并给出它的 URL;确实要再起一个隔离实例就加 `--no-reuse` |
| 桌面版点窗口的 × | 问一句:**最小化(保持服务运行,默认)** / 退出 / 取消。最小化后收进托盘,下次打开秒开 |
| 复用的是命令行起的服务,然后在桌面版点 × | 第二项变成「只关窗口,服务由起它的终端管」—— 桌面版不去关别人的服务 |
接缝是 `~/.archview/running.json`(服务启动时登记、退出时撤销)。判活三段缺一不可:
登记里 `host` 是本机、pid 还活着、`GET /api/ping` 回的 `pid` 与登记一致。
第三条是关键 —— 只验「端口能连上」会让桌面版连上一个抢到同一端口的别的程序。
> ⚠️ 没有代码签名,第一次运行会被 SmartScreen 拦一次(「更多信息」→「仍要运行」)。
## 或者装命令行版
```bash
npm i -g archview # 四个命令进 PATH:archview / archview-serve / archview-mcp / archview-skill
archview init d:/code/my-repo && archview build && archview serve --open
npx archview init . # 不装,试一次就走
```
已发布在 npm:**[archview](https://www.npmjs.com/package/archview)**。不需要 pnpm、不需要 clone、不需要
build,唯一的硬门槛是 **Node ≥ 22.5**(`node:sqlite`)。首次 `npm i` 会顺带下 CodeGraph
的平台子包(Windows x64 那个 248.7 MB,自带 node.exe 与原生模块)—— 这是预期的,
我们刻意不把它打进包里(打进去等于让每个平台的用户都下载全部六份)。
三条路怎么选:
| 想要 | 走哪条 |
|---|---|
| 不碰终端,装个软件点开就看 | ⬆ 上面那个安装包 |
| 命令行 / CI / 接 MCP | `npm i -g archview` |
| 改代码、跑验收脚本 | [源码](#5-上手从取代码到看见图) |
三者**共用同一份工作区注册表**(`~/.archview/workspaces.json`),所以随便混着用,
都看到同一批项目。桌面版细节见 [Windows 独立窗口版](#windows-独立窗口版安装包)。
---
## 🚀 让 AI 帮你从源码装
想要命令行版(CI、脚本、接 MCP),或者想改代码 —— 这件事可以整个丢给 AI。
把下面这段**整段复制**,粘给任何能读网页、能跑命令的 AI 助手(Kiro / Cursor / Claude Code / Codex …),
把 `<我的项目路径>` 换成你要分析的仓库:
```text
帮我装 ArchView 并把我的项目接进去。
仓库:https://github.com/LZZLHY/archview
安装剧本(先读这个,它是给你写的):https://raw.githubusercontent.com/LZZLHY/archview/main/SETUP-FOR-AI.md
我的项目在:<我的项目路径>
照剧本走:环境体检 → clone → pnpm install + pnpm build → archview init 我的项目 → archview build → 起服务。
剧本里标了「决策点」的地方问我一下再决定(尤其是装到哪、要不要改我的 AI 宿主配置、要不要现在开始写摘要)。
最后把带 token 的面板 URL 给我。
```
剧本([`SETUP-FOR-AI.md`](./SETUP-FOR-AI.md))里每个阶段都有「怎么知道这一步成了」,还有一节
「常见失败与对策」。它设计成 AI 在 clone 之前就能通过 raw URL 读到。
**自己动手**:跳到 [第 5 节](#5-上手从取代码到看见图),五步命令能复制粘贴跑通;
或者一条命令搞定前两步:
```powershell
# Windows PowerShell(先 clone,再跑仓库里的脚本)
powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
```
```bash
# macOS / Linux
bash scripts/setup.sh
```
**想先判断值不值得看**:第 3 节(它凭什么存在)与第 10 节(已知限制 / 谁不该用它)。
| | |
|---|---|
| [📦 直接下载(Windows)](#-直接下载windows不用装-node不用-clone) | [8. 验收脚本](#8-验收脚本) |
| [🚀 让 AI 帮你从源码装](#-让-ai-帮你从源码装) | [9. 实测数字(附出处)](#9-实测数字附出处) |
| [1. 它解决什么问题](#1-它解决什么问题) | [10. 已知限制 / 谁不该用它](#10-已知限制--谁不该用它) |
| [2. 两个 MIT 项目的融合体](#2-它是两个-mit-项目的融合体这里不避讳) | [11. 架构与包结构](#11-架构与包结构) |
| [3. LLM 永远不写拓扑](#3-为什么值得单独存在llm-永远不写拓扑) | [12. 许可与致谢](#12-许可与致谢) |
| [4. 需要什么](#4-需要什么) | [13. 想改点什么](#13-想改点什么) |
| [5. 上手:从取代码到看见图](#5-上手从取代码到看见图) | [`CONNECT-FOR-AI.md`](./CONNECT-FOR-AI.md) 给 AI 的**接入**剧本(软件已在跑) |
| [6. 让 agent 补上语义](#6-让-agent-补上语义) | [`SETUP-FOR-AI.md`](./SETUP-FOR-AI.md) 给 AI 的**安装**剧本(从源码) |
| [7. 数据放哪 / 什么该提交进 git](#7-数据放哪--什么该提交进-git) | [`AGENTS.md`](./AGENTS.md) 仓库根路牌(agent 工具自动读) |
| | [Release 页](https://github.com/LZZLHY/archview/releases/latest) 安装包下载 |
---
## 1. 它解决什么问题
假设你一个人在写一个 9 个 ohpm 模块的 HarmonyOS 应用(这就是本项目的起因)。你想要两样东西:
1. **给自己看**:一张能点开、能下钻、能看到「entry 依赖了哪几个 HAR、commons 被谁用」的图;
2. **给 agent 看**:Cursor / Kiro / Claude Code 里的助手能准确知道项目结构,别再靠 grep 猜。
现成的东西各缺一半:
| 工具 | 有什么 | 缺什么 |
|---|---|---|
| [CodeGraph](https://github.com/colbymchenry/codegraph) | tree-sitter 挖出来的确定性结构事实,几十种语言 | 没有界面 |
| [Understand-Anything](https://github.com/Egonex-AI/Understand-Anything) | 很好的 React + ELK 架构图 dashboard | 图里的结构是 LLM 挖的;不认 ohpm/ArkTS |
ArchView 把两头接起来:**CodeGraph 出事实,UA 的面板出界面,LLM 只补语义。**
### ⚠️ 先看这个:你会得到几个模块,取决于你的仓库形状
「模块之间怎么依赖」这张图的前提是**有多个模块**。模块不是我们猜的,是从你仓库里的
事实派生的(铁律 1),所以不同形状的仓库结果不一样:
| 你的仓库 | 模块从哪来 | 结果 |
|---|---|---|
| pnpm/npm workspace、Cargo workspace、多 `go.mod`、HarmonyOS ohpm(`oh-package.json5` 的 `file:` 依赖) | 包声明 | 一个包一个模块,总览图有连线。这是最理想的情况 |
| **单个包**(一个 `package.json` + `src/core`、`src/api`、`src/utils`) | 目录层级 | 自动按目录切:`src` 只有 1 个模块时会往下钻一层,切出 `src/core`、`src/api`、`src/utils`。总览图有连线 |
| 代码**全在一个目录里**(`src/` 下平铺,或全在 `src/lib/` 里) | 目录层级,但切不出来 | **只有 1 个模块,总览图自然没有连线** —— 这不是 bug,是「一个模块内部不存在模块间依赖」。这时候看**下钻视图**(点开那个模块看文件级依赖),或者在 `.archview/config.json` 里设 `modules.strategy: "pathDepth"` + `modules.pathDepth` 指定切到第几层 |
`archview build` 每次都会打印一行说明模块是怎么来的,例如:
```
模块策略 pathDepth —— 未命中工程化模块声明(…),退化到路径深度切分;
按路径首段只得到 1 个模块(src),已自动下钻到第 2 层,切出 4 个模块:src、src/api、src/core、src/utils
```
`archview status` 与面板列表页给的是同一句话。**看不懂自己的模块为什么是这样分的时候先看它。**
⚠️ 一个反直觉的点:`modules.labels` 只能给**已经被识别出来的**模块改显示名和描述,
它**不能定义模块**。往里写一个不存在的 key 不会创造出模块(build 会为此报一条告警)。
要改「模块怎么分」,改的是 `modules.strategy` / `modules.pathDepth`。
### ⚠️ 仓库里有大目录(构建产物、预编译二进制、容器上下文)的话,先写一份 `codegraph.json`
这是两个不同的开关,作用点不一样,第一次用的人几乎都会踩:
| 想要 | 改哪个 | 效果 |
|---|---|---|
| 让某些目录**根本不被索引** | **`<仓库根>/codegraph.json`**(CodeGraph 自己的配置) | 索引更快更小。这是根治 |
| 索引它们但**不进图** | `.archview/config.json` 的 `include.excludePathPrefixes` / `excludePathPatterns` | 索引照样花那几分钟、照样占那几百 MB |
`codegraph.json` 是 **CodeGraph 的**配置文件,不是我们的 —— **ArchView 从不生成也从不修改它**,
所以不会有人替你写,它也不会被我们覆盖。形状就这么简单:
```json
{ "exclude": ["prebuilt/", "docker/output/", ".tmp-build/", "third_party/"] }
```
写完**删掉 `.codegraph/` 再重建一次**(索引要重新建才会让 exclude 生效)。
本轮的实测,数字就是这么来的 —— 同一个 HarmonyOS 仓库(281 个 `.ets`),
唯一差别是有没有这份文件:
| | 索引库 | 重建耗时 | 图 |
|---|---|---|---|
| **有** `codegraph.json`(7 条 exclude) | **56 MB** | **5.9 秒** | 9,007 节点 / 27,952 边 / 11 个模块 |
| 没有 | 1,493 MB | 4 分钟 | 364,690 节点 / 1,244,410 边 —— **写不出来** |
最后那一格不是夸张:`graph.json` 靠 `JSON.stringify` 落盘,而 V8 的字符串上限约 512 MiB,
超过会抛一句 `Invalid string length`。所以现在有两道拦阻,**都会点名 `codegraph.json`**:
索引文件数偏大时在建图**之前**就给一句告警(不让你白等四分钟),图真的超限时明确拒绝写入
并给出上面那份 JSON。
## 2. 它是两个 MIT 项目的融合体,这里不避讳
| 层 | 来源 | 归属 |
|---|---|---|
| 结构提取(事实) | CodeGraph | 外部 npm 依赖 `@colbymchenry/codegraph`,我们只读它的 SQLite 索引、调它的 bin |
| 界面(React + xyflow + ELK dashboard) | Understand-Anything | **全量 vendor**,逐文件带上游标注,成为我们的代码 |
| 图 schema / 校验器 | Understand-Anything | 原样搬(`packages/core/src/types.ts`、`schema.ts`),刻意保持字节级兼容,好让 vendored 面板不改就能渲染 |
| skill / 语言与框架指导 / agent 流程 | Understand-Anything | 搬过来后逐份改造。上游 24 门语言之外补了 ArkTS 与 13 门 CodeGraph 支持而上游缺指导的语言,合计 38 份 |
| 语义摘要 | 你自己的 LLM agent | 运行时产生,存在被分析仓库里 |
| 缝合、单端口服务、MCP、多工作区、模块策略、框架 deriver | ArchView 原创 | — |
两者皆 MIT。署名与**逐文件出处**在 [`NOTICE`](./NOTICE),本项目自己的许可在 [`LICENSE`](./LICENSE)。
## 3. 为什么值得单独存在:LLM 永远不写拓扑
这是本项目唯一的技术根据,也是唯一一条不许妥协的规则:
> 节点和边**只能**由 CodeGraph 的 tree-sitter 输出派生。LLM/agent 只能提供 `summary` 与 `tags`,永远不写节点、不写边、不写模块划分。
区别很具体。拓扑由 LLM 生成的项目,必须再写一堆修补脚本擦屁股:规范化对不上的 ID、丢掉指向不存在节点的悬空边、翻转方向搞反的边。这些脚本本身就是「结构不可信」的证据。ArchView 不需要它们 —— 一条边存在,是因为 tree-sitter 在源码里真的解析到了那个引用。
配套的三条规则(覆盖率、layer 覆盖、文件级边上卷)与全部实现约束在 [`CONTRACT.md`](./CONTRACT.md)。MCP 的工具面里**不存在**任何写图的工具。
## 4. 需要什么
| 依赖 | 版本 | 为什么 |
|---|---|---|
| Node.js | **>= 22.5**(各包 `engines` 就是这么写的) | `packages/core` 用内置的 `node:sqlite`(`DatabaseSync`)只读 CodeGraph 的索引库。这个模块在 Node 22.5 之前不存在,低版本连 `import` 都过不去 |
| pnpm | 10.x(根 `package.json` 的 `packageManager` 钉的是 `pnpm@10.28.2`) | 这是个 pnpm workspace,六个包互相 `workspace:*` 依赖 |
| git | 任意近期版本 | 可选。没有 git 也能建图,只是 `graph.project.gitCommitHash` 会是 `unknown`,「这张图对应哪个 commit」这条线索没了 |
不需要全局安装 CodeGraph:它是 `packages/core` 的普通 npm 依赖,`archview init` 会从 `node_modules` 解析它的 bin 并代你调用(一律带 `DO_NOT_TRACK=1` 与 `CODEGRAPH_NO_UPDATE_CHECK=1`)。
### 发布状态:两种形态都已上线
| 形态 | 地址 | 体积 |
|---|---|---|
| **npm 单包** `archview`(四个 bin) | <https://www.npmjs.com/package/archview> | 1.3 MB 压缩 / 4.0 MB 解包 / 219 文件 |
| **Windows 安装包**(独立窗口,自带 Node 与 CodeGraph) | [Release 页](https://github.com/LZZLHY/archview/releases/latest) | 35 MB 压缩 / 268 MB 解包 |
npm 那份在一个**完全干净的临时目录**里被真装真跑过(从 registry 拉,不是本地 tarball):
`npm i archview` 8.8 秒 → `archview init` 真调 CodeGraph 建索引 → `archview build` 建出图
(单包布局自动下钻出 2 个模块)→ `archview-serve` 起服务、图端点与接入端点全部 200、
`prompt` 由 `@archview/skill` 渲染而不是内置兜底、两份 zod 的 alias 隔离生效(mcp 3.25.76 / core 3.24.1)。
`@archview/core` 这些名字**永远**不会出现在 npm 上:它们是仓库内部的 workspace 包名
(六个包的 `package.json` 都标了 `private: true`),发布的只有 `archview` 这一个包。
所以 MCP 配置不要写 `npx -y @archview/mcp`(那个包名不存在),要写 `npx -y -p archview archview-mcp`,
或者直接用装出来的 `archview-mcp` 这个 bin —— 最好的写法是指向本机真实存在的 `mcp.js` 绝对路径
(面板与 `GET /api/onboarding` 给的就是它)。
**要发新版本的人**(需要 `npm login`,或者 `~/.npmrc` 里有带 publish 权限的 token;
npm 现在默认要求 2FA,交互式发布得加 `--otp=<6 位码>`):
```bash
pnpm run npm:publish # = 先 build 六个包 → 组装 npm-package/ → npm publish
```
只想看会发出去什么:
```bash
pnpm run npm:pack # 产出 npm-package/archview-<版本>.tgz
npm publish ./npm-package --dry-run # 逐条清单,不落地
```
组装器是 [`scripts/build-npm-package.mjs`](./scripts/build-npm-package.mjs),它的文件头写清了
「为什么是单包」「为什么不用 bundler」以及四道守卫(dist 缺失 / dist 比 src 旧 / 版本号不统一 /
产物自检不过 → 一律拒绝打包,不会发出空包或旧代码)。
**仍然可以从源码装**(第 5 节):想改代码、想跑验收脚本、想用 `pnpm --filter` 单独构建某个包,
都走源码路。那条路需要 Node ≥ 22.5 **与 pnpm**,首次要等一次 `pnpm install` + `pnpm build`
(本机实测:全新克隆 install 5.0s、build 17.1s,全流程 25.6s;pnpm store 冷的机器上几分钟正常),
更新走 `git pull` + 重新 build(`dist/` 是 gitignore 的,pull 只换源码不换产物)。
skill 安装器写 MCP 配置时按「本机真实存在的文件优先」:npm 装的形态下它指向
`node_modules/archview/packages/mcp/dist/bin/mcp.js`,源码形态下指向
`packages/mcp/dist/bin/mcp.js`,两边都拿不到才退回 `npx -y -p archview archview-mcp`
并在文案里说明那条要求包已发布。`command` 用的是**当前进程的 node 绝对路径**而不是裸
`"node"`:桌面版自带运行时、不要求用户装 Node,而裸 `"node"` 依赖宿主的 PATH
(可能没有、可能是 18、可能被 nvm 切走)。
### Windows 独立窗口版(安装包)
除了命令行,还有一个**有自己窗口的 Windows 软件** —— `desktop/`,Tauri 2 的壳。
**已发布,直接下载**:
> **[⬇ ArchView_0.1.0_x64-setup.exe](https://github.com/LZZLHY/archview/releases/latest)**
> (35 MB,Windows 10/11 x64,`%LOCALAPPDATA%` 安装免 UAC)
也可以自己打:
```powershell
cd desktop
npm install # 只装 @tauri-apps/cli 一个本机开发工具
npm run build # 产出 ArchView_<版本>_x64-setup.exe(~35 MB)
```
(需要 Rust 工具链:rustup + MSVC。`npm run build` 会自己先跑
`scripts/stage-resources.mjs` 把 npm 单包与生产依赖摆进 `bundle.resources`,
那一步任何环节失败都会中止构建 —— 所以打不出「缺东西的安装包」。)
它跟浏览器版**不是两个东西**:窗口里装的就是 `packages/web/dist` 那个 SPA,
壳只负责 spawn 同一份 `packages/server` 并在退出时收掉它,Rust 侧一行服务逻辑都没有
([`CONTRACT.md`](./CONTRACT.md) 第 9 节把这条钉成了硬约束)。所以面板、CLI、MCP、
桌面版对同一件事给同一个数。
装出来的软件**自带 Node 与 CodeGraph**,用户机器上什么都不用先装:
| | 命令行(npm / 源码) | 独立窗口版 |
|---|---|---|
| 要先装 Node ≥ 22.5 | 要 | **不要**(随包自带) |
| 要下 CodeGraph | 首次 `npm i` 时下 | **不要**(已在安装包里) |
| 安装包体积 | 1.3 MB + 依赖 | **~35 MB**(解包 268 MB) |
| 装到哪 | 全局 / 项目 `node_modules` | `%LOCALAPPDATA%\ArchView`,**免 UAC** |
| 工作区注册表 | `~/.archview/workspaces.json` | 同一份(两边看到同一批工作区) |
装完的用法就是「开窗 → 在列表页填一个仓库的绝对路径 → 点重建 → 看图」,全程不碰终端。
**没有索引时重建会自己代跑 `codegraph init`**(契约第 6 节)—— 老实现在这里回一条
「先跑 `archview init`」,而那是一条只有窗口的用户没法执行的命令。
还没做的两件事,说清楚:**没有代码签名**(第一次运行会被 SmartScreen 拦一次,
「更多信息 → 仍要运行」)、**只有 Windows x64**(打包脚本按当前平台取 CodeGraph 的
平台子包,跨平台要在目标平台上各打一次)。细节见 [`desktop/README.md`](./desktop/README.md)。
### 平台现状(别指望我们全平台都测过)
- **Windows** —— 主要开发与验证平台。本 README 里的命令与输出都来自 Windows 11(build 26200)+ PowerShell + Node 22.20.0 + pnpm 10.28.2 上的实跑。skill 安装用 junction,不需要管理员权限。查端口占用:`netstat -ano | findstr :7420`。
- **macOS / Linux** —— 代码里所有平台相关分支都写了(打开浏览器用 `open`/`xdg-open`,skill 安装退化成 symlink),但**我们没有在这两个平台上系统性跑过验收**。遇到问题请开 issue,别当成「官方支持」。
- 运行时会看到一行 `ExperimentalWarning: SQLite is an experimental feature`。这是 Node 对 `node:sqlite` 的常规提示,不是错误。
---
## 5. 上手:从取代码到看见图
五步。每一步都写清楚**在哪跑**、**跑完发生什么**、**怎么知道成功了**。
> 不想自己走这五步?把 [开头那段提示词](#-让-ai-帮你从源码装) 粘给你的 AI 助手,
> 它会照 [`SETUP-FOR-AI.md`](./SETUP-FOR-AI.md) 做完前三步。
### 第 1 步:取代码
npm 上现在还没有它(见第 4 节:包已经打好、还没 publish),所以第一步是把仓库弄到本机。
发布之后这一节整节都可以跳过 —— `npm i -g archview` 之后直接从第 3 步(`archview init`)开始。
四条路,挑一条:
**A. `git clone`(首选)** —— 以后 `git pull` 就能更新。
```powershell
# Windows PowerShell
git clone --branch main --single-branch https://github.com/LZZLHY/archview.git "$env:USERPROFILE\archview"
cd "$env:USERPROFILE\archview"
```
```bash
# macOS / Linux
git clone --branch main --single-branch https://github.com/LZZLHY/archview.git "$HOME/archview"
cd "$HOME/archview"
```
**B. 下载 zip(没有 git 时)** —— 代价:以后更新只能重新下载覆盖。
```powershell
# Windows PowerShell
Invoke-WebRequest https://github.com/LZZLHY/archview/archive/refs/heads/main.zip -OutFile "$env:TEMP\archview.zip"
Expand-Archive "$env:TEMP\archview.zip" -DestinationPath "$env:TEMP\av" -Force
Move-Item "$env:TEMP\av\archview-main" "$env:USERPROFILE\archview"
```
```bash
# macOS / Linux
curl -L https://github.com/LZZLHY/archview/archive/refs/heads/main.zip -o /tmp/archview.zip
unzip -q /tmp/archview.zip -d /tmp/av && mv /tmp/av/archview-main "$HOME/archview"
```
解压出来的目录名是 `archview-main`,记得改名(上面两条已经代你改了)。
**C. `gh repo clone`(装了 GitHub CLI 时)**
```bash
gh repo clone LZZLHY/archview "$HOME/archview"
```
**D. 一键脚本(把第 1、2 步一起做完)** —— 取代码 + `pnpm install` + `pnpm build` + 再 `pnpm install` 一次(补 bin 链接)+ 自检。
先用 A/B/C 拿到代码,然后:
```powershell
# Windows PowerShell。-ExecutionPolicy Bypass 只影响这一次调用,不改系统策略
powershell -ExecutionPolicy Bypass -File scripts\setup.ps1
powershell -ExecutionPolicy Bypass -File scripts\setup.ps1 -Dir D:\tools\archview -Ref main
```
```bash
# macOS / Linux
bash scripts/setup.sh
bash scripts/setup.sh --dir /opt/archview --ref main
```
还没 clone、想直接从云端拿脚本的话,**先下载看一眼再跑**,别无脑管道执行远程脚本:
```powershell
irm https://raw.githubusercontent.com/LZZLHY/archview/main/scripts/setup.ps1 -OutFile "$env:TEMP\av-setup.ps1"
Get-Content "$env:TEMP\av-setup.ps1" -TotalCount 60 # 看一眼
powershell -ExecutionPolicy Bypass -File "$env:TEMP\av-setup.ps1"
```
```bash
curl -fsSL https://raw.githubusercontent.com/LZZLHY/archview/main/scripts/setup.sh -o /tmp/av-setup.sh
less /tmp/av-setup.sh # 看一眼
bash /tmp/av-setup.sh
```
脚本参数:`-Dir/--dir <路径>`(默认 `~/archview`)、`-Ref/--ref <分支或 tag>`、
`-SkipBuild/--skip-build`、`-Help/--help`。三条行为保证:目录已存在且是 archview 仓库 →
`git pull` + 重新 build,不重复 clone;已存在但**不是** archview 仓库 → 报错退出、那个目录一个字节都不动;
前置检查失败 → 给可执行的下一步而不是裸 `exit 1`。它**刻意不做** `archview init` 与起服务 ——
那涉及你自己的仓库和常驻进程,得你或你的 AI 显式决定。
> 怎么知道成功了:安装目录下 `package.json` 存在且它的 `name` 是 `archview`。
> 用 git 取的还能 `git rev-parse --short HEAD` 看到一个 sha。
>
> 路径尽量别带空格 —— 能用,但之后每条命令的路径参数都得记着加引号。
### 第 2 步:装依赖 + 构建
在**仓库根**(也就是第 1 步落地的那个目录,比如 `~/archview`):
```bash
pnpm install
pnpm build
```
跑完会发生什么:六个包各自编译。五个 node 包 `tsc` 出 `dist/`,`packages/web` 用 Vite 出 `packages/web/dist/`(面板前端产物,服务靠它才有页面)。
怎么知道成功了:`packages/cli/dist/bin/archview.js` 与 `packages/web/dist/index.html` 都存在,且下面这条能打出帮助:
```bash
pnpm archview --help
```
> **`pnpm install` 首次会刷一串 `WARN Failed to create bin at ... ENOENT`。**
> 四个包的 `bin` 都指向 `dist/`,而首次 install 时 `dist/` 还不存在,所以 pnpm 建不出 `node_modules/.bin/` 里的链接(**最近一次从 GitHub 全新克隆实测 13 条**;此前另一次运行是 12 条 —— 条数随 pnpm 版本与 store 布局微变,**别把条数当判据**,看到这类 WARN 就当正常)。**它是无害的**:下面所有命令走的是仓库根的 npm script `pnpm archview`(等价于 `node packages/cli/dist/bin/archview.js`),不依赖 bin 链接。
> 想拿到真正的 `archview` / `archview-skill` 命令:`pnpm build` 之后**再跑一次 `pnpm install`**,这次链接会建好,之后 `pnpm exec archview --version` 与 `pnpm exec archview-skill …` 都能用。
> **走一键脚本(`scripts/setup.ps1` / `setup.sh`)的话这一步已经代你做了**(它在 build 之后又 install 了一次,并当场验证 `archview-skill` 的链接在不在)—— 少了它,文档里所有 `pnpm exec archview-skill …` 的命令都会报 `not recognized` / `Command "archview-skill" not found`。
> 我们刻意没加 `prepare` 脚本自动编译 —— 只想装依赖(CI 缓存、只改文档)的场合不该被迫等一次 Vite 全量构建。
> ### ⚠️ `typecheck` 必须在 `build` 之后跑
>
> ```bash
> pnpm -r run build # 先这个
> pnpm -r run typecheck # 再这个
> ```
>
> 反过来一定失败,报一串 `TS2307: Cannot find module '@archview/core'`(或它的 subpath,例如 `'@archview/core/themes'`)`or its corresponding type declarations`。原因是跨包类型走的是各包 `package.json` 的 `exports` → `dist/*.d.ts`,而 `dist/` 是 gitignore 的:**没 build 就没有 `.d.ts`**。仓库里没有 TS project references,也没有把类型指回 `src` 的 path 别名,所以这不是配置疏漏,而是既有性质 —— 陌生人一定会踩,记住顺序就行。
>
> 同一条机制在日常开发里也会咬人:只要有人在某个包的 `src/` 里新增了一个导出 subpath,其它包在**那个包重新 build 之前**都 typecheck 不过。看到 `TS2307` 先想「是不是该先 build」。
### 第 3 步:接入第一个要分析的仓库
还在 ArchView 仓库根。把路径换成你自己的仓库:
```bash
pnpm archview init d:/code/my-repo
```
跑完会发生什么(五步,每步都幂等,重复跑只是把现状告诉你):
1. 环境检查(Node 版本、目录存在、是不是 git 仓库)
2. 在**你那个仓库**里建 CodeGraph 索引 `.codegraph/codegraph.db`(已有就跳过)
3. 写 `.archview/config.json`(已有就不覆盖;`--force-config` 才重写),并按 `oh-package.json5` / `pnpm-workspace.yaml` / `Cargo.toml` / `go.mod` 自动预填模块骨架
4. 幂等地往**你那个仓库**的 `.gitignore` 追加一个带标记的块(详见第 7 节)
5. 把它登进 `archview/workspaces.json`(工作区注册表)
怎么知道成功了:最后打印工作区 id 与下一步命令。实跑输出(本次用的是 ArchView 自己的源码副本作为被分析仓库):
```
[2/5] CodeGraph 索引(结构事实的唯一来源,铁律 1)
→ codegraph init "…/selfcopy"(大仓可能要几分钟,超时 1800s)
* Indexed 160 files
• 2,142 nodes, 6,797 edges in 1.4s
✓ 索引建好了(exit 0,2.5s)-> …/selfcopy/.codegraph
[3/5] .archview/config.json
✓ 已写入:…/selfcopy/.archview/config.json
模块识别:npmWorkspaces —— 自动识别命中 pnpm-workspace.yaml / package.json workspaces,6 个模块
[5/5] 登记进 workspaces.json
✓ 已登记:selfcopy -> …/selfcopy
接入完成。下一步:
archview build selfcopy # 建面板数据(codegraph sync + 建图 + 简报)
archview serve --open # 起服务(127.0.0.1:7420),打开列表页
archview status selfcopy # 随时看索引/图/摘要覆盖率/漂移
```
常用选项:`--id <id>`(进 URL,只允许 `[a-z0-9][a-z0-9_-]*`)、`--name "显示名"`、`--skip-index`、`--telemetry-off`、`--json`(机器可读:工作区 id、配置/gitignore 动作、模块识别结论、下一步命令;五步进度进 `log` 字段)。完整清单:`pnpm archview init --help`。
### 第 4 步:建面板数据
```bash
pnpm archview build # 只登记了一个工作区时可以不带 id
pnpm archview build my-repo # 多个工作区时说清是哪个
```
跑完会发生什么:`codegraph sync`(让索引跟上磁盘)→ 建图 → 写 `.archview/graph.json` 与 `meta.json` → 生成 `.archview/briefs/*.json`(给 LLM 的结构简报)→ 再确认一次 `.gitignore` 块。
怎么知道成功了:每一步前面是 `✓`,末尾给出节点/边/模块与摘要覆盖率。本次实跑:
```
✓ codegraph sync 319 ms exit 0
✓ buildGraph 72 ms 981 节点 / 3851 边 / 7 layer
✓ writeGraph 6 ms
✓ writeMeta 1 ms
✓ buildAllBriefs 3 ms 7 份简报
✓ ensureGitignoreBlock 0 ms unchanged
节点 981 边 3851(文件级 734) 文件节点 158 模块 7
摘要 已应用 0 覆盖率 0.0%(分母=文件节点+框架组件)
```
覆盖率 0% 是正常的第一次结果 —— 语义摘要要靠 agent 写,见第 6 节。
有告警时(摘要被误放进子目录导致一条都没读到、`config.json` 校验失败整份回退、`modules.labels` 里的 key 对不上任何模块)`build` 会把它们单独拎出来重打一遍并给出修法。**告警不是失败**,图确实建出来了,但那些东西没生效。
要在脚本/agent 里解析结果就用 `--json`(比 `--quiet` 有用得多:它给的是完整的 `RebuildResult` —— `steps` / `log` / `warnings` / `before` / `after` / `files` / `moduleStrategy` / `summaries` / `agentGuide`,与面板的 `POST api/rebuild`、MCP 的 `archview_rebuild` 是同一个对象):
```bash
pnpm archview build my-repo --json
```
随时复查实况:
```bash
pnpm archview status # 不带 id 就把注册表里所有工作区各打一段
pnpm archview status my-repo --json
```
`status` 的数字与列表页、MCP 的 `archview_status` 来自同一个函数 —— 不会出现两个不同的覆盖率。
### 第 5 步:起服务看图
```bash
pnpm archview serve --open
```
跑完会发生什么:**一个进程、一个端口服务所有已登记的工作区**。默认 `127.0.0.1:7420`;被占用时自动往上找(最多 20 个),显式给了 `--port` 就不换、占用即失败。启动打印一次性 session token,所有 `api/*` 都校验它。
怎么知道成功了:横幅长这样(本次实跑,token 已截断):
```
ArchView 服务已启动 127.0.0.1:7420(只绑本机)
注册表 …\workspaces.json
工作区 selfcopy
面板产物 …\packages\web\dist
🔑 http://127.0.0.1:7420/?token=be5fea76…27d1
所有 api/* 都要带这个 token(?token= 或 x-archview-token 头)。token 每次启动随机生成,进程重启会换。
Ctrl-C 停止。(进程重启会换 token。)
```
给了 `--token <你定的串>` 时最后两行改成「token 由 `--token` 指定,重启不变」并提醒别提交它 ——
文案按「有没有显式给 `--token`」分岔,免得固定了 token 的人以为没生效。
「面板产物」那一行必须指到一个真实存在的 `packages/web/dist` —— 没构建前端时列表页会显式提示 **面板前端未构建**,这时跑 `pnpm --filter @archview/web build`。
列表页上每个工作区一张卡片,四个按钮:**打开面板** / **重建数据** / **复制 agent 提示词** / **漂移详情**。
本次对服务的实测(全部带 token):
| 端点 | 结果 |
|---|---|
| `GET /` | 200,工作区列表页(32.7 KB) |
| `GET /w/<id>/` | 200,dashboard SPA |
| `GET /w/<id>` (少了尾斜杠) | **301** -> `/w/<id>/`(带上原 query)。少了这条重定向面板会白屏 |
| `GET /w/<id>/api/graph.json` | 200,1.5 MB |
| `GET /w/<id>/api/config.json` `meta.json` `staleness.json` | 200 |
| `GET /w/<id>/api/prompt` | 200,给 agent 的引导提示词 |
| `GET /api/workspaces` | 200 |
| `GET /skill/download` | 200,160 KB gzip |
| `GET /w/<id>/api/domain-graph.json` | 404(我们不产出它,vendored 面板会安静降级) |
| 不带 token 取 `graph.json` | **403** |
### 三种调用方式,随便挑
上面所有命令都写成 `pnpm archview …`(仓库根的 npm script),因为它**在首次 install 之后立刻可用**,
不依赖 bin 链接。另外两种等价写法:
```bash
# ① 直接跑 node,连 pnpm 都不要(脚本化、给 AI 用最省事)
node packages/cli/dist/bin/archview.js --help # 总览
node packages/cli/dist/bin/archview.js init --help # 每个子命令都有 --help
node packages/server/dist/bin/serve.js --port 7500 # 只起服务,跟 archview serve 是同一个 startServer
# 注意它没有 --help:给任何参数都直接起服务并常驻
# ② 真正的 archview 命令 —— 需要 bin 链接,也就是 build 之后再 install 一次
pnpm install # 这次不会再刷 ENOENT WARN,链接会建好(一键脚本已代你做过)
pnpm exec archview --version
pnpm exec archview-skill --help
```
注意 ② 只在**这个仓库内**有效(bin 链接在仓库的 `node_modules/.bin/`)。想让这四个命令进全局 PATH
就得走 npm 那条路(`npm i -g archview`),而它现在还没 publish —— 见第 4 节。
---
## 6. 让 agent 补上语义
图建好之后节点是有了,但每个节点的 `summary` 还只是确定性兜底句(docstring / 由签名合成 / `<名字> —— <路径> 中的 <kind>`)。把它们换成人话是 agent 的活。
### 零安装路径(先用这个)
1. 列表页点 **复制 agent 提示词**(等价于 `GET /w/<id>/api/prompt`)。
2. 把提示词粘给正在编辑那个仓库的 AI。提示词很短,主要作用是指向工作区里的 `<workspace>/.archview/AGENT-GUIDE.md` —— 一个本地文件,任何工具都能读。
3. **`AGENT-GUIDE.md` 需要生成一次**(`build` 不会自动生成它):
```bash
pnpm archview skill guide --workspace d:/code/my-repo --write
```
本次实跑写出 22 KB(22185 字节),十节内容:铁律、这个工作区现在的样子、具体缺哪些摘要(逐个 nodeId 列出)、过期的摘要、你的输入(结构简报路径)、按检测到的语言挑好的指导、输出格式与提交方式、触发重建 + 只读地确认现状的端点、交付前自检清单、报告格式。提示词里也带了这条命令,agent 自己会跑。
**生成一次之后就不用再管它**:往后每次重建(面板按钮 / `archview build` / `POST api/rebuild` / MCP `archview_rebuild`)都会把它整份重写(契约第 2 节要求如此,所以它在 gitignore 里)。重建的 `steps` 里能看到 `writeAgentGuide` 这一步跑了没跑。反过来说,**文件不存在时重建不会替你创建** —— 我们不往你的工作区塞你没要过的文件。
4. agent 照指南往 `.archview/summaries/<分片>.json` 写摘要。分片名 = 模块 key 里的 `/` 换成 `_`(模块 `packages/core` → `packages_core.json`)。**这个目录是平的,写进子目录的摘要一条都不会被读**(会告警,但那一轮活白干了)。
5. 重建:`pnpm archview build`,或列表页点「重建数据」,或 agent 自己 `POST /w/<id>/api/rebuild?token=…`。
6. 面板上出现语义,`status` 的覆盖率往上走。
摘要提交有服务端护栏(默认值在 `packages/core/src/limits.ts`):每条 30–140 字、标签 ≤6 个且每个 ≤16 字、单批 ≤200 条(超了**整批拒绝**,一条都不写盘,不截断),再加一份「空话词表」拦截「负责处理相关逻辑」这类废话。阈值与词表的真身只有一份,在 `@archview/core`:MCP 用它执法、skill 用同一份写指南 —— 免得说明书和执法者不一致(历史上就出过这个 bug)。
⚠️ **护栏只在 MCP 提交这条路上自动执法。** 上面第 4 步那样直接写摘要分片时没有任何东西检查它们(写砸了不报错,静默进面板)。所以直接写文件之后跑一次自检,判据与 MCP 完全同一份代码(`@archview/core` 的 `checkSummaryItem`):
```bash
pnpm exec archview-skill check-summaries --workspace d:/code/my-repo
# 报 "archview-skill not found" 就是 bin 链接还没建(build 之后没再 install 过)。
# 两条出路,任选一条:
pnpm install # 补上链接,之后上面那条就能用
node packages/skill/dist/bin/skill.js check-summaries --workspace d:/code/my-repo # 不依赖链接
```
逐条报告孤儿 nodeId、长度越界、tags 数量/长度/与确定性标签撞车、空话命中、`hash` 是否等于当前 content_hash,外加 `summaries/` 下有没有子目录、分片 JSON 是否合法;有不合规项时**退出码非 0**。AGENT-GUIDE 的方式 A 段落与自检清单里都指向它。
### 进阶路径:装 skill + MCP
更省 token(不必通读源码,读结构简报即可),提交时有结构化校验。
```bash
pnpm archview skill hosts # 支持哪些宿主与各自的路径依据
pnpm archview skill install kiro --dry-run # 先看它要动哪些文件(什么都不写)
pnpm archview skill install kiro # 真装
pnpm archview skill verify # 语言/框架指导自检
```
实测:`skill hosts` 列出 7 个已确认宿主(kiro、claude、cursor、codex、opencode、gemini、copilot CLI)与一批**明确不支持**的(路径随平台/版本变、没法复现验证的,我们不猜,用 `/skill/download` 手动放)。`skill verify` 实测「语言指导 38 份,框架指导 10 份,全部存在、非占位符、都有上游标注与 ArchView 改造段落」。
Kiro 优先:skill 装到 `~/.kiro/skills/archview`,agent 定义 `~/.kiro/agents/archview.json`,MCP 写 `~/.kiro/settings/mcp.json`。安装器**合并不覆盖**(只动 `mcpServers.archview` 一个键),改写已有文件前留 `.bak-<时间戳>`,Windows 用 junction 不用 symlink。`--dry-run` 会把要写的内容原样打出来(实测确认它一个字节都不写),`--home <dir>` 可以把 HOME 指到别处试。
MCP 配置也可以不装 skill 自己抄:`AGENT-GUIDE.md` 与 `api/prompt` 的 `meta.mcp.snippet` 里就带着可直接粘的片段,指向同仓已构建的 `packages/mcp/dist/bin/mcp.js`。
六个 MCP 工具,只读 + 提交摘要,**没有任何写图的工具**:
| 工具 | 作用 |
|---|---|
| `archview_status` | 索引/图/摘要覆盖率/漂移 |
| `archview_list_modules` | 模块清单与依赖,附各模块的分片名 |
| `archview_missing_summaries` | 缺摘要或过期的节点,每条附结构简报 |
| `archview_submit_summaries` | 提交摘要,服务端逐条校验并回报哪条被拒、为什么、怎么改 |
| `archview_rebuild` | codegraph sync + 重建图 |
| `archview_validate` | 校验当前图并回报 issues |
任何宿主都能直接下载 skill 包:`GET /skill/download`(tar.gz),或 `GET /skill/*` 明文浏览单个文件(比如 `/skill/SKILL.md`)。
---
## 7. 数据放哪 / 什么该提交进 git
**这一节决定了你换机器之后摘要还在不在。** 数据全落在**被分析的那个仓库**里,不在 ArchView 仓库里:
```
<你的仓库>/
.codegraph/ CodeGraph 索引(SQLite,外部工具的,我们只读) → 不提交
codegraph.json CodeGraph 的排除清单,可选、手写 → 写了就提交(团队共享口径)
.archview/
config.json 语言、模块策略与标签、边阈值、输出语言 → **提交**
summaries/*.json LLM 摘要,按模块分片 → **提交**(这是资产)
graph.json 派生图,面板的数据源 → 不提交
meta.json content_hash 快照(漂移检测的依据) → 不提交
briefs/*.json 给 LLM 的结构简报 → 不提交
AGENT-GUIDE.md 给 agent 的操作说明(每次生成整份重写,含时间戳)→ 不提交
```
判断标准只有一条:**人和 LLM 攒出来的东西提交,工具能重新算出来的东西不提交。**
- `summaries/` 是几百条人工/LLM 写的中文摘要,重新生成要花掉真金白银的 token。它跟着代码走 —— 换机器、换人、换 agent 都还在。
- `config.json` 是团队对「模块怎么分、边阈值多少、输出什么语言」的共识。
- 其余都是 `archview build` 十秒内能重算的。`AGENT-GUIDE.md` 尤其不该提交:它每次重建整份重写并带时间戳,提交它只会制造冲突。
`archview init` 与每次 `rebuild` 都会**幂等地**往你那个仓库的 `.gitignore` 追加这个块(靠标记识别,重复运行不重复追加,也不动你原有的行):
```gitignore
# >>> archview >>>
.codegraph/
.archview/graph.json
.archview/meta.json
.archview/briefs/
.archview/AGENT-GUIDE.md
# .archview/summaries/ 与 .archview/config.json 故意不忽略——它们要提交
# <<< archview <<<
```
### ArchView 仓库自己的 .gitignore
本仓库的 `.gitignore` 排除 `node_modules/`、`dist/`(五个包的 tsc 产物与 `packages/web` 的 Vite 产物同名,一条覆盖)、`dist-pack/`、`*.tsbuildinfo`、`.tmp/`、`.codegraph/` 与 `*.db*`、`*.log`、`.env*`、编辑器目录,以及 **`workspaces.json`**。
`workspaces.json` 是工作区注册表,内容是本机绝对路径(`d:/code/my-repo`),因机器而异 —— 所以新克隆的仓库里这张表一定是空的,这是设计而不是缺失。用 `archview init` 自己造。
## 8. 验收脚本
五个脚本加一组单元测试,合起来一百五十多项断言。大部分是「起服务 → 测 → 停」,不留常驻进程,对被检查工作区的写入可逆。
**共同前置只有一条**:`pnpm build`。**协议层的三个脚本(`packages/server/scripts/acceptance.mjs`、`packages/mcp/scripts/acceptance.mjs`、`final-check.mjs`)不给 `--workspace` 就自建一次性 fixture 工作区**,不需要你准备任何工作区 —— 陌生克隆下来直接 `node final-check.mjs` 就能跑。只有 `packages/core/scripts/selfcheck.mjs` 仍然必须给一个真实工作区目录。
下面「实测」一列给两套数字:**fixture 模式**(默认,见下)与 `--workspace <id>` 打真实工作区。ArkTS 专项断言在 fixture 与任何非 ArkTS 工作区上都会明确标成「跳过 / 不适用」,不算失败。
**「跳过」与「失败」的界线**:只在某项检查的前提在这个工作区上不成立时才跳过(图里没有 `.ets`
节点 → ArkTS 高亮无从检查;只有 1 个 layer → 模块间连线无从成立)。**多模块工作区上没有模块间
连线是真 bug,照样报失败**(先看「铁律4 文件级边」是不是 0 —— 不上卷就没有模块总览连线)。
### 默认路径一个字节都不碰用户数据
协议层验收有两种模式,界线就是有没有 `--workspace`:
- **不给 `--workspace`(默认)**:脚本自己在 `os.tmpdir()` 里造一个**一次性 fixture 仓库**
(`scripts/lib/fixture-workspace.mjs`:`pnpm-workspace.yaml` + 4 个包、包之间走深路径的真实跨模块
import、十几个 `.ts` 含 class/function/interface、`git init` + 一次 commit、按护栏写好的摘要每分片
≥ 2 条),跑 CodeGraph 索引、建两次图,登记进**它自己的**临时注册表,跑完删掉。**你的仓库、
你的摘要、仓库根那张 `workspaces.json`,一个字节都不会被碰。** 想留下 fixture 看看:`--keep-fixture`。
- **给了 `--workspace <id>`**:跑在那个真实工作区上,开跑前用一个方框显眼地告诉你这次会写什么。
`mcp` 那个**会写**该工作区的 `.archview/`(挪走一条摘要造缺口、改 `graph.json`、真跑一次 rebuild),
备份 / 逐文件 sha256 校验 / 先拷再 rename / 结束时比对,护栏一条不少。
**为什么默认换成 fixture。** 「刻意不用桩数据、真实数字才有价值」这条理由**只对
`packages/core/scripts/selfcheck.mjs` 成立** —— 它验的是 builder 在真实代码上的语义
(2253 条 `auto-corrected` 警告就是在真实数据上才暴露的)。而 server / mcp 的验收验的是
**端点行为与工具协议**,自建一个小仓库完全够。代价却是真实的:这些脚本会删摘要条目、改
`graph.json`、真跑 rebuild。此前的两轮修补(修还原逻辑、取消「注册表第一条」默认回退、加备份校验)
都没动根因 —— 只要默认靶子是用户的真实仓库,安全就永远只靠「每一处备份代码都没写错」,
而事故(一次 `restore()` 静默删掉一个工作区里 308 条不可再生的人工摘要,脚本还报
`PASS 已恢复原样`)已经证明这个假设不成立。
**`rebuild` 的写入面比 `.archview/` 大一圈,备份名单跟着走。** `ensureGitignoreBlock` 碰的是
**工作区根的 `.gitignore`**(改写前的原文落在 `.gitignore.archview-bak`),所以 mcp 验收的备份名单
现在**相对工作区根**解析:`.archview/graph.json`、`.archview/meta.json`、`.archview/briefs`、
`.archview/summaries`、`.archview/AGENT-GUIDE.md`、`.gitignore`、`.gitignore.archview-bak`。
备份 → 逐文件 sha256 校验 → 还原 → 结束时比对,这两个根文件与 `.archview/` 走的是同一条路。
(此前名单相对 `.archview/` 解析,于是一次 `--workspace` 验收改写了用户的 `.gitignore` 而护栏
一无所知 —— 它只看 `.archview/`。)`server` 那个脚本在 `--workspace` 模式下也给这两个文件加了
备份 / 还原 / 逐字节比对。
名单外唯一会被写的东西是 `.codegraph/codegraph.db`(`codegraph sync` 改它):**刻意不还原** ——
几十到几百 MB 的可再生索引,拷进备份的代价远大于收益。
**托管块内的行不再被静默清除。** 块的语义是整块替换,所以写在 `# >>> archview >>>` 与
`# <<< archview <<<` **之间**的用户规则以前会在下一次 rebuild 消失。现在 `ensureGitignoreBlock`
发现块内有不是自己生成的行时**拒绝写入**(`refused`),把行号与原文报进 rebuild 日志、
`archview status`、列表页与 `archview_status` 的 warnings,一个字节都不动。自己的规则写在块外。
| 脚本 | 怎么指定工作区 | 实测 |
|---|---|---|
| `node final-check.mjs` | 不给 = fixture(只读,跑完删);`--workspace <id>` / `ARCHVIEW_CHECK_WS` 打真实工作区,同样只读、不 rebuild | 断言总数随工作区形状变,**跳过的项不计入分母**(所以打印出来的永远是 `N/N`)。实测:**fixture 14/14 + 1 跳过**(ArkTS 高亮不适用);ArkTS 多模块真实工作区 **15/15**;单模块工作区 **13/13 + 2 跳过**(再跳过「模块总览有连线」—— 只有 1 个 layer 时跨 layer 的边无从成立,见第 1 节那张表) |
| `node packages/server/scripts/acceptance.mjs` | 不给 = fixture;`--workspace <id>` / `ARCHVIEW_ACCEPT_WS` 打真实工作区(只读,除非 `--rebuild` —— 那会真的重建它的 `.archview/`)。**没有「注册表第一条」这种回退** | **fixture 46 通过 / 0 失败**(ArkTS 与 json5 两条断言在 fixture 上自动跳过);ArkTS 真实工作区 **47 通过 / 0 失败** |
| `pnpm --filter @archview/server run test` | 不用指定;末项会遍历注册表里所有工作区校验它们的列表页 payload | **17/17 通过**(`node --test`,跑完约 0.8s;比旧版多一项:CONTRACT.md 第 2 节的 gitignore 块与 `GITIGNORE_BLOCK_BODY` 逐字一致 —— 这条约束以前只写在契约里,没有判据) |
| `node packages/mcp/scripts/acceptance.mjs` | 不给 = fixture(fixture 自带合规摘要,缺口造得出来);`--workspace <id>` / `ARCHVIEW_MCP_WS` / `ARCHVIEW_ACCEPT_WS` 打真实工作区,那个工作区**必须已经有 LLM 摘要**(脚本靠挪走一条造缺口)。`--skip-rebuild` 跳过最后那次真重建;`ARCHVIEW_WORKSPACES` 可以换注册表 | **fixture 37/37 通过**,含末项「工作区已恢复原样(`.archview/` **与工作区根的 `.gitignore`** 逐文件 sha256 相同)」。显式模式下开跑就打印备份目录路径;备份做完会与工作区逐文件比对 sha256,不一致就当场 `exit 1`(那时还一个字节都没改)。`Ctrl-C` 走与 `finally` 同一条清理路径,退出码 130 |
| `node packages/core/scripts/selfcheck.mjs --workspace <dir>` | `--workspace` **必填,且是目录不是 id**;`--summaries <dir>` 可选;`--keep` 保留中间产物。不传参数时打印用法与本机已登记的工作区 | **9/9 通过**(其中一项:`.gitignore` 在**五种**真实形状下用户自有行一字不少 —— 第五种是「用户把规则写在托管块里」,判据是必须 `refused`)。被检查的工作区一个字节都不写(末项就是验证这个) |
| `pnpm archview skill verify` | 无前置,不碰任何工作区 | 语言指导 **38** 份 + 框架指导 **10** 份全部通过 |
### 跑之前先清环境变量残留
```powershell
# PowerShell
Remove-Item Env:ARCHVIEW_ACCEPT_WS,Env:ARCHVIEW_WORKSPACES,Env:ARCHVIEW_MCP_WS,Env:ARCHVIEW_CHECK_WS -ErrorAction SilentlyContinue
```
```bash
# bash / zsh
unset ARCHVIEW_ACCEPT_WS ARCHVIEW_WORKSPACES ARCHVIEW_MCP_WS ARCHVIEW_CHECK_WS
```
`ARCHVIEW_WORKSPACES` 换掉的是**读哪张注册表**,`ARCHVIEW_ACCEPT_WS` / `ARCHVIEW_MCP_WS` / `ARCHVIEW_CHECK_WS` 换掉的是**测哪个工作区**。它们在 shell 里残留一次,就会出现「明明没改代码,验收数字却变了」这种最费时间的假象 —— 因为你测的已经是另一个仓库了。同一个道理也适用于命令行:`archview init|build|status --workspaces <file>` 可以显式指定注册表,多注册表并行时建议每条命令都写上,别靠环境变量记状态。
三个坑,踩过才知道:
- **`selfcheck.mjs` 结束时会把整个 `archview/.tmp/` 删掉**(除非给 `--keep`),不只是它自己那个子目录。别把想留的东西放在 `.tmp/` 下。
- **裸跑三个协议层脚本现在会自建 fixture,不再 `exit 1`。** 上一版是「不给 `--workspace` 就打印用法并退出」,现在是「不给就用一次性 fixture」。所以 `node packages/mcp/scripts/acceptance.mjs` 直接跑得通 —— 它打的是 `os.tmpdir()` 里自己造的仓库,不是你的。
- **MCP 验收里「写入现有分片时先合并再写」这条断言,要求被挑中的那个分片里除了缺口之外还有别的条目。** 脚本按文件名排序取第一个含 file 节点的分片来造缺口,如果那个分片恰好只有一条摘要(比如只有一个文件的 `_other` 模块),缺口造完分片就空了,这条断言就无从成立,会报一条 `36/37`。fixture 因此刻意给每个 file 节点都写摘要(每个分片 ≥ 2 条,生成器里有硬断言把关)。打真实工作区遇到这条失败是工作区形状问题,不是代码问题:把摘要写全一点,或让第一个分片对应一个多文件模块即可。
- **`--workspace` 模式下「幂等重建」那条断言要求工作区的 `graph.json` 与它的源码是同步的。** 断言比的是重建前后的节点数;如果该仓库在上次 `archview build` 之后改过源码,重建自然会得出不同的节点数(实测:某工作区 8972 → 8969,因为源码里少了一个文件),报一条 `36/37`。这是工作区状态问题:先 `archview build` 一次再跑验收。fixture 模式没有这个问题(图是刚建的)。
## 9. 实测数字(附出处)
数字随仓库内容变化,所以每一条都标明是哪个仓库、什么时候、什么口径。
**A. ArchView 分析自己**(本次为写这份 README 重跑;被分析的是 ArchView 源码的一份副本,排除了 `node_modules/`、`dist/`、`.tmp/`;Windows 11 / Node 22.20.0 / pnpm 10.28.2):
| 项 | 值 |
|---|---|
| CodeGraph 索引 | 160 文件 / 2142 节点 / 6797 边(1.4s);语言 typescript(114) tsx(37) javascript(7) yaml(2) |
| 图 | **981 节点**(function 578 / class 245 / file 158) / **3851 边**,建图 72 ms |
| 文件级边(铁律 4 的上卷) | **734**(其中上卷新增 680) |
| layer | **7**(6 个 pnpm 包 + `_other`),模块策略 `npmWorkspaces`(命中 `pnpm-workspace.yaml`) |
| 模块总览连线 | **8 对模块之间共 210 条聚合边** |
| 空 `summary` 节点 | **0**(981 个节点全非空,这是铁律 2 的验收指标) |
| 摘要覆盖 | 首次建图 **0 / 158**(0%)—— 语义要靠 agent 写,这就是一个新工作区该有的样子 |
| 各模块文件数 | web 84 / server 23 / core 17 / cli 12 / skill 11 / mcp 10 / `_other` 1 |
数字会随源码变动漂移:同一套口径在**此前一个更早的源码版本**上跑出来是 899 节点 / 6 模块 / 618 文件级边 / 6 对模块之间 148 条聚合边。差异全部来自源码本身长大了,不是口径变了 —— 所以别把这些数当基准值去断言,要断言就跑验收脚本。
**B. AMCL(作者机器上的一个 HarmonyOS / ArkTS 应用,ohpm 多模块)** —— 这些数字是**只读**观测到的(读它已经建好的 `graph.json`,没有重建过它):**8972 节点 / 27864 边 / 11 模块 / 文件级边 3216 / 摘要 308 条(覆盖 308 / 682,其中框架组件 71)**,模块策略 `ohpm`。更早一个版本上同一套口径是 4277 节点 / 16124 边 / 10 模块 / 摘要 304 —— 差异全部来自那个应用自己长大了。`packages/core/src/limits.ts` 里的摘要长度区间(40–80 字)就是从这批人工摘要量出来的:min 36 / p50 57 / p95 78 / max 108 字。
**C. 验收用的一次性 fixture 仓库**(`scripts/lib/fixture-workspace.mjs` 现造现删,所以这些数字每次都一样,可以拿来当基准):4 个 pnpm 包 / 13 个 `.ts` / 20 个文件 → CodeGraph 索引 0.8s → **49 节点 / 150 边 / 4 模块 / 文件级边 43 / 摘要 13 条(覆盖 13/13)**,模块策略 `npmWorkspaces`,模块总览 6 对模块之间 26 条聚合边,`gitCommitHash` 是真的(`git init` + 一次 commit)。从造目录到图就绪约 **2.0s**。
## 10. 已知限制 / 谁不该用它
诚实清单。不吹。
- **单机工具,没有多用户模型。** 只绑 `127.0.0.1`,鉴权只有一个进程级一次性 session token。没有账号、没有角色、没有审计。**不要暴露到外网**,也不要当团队服务部署。
- **并发重建是互斥的,但拿不到锁会直接失败而不是排队。** 三个入口(面板 / `archview build` / MCP 的 `archview_rebuild`)走同一把文件锁 `.archview/.rebuild.lock`。第二个请求会立刻收到「另一个进程正在重建(pid X,从 Y 开始)」,HTTP 那层回 **409**。刻意不排队:排队会让浏览器一直转圈,而两次全量 `codegraph sync` 排在一起对你没有价值。死锁自愈两条 —— 同机持锁进程已经不在,或超过 30 分钟。**锁只保护重建**:它不管「一边重建、一边有人手改 `summaries/`」,那种情况以最后写盘的为准。
- **写盘是原子的**(同目录临时文件 + `rename`),覆盖 `graph.json` / `meta.json` / `briefs/` / 摘要分片 / `config.json` / 注册表。断电或强杀进程不会留下半截文件。摘要分片另有两道护栏:读不懂旧内容就拒绝覆盖、条目数只允许增加或持平(详见 `CONTRACT.md` 第 5 节)。
- **`/skill/download` 与 `/skill/*` 不校验 token,但能读到的东西是白名单化的。** 它们吐的是随包发布的 skill 文档(`SKILL.md`、`languages/*.md`、`frameworks/*.md`),本来就要给任何 agent 宿主直接拿,所以刻意没上门禁。**可浏览集合 == 随包发布集合**,两者共用同一个目录遍历,而那个遍历排除 `node_modules` / `dist` / `.git` 且只收真实文件 —— 所以符号链接一律不在集合里。这一点是补出来的:老实现只做「不许 `..` + 前缀检查」,挡住了传统穿越,却挡不住 pnpm 在 `packages/skill/node_modules/@archview/core` 放的那个指向 `packages/core` 的链接(链接的文本路径是 skill 目录的子路径,前缀检查一路放行),实测 `GET /skill/node_modules/@archview/core/src/builder.ts` 返回 200 加 25 KB 源码 —— 那已经不是「无 token 的取舍」,是无鉴权的任意文件读。会读你代码的端点(`api/graph.json`、`api/file`、`api/rebuild` …)全部校验 token。
- **摘要质量完全取决于你的 agent 和你给它的预算。** ArchView 只保证「拓扑是真的」和「不许写空话」,不保证摘要写得好。护栏能拦住空话词表里的废话,拦不住一句正确但没用的话。
- **HarmonyOS / ArkTS 是唯一验证充分的场景。** ohpm 模块识别、ArkUI 组件树两跳折叠、`.ets` 高亮都是在真实 ArkTS 工程上打磨的。其它语言只做了**结构层**验证(能索引、能建图、模块能识别、面板能渲染),没有针对性的框架推导,语言指导也只做了文档层自检。
- **只在 Windows 上系统性跑过验收。** macOS / Linux 的平台分支写了但没测。
- **不是「一键理解任意仓库」。** 第一次 `init` 大仓可能要几分钟(CodeGraph 索引),摘要要 agent 跑好几轮。它适合你打算长期维护的项目,不适合十分钟浏览一个陌生仓库。
- **模块总览的 layer 间边是无向的。** vendored 的 `aggregateLayerEdges` 把 A→B 与 B→A 合并了。方向信息在下钻视图里还在。
- **走「包根 barrel」的跨包 import 解析不出来,所以模块总览上会少边。** CodeGraph 能解析深路径的跨包引用(`import … from '../../server/src/rebuild.js'` 这种),但 `import { startServer } from '@archview/server'` —— 即指向包入口、由 `package.json` 的 `exports` 再转发到实现文件的那种 —— 解析不到目标符号,于是这条依赖不进图。本仓库自己就是例子:`packages/cli/src/commands/serve.ts` 走 barrel,简报的 `importsFrom` 里没有 server;`build.ts` 走深路径,解析出来了。**看到模块总览上少一条你确信存在的边,先怀疑这个原因**(去简报里看那个文件的 `importsFrom`:为空或缺目标,就是它)。这是上游 CodeGraph 的解析能力边界,不是可配置项;我们**刻意不在 builder 里按包名猜补**这条边 —— 猜出来的拓扑就是 LLM 写拓扑的另一种形式,违反铁律 1。真要在图上看到它,把那处 import 改成深路径(或等 CodeGraph 支持)。
- **边按 `confidence` / `resolvedBy` 过滤**(默认阈值 0.7,丢弃 `heuristic`)。不过滤会出现纯靠名字撞出来的假模块依赖(实测存在 `confidence: 0.3` 的 fuzzy 边)。反过来说,被过滤掉的真依赖也就看不见了。
- **CodeGraph 遥测默认开启**,但我们代你调它时一律带 `DO_NOT_TRACK=1` 与 `CODEGRAPH_NO_UPDATE_CHECK=1`(写在 `runCodegraph` 里,不是可选项)。想把它的全局开关也关掉:`pnpm archview init … --telemetry-off`。
- **源码浏览端点有硬限制**:`/w/<id>/api/file` 只允许图里出现过的 `filePath`(白名单)、拒绝 `..` 与绝对路径、上限 1 MB、拒绝二进制。
## 11. 架构与包结构
```
archview/
package.json pnpm workspace 根(scripts: build / typecheck / selfcheck / archview)
LICENSE NOTICE README.md CONTRACT.md
AGENTS.md 仓库根路牌(多个 agent 工具会自动读它):装 → SETUP-FOR-AI,改代码 → CONTRACT
SETUP-FOR-AI.md 给 AI 的一次性安装剧本(阶段 + 成功判据 + 决策点 + 失败对策)
scripts/setup.ps1 一键准备(Windows):取代码 + install + build + 补 bin 链接 + 自检。幂等,不碰你的仓库
scripts/setup.sh 同上(macOS / Linux;只做过 bash -n 语法检查,未在真实 Unix 上跑过)
workspaces.json 工作区注册表(本机绝对路径,不提交)
final-check.mjs 整体验收(起→测→停)
packages/
core/ 图模型与校验(vendored UA schema)、CodeGraph 读取、builder(CG→图)、
模块策略、框架 deriver、结构简报、.archview/ 布局与选择性 gitignore、
提交护栏阈值与空话词表(唯一真身)
web/ vendored 改造的 dashboard。按 /w/<id>/api/* 取数,中文默认开
server/ 单端口服务:工作区列表页 + 每工作区的面板与只读 API + rebuild
bin: packages/server/dist/bin/serve.js (archview-serve)
mcp/ MCP server(stdio)。六个工具,只读 + 提交摘要
bin: packages/mcp/dist/bin/mcp.js (archview-mcp)
skill/ SKILL.md 顶层提示词、38 份语言指导 + 10 份框架指导、
AGENT-GUIDE.md 生成器、多宿主安装器
bin: packages/skill/dist/bin/skill.js (archview-skill)
cli/ 统一入口:init | build | serve | status | skill
bin: packages/cli/dist/bin/archview.js (archview)
```
> **「六个包」与 `pnpm install` 打的 `Scope: all 7 workspace projects` 说的是同一件事。**
> `packages/` 下确实是六个包;第七个是**仓库根自己**(`archview`,pnpm 把 workspace 根也算一个
> project,因为它有自己的 `package.json` 与 scripts)。根这个 project 不产出 `dist/`,也不发布 ——
> 它只承载 `pnpm build` / `pnpm typecheck` / `pnpm archview` 这些 script。看到 7 别以为多装了东西。
`cli` 不重实现任何逻辑:`build` 调 server 的 `rebuildOnce`,`status` 调 `inspectWorkspace`,`serve` 调 `startServer`,`skill` 原样转发给 `archview-skill`。理由是**面板、MCP、命令行对同一件事必须给同一个数** —— 覆盖率这种指标一旦有两个来源,两个数一定会分叉。
数据流一句话:
```
你的源码 ──tree-sitter──▶ .codegraph/codegraph.db ──builder──▶ .archview/graph.json ──▶ 面板 / MCP
▲
.archview/summaries/*.json ──┘ (只贡献 summary 与 tags)
▲
你的 LLM agent ┘(读 .archview/briefs/*.json,不读源码)
```
## 12. 许可与致谢
ArchView 自己是 MIT([`LICENSE`](./LICENSE))。它站在两个同样是 MIT 的项目上:
- **[Understand-Anything](https://github.com/Egonex-AI/Understand-Anything)** — MIT, © Yuxiang Lin and Infinite Universe, Inc. 面板、图 schema、校验器、skill 与语言/框架指导都来自它。我们**全量 vendor 并改造**,每个 vendored 文件头部都写着上游路径与改了什么。
- **[CodeGraph](https://github.com/colbymchenry/codegraph)** — MIT, © Colby McHenry. 全部结构事实的来源。**没有 vendor**:我们依赖发布的 npm 包,只读它的 SQLite 索引,调它的 bin。
逐文件出处与两者的完整署名在 [`NOTICE`](./NOTICE)。如果这个项目对你有用,请先去给上面两个仓库点星 —— ArchView 只是把它们接了起来。
## 13. 想改点什么
先读 [`CONTRACT.md`](./CONTRACT.md)([`AGENTS.md`](./AGENTS.md) 是给 agent 的一页速览,指向同一处)。
**它是硬约束地基,不是风格指南** —— 四条铁律(LLM 不写拓扑 / summary 非空 / layer 覆盖全部文件节点 / 文件级边必须上卷)、冻结的节点 ID 方案、图 schema、模块策略、服务端点表、MCP 工具面,全在里面,每一条都写了「为什么」和「违反了会发生什么」。违反其中任何一条是设计错误。
尤其注意两处:
- **节点 ID 方案是冻结的。** 摘要文件用节点 ID 做 key,改 ID 等于作废所有人已有的摘要资产。
- **图 schema 与 vendored 的 UA schema 完全一致,不加不减。** 面板是照搬的,schema 一动就要改面板。私有信息走节点的 passthrough 字段(边不是 passthrough,额外字段会被静默 strip,别依赖)。
改完至少跑:
```bash
pnpm -r run build # 一定在 typecheck 之前
pnpm -r run typecheck
pnpm --filter @archview/server run test
node packages/core/scripts/selfcheck.mjs --workspace <你的工作区目录> # 只有这个必须给真实工作区
node packages/server/scripts/acceptance.mjs # 不给 --workspace = 自建一次性 fixture
node packages/mcp/scripts/acceptance.mjs # 同上;给了 --workspace 它才会写那个工作区
node final-check.mjs # 同上;只读
```
跑之前先清掉 `ARCHVIEW_*` 环境变量残留(第 8 节给了两个 shell 的命令),否则你测的可能是另一个仓库。
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues