@ghostlygawd/codeweb 0.9.0
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/.claude-plugin/plugin.json +30 -0
- package/CHANGELOG.md +480 -0
- package/LICENSE +21 -0
- package/README.md +573 -0
- package/agents/codeweb-dissector.md +56 -0
- package/agents/codeweb-domain-mapper.md +63 -0
- package/commands/codeweb.md +57 -0
- package/hooks/hooks.json +47 -0
- package/hooks/post-edit-diff.mjs +83 -0
- package/hooks/pre-edit-impact.mjs +94 -0
- package/hooks/session-brief.mjs +53 -0
- package/package.json +64 -0
- package/scripts/annotate.mjs +46 -0
- package/scripts/bench-ts-engine.mjs +59 -0
- package/scripts/bench.mjs +133 -0
- package/scripts/break-cycles.mjs +0 -0
- package/scripts/brief.mjs +28 -0
- package/scripts/build-report.mjs +182 -0
- package/scripts/campaign.mjs +61 -0
- package/scripts/check-consistency.mjs +45 -0
- package/scripts/ci-gate.mjs +86 -0
- package/scripts/cluster3.mjs +112 -0
- package/scripts/codemod.mjs +130 -0
- package/scripts/context-pack.mjs +118 -0
- package/scripts/deadcode.mjs +96 -0
- package/scripts/diff.mjs +0 -0
- package/scripts/explain.mjs +87 -0
- package/scripts/extract-symbols.mjs +1307 -0
- package/scripts/find-similar.mjs +86 -0
- package/scripts/find.mjs +50 -0
- package/scripts/fitness.mjs +90 -0
- package/scripts/grammars/PROVENANCE.md +36 -0
- package/scripts/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/scripts/grammars/tree-sitter-go.wasm +0 -0
- package/scripts/grammars/tree-sitter-java.wasm +0 -0
- package/scripts/grammars/tree-sitter-python.wasm +0 -0
- package/scripts/grammars/tree-sitter-rust.wasm +0 -0
- package/scripts/grammars/tree-sitter-typescript.wasm +0 -0
- package/scripts/hotspots.mjs +53 -0
- package/scripts/lib/annotations.mjs +51 -0
- package/scripts/lib/bench-core.mjs +178 -0
- package/scripts/lib/brief-core.mjs +92 -0
- package/scripts/lib/campaign.mjs +73 -0
- package/scripts/lib/claims-check.mjs +44 -0
- package/scripts/lib/cli.mjs +140 -0
- package/scripts/lib/complexity.mjs +68 -0
- package/scripts/lib/dup-check.mjs +51 -0
- package/scripts/lib/find-core.mjs +85 -0
- package/scripts/lib/gate-md.mjs +50 -0
- package/scripts/lib/graph-ops.mjs +319 -0
- package/scripts/lib/hotspots.mjs +34 -0
- package/scripts/lib/query-core.mjs +85 -0
- package/scripts/lib/reading-order.mjs +53 -0
- package/scripts/lib/reliance.mjs +143 -0
- package/scripts/lib/risk.mjs +18 -0
- package/scripts/lib/shingles.mjs +39 -0
- package/scripts/lib/skeleton.mjs +41 -0
- package/scripts/lib/stats.mjs +92 -0
- package/scripts/lib/ts-engine.mjs +605 -0
- package/scripts/mcp-server.mjs +370 -0
- package/scripts/optimize.mjs +152 -0
- package/scripts/overlap.mjs +380 -0
- package/scripts/placement.mjs +93 -0
- package/scripts/query.mjs +94 -0
- package/scripts/reading-order.mjs +36 -0
- package/scripts/refresh.mjs +73 -0
- package/scripts/release-utils.mjs +158 -0
- package/scripts/release.mjs +62 -0
- package/scripts/report-template.html +730 -0
- package/scripts/review.mjs +112 -0
- package/scripts/risk.mjs +77 -0
- package/scripts/run.mjs +96 -0
- package/scripts/screenshot.mjs +104 -0
- package/scripts/simulate-edit.mjs +70 -0
- package/scripts/stats.mjs +31 -0
- package/scripts/trend.mjs +147 -0
- package/scripts/version-sync.mjs +19 -0
- package/skills/codebase-anatomy/SKILL.md +174 -0
- package/skills/codebase-anatomy/references/engine-detection.md +49 -0
- package/skills/codebase-anatomy/references/graph-schema.md +120 -0
- package/skills/codebase-anatomy/references/overlap-heuristics.md +52 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Propagate the canonical version (package.json) and the canonical MCP tool count
|
|
4
|
+
* (scripts/mcp-server.mjs) out to every place they would otherwise drift:
|
|
5
|
+
* the plugin manifest and the skill frontmatter. Run after bumping package.json.
|
|
6
|
+
*
|
|
7
|
+
* node scripts/version-sync.mjs (or: npm run version-sync)
|
|
8
|
+
*/
|
|
9
|
+
import { dirname, join } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { getVersion, mcpToolCount, applySync } from './release-utils.mjs';
|
|
12
|
+
|
|
13
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
14
|
+
const version = getVersion(ROOT);
|
|
15
|
+
const count = mcpToolCount(ROOT);
|
|
16
|
+
const changed = applySync(ROOT, version, count);
|
|
17
|
+
|
|
18
|
+
process.stdout.write(`version-sync: v${version}, ${count} MCP tools.\n`);
|
|
19
|
+
process.stdout.write(changed.length ? ` updated: ${changed.join(', ')}\n` : ' all targets already in sync.\n');
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: codebase-anatomy
|
|
3
|
+
description: Dissect a codebase to atomic nodes (functions, classes, symbols), wire the call/import web, tag each node's domain, and build a cross-domain overlap graph that ranks consolidation/de-duplication opportunities, then render an interactive HTML map. Use to restructure your own codebase into well-defined non-duplicative systems, OR to fully map and review an external repo (git URL / owner-repo) before adopting it. Triggers include "map this codebase", "dependency/relationship graph", "find duplication/overlap", "atomic dissection", "review this repo before I use it", and the /codeweb command.
|
|
4
|
+
version: 0.9.0
|
|
5
|
+
metadata:
|
|
6
|
+
origin: community
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Codebase Anatomy (codeweb)
|
|
10
|
+
|
|
11
|
+
Build a "biological web" of a system: every atomic part (function, class, method, exported
|
|
12
|
+
symbol) is a node; calls/imports/inheritance are the edges; each node belongs to a domain; and
|
|
13
|
+
an **overlap graph** shows where separate parts do the same work — the simplification targets.
|
|
14
|
+
|
|
15
|
+
The goal is not a pretty picture. It is an **evidence-backed restructure plan**: which
|
|
16
|
+
duplicated logic should collapse into one well-defined system, and who should depend on it.
|
|
17
|
+
|
|
18
|
+
## When to Use
|
|
19
|
+
|
|
20
|
+
- Restructuring your own codebase and you need to *see* the real dependency web and where it's
|
|
21
|
+
duplicative before moving code.
|
|
22
|
+
- Onboarding to a large/legacy system at symbol resolution, not just folder-level.
|
|
23
|
+
- Reviewing an **external** repo (a plugin, library, or template you found on GitHub) end-to-end
|
|
24
|
+
before adopting it — what it does, how it's wired, and whether it's worth committing to.
|
|
25
|
+
- Hunting cross-cutting duplication that file/module tools miss (the same check coded N times in
|
|
26
|
+
N domains).
|
|
27
|
+
|
|
28
|
+
## Two Modes
|
|
29
|
+
|
|
30
|
+
- **internal** — analyze the current project; produce a domain map + overlap graph + restructure
|
|
31
|
+
recommendations.
|
|
32
|
+
- **external** — `target` is a git URL or `owner/repo`. Clone it **read-only** to a temp dir,
|
|
33
|
+
map it the same way, and add an **adoption review** (risk, dependencies, architecture verdict,
|
|
34
|
+
"should you adopt this?").
|
|
35
|
+
|
|
36
|
+
Auto-detect: a URL or `owner/repo` ⇒ external; a path or `.` ⇒ internal. `--mode` overrides.
|
|
37
|
+
|
|
38
|
+
## Non-Negotiable Rules
|
|
39
|
+
|
|
40
|
+
- **Never execute the target.** No build, run, test, install, or entrypoint — for internal or
|
|
41
|
+
external code. Only read files and invoke read-only static-analysis tools that inspect files
|
|
42
|
+
already on disk. This is absolute for external repos (their toolchain can run their code).
|
|
43
|
+
- **Evidence over guesswork.** Every node, edge, domain, and overlap must trace to code you read
|
|
44
|
+
or a tool emitted. When unsure of an edge or an overlap, omit it or mark it `low` — never
|
|
45
|
+
inflate. A report that cries wolf is worse than no report.
|
|
46
|
+
- **No silent truncation.** If you cap depth, sample, or skip files, say so in the report and in
|
|
47
|
+
`meta`. Coverage gaps must be visible.
|
|
48
|
+
- **One graph, one schema.** Everything conforms to `references/graph-schema.md`. The HTML
|
|
49
|
+
renderer and all agents depend on it.
|
|
50
|
+
- **Reuse, don't reinvent.** Hand off to `repo-scan` (file/library classification),
|
|
51
|
+
`codebase-onboarding` (guides), `refactor-cleaner` (acting on the list) rather than redoing
|
|
52
|
+
their jobs.
|
|
53
|
+
|
|
54
|
+
## Outputs (under `<target>/.codeweb/`)
|
|
55
|
+
|
|
56
|
+
1. `graph.json` — the web: `nodes`, `edges`, `domains`, `overlaps`, plus `meta` (roles,
|
|
57
|
+
staleness stamps, stats).
|
|
58
|
+
2. `report.html` — self-contained interactive map (force graph, domain tree, node details,
|
|
59
|
+
ranked overlap tab; product-only filter by default). No network/CDN.
|
|
60
|
+
3. `report.md` — the same map as plain markdown (mermaid domain graph + overlaps).
|
|
61
|
+
4. `overlap.md` — the ranked consolidation opportunities in plain markdown.
|
|
62
|
+
5. `optimize.md` — the consolidation advisory (ready / blocked / review tiers).
|
|
63
|
+
6. `fragment.json` — the raw extractor output (pipeline stage 1).
|
|
64
|
+
7. (external mode) an adoption review section appended to `overlap.md` and your final summary.
|
|
65
|
+
|
|
66
|
+
## References
|
|
67
|
+
|
|
68
|
+
- `references/graph-schema.md` — the exact JSON shape and merge rules. **Read before dissecting.**
|
|
69
|
+
- `references/engine-detection.md` — the hybrid engine: which analysis tool per language, how to
|
|
70
|
+
probe for it, and depth/scaling rules.
|
|
71
|
+
- `references/overlap-heuristics.md` — the four overlap kinds and the severity rubric.
|
|
72
|
+
|
|
73
|
+
## Core Workflow
|
|
74
|
+
|
|
75
|
+
### 0 — Scope & acquire
|
|
76
|
+
|
|
77
|
+
- Parse `target`, `--depth`, `--engine`, `--focus`, `--mode`, `--open`.
|
|
78
|
+
- internal: resolve the path (default `.`). external: `git clone --depth 1 <url>` into a temp
|
|
79
|
+
dir (e.g. `${TMPDIR}/codeweb-<repo>`); **do not** run anything inside it.
|
|
80
|
+
- Detect languages, package managers, and repo size (`rg --files | wc -l`). Choose effective
|
|
81
|
+
depth: `auto` ⇒ module-level overview, then symbol-level on the densest/most-overlapping
|
|
82
|
+
subsystems. Probe for analysis tools per `engine-detection.md`; record the engine.
|
|
83
|
+
|
|
84
|
+
### Fast path (default) — one-command deterministic engine
|
|
85
|
+
|
|
86
|
+
For languages the bundled extractor handles (**JavaScript, TypeScript, Python, Rust, Go, Java, C#, Ruby, PHP, Kotlin, Swift**), run the whole
|
|
87
|
+
pipeline in a single command instead of dissecting by hand. It is faster, cheaper, and
|
|
88
|
+
reproducible, and emits the same `graph.json` schema:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/run.mjs" "<target>" --target "<label>" --out-dir "<target>/.codeweb"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
It chains extract → cluster (directory-anchored domains) → overlap (body-confirmed duplication)
|
|
95
|
+
→ optimize (consolidation advisory) → render, writing `fragment.json`, `graph.json`, `overlap.md`, `optimize.md`, `report.html`, `report.md` into the
|
|
96
|
+
workspace (defaults to `<plugin>/.codeweb/runs/<slug>/` when `--out-dir` is omitted). The script is
|
|
97
|
+
read-only over the target and resolves its own paths, so it works from any cwd. **When it
|
|
98
|
+
succeeds, skip steps 1–5 and go to step 6.** Use the agent-based passes below only as a
|
|
99
|
+
**fallback** — for languages the extractor can't parse, an explicit `--engine read`, or to enrich
|
|
100
|
+
findings the scripts left `low`-confidence.
|
|
101
|
+
|
|
102
|
+
### 1 — Survey & partition (agent-based fallback; skip if the fast path ran)
|
|
103
|
+
|
|
104
|
+
- Build the subsystem map: top-level source dirs / packages, excluding vendored and build dirs
|
|
105
|
+
(lean on `repo-scan`'s classification idea — project vs third-party vs artifact).
|
|
106
|
+
- Partition the project code into ~4–12 scopes of comparable size. These are the parallel units.
|
|
107
|
+
|
|
108
|
+
### 2 — Dissect (parallel `codeweb-dissector`)
|
|
109
|
+
|
|
110
|
+
- Spawn one `codeweb-dissector` per scope **in parallel** (single message, multiple agents).
|
|
111
|
+
Pass each: its scope, the engine mode, the detected tools, and the node/edge schema.
|
|
112
|
+
- Each returns `{nodes, edges}` for its slice. They may reference ids outside their scope.
|
|
113
|
+
- If subagents aren't available, run the passes sequentially with the same instructions.
|
|
114
|
+
|
|
115
|
+
### 3 — Merge
|
|
116
|
+
|
|
117
|
+
- Union all nodes (dedupe by `id`) and edges (unique by `from,to,kind`; sum `weight`).
|
|
118
|
+
- Drop edges whose endpoints aren't in the node set (dangling refs to skipped code).
|
|
119
|
+
|
|
120
|
+
### 4 — Domain-map & overlap (`codeweb-domain-mapper`)
|
|
121
|
+
|
|
122
|
+
- Pass the merged `{nodes, edges}` to one `codeweb-domain-mapper` with `overlap-heuristics.md`.
|
|
123
|
+
- It returns per-node `domain`, a `domains[]` summary, and ranked `overlaps[]`. Merge `domain`
|
|
124
|
+
back onto each node by `id`; attach `domains` and `overlaps` to the graph.
|
|
125
|
+
|
|
126
|
+
### 5 — Stamp & render
|
|
127
|
+
|
|
128
|
+
- Write `.codeweb/graph.json`.
|
|
129
|
+
- Run the renderer:
|
|
130
|
+
```bash
|
|
131
|
+
node "${CLAUDE_PLUGIN_ROOT}/scripts/build-report.mjs" "<target>/.codeweb/graph.json"
|
|
132
|
+
```
|
|
133
|
+
(add `--open` to launch it). It computes `meta.stats`, stamps `generatedAt`, drops any
|
|
134
|
+
remaining dangling edges, writes the normalized graph back to `graph.json`, and writes
|
|
135
|
+
`report.html` + `report.md` beside it.
|
|
136
|
+
|
|
137
|
+
### 6 — Overlap.md + summary
|
|
138
|
+
|
|
139
|
+
- Write `overlap.md`: the ranked overlaps as a checklist — title, kind, severity, the symbols
|
|
140
|
+
involved, evidence, and the named consolidation target. This is the restructure plan.
|
|
141
|
+
- Report to the user: artifact paths, the domain count, and the **top 3–5 consolidation
|
|
142
|
+
opportunities** with their recommendations.
|
|
143
|
+
|
|
144
|
+
### 7 — External adoption review (external mode only)
|
|
145
|
+
|
|
146
|
+
Append an adoption verdict: what the repo is and does (from the domain map), notable
|
|
147
|
+
dependencies and risk surface (lean on `security-review` / `repo-scan` signals), architecture
|
|
148
|
+
quality (cohesion vs the overlap graph), and a clear **adopt / adapt / avoid** recommendation
|
|
149
|
+
with reasons. Then clean up the temp clone.
|
|
150
|
+
|
|
151
|
+
## Scaling
|
|
152
|
+
|
|
153
|
+
Follow `engine-detection.md`: module depth for first pass and huge repos; expand to symbol depth
|
|
154
|
+
on the top subsystems by size and cross-domain edge density. If total nodes would exceed ~2000,
|
|
155
|
+
cap symbol expansion to the focus area and state it. The HTML renderer auto-starts in
|
|
156
|
+
domain-aggregated view above ~600 nodes and lets the user expand.
|
|
157
|
+
|
|
158
|
+
## Handoffs
|
|
159
|
+
|
|
160
|
+
- `refactor-cleaner` — execute the consolidation list.
|
|
161
|
+
- `codebase-onboarding` — turn the domain map into an onboarding guide.
|
|
162
|
+
- `code-tour` — anchor a guided tour to the symbol index.
|
|
163
|
+
- `repo-scan` — deeper file-level / third-party classification.
|
|
164
|
+
|
|
165
|
+
## Output Format
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
TARGET — path/url · mode · languages · engine · depth
|
|
169
|
+
WEB — nodes / edges / domains (+ coverage caveats)
|
|
170
|
+
DOMAINS — each domain: name · node count · one-line role
|
|
171
|
+
OVERLAPS — ranked: severity · kind · title · → consolidation target
|
|
172
|
+
ARTIFACTS— .codeweb/graph.json · report.html · overlap.md
|
|
173
|
+
VERDICT — (external only) adopt / adapt / avoid, with reasons
|
|
174
|
+
```
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Hybrid engine — tool detection & fallback
|
|
2
|
+
|
|
3
|
+
codeweb prefers **precise edges from static-analysis tools** when they are installed, and falls
|
|
4
|
+
back to **agent reading** otherwise. Per subsystem, detect what's available, use it, and record
|
|
5
|
+
the chosen engine in `meta.engine`.
|
|
6
|
+
|
|
7
|
+
> **Native fast path (no tools required):** JavaScript, TypeScript, Python, Rust, and Go are parsed
|
|
8
|
+
> directly by the bundled regex extractor (`scripts/extract-symbols.mjs`). The table below is the
|
|
9
|
+
> *optional* sharpening / agent-fallback path — for these languages it only refines edges, and for
|
|
10
|
+
> the others it is the primary route.
|
|
11
|
+
|
|
12
|
+
## Detection order (per language)
|
|
13
|
+
|
|
14
|
+
| Language | Preferred tool(s) | Detect with | Emits |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| JS/TS | `madge` (imports), `ts-morph`/`tsc --listFiles` | `npx --no-install madge -V`; `tsconfig.json` present | import graph, modules |
|
|
17
|
+
| Python | `pydeps`, `pyan3`, stdlib `ast` via a one-off script | `python -c "import ast"`; `pip show pydeps` | import + call graph |
|
|
18
|
+
| Go | `go list -deps`, `callgraph` (golang.org/x/tools) | `go version` | package + call graph |
|
|
19
|
+
| Rust | `cargo modules`, `cargo-call-stack` | `cargo --version` | module tree, calls |
|
|
20
|
+
| Java/Kotlin | `jdeps` (JDK), tree-sitter | `jdeps -version` | class/package deps |
|
|
21
|
+
| C/C++ | `clangd`/`clang -emit-ast`, `cscope`, `ctags` | `clang --version`; `ctags --version` | symbols, includes |
|
|
22
|
+
| Any | **universal-ctags** (symbols), **tree-sitter** (AST), `ripgrep` (call sites) | `ctags --version`; `tree-sitter --version` | symbol index |
|
|
23
|
+
|
|
24
|
+
`universal-ctags` + `ripgrep` is the broad fallback spine: ctags gives the symbol list with
|
|
25
|
+
locations; ripgrep finds call sites to draw edges. Both are read-only and cross-language.
|
|
26
|
+
|
|
27
|
+
## Rules
|
|
28
|
+
|
|
29
|
+
- **Probe, don't install.** Check for a tool with a version/`--help` call. If it's missing,
|
|
30
|
+
do **not** `npm install` / `pip install` / `cargo install` it — drop to reading. (Especially
|
|
31
|
+
for external repos: installing their toolchain can execute their code.)
|
|
32
|
+
- **Tool output is the spine, reading fills the flesh.** Even with a tool, read representative
|
|
33
|
+
files to populate `summary`, `kind`, `loc`, and to confirm call edges the tool can't resolve
|
|
34
|
+
(dynamic dispatch, reflection, DI).
|
|
35
|
+
- **Record per-subsystem engine.** A repo can be `tools` for its Go service and `read` for its
|
|
36
|
+
shell scripts. Set `meta.engine` to the dominant mode, and note mixed coverage in the report.
|
|
37
|
+
- **Never execute the target.** No build, no run, no test, no entrypoint. Only invoke analysis
|
|
38
|
+
tools that statically inspect files already on disk.
|
|
39
|
+
|
|
40
|
+
## Scaling / depth
|
|
41
|
+
|
|
42
|
+
- `module` depth: stop at file/module nodes and import edges — fast, good for first pass and
|
|
43
|
+
huge repos. Default top-level view.
|
|
44
|
+
- `symbol` depth: expand to function/class/method nodes and call edges — run it on the densest
|
|
45
|
+
or most-overlapping subsystems rather than the whole repo when node counts get large.
|
|
46
|
+
- `auto`: module-level everywhere, then symbol-level on the top subsystems by size and by
|
|
47
|
+
cross-domain edge density (where overlap is most likely).
|
|
48
|
+
- If total nodes would exceed ~2000, cap symbol expansion to the focus area and say so in the
|
|
49
|
+
report — never silently truncate.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# codeweb graph schema (`graph.json`)
|
|
2
|
+
|
|
3
|
+
The single source of truth that flows between dissectors, the domain-mapper, and the HTML
|
|
4
|
+
renderer. All examples below use synthetic values.
|
|
5
|
+
|
|
6
|
+
```json
|
|
7
|
+
{
|
|
8
|
+
"meta": {
|
|
9
|
+
"target": "src/ or https://github.com/owner/repo",
|
|
10
|
+
"mode": "internal | external",
|
|
11
|
+
"engine": "hybrid | tools | read",
|
|
12
|
+
"complexityEngine": "tree-sitter(...)", // present ONLY under --engine tree-sitter; signals exact
|
|
13
|
+
// complexity + class-qualified method ids + dispatch edges
|
|
14
|
+
"depth": "module | symbol | auto",
|
|
15
|
+
"languages": ["typescript", "python"],
|
|
16
|
+
"generatedAt": "ISO-8601 string, stamped by the renderer (not by agents)",
|
|
17
|
+
"stats": { "files": 0, "nodes": 0, "edges": 0, "domains": 0, "overlaps": 0 }
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"nodes": [
|
|
21
|
+
{
|
|
22
|
+
"id": "src/auth/login.ts:loginUser", // <repo-relative-path>:<symbol> (path alone for file/module nodes).
|
|
23
|
+
// Under --engine tree-sitter a METHOD's <symbol> is class-qualified
|
|
24
|
+
// (`Class.method`) so same-named methods don't collide; bare otherwise.
|
|
25
|
+
"label": "loginUser", // display name — a method label stays BARE (`method`, never `Class.method`)
|
|
26
|
+
"kind": "function", // function | class | method | module | file
|
|
27
|
+
"file": "src/auth/login.ts",
|
|
28
|
+
"line": 42,
|
|
29
|
+
"loc": 120, // size of the symbol body, for node radius
|
|
30
|
+
"exports": true,
|
|
31
|
+
"domain": "auth", // assigned by domain-mapper (empty from dissectors)
|
|
32
|
+
"summary": "Authenticates a user and issues a session token.",
|
|
33
|
+
"complexity": 7, // F4: approximate cyclomatic complexity (function|method only; absent on class/module)
|
|
34
|
+
"maxDepth": 3 // F4: max control-flow nesting depth (function|method only)
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
"edges": [
|
|
39
|
+
{
|
|
40
|
+
"from": "src/auth/login.ts:loginUser",
|
|
41
|
+
"to": "src/db/query.ts:runQuery",
|
|
42
|
+
"kind": "call", // call | import | inherit | ref | test (emitted) · dataflow (reserved)
|
|
43
|
+
"weight": 1 // number of occurrences; optional, default 1
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
|
|
47
|
+
"domains": [
|
|
48
|
+
{
|
|
49
|
+
"name": "auth",
|
|
50
|
+
"nodes": 12,
|
|
51
|
+
"summary": "Authentication, session issuance, and authorization checks.",
|
|
52
|
+
"files": ["src/auth/"] // optional, representative paths
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
|
|
56
|
+
"overlaps": [
|
|
57
|
+
{
|
|
58
|
+
"id": "ov1",
|
|
59
|
+
"title": "User validation duplicated across auth, billing, and api",
|
|
60
|
+
"kind": "duplicate-logic", // duplicate-logic | parallel-impl | shared-responsibility | tangled-domain
|
|
61
|
+
"severity": "high", // high | medium | low
|
|
62
|
+
"domains": ["auth", "billing", "api"],
|
|
63
|
+
"nodes": [
|
|
64
|
+
"src/auth/login.ts:validateUser",
|
|
65
|
+
"src/billing/charge.ts:checkUser",
|
|
66
|
+
"src/api/guard.ts:assertUser"
|
|
67
|
+
],
|
|
68
|
+
"evidence": "All three re-implement the same email + password + active-role check.",
|
|
69
|
+
"recommendation": "Extract a single auth.validateUser; have billing and api depend on it."
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Edge kinds
|
|
76
|
+
|
|
77
|
+
- **`call`** — a function/method invokes another, OR passes it by name as a higher-order argument
|
|
78
|
+
(`arr.map(fn)`, `rl.on('x', fn)`). The deterministic extractor resolves the target by import alias,
|
|
79
|
+
same-file definition, or a unique global definition, and DROPS ambiguous multi-definition names
|
|
80
|
+
rather than guess (precision over recall). A method call `obj.fn()` is NOT wired to a top-level `fn`
|
|
81
|
+
by the regex engine. Under `--engine tree-sitter`, dynamic-dispatch calls DO resolve: `this.m()`
|
|
82
|
+
(within a class) and typed-receiver `x.m()` (where `x: T` is a known class) wire to the
|
|
83
|
+
class-qualified method id (`<path>:T.m`); untyped/array/generic receivers are still dropped.
|
|
84
|
+
- **`import`** — a module imports a symbol from another module.
|
|
85
|
+
- **`inherit`** — a class extends/subclasses another (`class X extends Y`, `class X(Y):`), resolved
|
|
86
|
+
with the same precision gate as calls. Counts toward reachability: an extended base is not a
|
|
87
|
+
dead-code orphan, and `--impact` of a base includes its subclasses.
|
|
88
|
+
- **`ref`** — a symbol references a CLASS by identity without invoking it directly: `obj instanceof X`
|
|
89
|
+
or a static-method call `X.from(...)` (where `X` is an imported class or a same-file class). The
|
|
90
|
+
`.from()` site ALSO emits a `call` edge to the static method; the `ref` edge records the dependency on
|
|
91
|
+
the class itself so `--dependents <class>` surfaces every user (an `instanceof`/static-factory user is
|
|
92
|
+
not a `call`-edge caller). Precision-safe: an object-default alias (`import utils from './utils'`)
|
|
93
|
+
emits no `ref` — `utils` is not a class. Counts toward `--dependents` and reachability (not an orphan).
|
|
94
|
+
- **`test`** — a `call`/`ref` originating in a test file (`*.test.*`, `tests/` …) to a production
|
|
95
|
+
symbol, reclassified so production caller/orphan queries can exclude test-only usage while
|
|
96
|
+
`--dependents`/`--tests` still surface it.
|
|
97
|
+
- **`dataflow`** — RESERVED, not emitted by any stage today. Precise value/taint tracking
|
|
98
|
+
(source→sink) needs type/dispatch resolution and alias awareness the deterministic regex extractor
|
|
99
|
+
does not have; a noisy approximation would undermine the precision the other edge kinds guarantee
|
|
100
|
+
(codeweb's whole contract is "don't guess"). Reserved for a future optional type-resolution tier —
|
|
101
|
+
the schema lists it so consumers can forward-handle it, but no code produces it. Use an external
|
|
102
|
+
analyzer (Semgrep/CodeQL) for taint until then.
|
|
103
|
+
|
|
104
|
+
## Merge rules (orchestrator)
|
|
105
|
+
|
|
106
|
+
- Dissectors emit `nodes` + `edges` per scope. Deduplicate by node `id`; an edge is unique by
|
|
107
|
+
`(from, to, kind)` — sum `weight` on collision.
|
|
108
|
+
- Edges may reference node ids owned by another scope. After merging all dissectors, drop any
|
|
109
|
+
edge whose `from` or `to` id does not exist as a node (dangling reference to skipped code).
|
|
110
|
+
- The domain-mapper returns `{nodes:[{id,domain}], domains, overlaps}`. Merge each `domain`
|
|
111
|
+
back onto the matching full node by `id`.
|
|
112
|
+
- The renderer (`build-report.mjs`) computes `meta.stats`, stamps `meta.generatedAt`, and persists
|
|
113
|
+
both back into `graph.json` (along with the dangling-edge drop), so the on-disk graph matches the
|
|
114
|
+
rendered report.
|
|
115
|
+
|
|
116
|
+
## Minimum viable graph
|
|
117
|
+
|
|
118
|
+
A graph is renderable with just `nodes` and `edges`. `domains` and `overlaps` enrich the
|
|
119
|
+
report; if absent, the renderer treats every node as domain `"unassigned"` and shows an empty
|
|
120
|
+
overlap tab.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Overlap detection heuristics
|
|
2
|
+
|
|
3
|
+
The overlap graph is the payoff of codeweb: it shows where the system does the **same work in
|
|
4
|
+
more than one place**, so it can be restructured into well-defined, non-duplicative systems.
|
|
5
|
+
|
|
6
|
+
## The four overlap kinds
|
|
7
|
+
|
|
8
|
+
| Kind | Signal | Example |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `duplicate-logic` | The same algorithm/validation/transform appears in 2+ symbols with near-identical bodies or behaviour. | Three modules each parse the same date format by hand. |
|
|
11
|
+
| `parallel-impl` | Two or more competing implementations of one capability, often with diverging behaviour. | A `fetch` wrapper and an `axios` wrapper both used for API calls. |
|
|
12
|
+
| `shared-responsibility` | One concern is smeared across many domains with no single owner. | Retry/backoff logic copy-pasted into every network call site. |
|
|
13
|
+
| `tangled-domain` | A single symbol mixes responsibilities from multiple domains and should be split. | `saveOrder()` that also sends email, writes audit logs, and charges a card. |
|
|
14
|
+
|
|
15
|
+
## How to find them
|
|
16
|
+
|
|
17
|
+
1. **Name + signature clustering.** Group symbols whose names/params suggest the same job
|
|
18
|
+
(`validateUser`, `checkUser`, `assertUser`). Read the bodies to confirm they overlap.
|
|
19
|
+
2. **Edge-pattern clustering.** Symbols in different domains that call the same downstream set,
|
|
20
|
+
or that are called from the same set of callers, are candidates for consolidation.
|
|
21
|
+
3. **Structural similarity.** Similar control flow / similar literal sets (regexes, status
|
|
22
|
+
codes, field names) across symbols signals copy-paste drift.
|
|
23
|
+
4. **Cross-domain fan-in.** A utility imported by many domains may be fine (good reuse) — but a
|
|
24
|
+
*behaviour* re-implemented in many domains is overlap. Distinguish "one shared function used
|
|
25
|
+
widely" (healthy) from "the same idea coded N times" (overlap).
|
|
26
|
+
|
|
27
|
+
## Severity rubric
|
|
28
|
+
|
|
29
|
+
Score each overlap on three axes, then take the max-weighted band:
|
|
30
|
+
|
|
31
|
+
- **Duplication count** — 2 places = low, 3–4 = medium, 5+ = high.
|
|
32
|
+
- **Blast radius** — how many domains / how much traffic flows through the duplicated path.
|
|
33
|
+
Touches 1 domain = low, 2 = medium, 3+ or a core path = high.
|
|
34
|
+
- **Divergence risk** — have the copies already drifted (different edge cases, different bug
|
|
35
|
+
fixes)? Drift present = bump one band; security/correctness-sensitive logic = high.
|
|
36
|
+
|
|
37
|
+
`high` = block-worthy simplification target. `medium` = worth a refactor ticket. `low` = note.
|
|
38
|
+
|
|
39
|
+
## Writing the recommendation
|
|
40
|
+
|
|
41
|
+
Always name the **single well-defined system** the pieces should collapse into, and who should
|
|
42
|
+
depend on it. Good: *"Extract `auth.validateUser` as the one validator; billing and api import
|
|
43
|
+
it; delete the local copies."* Bad: *"Reduce duplication."* The recommendation is the bridge
|
|
44
|
+
from diagnosis to a concrete restructure.
|
|
45
|
+
|
|
46
|
+
## What is NOT overlap
|
|
47
|
+
|
|
48
|
+
- Genuinely independent logic that merely looks similar (different domains, different invariants).
|
|
49
|
+
- Intentional layering (an interface + one implementation).
|
|
50
|
+
- Test doubles / fixtures mirroring production shapes.
|
|
51
|
+
Flagging these erodes trust in the report — when unsure, mark `low` with explicit evidence
|
|
52
|
+
rather than inflating severity.
|