codeblast 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +83 -8
- package/README.zh-CN.md +57 -6
- package/SKILL.md +12 -5
- package/dist/bin.js +47 -30
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
**codeblast** is a deterministic code-graph CLI for TypeScript and Python repositories that tells developers and AI agents what breaks before a change is merged.
|
|
2
|
+
|
|
1
3
|
<p align="center">
|
|
2
4
|
<img src="assets/readme/hero.svg" width="100%" alt="codeblast — deterministic code graph: know what breaks before you merge"/>
|
|
3
5
|
</p>
|
|
@@ -13,6 +15,8 @@
|
|
|
13
15
|
<img src="https://img.shields.io/badge/license-MIT-8b949e?style=flat-square" alt="MIT"/>
|
|
14
16
|
</p>
|
|
15
17
|
|
|
18
|
+
## What it is
|
|
19
|
+
|
|
16
20
|
**codeblast parses your repository into a deterministic code graph and answers the three most expensive questions around any code change:**
|
|
17
21
|
> 🔗 **[Live interactive demo](https://alloevil.github.io/codeblast/)** — real architecture maps of tRPC / Tabby / sgp, with three-level drill-down
|
|
18
22
|
|
|
@@ -35,19 +39,47 @@ Built for humans (CLI / interactive HTML / PR comments) and for AI agents ([SKIL
|
|
|
35
39
|
<a href="https://alloevil.github.io/codeblast/tabby-arch.html">
|
|
36
40
|
<img src="assets/readme/arch-hover.png" alt="Tabby architecture map with hover highlighting: all edges into tabby-core lit up"/>
|
|
37
41
|
</a>
|
|
38
|
-
<p align="center"><sub><b>Architecture map</b> — hover lights the dependency fan-in (Tabby,
|
|
42
|
+
<p align="center"><sub><b>Architecture map</b> — hover lights the dependency fan-in (Tabby, 70k★ · <a href="https://alloevil.github.io/codeblast/tabby-arch.html">live ↗</a>)</sub></p>
|
|
39
43
|
</td>
|
|
40
44
|
</tr>
|
|
41
45
|
</table>
|
|
42
46
|
|
|
47
|
+
## Install
|
|
48
|
+
|
|
43
49
|
```bash
|
|
44
50
|
npx codeblast demo # build a graph of the current repo, run one impact query, emit the map
|
|
45
51
|
npm i -g codeblast # or install globally; needs Node ≥ 22.13 (built-in sqlite) or Bun
|
|
52
|
+
# npm serves 0.3.0; this checkout is 0.3.1 (unpublished)
|
|
46
53
|
|
|
47
54
|
# Install as an agent skill (Claude Code, Codex, Cursor, and 14 more harnesses)
|
|
48
55
|
npx skills add alloevil/codeblast
|
|
49
56
|
```
|
|
50
57
|
|
|
58
|
+
### As a GitHub Action (one line)
|
|
59
|
+
|
|
60
|
+
```yaml
|
|
61
|
+
# .github/workflows/codeblast.yml
|
|
62
|
+
name: codeblast
|
|
63
|
+
on: pull_request
|
|
64
|
+
permissions:
|
|
65
|
+
contents: read
|
|
66
|
+
pull-requests: write
|
|
67
|
+
jobs:
|
|
68
|
+
analyze:
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
steps:
|
|
71
|
+
- uses: actions/checkout@v4
|
|
72
|
+
with: { fetch-depth: 0 } # the analyzer compares base and head commits
|
|
73
|
+
- uses: alloevil/codeblast@v0.3.1
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The action builds the analyzer from the ref you pinned (not from npm, which can lag),
|
|
77
|
+
posts one sticky comment per PR and updates it in place, and stays silent when the diff
|
|
78
|
+
has no structural change. Inputs: `base`, `head`, `repo-url`, `comment` (set to `false`
|
|
79
|
+
to only produce the file); outputs: `has_comment`, `comment_path`. If you prefer to own
|
|
80
|
+
the commenting step, copy [`.github/workflows-template/codeblast.yml`](.github/workflows-template/codeblast.yml)
|
|
81
|
+
instead — it runs the same command with `npx`.
|
|
82
|
+
|
|
51
83
|
## Why not yet another LLM diagram tool
|
|
52
84
|
|
|
53
85
|
```
|
|
@@ -55,14 +87,18 @@ LLM diagrams: code → model reads it → hand-drawn graph → render graph
|
|
|
55
87
|
codeblast: code → deterministic tsc/AST parse → graph → project graph = checkable facts
|
|
56
88
|
```
|
|
57
89
|
|
|
58
|
-
**Every node
|
|
90
|
+
**Every node and every static-analysis edge carries `file:line` evidence** you can open and verify (`co_change` edges put a co-commit count in that field instead of a source line; file-level nodes carry line 1).
|
|
59
91
|
The LLM does exactly one job in the pipeline: giving modules human-readable names — node membership and edges always come from static analysis.
|
|
60
92
|
|
|
61
93
|
## The three queries
|
|
62
94
|
|
|
95
|
+
<p align="center">
|
|
96
|
+
<img src="assets/readme/three-queries.svg" width="100%" alt="The three codeblast queries — impact: check the blast radius first; change: structural diff between two refs; archmap: module, file and symbol drill-down"/>
|
|
97
|
+
</p>
|
|
98
|
+
|
|
63
99
|
```bash
|
|
64
100
|
# Build the graph: auto-detects TS monorepos / Python, hash-based incremental updates
|
|
65
|
-
# (full build of tRPC
|
|
101
|
+
# (full build of tRPC: 957 files, 6248 nodes, 17072 edges — the sample run in SKILL.md)
|
|
66
102
|
codeblast index <repo> --db graph.db
|
|
67
103
|
|
|
68
104
|
# ① Impact — check the blast radius before you change anything
|
|
@@ -88,18 +124,39 @@ codeblast cochange <repo> graph.db
|
|
|
88
124
|
|
|
89
125
|
Copy [`.github/workflows-template/codeblast.yml`](.github/workflows-template/codeblast.yml) into your repo (it runs `npx codeblast pr-comment`, no other setup):
|
|
90
126
|
every PR gets an automatic comment with structural changes + blast radius + new symbols with no test coverage; **PRs with no structural change get zero comments**.
|
|
91
|
-
Replayed against 50 real commits: 42 correctly stayed silent
|
|
127
|
+
Replayed against 50 real commits: 42 correctly stayed silent. Comment usefulness is the honest weak spot —
|
|
128
|
+
four review rounds — rounds 1–3 independent blind review, round 4 by the current model — scored 25% / 75% / 57% / 20% useful, against 7/8 = 87.5% when the
|
|
129
|
+
authoring agent rated its own comments; both numbers and the fixes that followed each round are logged in
|
|
130
|
+
[intent.md](intent.md).
|
|
92
131
|
|
|
93
132
|
## The precision promise (bounded, and evidence-backed)
|
|
94
133
|
|
|
95
134
|
- **TypeScript at function level: zero missed impact within statically analyzable scope.** Verified by mutation testing:
|
|
96
135
|
inject mutations into a real repo → run the full test suite to get the ground-truth impact set → compare against predictions.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
136
|
+
Two benchmarks, both hard gates in the weekly acceptance workflow: **tRPC** (vitest, 957 files) **28/28**
|
|
137
|
+
and **graphql-tools** (jest + npm workspaces, 353 files) **10/10** — 100% recall on each, average precision
|
|
138
|
+
0.33–0.36. Favoring false positives over false negatives is a deliberate trade: in a controlled experiment,
|
|
139
|
+
restricting the result to the call channel (items whose path is call-graph only) raises precision to 0.70 but recall collapses to 14%. Data lives in [`eval/`](eval/).
|
|
100
140
|
- **Blind spots are explicitly flagged.** A blind spot is any call or import that static analysis cannot resolve to an in-repo target — dynamic calls, unresolved calls, failed external-dependency resolution, subprocess boundaries, test-framework globals — not just dynamic calls; each is recorded in `blind_spots` with an "impact may be underestimated" warning, never silently dropped.
|
|
101
141
|
- **Python is file-level.** Dynamic typing makes function-level zero-miss guarantees impossible in principle, and we don't pretend otherwise.
|
|
102
142
|
|
|
143
|
+
## When to use it
|
|
144
|
+
|
|
145
|
+
- You are about to change an exported TypeScript symbol in a monorepo and want the callsite list and the test list *before* you edit, not after CI fails.
|
|
146
|
+
- You are an AI agent editing code: `impact --json` before the edit puts the callsites in context, `change --json` after it catches scope creep and accidental deletions.
|
|
147
|
+
- You are reviewing a PR and want the structural delta — edges added, symbols renamed, new symbols with no test coverage — separated from formatting noise.
|
|
148
|
+
- You just inherited an unfamiliar TypeScript or Python repo and want a map whose every box and arrow can be opened at the source line that justifies it.
|
|
149
|
+
- You need the answer to be checkable by someone who does not trust the tool.
|
|
150
|
+
|
|
151
|
+
## When NOT to use it
|
|
152
|
+
|
|
153
|
+
- **You want function-level guarantees on Python.** Python is file-level with typed-call upgrades; duck typing makes a zero-miss promise impossible in principle and we don't pretend otherwise.
|
|
154
|
+
- **You want a short, precise impact list.** Mean precision is 0.33–0.36 overall; the call channel alone runs 0.702 (tRPC ablation) to 0.918 (graphql-tools) — a derived range, the min and max of the two per-benchmark means in [`eval/`](eval/), not one measured figure. The engine over-approximates on purpose — read the `call` channel first, treat the full list as the test set.
|
|
155
|
+
- **Your dependencies run through what static analysis cannot resolve** — dynamic `require`, `eval`, subprocess boundaries, uninstalled `node_modules`, test-framework globals. Those are reported as `blind_spots`, never silently dropped, but such a repo gets a thin graph.
|
|
156
|
+
- **You want a presentation diagram or a collaborative canvas.** `archmap` emits facts for navigation; feed its JSON to a rendering tool if you need something pretty.
|
|
157
|
+
- **You want cross-service / cross-repo edges, or Java.** The graph model reserves the node types, v1 does not fill those edges, and Java is explicitly not implemented ([intent.md](intent.md)).
|
|
158
|
+
- **You want the PR bot to replace a reviewer.** It is a structural-change signal whose usefulness measured between 20% and 75% depending on the review round.
|
|
159
|
+
|
|
103
160
|
## For AI agents
|
|
104
161
|
|
|
105
162
|
```
|
|
@@ -110,8 +167,26 @@ after editing: change HEAD~1 HEAD --json → self-check for scope creep and ac
|
|
|
110
167
|
The full contract and interpretation discipline (including "never pretend the blind-spot list is complete") is in [SKILL.md](SKILL.md).
|
|
111
168
|
Agent conventions: [AGENTS.md](AGENTS.md).
|
|
112
169
|
|
|
170
|
+
## FAQ
|
|
171
|
+
|
|
172
|
+
**What exactly does the zero-miss promise cover?** Within the statically analyzable scope of an in-repo TypeScript codebase, the predicted set of affected test files is a superset of the tests that actually fail. That is verified by mutation testing on two independent repos — trpc/trpc 28/28 killed mutants, ardatan/graphql-tools 10/10 — and enforced by the weekly [`acceptance`](.github/workflows/acceptance.yml) workflow, which fails the run and opens an issue if recall drops below 100%. Anything static analysis cannot resolve is listed as a blind spot, and unqualified "zero-miss" wording is banned by project rule.
|
|
173
|
+
|
|
174
|
+
**Why is precision so low, and is that a bug?** No, it is a measured trade. Mean precision is 0.358 on the 28-mutant tRPC run and 0.331 on the 10-mutant graphql-tools run, so most predicted affected tests do not fail. A controlled ablation that restricted the result to the call channel — a filter on the combined run (`predicted_call` in [`eval/mutation_check.py`](eval/mutation_check.py)) — raised call-channel precision to 0.702 and collapsed recall to 2 of 14 killed mutants. A later attempt to raise precision by pruning pure re-export barrels and narrowing interface fan-out was rejected by its own data because it would have produced guaranteed misses, so precision is no longer an optimization target.
|
|
175
|
+
|
|
176
|
+
**Does it work on Python?** Yes, at file level, with typed-call upgrades that resolve named-import calls and constructor/annotation-derived method calls, which is enough for the architecture map and a file-level change map. The function-level zero-miss promise stays TypeScript-only. The Python example is the [sgp map](https://alloevil.github.io/codeblast/sgp-arch.html), including a detected `sgp_utils ⇄ solver_transfer` cycle.
|
|
177
|
+
|
|
178
|
+
**How do I drive it from an AI coding agent?** Install it as a skill with `npx skills add alloevil/codeblast`, then run `codeblast impact <db> "<symbol>" --json` before editing and `codeblast change <repo> HEAD~1 HEAD --json` after. [SKILL.md](SKILL.md) carries the interpretation rules that matter: never present the impact list as complete while `blind_spot_count > 0`, never drop the `file` channel to shorten it, never claim function-level precision on Python, treat `truncated: true` as "run the full suite", and never report `co_change_hints` as impact.
|
|
179
|
+
|
|
180
|
+
**Where are the numbers I can check?** Machine-readable claims with metric, method, repro command and evidence path are published at [claims.json](https://alloevil.github.io/codeblast/claims.json); the raw mutation and PR-replay runs are archived under [`eval/`](eval/) as records of runs that were made (the harness writes to `/tmp`, so the files are manual copies with no commit or version pin, and `mutation_check.py` picks candidates with `ORDER BY RANDOM()` — read them as archived runs, not as one-command regenerations), and the acceptance log with every downgrade and rejected optimization is [intent.md](intent.md).
|
|
181
|
+
|
|
113
182
|
## Status & roadmap
|
|
114
183
|
|
|
115
|
-
M0 graph engine → M1 Impact → M3 architecture map → M4 graph diff + PR bot → M5 precision extensions — **all
|
|
184
|
+
M0 graph engine → M1 Impact → M3 architecture map → M4 graph diff + PR bot → M5 precision extensions — **all shipped**; two acceptance steps were downgraded rather than passed (M2's verification method, and M3's original "10 minutes, 5 questions" test which was never run — ⚠️ in [intent.md](intent.md)), and the SemArc alignment check was dropped. Single source of truth for design and acceptance criteria: [intent.md](intent.md).
|
|
116
185
|
|
|
117
186
|
MIT © 2026
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
<p align="center">
|
|
191
|
+
<a href="https://github.com/oil-oil/beautify-github-readme"><img src="./assets/readme/made-with-beautify.svg" width="300" alt="README made with beautify-github-readme"></a>
|
|
192
|
+
</p>
|
package/README.zh-CN.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
**codeblast** 是一个确定性代码图谱 CLI,面向 TypeScript / Python 仓库,让开发者和 AI agent 在合并之前就知道改动会炸到哪里。
|
|
2
|
+
|
|
1
3
|
<p align="center">
|
|
2
4
|
<img src="assets/readme/hero.svg" width="100%" alt="codeblast — deterministic code graph: know what breaks before you merge"/>
|
|
3
5
|
</p>
|
|
@@ -13,6 +15,8 @@
|
|
|
13
15
|
<img src="https://img.shields.io/badge/license-MIT-8b949e?style=flat-square" alt="MIT"/>
|
|
14
16
|
</p>
|
|
15
17
|
|
|
18
|
+
## 是什么
|
|
19
|
+
|
|
16
20
|
**codeblast 把仓库解析成一份确定性代码图谱,回答改代码前后最贵的三个问题:**
|
|
17
21
|
> 🔗 **[在线交互演示](https://alloevil.github.io/codeblast/)** — tRPC / Tabby / sgp 的实时架构图,点开即可三层下钻
|
|
18
22
|
|
|
@@ -24,6 +28,17 @@
|
|
|
24
28
|
|
|
25
29
|
给人看(CLI / 交互 HTML / PR 评论),也给 AI agent 用([SKILL.md](SKILL.md))——同一份图谱,两个出口。
|
|
26
30
|
|
|
31
|
+
## 安装
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npx codeblast demo # 给当前仓库建图、跑一次 impact 查询、导出架构图
|
|
35
|
+
npm i -g codeblast # 或全局安装;需要 Node ≥ 22.13(内置 sqlite)或 Bun
|
|
36
|
+
# npm 上的版本是 0.3.0;本仓库是 0.3.1(尚未发布)
|
|
37
|
+
|
|
38
|
+
# 作为 agent skill 安装(Claude Code、Codex、Cursor 等)
|
|
39
|
+
npx skills add alloevil/codeblast
|
|
40
|
+
```
|
|
41
|
+
|
|
27
42
|
## 为什么不是又一个 LLM 画图工具
|
|
28
43
|
|
|
29
44
|
```
|
|
@@ -31,13 +46,13 @@ LLM 画图: 代码 → 模型阅读理解 → 手写图 → 渲染 图
|
|
|
31
46
|
codeblast: 代码 → tsc/AST 确定性解析 → 图谱 → 投影 图 = 可验证的事实
|
|
32
47
|
```
|
|
33
48
|
|
|
34
|
-
|
|
49
|
+
**每个节点和每条静态分析边都带 `file:line` 证据**,可直接打开核对(`co_change` 边在该字段存的是共提交次数而非源码行;文件级节点行号为 1)。
|
|
35
50
|
LLM 在管线里只做一件事:给模块起人话名字——节点归属和边永远来自静态分析。
|
|
36
51
|
|
|
37
52
|
## 三个查询
|
|
38
53
|
|
|
39
54
|
```bash
|
|
40
|
-
# 建图:TS monorepo / Python 自动识别,hash 增量更新(tRPC
|
|
55
|
+
# 建图:TS monorepo / Python 自动识别,hash 增量更新(tRPC 957 文件:6248 节点 / 17072 边,样例运行见 SKILL.md)
|
|
41
56
|
codeblast index <repo> --db graph.db
|
|
42
57
|
|
|
43
58
|
# ① Impact —— 改动前查影响半径
|
|
@@ -60,18 +75,36 @@ codeblast cochange <repo> graph.db
|
|
|
60
75
|
|
|
61
76
|
复制 [`.github/workflows-template/codeblast.yml`](.github/workflows-template/codeblast.yml) 到目标仓库:
|
|
62
77
|
每个 PR 自动评论结构变化 + 影响半径 + 无测试覆盖的新增符号;**无结构变化的 PR 零评论**。
|
|
63
|
-
50 个真实提交回放:42
|
|
78
|
+
50 个真实提交回放:42 个正确静默。评论有效率是诚实的弱项——四轮评审(1–3 轮为独立盲评,第 4 轮由现模型担任评审人)为 25% / 75% / 57% / 20%,
|
|
79
|
+
而作者 agent 自评同一批 8 条评论为 7/8 = 87.5%;两个数字与每轮之后的修复都记在 [intent.md](intent.md)。
|
|
64
80
|
|
|
65
81
|
## 精度承诺(有边界,有证据)
|
|
66
82
|
|
|
67
83
|
- **TypeScript 函数级,静态可分析范围内零漏报。** 验收方法:变异测试对照
|
|
68
|
-
(真实仓库注入变异 → 全量测试得真实影响集 → 对比预测)。当前基准(tRPC,
|
|
84
|
+
(真实仓库注入变异 → 全量测试得真实影响集 → 对比预测)。当前基准(tRPC,957 文件):
|
|
69
85
|
**28/28 变异召回率 100%**,平均精确率 0.36——宁误报不漏报是刻意交换:
|
|
70
|
-
|
|
86
|
+
对照实验中把结果限制在调用链通道(对合并结果做 channel 过滤,见 `eval/mutation_check.py` 的 `predicted_call`)可将精确率提到 0.70,但召回率跌至 14%。数据在 [`eval/`](eval/)。
|
|
71
87
|
- **盲区显式标注。** 盲区 = 静态无法解析到仓内目标的调用/导入(含动态调用、未解析调用、外部依赖解析失败、子进程边界、测试框架全局),并非只有动态调用;
|
|
72
88
|
一律记入 blind_spots 并提示"影响可能被低估",绝不静默丢弃。
|
|
73
89
|
- **Python 为文件级。** 动态类型使函数级零漏报原理性不成立,不假装做到。
|
|
74
90
|
|
|
91
|
+
## 什么时候用它
|
|
92
|
+
|
|
93
|
+
- 你要改 monorepo 里某个导出符号,想在动手**之前**拿到 callsite 清单和必须跑的测试清单,而不是等 CI 红。
|
|
94
|
+
- 你是在改代码的 AI agent:改前 `impact --json` 把 callsite 送进上下文,改后 `change --json` 自查越界与误删。
|
|
95
|
+
- 你在评审 PR,想把结构变化(新增边、重命名符号、无测试覆盖的新符号)从格式噪音里分离出来。
|
|
96
|
+
- 你刚接手一个陌生的 TS / Python 仓库,想要一张每个框每条边都能点开核对源码行的地图。
|
|
97
|
+
- 你需要结论能被不信任本工具的人复核。
|
|
98
|
+
|
|
99
|
+
## 什么时候不要用它
|
|
100
|
+
|
|
101
|
+
- **你要 Python 的函数级保证。** Python 是文件级 + 类型化调用增强;鸭子类型使函数级零漏报原理性不成立,我们不假装做到。
|
|
102
|
+
- **你要一份短而准的影响清单。** 全量平均精确率 0.33–0.36;单看 call 通道为 0.702(tRPC 对照实验)到 0.918(graphql-tools)——这是派生区间(两个基准均值的 min/max,数据在 [`eval/`](eval/)),不是单次实测。引擎刻意过近似:`call` 通道优先读,全量清单当作"要跑的测试"。
|
|
103
|
+
- **你的依赖主要走静态分析看不见的路径** —— 动态 `require`、`eval`、子进程边界、未安装的 `node_modules`、测试框架全局。这些一律记入 `blind_spots` 不静默丢弃,但这样的仓库只会得到一张稀疏的图。
|
|
104
|
+
- **你要的是演示用图或协作画布。** `archmap` 输出的是用于导航的事实;要好看的图请把它的 JSON 喂给渲染工具。
|
|
105
|
+
- **你要跨服务 / 跨仓库边,或者 Java。** 图模型预留了节点类型但 v1 不填这些边,Java 明确未实现(见 [intent.md](intent.md))。
|
|
106
|
+
- **你想让 PR bot 代替评审人。** 它是结构变化信号,四轮评审(1–3 轮独立盲评,第 4 轮现模型自评)的有效率在 20%–75% 之间波动。
|
|
107
|
+
|
|
75
108
|
## 给 AI Agent 用
|
|
76
109
|
|
|
77
110
|
```
|
|
@@ -82,8 +115,26 @@ codeblast cochange <repo> graph.db
|
|
|
82
115
|
完整契约与解读纪律(含"禁止假装盲区清单完整")见 [SKILL.md](SKILL.md)。
|
|
83
116
|
Agent 规范另见 [AGENTS.md](AGENTS.md)。
|
|
84
117
|
|
|
118
|
+
## 常见问题
|
|
119
|
+
|
|
120
|
+
**零漏报承诺的边界到底是什么?** 在"仓内静态可分析的 TypeScript"范围内,预测的受影响测试文件集是真实失败测试集的超集。验收方式是两个独立仓库上的变异测试——trpc/trpc 28/28 被杀死变异、ardatan/graphql-tools 10/10——并由每周的 [`acceptance`](.github/workflows/acceptance.yml) 工作流把守:召回率低于 100% 即失败并自动开 issue。任何静态无法解析的东西都记入盲区,且项目规则禁止使用无限定的"零漏报"表述。
|
|
121
|
+
|
|
122
|
+
**精确率这么低是 bug 吗?** 不是,是被测量过的取舍。tRPC 30 变异集上平均精确率 0.358,graphql-tools 10 变异集上 0.331,即多数被预测的测试并不会失败。对照实验中把结果限制在 call 通道(对合并结果做过滤,见 `eval/mutation_check.py` 的 `predicted_call`)可把 call 通道精确率提到 0.702,但召回率跌到 14 个被杀死变异中的 2 个。后来一次"剪纯 re-export barrel + 收窄接口扇出"的精确率优化被自己的数据否决(必然产生漏报),因此精确率不再作为优化目标。
|
|
123
|
+
|
|
124
|
+
**支持 Python 吗?** 支持,文件级,并带类型化调用增强(具名导入调用、构造赋值/注解推断出的方法调用),足以支撑架构图与文件级 Change Map。函数级零漏报承诺仍为 TypeScript 独有。Python 实例见 [sgp 架构图](https://alloevil.github.io/codeblast/sgp-arch.html),其中检出了 `sgp_utils ⇄ solver_transfer` 循环依赖。
|
|
125
|
+
|
|
126
|
+
**怎么让 AI agent 用它?** 用 `npx skills add alloevil/codeblast` 装成 skill,改前跑 `codeblast impact <db> "<symbol>" --json`,改后跑 `codeblast change <repo> HEAD~1 HEAD --json`。[SKILL.md](SKILL.md) 写明了关键解读纪律:`blind_spot_count > 0` 时禁止声称清单完整、禁止为了缩短清单砍掉 `file` 通道、禁止对 Python 声称函数级精度、`truncated: true` 要建议跑全量测试、`co_change_hints` 不等于影响。
|
|
127
|
+
|
|
128
|
+
**可核对的数字在哪里?** 带指标、方法、复现命令和证据路径的机器可读清单发布在 [claims.json](https://alloevil.github.io/codeblast/claims.json);原始变异与 PR 回放数据作为**运行存档**放在 [`eval/`](eval/)(harness 写的是 `/tmp`,这些文件是人工拷贝、不带 commit 或版本标注,候选用 `ORDER BY RANDOM()` 抽取——当存档读,别当作一条命令就能重跑出来的产物);包含每次降级与被否决优化的验收台账是 [intent.md](intent.md)。
|
|
129
|
+
|
|
85
130
|
## 状态与路线
|
|
86
131
|
|
|
87
|
-
M0 图谱引擎 → M1 Impact → M3 架构图 → M4 图 diff + PR bot → M5
|
|
132
|
+
M0 图谱引擎 → M1 Impact → M3 架构图 → M4 图 diff + PR bot → M5 精度扩展,**全部交付**;其中两项验收是降级而非通过(M2 的验证方式、M3 原定的"陌生工程师 10 分钟 5 问"从未执行——见 intent.md 的 ⚠️),SemArc 对齐检查已作废。方案与验收标准的单一事实源:[intent.md](intent.md)。
|
|
88
133
|
|
|
89
134
|
MIT © 2026
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
<p align="center">
|
|
139
|
+
<a href="https://github.com/oil-oil/beautify-github-readme"><img src="./assets/readme/made-with-beautify.svg" width="300" alt="README made with beautify-github-readme"></a>
|
|
140
|
+
</p>
|
package/SKILL.md
CHANGED
|
@@ -52,13 +52,18 @@ codeblast index <repo-root> --db /tmp/graph.db
|
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
Auto-discovers every package `tsconfig.json` in a monorepo and ingests Python via AST. Re-running only
|
|
55
|
-
processes files whose content hash changed. Stdout is one JSON object
|
|
55
|
+
processes files whose content hash changed. Stdout is one JSON object — this is a real run of
|
|
56
|
+
`codeblast index` against [tRPC](https://github.com/trpc/trpc) at commit `66d0544` with codeblast 0.3.0
|
|
57
|
+
(timing is machine-dependent; the counts are not):
|
|
56
58
|
|
|
57
59
|
```json
|
|
58
|
-
{ "db": "
|
|
59
|
-
"nodes":
|
|
60
|
+
{ "db": "/tmp/graph.db", "seconds": 5.2, "tsconfigs": 34, "files_indexed": 957, "files_skipped": 0,
|
|
61
|
+
"nodes": 6248, "edges": 17072, "blind_spots": 14725, "failures": 0 }
|
|
60
62
|
```
|
|
61
63
|
|
|
64
|
+
Note that `blind_spots` is routinely large on a real TypeScript monorepo — it counts unresolved and
|
|
65
|
+
dynamic references, not errors. Judge graph health by `failures`, not by `blind_spots`.
|
|
66
|
+
|
|
62
67
|
Non-zero exit with `failures > 0` means the graph is incomplete — do not query it; report the failure.
|
|
63
68
|
|
|
64
69
|
## 2. Impact — what breaks if I change this
|
|
@@ -148,8 +153,10 @@ Replayed over 50 real commits: 42 stayed silent, 87.5% of the comments posted we
|
|
|
148
153
|
|
|
149
154
|
- TypeScript, function level: **zero missed impact within statically analyzable scope**, checked by
|
|
150
155
|
mutation testing (inject a fault, run the real test suite, compare failing tests to the prediction).
|
|
151
|
-
tRPC
|
|
152
|
-
|
|
156
|
+
Two benchmarks: tRPC (vitest, 957 files) 28/28, graphql-tools (jest, 353 files) 10/10 — precision
|
|
157
|
+
0.33–0.36 overall; the call channel alone is 0.702–0.918 (derived: min/max of the two per-benchmark
|
|
158
|
+
means, see eval/). Both are hard gates in the weekly acceptance
|
|
159
|
+
workflow, which opens an issue if either drops below 100%.
|
|
153
160
|
- Conservative edges over-approximate on purpose (an interface method call fans out to every implementer).
|
|
154
161
|
- Python: file-level; typed calls (`b = Builder(); b.method()`, annotated parameters) are function-level;
|
|
155
162
|
untyped attribute chains fall back to file level and are recorded as blind spots.
|
package/dist/bin.js
CHANGED
|
@@ -1058,7 +1058,47 @@ var init_impact = __esm(() => {
|
|
|
1058
1058
|
|
|
1059
1059
|
// src/impact-cli.ts
|
|
1060
1060
|
var exports_impact_cli = {};
|
|
1061
|
-
|
|
1061
|
+
function printHuman() {
|
|
1062
|
+
const byLevel = { direct: 0, indirect: 0, tests: 0 };
|
|
1063
|
+
for (const it of result.items)
|
|
1064
|
+
byLevel[it.level]++;
|
|
1065
|
+
const callItems = result.items.filter((it) => it.channel === "call");
|
|
1066
|
+
const fileNamed = result.items.filter((it) => it.channel === "file" && !it.named_miss);
|
|
1067
|
+
const fileUnnamed = result.items.filter((it) => it.channel === "file" && it.named_miss);
|
|
1068
|
+
console.log(`target: ${result.target}`);
|
|
1069
|
+
console.log(`impact: ${result.items.length} nodes (direct=${byLevel.direct} indirect=${byLevel.indirect} tests=${byLevel.tests})${result.truncated ? " [TRUNCATED — wide impact, run the full suite]" : ""}`);
|
|
1070
|
+
console.log(` ├─ call-graph reachable (high confidence): ${callItems.length}`);
|
|
1071
|
+
console.log(` ├─ reachable via named import: ${fileNamed.length}`);
|
|
1072
|
+
console.log(` └─ reachable via unnamed import (execution closure, conservative — do not skip): ${fileUnnamed.length}`);
|
|
1073
|
+
if (result.blind_spot_count > 0)
|
|
1074
|
+
console.log(`blind spots in target file: ${result.blind_spot_count} (impact may be underestimated)`);
|
|
1075
|
+
if (result.co_change_hints.length > 0) {
|
|
1076
|
+
console.log(`co-change hints (no static edge, but historically changed together):`);
|
|
1077
|
+
for (const h of result.co_change_hints)
|
|
1078
|
+
console.log(` ~ ${h.file} (${h.co_commits} co-commits, ${h.evidence})`);
|
|
1079
|
+
}
|
|
1080
|
+
console.log(`query: ${ms}ms
|
|
1081
|
+
`);
|
|
1082
|
+
const testItems = result.items.filter((it) => it.level === "tests");
|
|
1083
|
+
const callNonTest = callItems.filter((it) => it.level !== "tests");
|
|
1084
|
+
const fileNonTest = [...fileNamed, ...fileUnnamed].filter((it) => it.level !== "tests");
|
|
1085
|
+
const shown = showAll ? [...callItems, ...fileNamed, ...fileUnnamed] : [...callNonTest, ...testItems];
|
|
1086
|
+
const LIMIT = 40;
|
|
1087
|
+
for (const it of shown.slice(0, LIMIT)) {
|
|
1088
|
+
const conf = it.confidence === "conservative" ? " ~" : "";
|
|
1089
|
+
const ch = it.channel === "file" ? it.named_miss ? " ·closure" : " ·import" : "";
|
|
1090
|
+
console.log(` [${it.level}${conf}${ch}] ${it.id} (${it.kind}, ${it.hops} hop, via ${it.via_file}:${it.via_line})`);
|
|
1091
|
+
}
|
|
1092
|
+
if (shown.length > LIMIT)
|
|
1093
|
+
console.log(` ... and ${shown.length - LIMIT} more`);
|
|
1094
|
+
if (!showAll && fileNonTest.length > 0) {
|
|
1095
|
+
const files = new Set(fileNonTest.map((it) => it.file)).size;
|
|
1096
|
+
console.log(`
|
|
1097
|
+
+ ${fileNonTest.length} conservative items across ${files} files reachable only via imports — --all to list them.`);
|
|
1098
|
+
console.log(` They are part of the no-false-negative promise: run the tests above, do not treat this list as noise.`);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
var dbPath2, query, maxFlag, maxNodes, showAll, db2, targetId, exact, t02, result, ms;
|
|
1062
1102
|
var init_impact_cli = __esm(() => {
|
|
1063
1103
|
init_db();
|
|
1064
1104
|
init_impact();
|
|
@@ -1069,6 +1109,7 @@ var init_impact_cli = __esm(() => {
|
|
|
1069
1109
|
}
|
|
1070
1110
|
maxFlag = process.argv.indexOf("--max");
|
|
1071
1111
|
maxNodes = maxFlag >= 0 ? Number(process.argv[maxFlag + 1]) : 500;
|
|
1112
|
+
showAll = process.argv.includes("--all");
|
|
1072
1113
|
db2 = openDatabase(dbPath2, { readonly: true });
|
|
1073
1114
|
targetId = query;
|
|
1074
1115
|
exact = db2.prepare("SELECT id FROM nodes WHERE id = ?").get(query);
|
|
@@ -1090,37 +1131,13 @@ var init_impact_cli = __esm(() => {
|
|
|
1090
1131
|
result = impact(db2, targetId, maxNodes);
|
|
1091
1132
|
ms = (performance.now() - t02).toFixed(0);
|
|
1092
1133
|
if (process.argv.includes("--json")) {
|
|
1093
|
-
|
|
1094
|
-
process.exit(0);
|
|
1095
|
-
}
|
|
1096
|
-
byLevel = { direct: 0, indirect: 0, tests: 0 };
|
|
1097
|
-
for (const it of result.items)
|
|
1098
|
-
byLevel[it.level]++;
|
|
1099
|
-
callItems = result.items.filter((it) => it.channel === "call");
|
|
1100
|
-
fileNamed = result.items.filter((it) => it.channel === "file" && !it.named_miss);
|
|
1101
|
-
fileUnnamed = result.items.filter((it) => it.channel === "file" && it.named_miss);
|
|
1102
|
-
console.log(`target: ${result.target}`);
|
|
1103
|
-
console.log(`impact: ${result.items.length} nodes (direct=${byLevel.direct} indirect=${byLevel.indirect} tests=${byLevel.tests})${result.truncated ? " [TRUNCATED — wide impact, run the full suite]" : ""}`);
|
|
1104
|
-
console.log(` ├─ call-graph reachable (high confidence): ${callItems.length}`);
|
|
1105
|
-
console.log(` ├─ reachable via named import: ${fileNamed.length}`);
|
|
1106
|
-
console.log(` └─ reachable via unnamed import (execution closure, conservative — do not skip): ${fileUnnamed.length}`);
|
|
1107
|
-
if (result.blind_spot_count > 0)
|
|
1108
|
-
console.log(`blind spots in target file: ${result.blind_spot_count} (impact may be underestimated)`);
|
|
1109
|
-
if (result.co_change_hints.length > 0) {
|
|
1110
|
-
console.log(`co-change hints (no static edge, but historically changed together):`);
|
|
1111
|
-
for (const h of result.co_change_hints)
|
|
1112
|
-
console.log(` ~ ${h.file} (${h.co_commits} co-commits, ${h.evidence})`);
|
|
1113
|
-
}
|
|
1114
|
-
console.log(`query: ${ms}ms
|
|
1134
|
+
process.stdout.write(JSON.stringify(result) + `
|
|
1115
1135
|
`);
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
console.log(` [${it.level}${conf}${ch}] ${it.id} (${it.kind}, ${it.hops} hop, via ${it.via_file}:${it.via_line})`);
|
|
1136
|
+
db2.close();
|
|
1137
|
+
process.exitCode = 0;
|
|
1138
|
+
} else {
|
|
1139
|
+
printHuman();
|
|
1121
1140
|
}
|
|
1122
|
-
if (ordered.length > 40)
|
|
1123
|
-
console.log(` ... and ${ordered.length - 40} more`);
|
|
1124
1141
|
});
|
|
1125
1142
|
|
|
1126
1143
|
// src/graph-diff.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeblast",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Know what breaks before you merge — mutation-tested code graph with architecture, change & impact maps. Evidence on every edge. For humans and AI agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"impact-analysis",
|