codeblast 0.3.0 โ 0.3.2
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 +99 -8
- package/README.zh-CN.md +51 -6
- package/SKILL.md +22 -5
- package/dist/bin.js +215 -16
- 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.1
|
|
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
|
|
@@ -77,7 +113,8 @@ codeblast change <repo> main~5 main --json
|
|
|
77
113
|
|
|
78
114
|
# โข Architecture Map โ interactive HTML: module โ file โ symbol drill-down,
|
|
79
115
|
# symbols link to source lines
|
|
80
|
-
codeblast archmap graph.db --out arch.html --repo-url <github-url>
|
|
116
|
+
codeblast archmap graph.db --out arch.html --repo-url <github-url> \
|
|
117
|
+
--site-url https://example.github.io/repo --og-image <share-image-url>
|
|
81
118
|
|
|
82
119
|
# Optional: mine git co-change coupling (protocol pairs, config + consumers โ
|
|
83
120
|
# edges static analysis can't see)
|
|
@@ -88,19 +125,53 @@ codeblast cochange <repo> graph.db
|
|
|
88
125
|
|
|
89
126
|
Copy [`.github/workflows-template/codeblast.yml`](.github/workflows-template/codeblast.yml) into your repo (it runs `npx codeblast pr-comment`, no other setup):
|
|
90
127
|
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
|
|
128
|
+
Replayed against 50 real commits: 42 correctly stayed silent. Comment usefulness is the honest weak spot โ
|
|
129
|
+
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
|
|
130
|
+
authoring agent rated its own comments; both numbers and the fixes that followed each round are logged in
|
|
131
|
+
[intent.md](intent.md).
|
|
92
132
|
|
|
93
133
|
## The precision promise (bounded, and evidence-backed)
|
|
94
134
|
|
|
95
135
|
- **TypeScript at function level: zero missed impact within statically analyzable scope.** Verified by mutation testing:
|
|
96
136
|
inject mutations into a real repo โ run the full test suite to get the ground-truth impact set โ compare against predictions.
|
|
97
|
-
Two benchmarks, both hard gates in the weekly acceptance workflow: **tRPC** (vitest,
|
|
137
|
+
Two benchmarks, both hard gates in the weekly acceptance workflow: **tRPC** (vitest, 957 files) **28/28**
|
|
98
138
|
and **graphql-tools** (jest + npm workspaces, 353 files) **10/10** โ 100% recall on each, average precision
|
|
99
139
|
0.33โ0.36. Favoring false positives over false negatives is a deliberate trade: in a controlled experiment,
|
|
100
|
-
|
|
140
|
+
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/).
|
|
101
141
|
- **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.
|
|
102
142
|
- **Python is file-level.** Dynamic typing makes function-level zero-miss guarantees impossible in principle, and we don't pretend otherwise.
|
|
103
143
|
|
|
144
|
+
## When to use it
|
|
145
|
+
|
|
146
|
+
- 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.
|
|
147
|
+
- 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.
|
|
148
|
+
- You are reviewing a PR and want the structural delta โ edges added, symbols renamed, new symbols with no test coverage โ separated from formatting noise.
|
|
149
|
+
- 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.
|
|
150
|
+
- You need the answer to be checkable by someone who does not trust the tool.
|
|
151
|
+
|
|
152
|
+
## When NOT to use it
|
|
153
|
+
|
|
154
|
+
- **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.
|
|
155
|
+
- **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.
|
|
156
|
+
- **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.
|
|
157
|
+
- **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.
|
|
158
|
+
- **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)).
|
|
159
|
+
- **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.
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
### Unified change safety check
|
|
163
|
+
|
|
164
|
+
For agents and CI that need one decision instead of composing `change` and `impact`, run:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
codeblast check-change <repo> <base-sha> <head-sha> --json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
The output contains `decision` (`safe-to-review`, `targeted-review`, or `review`), `risk`, reasons,
|
|
171
|
+
recommended actions, structural facts, predicted affected-test count, blind-spot count, truncation,
|
|
172
|
+
and the complete graph diff. It is a conservative routing decision, not a claim that the change is
|
|
173
|
+
safe to merge. `review` is required when API surface contracts, removed symbols, or incomplete impact
|
|
174
|
+
results are detected.
|
|
104
175
|
## For AI agents
|
|
105
176
|
|
|
106
177
|
```
|
|
@@ -111,8 +182,28 @@ after editing: change HEAD~1 HEAD --json โ self-check for scope creep and ac
|
|
|
111
182
|
The full contract and interpretation discipline (including "never pretend the blind-spot list is complete") is in [SKILL.md](SKILL.md).
|
|
112
183
|
Agent conventions: [AGENTS.md](AGENTS.md).
|
|
113
184
|
|
|
185
|
+
## FAQ
|
|
186
|
+
|
|
187
|
+
**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.
|
|
188
|
+
|
|
189
|
+
**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.
|
|
190
|
+
|
|
191
|
+
**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.
|
|
192
|
+
|
|
193
|
+
**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.
|
|
194
|
+
|
|
195
|
+
**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).
|
|
196
|
+
|
|
197
|
+
### PR bot feedback
|
|
198
|
+
|
|
199
|
+
Feedback is intentionally issue-based and privacy-safe. Report only the public PR URL, the affected
|
|
200
|
+
decision category, and a redacted explanation. Do not include source code, secrets, proprietary diffs,
|
|
201
|
+
or full repository contents. The useful labels are false positive, missed impact, noise, incorrect
|
|
202
|
+
silence, or incorrect recommendation. Feedback is qualitative calibration data; it is not telemetry
|
|
203
|
+
and codeblast does not require access to customer repositories.
|
|
204
|
+
|
|
114
205
|
## Status & roadmap
|
|
115
206
|
|
|
116
|
-
M0 graph engine โ M1 Impact โ M3 architecture map โ M4 graph diff + PR bot โ M5 precision extensions โ **all
|
|
207
|
+
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).
|
|
117
208
|
|
|
118
209
|
MIT ยฉ 2026
|
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.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,20 @@ 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
|
package/SKILL.md
CHANGED
|
@@ -52,13 +52,17 @@ 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 the historical codeblast 0.3.0 benchmark
|
|
56
57
|
|
|
57
58
|
```json
|
|
58
|
-
{ "db": "
|
|
59
|
-
"nodes":
|
|
59
|
+
{ "db": "/tmp/graph.db", "seconds": 5.2, "tsconfigs": 34, "files_indexed": 957, "files_skipped": 0,
|
|
60
|
+
"nodes": 6248, "edges": 17072, "blind_spots": 14725, "failures": 0 }
|
|
60
61
|
```
|
|
61
62
|
|
|
63
|
+
Note that `blind_spots` is routinely large on a real TypeScript monorepo โ it counts unresolved and
|
|
64
|
+
dynamic references, not errors. Judge graph health by `failures`, not by `blind_spots`.
|
|
65
|
+
|
|
62
66
|
Non-zero exit with `failures > 0` means the graph is incomplete โ do not query it; report the failure.
|
|
63
67
|
|
|
64
68
|
## 2. Impact โ what breaks if I change this
|
|
@@ -88,9 +92,21 @@ Output (`--json`):
|
|
|
88
92
|
via_file: string; via_line: number; // where the dependency occurs (rule 6)
|
|
89
93
|
}>;
|
|
90
94
|
co_change_hints: Array<{ file: string; co_commits: number; evidence: string }>; // rule 5
|
|
95
|
+
guidance: {
|
|
96
|
+
review_first: string[]; // call-channel non-test nodes
|
|
97
|
+
run_tests: string[]; // test file paths, de-duplicate before running
|
|
98
|
+
conservative: string[]; // non-test file-channel nodes; never discard
|
|
99
|
+
warnings: string[]; // truncation and blind-spot warnings
|
|
100
|
+
};
|
|
91
101
|
}
|
|
92
102
|
```
|
|
93
103
|
|
|
104
|
+
The `guidance` object is a convenience projection of `items`; it does not add analysis results.
|
|
105
|
+
Use `review_first` for the initial callsite checklist, `run_tests` for the affected test files,
|
|
106
|
+
and `conservative` as the import/re-export safety net. `warnings` is non-empty when the result is
|
|
107
|
+
incomplete or the target file contains unresolved analysis. Always retain `via_file` and `via_line`
|
|
108
|
+
when reporting a dependency.
|
|
109
|
+
|
|
94
110
|
How to use it: `items.filter(level === "direct")` is the callsite checklist. `items.filter(level ===
|
|
95
111
|
"tests")` de-duplicated by `file` is the test set to run. Test-directory fixtures are included
|
|
96
112
|
conservatively; estimate test cost by distinct files, not item count.
|
|
@@ -148,8 +164,9 @@ Replayed over 50 real commits: 42 stayed silent, 87.5% of the comments posted we
|
|
|
148
164
|
|
|
149
165
|
- TypeScript, function level: **zero missed impact within statically analyzable scope**, checked by
|
|
150
166
|
mutation testing (inject a fault, run the real test suite, compare failing tests to the prediction).
|
|
151
|
-
Two benchmarks: tRPC (vitest,
|
|
152
|
-
0.33โ0.36 overall
|
|
167
|
+
Two benchmarks: tRPC (vitest, 957 files) 28/28, graphql-tools (jest, 353 files) 10/10 โ precision
|
|
168
|
+
0.33โ0.36 overall; the call channel alone is 0.702โ0.918 (derived: min/max of the two per-benchmark
|
|
169
|
+
means, see eval/). Both are hard gates in the weekly acceptance
|
|
153
170
|
workflow, which opens an issue if either drops below 100%.
|
|
154
171
|
- Conservative edges over-approximate on purpose (an interface method call fans out to every implementer).
|
|
155
172
|
- Python: file-level; typed calls (`b = Builder(); b.method()`, annotated parameters) are function-level;
|
package/dist/bin.js
CHANGED
|
@@ -1131,7 +1131,16 @@ var init_impact_cli = __esm(() => {
|
|
|
1131
1131
|
result = impact(db2, targetId, maxNodes);
|
|
1132
1132
|
ms = (performance.now() - t02).toFixed(0);
|
|
1133
1133
|
if (process.argv.includes("--json")) {
|
|
1134
|
-
|
|
1134
|
+
const guidance = {
|
|
1135
|
+
review_first: result.items.filter((it) => it.channel === "call" && it.level !== "tests").map((it) => it.id),
|
|
1136
|
+
run_tests: result.items.filter((it) => it.level === "tests").map((it) => it.file),
|
|
1137
|
+
conservative: result.items.filter((it) => it.channel === "file" && it.level !== "tests").map((it) => it.id),
|
|
1138
|
+
warnings: [
|
|
1139
|
+
...result.truncated ? ["impact_truncated_run_full_test_suite"] : [],
|
|
1140
|
+
...result.blind_spot_count > 0 ? ["blind_spots_may_underestimate_impact"] : []
|
|
1141
|
+
]
|
|
1142
|
+
};
|
|
1143
|
+
process.stdout.write(JSON.stringify({ ...result, guidance }) + `
|
|
1135
1144
|
`);
|
|
1136
1145
|
db2.close();
|
|
1137
1146
|
process.exitCode = 0;
|
|
@@ -1436,7 +1445,7 @@ async function layoutGraph(nodes, edges) {
|
|
|
1436
1445
|
}))
|
|
1437
1446
|
};
|
|
1438
1447
|
}
|
|
1439
|
-
var CLIENT_JS, dbPath3, outFlag2, outPath2, overlayFlag, overlayPath, repoFlag, repoUrl, impactFlag, impactTarget, diffFlag, diffBase, db3, overlay, TEST_RE, moduleOf = (file) => {
|
|
1448
|
+
var CLIENT_JS, dbPath3, outFlag2, outPath2, overlayFlag, overlayPath, repoFlag, repoUrl, impactFlag, impactTarget, diffFlag, diffBase, siteFlag, siteUrl, ogImageFlag, ogImage, repoLabel, pageUrl, pageKind, pageTitle, pageDesc, escAttr = (s) => s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), headMeta, db3, overlay, TEST_RE, moduleOf = (file) => {
|
|
1440
1449
|
if (TEST_RE.test(file))
|
|
1441
1450
|
return "tests";
|
|
1442
1451
|
const ix = file.indexOf("/");
|
|
@@ -1460,6 +1469,51 @@ var init_archmap_html = __esm(async () => {
|
|
|
1460
1469
|
impactTarget = impactFlag >= 0 ? process.argv[impactFlag + 1] : undefined;
|
|
1461
1470
|
diffFlag = process.argv.indexOf("--diff");
|
|
1462
1471
|
diffBase = diffFlag >= 0 ? process.argv[diffFlag + 1] : undefined;
|
|
1472
|
+
siteFlag = process.argv.indexOf("--site-url");
|
|
1473
|
+
siteUrl = siteFlag >= 0 ? process.argv[siteFlag + 1].replace(/\/+$/, "") : undefined;
|
|
1474
|
+
ogImageFlag = process.argv.indexOf("--og-image");
|
|
1475
|
+
ogImage = ogImageFlag >= 0 ? process.argv[ogImageFlag + 1] : undefined;
|
|
1476
|
+
repoLabel = (() => {
|
|
1477
|
+
const m = repoUrl?.match(/github\.com\/([^/]+\/[^/]+)/);
|
|
1478
|
+
if (m)
|
|
1479
|
+
return m[1].replace(/\.git$/, "");
|
|
1480
|
+
const base = outPath2.split("/").pop() ?? outPath2;
|
|
1481
|
+
return base.replace(/\.html$/, "");
|
|
1482
|
+
})();
|
|
1483
|
+
pageUrl = siteUrl ? `${siteUrl}/${outPath2.split("/").pop()}` : undefined;
|
|
1484
|
+
pageKind = impactTarget ? "blast-radius map" : diffBase ? "change map" : "architecture map";
|
|
1485
|
+
pageTitle = `${repoLabel} โ ${pageKind}`;
|
|
1486
|
+
pageDesc = `Interactive architecture map of ${repoLabel} generated by codeblast: a module-level` + ` dependency graph where hovering a node lights up its fan-in and clicking it shows its files,` + ` symbols and import edges.` + (impactTarget ? ` Includes the blast-radius overlay for ${impactTarget}.` : "") + (overlayPath ? " Includes the change overlay." : "");
|
|
1487
|
+
headMeta = [
|
|
1488
|
+
`<title>${escAttr(pageTitle)}</title>`,
|
|
1489
|
+
`<meta name="description" content="${escAttr(pageDesc)}">`,
|
|
1490
|
+
...pageUrl ? [`<link rel="canonical" href="${escAttr(pageUrl)}">`] : [],
|
|
1491
|
+
`<meta property="og:type" content="website">`,
|
|
1492
|
+
`<meta property="og:title" content="${escAttr(pageTitle)}">`,
|
|
1493
|
+
`<meta property="og:description" content="${escAttr(pageDesc)}">`,
|
|
1494
|
+
...pageUrl ? [`<meta property="og:url" content="${escAttr(pageUrl)}">`] : [],
|
|
1495
|
+
`<meta property="og:site_name" content="codeblast">`,
|
|
1496
|
+
...ogImage ? [`<meta property="og:image" content="${escAttr(ogImage)}">`] : [],
|
|
1497
|
+
`<meta name="twitter:card" content="${ogImage ? "summary_large_image" : "summary"}">`,
|
|
1498
|
+
`<meta name="twitter:title" content="${escAttr(pageTitle)}">`,
|
|
1499
|
+
`<meta name="twitter:description" content="${escAttr(pageDesc)}">`,
|
|
1500
|
+
...ogImage ? [`<meta name="twitter:image" content="${escAttr(ogImage)}">`] : [],
|
|
1501
|
+
`<script type="application/ld+json">`,
|
|
1502
|
+
JSON.stringify({
|
|
1503
|
+
"@context": "https://schema.org",
|
|
1504
|
+
"@type": "WebPage",
|
|
1505
|
+
name: pageTitle,
|
|
1506
|
+
description: pageDesc,
|
|
1507
|
+
inLanguage: "zh-CN",
|
|
1508
|
+
...pageUrl ? { url: pageUrl } : {},
|
|
1509
|
+
...repoUrl ? {
|
|
1510
|
+
isBasedOn: repoUrl,
|
|
1511
|
+
about: { "@type": "SoftwareSourceCode", name: repoLabel, codeRepository: repoUrl }
|
|
1512
|
+
} : {}
|
|
1513
|
+
}, null, 2),
|
|
1514
|
+
`</script>`
|
|
1515
|
+
].join(`
|
|
1516
|
+
`);
|
|
1463
1517
|
if (!dbPath3) {
|
|
1464
1518
|
console.error("usage: codeblast archmap <graph.db> --out arch.html");
|
|
1465
1519
|
process.exit(1);
|
|
@@ -1629,10 +1683,10 @@ var init_archmap_html = __esm(async () => {
|
|
|
1629
1683
|
fileEdges: importRows
|
|
1630
1684
|
};
|
|
1631
1685
|
html = `<!DOCTYPE html>
|
|
1632
|
-
<html lang="zh">
|
|
1686
|
+
<html lang="zh-CN">
|
|
1633
1687
|
<head>
|
|
1634
1688
|
<meta charset="utf-8">
|
|
1635
|
-
|
|
1689
|
+
${headMeta}
|
|
1636
1690
|
<style>
|
|
1637
1691
|
:root { --bg:#0d1117; --panel:#161b22; --border:#30363d; --fg:#e6edf3; --dim:#8b949e;
|
|
1638
1692
|
--accent:#58a6ff; --warn:#f85149; --ok:#3fb950; }
|
|
@@ -1671,6 +1725,11 @@ var init_archmap_html = __esm(async () => {
|
|
|
1671
1725
|
.edge.cyclic { stroke: var(--warn); stroke-dasharray: 5 3; stroke-width: 2;
|
|
1672
1726
|
animation: cycflow 1.2s linear infinite; }
|
|
1673
1727
|
@keyframes cycflow { to { stroke-dashoffset: -16; } }
|
|
1728
|
+
/* ๅพช็ฏไพ่ต็จ่็บฟ้ๆญข่กจ็คบๅณๅฏ โโ ่็บฟไธ้ข่ฒๆฟ่ฝฝไฟกๆฏ๏ผๆตๅจๅชๆฏ่ฎฉๅฎๆดๆพ็ผใ
|
|
1729
|
+
reduced-motion ไธๅๆๅจ็ป๏ผ่ไธๆฏๅปๆ่็บฟใ */
|
|
1730
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1731
|
+
.edge.cyclic { animation: none; }
|
|
1732
|
+
}
|
|
1674
1733
|
.edge.hi { stroke: #d29922 !important; stroke-width: 2.6; stroke-opacity: 1; }
|
|
1675
1734
|
.edge.dim { stroke-opacity: 0.1; }
|
|
1676
1735
|
.edge.faint { stroke-opacity: 0.06; }
|
|
@@ -1955,6 +2014,54 @@ var init_pr_silence = __esm(() => {
|
|
|
1955
2014
|
AUX_RE = /^(www|docs|examples)\//;
|
|
1956
2015
|
});
|
|
1957
2016
|
|
|
2017
|
+
// src/pr-decision.ts
|
|
2018
|
+
function reviewDecision(input) {
|
|
2019
|
+
const { diff, prodNodesAdded, bodyChanged, affectedTests, truncated, blindSpotCount } = input;
|
|
2020
|
+
const apiContractions = diff.visibilityChanged.filter((v) => !v.nowExported).length;
|
|
2021
|
+
const apiChanges = apiContractions + diff.signatureChanged.length;
|
|
2022
|
+
const removals = diff.nodesRemoved.length;
|
|
2023
|
+
const behaviorChanges = bodyChanged.length;
|
|
2024
|
+
const reasons = [];
|
|
2025
|
+
const recommendedActions = [];
|
|
2026
|
+
let risk = "low";
|
|
2027
|
+
if (apiContractions > 0 || removals > 0 || truncated)
|
|
2028
|
+
risk = "high";
|
|
2029
|
+
else if (apiChanges > 0 || behaviorChanges > 0 || affectedTests > 0 || blindSpotCount > 0)
|
|
2030
|
+
risk = "medium";
|
|
2031
|
+
if (apiContractions > 0)
|
|
2032
|
+
reasons.push(`${apiContractions} exported symbol${apiContractions === 1 ? " is" : "s are"} no longer public`);
|
|
2033
|
+
if (removals > 0)
|
|
2034
|
+
reasons.push(`${removals} symbol${removals === 1 ? " was" : "s were"} removed`);
|
|
2035
|
+
if (diff.signatureChanged.length > 0)
|
|
2036
|
+
reasons.push(`${diff.signatureChanged.length} exported signature${diff.signatureChanged.length === 1 ? " changed" : "s changed"}`);
|
|
2037
|
+
if (behaviorChanges > 0)
|
|
2038
|
+
reasons.push(`${behaviorChanges} function bod${behaviorChanges === 1 ? "y" : "ies"} changed`);
|
|
2039
|
+
if (affectedTests > 0)
|
|
2040
|
+
reasons.push(`${affectedTests} test file${affectedTests === 1 ? " is" : "s are"} in the predicted impact set`);
|
|
2041
|
+
if (truncated)
|
|
2042
|
+
reasons.push("the impact set exceeded the reporting limit");
|
|
2043
|
+
if (blindSpotCount > 0)
|
|
2044
|
+
reasons.push(`${blindSpotCount} static-analysis blind spot${blindSpotCount === 1 ? "" : "s"} may hide impact`);
|
|
2045
|
+
if (reasons.length === 0 && prodNodesAdded.length > 0)
|
|
2046
|
+
reasons.push(`${prodNodesAdded.length} production symbol${prodNodesAdded.length === 1 ? " was" : "s were"} added`);
|
|
2047
|
+
if (reasons.length === 0)
|
|
2048
|
+
reasons.push("only low-risk structural additions were found");
|
|
2049
|
+
if (apiChanges > 0 || removals > 0)
|
|
2050
|
+
recommendedActions.push("Review the public API changes and migration impact.");
|
|
2051
|
+
if (truncated)
|
|
2052
|
+
recommendedActions.push("Run the full test suite; the reported impact list is truncated.");
|
|
2053
|
+
else if (affectedTests > 0)
|
|
2054
|
+
recommendedActions.push(`Run the ${affectedTests} predicted affected test file${affectedTests === 1 ? "" : "s"}.`);
|
|
2055
|
+
if (blindSpotCount > 0)
|
|
2056
|
+
recommendedActions.push("Inspect the reported blind spots before treating the impact set as complete.");
|
|
2057
|
+
if (behaviorChanges > 0)
|
|
2058
|
+
recommendedActions.push("Review the changed function bodies even where the exported shape is unchanged.");
|
|
2059
|
+
if (recommendedActions.length === 0)
|
|
2060
|
+
recommendedActions.push("Review the named structural additions; no existing API contraction was detected.");
|
|
2061
|
+
const summary = risk === "high" ? "Review before merge: the change removes API surface, deletes symbols, or has an incomplete impact set." : risk === "medium" ? "Targeted review recommended: behavior, API shape, tests, or blind spots changed." : "Low structural risk: only additive changes with no predicted affected tests were found.";
|
|
2062
|
+
return { risk, summary, reasons, recommendedActions };
|
|
2063
|
+
}
|
|
2064
|
+
|
|
1958
2065
|
// src/pr-comment.ts
|
|
1959
2066
|
var exports_pr_comment = {};
|
|
1960
2067
|
import fs7 from "node:fs";
|
|
@@ -1993,7 +2100,7 @@ var args3, repo2, baseSha, headSha, urlFlag, repoUrl2, dbPathA = "/tmp/codeblast
|
|
|
1993
2100
|
const wrap = s.kind === "interface" || s.kind === "const" ? (t) => t : (t) => `(${t})`;
|
|
1994
2101
|
const what = s.kind === "interface" ? "ๆๅๅๅ" : s.kind === "const" ? "็ฑปๅๅๅ" : "";
|
|
1995
2102
|
return `- \`${s.name}\`${what ? ` ${what}` : ""}: \`${wrap(clip(s.from))}\` โ \`${wrap(clip(s.to))}\` ๏ผ${link(s.file, s.line)}๏ผ`;
|
|
1996
|
-
}, apiSig, testSig, uncovered, impactRows, prodNodesAdded, diffLineCount, bodySignal, coreNamed;
|
|
2103
|
+
}, apiSig, testSig, uncovered, impactRows, affectedTestCount = 0, anyImpactTruncated = false, totalBlindSpots = 0, prodNodesAdded, diffLineCount, bodySignal, coreNamed, decision;
|
|
1997
2104
|
var init_pr_comment = __esm(async () => {
|
|
1998
2105
|
init_db();
|
|
1999
2106
|
init_proc();
|
|
@@ -2106,6 +2213,9 @@ var init_pr_comment = __esm(async () => {
|
|
|
2106
2213
|
const r = impact(dbB2, n.id, 2000);
|
|
2107
2214
|
const callItems = r.items.filter((i) => i.channel === "call");
|
|
2108
2215
|
const tests = callItems.filter((i) => i.level === "tests").length;
|
|
2216
|
+
affectedTestCount += tests;
|
|
2217
|
+
anyImpactTruncated ||= r.truncated;
|
|
2218
|
+
totalBlindSpots += r.blind_spot_count;
|
|
2109
2219
|
impactRows.push(`| \`${n.name}\` | ${n.kind} | ${callItems.length} | ${tests} | ${link(n.file, n.line)} |`);
|
|
2110
2220
|
const blind = r.blind_spot_count > 0;
|
|
2111
2221
|
if (tests === 0 && n.kind !== "interface" && n.kind !== "const") {
|
|
@@ -2153,16 +2263,101 @@ var init_pr_comment = __esm(async () => {
|
|
|
2153
2263
|
if (total2 === 0 && bodyChanged.length > 0) {
|
|
2154
2264
|
lines3[2] = `**ๆ ็ปๆๅๆด**,ไฝๆ ${bodyChanged.length} ไธชๅฝๆฐไฝๅ
ๆนๅจ๏ผ่งไธ๏ผ`;
|
|
2155
2265
|
}
|
|
2266
|
+
decision = reviewDecision({
|
|
2267
|
+
diff: diff2,
|
|
2268
|
+
prodNodesAdded,
|
|
2269
|
+
bodyChanged,
|
|
2270
|
+
affectedTests: affectedTestCount,
|
|
2271
|
+
truncated: anyImpactTruncated,
|
|
2272
|
+
blindSpotCount: totalBlindSpots
|
|
2273
|
+
});
|
|
2274
|
+
lines3.splice(3, 0, `> **Review decision ยท ${decision.risk.toUpperCase()}** โ ${decision.summary}`, `> ${decision.reasons.join("; ")}.`, ``, `### Recommended checks`, ``, ...decision.recommendedActions.map((action) => `- ${action}`), ``);
|
|
2156
2275
|
lines3.push(`<sub>็ฑ [codeblast](https://github.com/alloevil/codeblast) ็ๆ ยท ๆฏๆก็ป่ฎบๅบไบ้ๆๅๆ,ๅซ่ฏๆฎ้พๆฅ ยท ๅจๆ่ฐ็จ็ฒๅบไธๅจๆฌๆฅๅๅ
ยท ่ฏ่ฎบไธๅ?[30 ็งๅ้ฆ](https://github.com/alloevil/codeblast/issues/new?template=bot-feedback.yml&title=${encodeURIComponent(`[feedback] ${baseSha.slice(0, 7)}..${headSha.slice(0, 7)}`)})</sub>`);
|
|
2157
2276
|
console.log(lines3.join(`
|
|
2158
2277
|
`));
|
|
2159
2278
|
});
|
|
2160
2279
|
|
|
2280
|
+
// src/check-change.ts
|
|
2281
|
+
var exports_check_change = {};
|
|
2282
|
+
import fs8 from "node:fs";
|
|
2283
|
+
function buildGraphAt3(ref, db) {
|
|
2284
|
+
const wt = `/tmp/codeblast-check-${ref.replace(/[^\w]/g, "_")}`;
|
|
2285
|
+
spawnSync(["git", "worktree", "remove", "--force", wt], { cwd: repo3 });
|
|
2286
|
+
const add = spawnSync(["git", "worktree", "add", "--detach", wt, ref], { cwd: repo3 });
|
|
2287
|
+
if (add.exitCode !== 0)
|
|
2288
|
+
throw new Error(add.stderr.slice(0, 300));
|
|
2289
|
+
try {
|
|
2290
|
+
const built = spawnSync(selfCommand("index", wt, "--db", db));
|
|
2291
|
+
if (built.exitCode !== 0)
|
|
2292
|
+
throw new Error(built.stderr.slice(0, 500));
|
|
2293
|
+
} finally {
|
|
2294
|
+
spawnSync(["git", "worktree", "remove", "--force", wt], { cwd: repo3 });
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
var repo3, baseSha2, headSha2, dbAPath = "/tmp/codeblast-check-base.db", dbBPath = "/tmp/codeblast-check-head.db", dbA3, dbB3, diff3, prodNodesAdded2, bodyChanged2, total3, affectedTests = 0, truncated = false, blindSpots = 0, diffLineCount2, decision2, output;
|
|
2298
|
+
var init_check_change = __esm(() => {
|
|
2299
|
+
init_db();
|
|
2300
|
+
init_proc();
|
|
2301
|
+
init_impact();
|
|
2302
|
+
init_pr_silence();
|
|
2303
|
+
[repo3, baseSha2, headSha2] = process.argv.slice(2);
|
|
2304
|
+
if (!repo3 || !baseSha2 || !headSha2) {
|
|
2305
|
+
console.error("usage: codeblast check-change <repo> <base-sha> <head-sha> --json");
|
|
2306
|
+
process.exit(1);
|
|
2307
|
+
}
|
|
2308
|
+
for (const f of [dbAPath, dbBPath])
|
|
2309
|
+
for (const suffix of ["", "-wal", "-shm"])
|
|
2310
|
+
fs8.rmSync(f + suffix, { force: true });
|
|
2311
|
+
buildGraphAt3(baseSha2, dbAPath);
|
|
2312
|
+
buildGraphAt3(headSha2, dbBPath);
|
|
2313
|
+
dbA3 = openDatabase(dbAPath, { readonly: true });
|
|
2314
|
+
dbB3 = openDatabase(dbBPath, { readonly: true });
|
|
2315
|
+
diff3 = graphDiff(dbA3, dbB3);
|
|
2316
|
+
prodNodesAdded2 = diff3.nodesAdded.filter((n) => !TEST_RE3.test(n.file));
|
|
2317
|
+
bodyChanged2 = [];
|
|
2318
|
+
total3 = structuralTotal(diff3);
|
|
2319
|
+
for (const node of [...diff3.nodesAdded, ...diff3.renamed.map((r) => ({ id: `${r.file}#${r.to}`, kind: r.kind, name: r.to, file: r.file, line: 0 }))].slice(0, 15)) {
|
|
2320
|
+
try {
|
|
2321
|
+
const result = impact(dbB3, node.id, 2000);
|
|
2322
|
+
affectedTests += new Set(result.items.filter((item) => item.level === "tests").map((item) => item.file)).size;
|
|
2323
|
+
truncated ||= result.truncated;
|
|
2324
|
+
blindSpots += result.blind_spot_count;
|
|
2325
|
+
} catch {}
|
|
2326
|
+
}
|
|
2327
|
+
diffLineCount2 = spawnSync(["git", "diff", "--numstat", baseSha2, headSha2], { cwd: repo3 }).stdout.split(`
|
|
2328
|
+
`).reduce((sum, line) => {
|
|
2329
|
+
const match = line.match(/^(\d+)\t(\d+)\t/);
|
|
2330
|
+
return sum + (match ? Number(match[1]) + Number(match[2]) : 0);
|
|
2331
|
+
}, 0);
|
|
2332
|
+
decision2 = reviewDecision({ diff: diff3, prodNodesAdded: prodNodesAdded2, bodyChanged: bodyChanged2, affectedTests, truncated, blindSpotCount: blindSpots });
|
|
2333
|
+
output = {
|
|
2334
|
+
range: `${baseSha2}..${headSha2}`,
|
|
2335
|
+
decision: decision2.risk === "high" ? "review" : decision2.risk === "medium" ? "targeted-review" : "safe-to-review",
|
|
2336
|
+
risk: decision2.risk,
|
|
2337
|
+
summary: decision2.summary,
|
|
2338
|
+
reasons: decision2.reasons,
|
|
2339
|
+
recommended_actions: decision2.recommendedActions,
|
|
2340
|
+
structural_changes: total3,
|
|
2341
|
+
affected_test_files: affectedTests,
|
|
2342
|
+
blind_spot_count: blindSpots,
|
|
2343
|
+
truncated,
|
|
2344
|
+
signals: {
|
|
2345
|
+
core_named: coreNamedCount(diff3, prodNodesAdded2),
|
|
2346
|
+
body: bodySignalCount(bodyChanged2, diffLineCount2, () => false),
|
|
2347
|
+
aux_only: total3 > 0 && coreNamedCount(diff3, prodNodesAdded2) === 0 && bodyChanged2.length === 0
|
|
2348
|
+
},
|
|
2349
|
+
diff: diff3
|
|
2350
|
+
};
|
|
2351
|
+
console.log(JSON.stringify(output));
|
|
2352
|
+
dbA3.close();
|
|
2353
|
+
dbB3.close();
|
|
2354
|
+
});
|
|
2355
|
+
|
|
2161
2356
|
// src/demo.ts
|
|
2162
2357
|
var exports_demo = {};
|
|
2163
|
-
import
|
|
2358
|
+
import fs9 from "node:fs";
|
|
2164
2359
|
import path3 from "node:path";
|
|
2165
|
-
var
|
|
2360
|
+
var repo4, db6 = "/tmp/codeblast-demo.db", out = "/tmp/codeblast-demo-arch.html", run = (label, args) => {
|
|
2166
2361
|
console.log(`
|
|
2167
2362
|
\x1B[36mโธ ${label}\x1B[0m`);
|
|
2168
2363
|
console.log(` $ codeblast ${args.slice(2).join(" ")}`);
|
|
@@ -2182,12 +2377,12 @@ var repo3, db6 = "/tmp/codeblast-demo.db", out = "/tmp/codeblast-demo-arch.html"
|
|
|
2182
2377
|
var init_demo = __esm(() => {
|
|
2183
2378
|
init_db();
|
|
2184
2379
|
init_proc();
|
|
2185
|
-
|
|
2186
|
-
console.log(`codeblast demo โ target: ${
|
|
2380
|
+
repo4 = path3.resolve(process.argv[2] ?? path3.join(import.meta.dirname, ".."));
|
|
2381
|
+
console.log(`codeblast demo โ target: ${repo4}`);
|
|
2187
2382
|
for (const s of ["", "-wal", "-shm"])
|
|
2188
|
-
|
|
2189
|
-
run("1/4 build graph", selfCommand("index",
|
|
2190
|
-
run("2/4 incremental rerun (should skip everything)", selfCommand("index",
|
|
2383
|
+
fs9.rmSync(db6 + s, { force: true });
|
|
2384
|
+
run("1/4 build graph", selfCommand("index", repo4, "--db", db6));
|
|
2385
|
+
run("2/4 incremental rerun (should skip everything)", selfCommand("index", repo4, "--db", db6));
|
|
2191
2386
|
conn = openDatabase(db6, { readonly: true });
|
|
2192
2387
|
pick = conn.prepare(`SELECT n.id, COUNT(DISTINCT e.src) c FROM nodes n
|
|
2193
2388
|
JOIN edges e ON e.dst = n.id AND e.kind = 'calls'
|
|
@@ -2212,7 +2407,7 @@ var init_demo = __esm(() => {
|
|
|
2212
2407
|
|
|
2213
2408
|
// src/name-modules.ts
|
|
2214
2409
|
var exports_name_modules = {};
|
|
2215
|
-
import
|
|
2410
|
+
import fs10 from "node:fs";
|
|
2216
2411
|
var dbPath6, overlayFlag2, overlayPath2, db7, TEST_RE4, files3, byModule, symStmt, evidence, prompt, cmd;
|
|
2217
2412
|
var init_name_modules = __esm(async () => {
|
|
2218
2413
|
init_db();
|
|
@@ -2250,7 +2445,7 @@ ${JSON.stringify(evidence, null, 1)}`;
|
|
|
2250
2445
|
if (!cmd) {
|
|
2251
2446
|
const applyFlag = process.argv.indexOf("--apply");
|
|
2252
2447
|
if (applyFlag >= 0) {
|
|
2253
|
-
const namesJson = JSON.parse(
|
|
2448
|
+
const namesJson = JSON.parse(fs10.readFileSync(process.argv[applyFlag + 1], "utf8"));
|
|
2254
2449
|
const overlay = await loadOverlay(overlayPath2);
|
|
2255
2450
|
for (const [mod, v] of Object.entries(namesJson)) {
|
|
2256
2451
|
const existing = overlay.modules[mod];
|
|
@@ -2259,7 +2454,7 @@ ${JSON.stringify(evidence, null, 1)}`;
|
|
|
2259
2454
|
overlay.modules[mod] = { ...existing, name: `${v.name}`, ...v.desc ? {} : {} };
|
|
2260
2455
|
overlay.modules[mod].desc = v.desc;
|
|
2261
2456
|
}
|
|
2262
|
-
|
|
2457
|
+
fs10.writeFileSync(overlayPath2, JSON.stringify(overlay, null, 2));
|
|
2263
2458
|
console.error(`overlay written: ${overlayPath2}`);
|
|
2264
2459
|
} else {
|
|
2265
2460
|
console.log(prompt);
|
|
@@ -2276,7 +2471,7 @@ ${JSON.stringify(evidence, null, 1)}`;
|
|
|
2276
2471
|
overlay.modules[mod] = { ...overlay.modules[mod], name: v.name };
|
|
2277
2472
|
overlay.modules[mod].desc = v.desc;
|
|
2278
2473
|
}
|
|
2279
|
-
|
|
2474
|
+
fs10.writeFileSync(overlayPath2, JSON.stringify(overlay, null, 2));
|
|
2280
2475
|
console.error(`overlay written: ${overlayPath2}`);
|
|
2281
2476
|
}
|
|
2282
2477
|
});
|
|
@@ -2290,6 +2485,7 @@ var ROUTES = {
|
|
|
2290
2485
|
mermaid: () => Promise.resolve().then(() => (init_archmap(), exports_archmap)),
|
|
2291
2486
|
cochange: () => Promise.resolve().then(() => (init_cochange(), exports_cochange)),
|
|
2292
2487
|
"pr-comment": () => init_pr_comment().then(() => exports_pr_comment),
|
|
2488
|
+
"check-change": () => Promise.resolve().then(() => (init_check_change(), exports_check_change)),
|
|
2293
2489
|
demo: () => Promise.resolve().then(() => (init_demo(), exports_demo)),
|
|
2294
2490
|
"name-modules": () => init_name_modules().then(() => exports_name_modules)
|
|
2295
2491
|
};
|
|
@@ -2304,9 +2500,12 @@ usage: codeblast <command> [args]
|
|
|
2304
2500
|
change <repo> <ref-a> <ref-b> [--json] structural diff between two refs
|
|
2305
2501
|
archmap <graph.db> --out arch.html interactive architecture map
|
|
2306
2502
|
[--impact <sym>] [--diff <base.db>] ...with impact / change overlay
|
|
2503
|
+
[--repo-url <url>] names the page and its JSON-LD (already used above)
|
|
2504
|
+
[--site-url <base>] [--og-image <u>] canonical/og for the published page (optional)
|
|
2307
2505
|
mermaid <graph.db> module map as mermaid
|
|
2308
2506
|
cochange <repo> <graph.db> mine git history coupling
|
|
2309
2507
|
pr-comment <repo> <base-sha> <head-sha> PR review comment (silent if no change)
|
|
2508
|
+
check-change <repo> <base-sha> <head-sha> machine-readable merge safety decision
|
|
2310
2509
|
demo [repo] build + query + map in one shot
|
|
2311
2510
|
|
|
2312
2511
|
docs: https://github.com/alloevil/codeblast ยท demos: https://alloevil.github.io/codeblast/`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeblast",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
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",
|