Skip to content

fix(go): report package-level importers as packages, not per-file hubs - #292

Merged
JordanCoin merged 3 commits into
mainfrom
fix/go-package-importers
Nov 6, 2026
Merged

JordanCoin merged 3 commits into
mainfrom
fix/go-package-importers

Conversation

@JordanCoin

Copy link
Copy Markdown
Owner

Closes #285.

Go import edges are package-level, but hub lists, --importers, handoff and find rendered them as file-level blast radius: every scanner/*.go showed 73 importers, get_hubs listed 95 near-identical entries.

  • scanner/filegraph.go: CollapseGoPackageHubs merges .go files in one directory with the same importer set into one dir/ (package, N files, M importers) entry; IsFileHub / SharedGoPackage make the per-file hub flag false for a file in a multi-file Go package (a one-file package keeps it).
  • Collapsed everywhere a hub list is shown: MCP get_hubs and get_structure, session-start high-impact list, handoff prefix (HubSummary gains files).
  • --importers and MCP get_importers: Package codemap/cmd is imported by 6 files (Go imports name packages, not files), no HUB banner, is_hub false in JSON. get_file_context: 📦 HUB PACKAGE <pkg> - N files import the package, not necessarily this file. find: importers: 73 (of package codemap/scanner), new package field.
  • Tests: TestGoPackageHubsListedOncePerPackage, TestImportersCLIGoPackageHubIsNotAFileHub, TestSummarizeHubsListsGoPackageOnce.

On this repo: get_hubs 95 -> 14 entries, top rows scanner/ (package, 20 files, 73 importers), internal/projectpath/ (package, 2 files, 48 importers), analysis/contracts.go (44 importers).

Left unchanged on purpose: HubFiles(), watch-state Hubs and context TopHubs JSON (machine contracts); the pre/post-edit hub warning (no package data there; one-line follow-up if wanted).

go test ./... -count=1 passes; gofmt -l clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FWQw8AjoCA1yjN3T59vk46

Go import edges are package-level, so every non-test file of a package
carries the same importer set. Hub lists ranked those files as separate
hubs (get_hubs on this repo: 95 entries, 20 of them scanner/*.go at 73
importers each; the handoff prefix filled its 15 slots with scanner
files), and --importers raised HUB FILE on a file whose count belongs to
its package.

- scanner: CollapseGoPackageHubs groups .go files of one directory that
  share an importer set into one "dir/ (package, F files, N importers)"
  entry. It keys on the importer set rather than go.mod, so it also works
  on the daemon-state importer map. IsFileHub/SharedGoPackage make the
  per-file hub flag false for files of a multi-file package; a one-file
  package keeps it.
- get_hubs, get_structure, session-start hub list and the handoff prefix
  use the collapsed list (HubSummary gains an omitempty files field).
- --importers (CLI and MCP) says "Package X is imported by N files" with
  no HUB FILE warning; is_hub is false in the JSON report; "Imports N
  hub(s)" counts collapsed entries.
- find labels a package-level count "(of package X)" instead of "(hub)".

Non-Go files and one-file Go packages behave as before. HubFiles, the
daemon state and context TopHubs still list files.

Closes #285

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQw8AjoCA1yjN3T59vk46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Two moderate inconsistencies remain between package-aware text output and structured MCP results, leaving consumers unable to distinguish file-level from package-level data.

2 open issues
What changed in this PR

This pull request makes Go dependency reporting package-aware, preventing multi-file packages from appearing as repeated file-level hubs across CLI, MCP, handoff, hooks, and search output.

Changes:

  • Adds Go package hub collapsing and distinguishes package hubs from genuine file hubs.
  • Updates importer, hub, handoff, session-start, MCP, and find output with package-aware labels and metadata.
  • Adds regression coverage for multi-file and single-file Go package behavior.
File Summary Review notes
scanner/​gopackage_test.go Tests package hub grouping and file-hub semantics. —
scanner/​filegraph.go Adds package-aware hub classification and collapsed hub entries. Moderate, 1 vote: Indexed file count alone may misclassify a partially scanned multi-file package as a file hub.
mcp/​main.go Updates structure, importer, hub, and file-context rendering. Moderate, 2 votes: Structured get_structure output still exposes per-file rows while text output is package-collapsed.
mcp/​analysis_output.go Reports file-level hub status in structured importer output. Moderate, 2 votes: Structured output lacks package/granularity metadata for package-level importer counts.
importers_go_package_test.go Tests package-level CLI importer reporting. —
handoff/​types.go Adds package file counts and package-aware hub formatting. —
handoff/​render.go Renders package-aware hub summaries. —
handoff/​build.go Collapses Go package hubs in handoff summaries. —
handoff/​build_more_test.go Tests deduplicated handoff hub summaries. —
find/​find.go Adds package metadata and package-aware importer rendering. Moderate, 1 vote: _test.go hits can show package-level annotations with a zero importer count, incorrectly implying the count is package-level.
cmd/​hooks.go Collapses package hubs in session-start output. —
blast_radius.go Updates importer reporting for Go package granularity. —

Review effort: Lite

Comment thread mcp/analysis_output.go Resolved
Comment thread mcp/main.go Resolved
JordanCoin and others added 2 commits November 6, 2026 10:28
get_importers' structured result now says what importer_count counts:
granularity "package" plus the package import path for a Go file in a
module graph ("file" otherwise), and package_hub when a multi-file
package is a hub although is_hub stays false for the file.

get_structure's structured result gains hub_entries, the same
package-collapsed list the text output renders; hubs stays the file list
so existing consumers keep working.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQw8AjoCA1yjN3T59vk46
Resolves cmd/hooks.go against #291: the collapsed package hub list now renders inside the captured session-start trailer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQw8AjoCA1yjN3T59vk46
@JordanCoin
JordanCoin merged commit afc8efe into main Nov 6, 2026
13 checks passed
JordanCoin added a commit that referenced this pull request Nov 11, 2026
…295)

The tree starred the five largest source files with no legend, so a reader
could not tell what the star meant, and on this repo it landed on
main_more_test.go. File size says nothing about blast radius.

The star now marks hubs from the file graph, with the importer count inline:
`types.ts ←41` for a file, `scanner/ ←73` on the directory of a multi-file Go
package (Go imports name packages, so the count belongs to the package, per
#292). N is the importer count used by get_hubs and the session-start hub
list; a hub needs 3+ non-test importers, so test files are never starred.

One legend line under the tree says what the marker and N mean, and carries
the graph note: partial coverage, or why stars are missing (no graph, or the
repo is above the 5000-file graph limit).

Hubs come from the daemon's validated graph when it was built with the same
filters, else a fresh BuildFileGraph scan (docling, no daemon: 0.41s -> 1.8s
per `codemap .`; codemap with a warm daemon: no measurable change).

The session-start hook drops its own hub block when the tree carries the
legend, so hubs are stated once and the werkzeug tree still fits the 10,000
char cap at full depth.

--json, --deps, --skyline and --diff output are unchanged. MCP get_structure
renders without hub data, so its tree loses the old size stars and gains no
legend; its HUB FILES section is unchanged.


Claude-Session: https://claude.ai/code/session_01FWQw8DzwM4bQnL7mvk6Fsq

Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
JordanCoin added a commit that referenced this pull request Nov 11, 2026
…296)

* fix(tree): star marks import hubs, not the largest files; add legend

The tree starred the five largest source files with no legend, so a reader
could not tell what the star meant, and on this repo it landed on
main_more_test.go. File size says nothing about blast radius.

The star now marks hubs from the file graph, with the importer count inline:
`types.ts ←41` for a file, `scanner/ ←73` on the directory of a multi-file Go
package (Go imports name packages, so the count belongs to the package, per
#292). N is the importer count used by get_hubs and the session-start hub
list; a hub needs 3+ non-test importers, so test files are never starred.

One legend line under the tree says what the marker and N mean, and carries
the graph note: partial coverage, or why stars are missing (no graph, or the
repo is above the 5000-file graph limit).

Hubs come from the daemon's validated graph when it was built with the same
filters, else a fresh BuildFileGraph scan (docling, no daemon: 0.41s -> 1.8s
per `codemap .`; codemap with a warm daemon: no measurable change).

The session-start hook drops its own hub block when the tree carries the
legend, so hubs are stated once and the werkzeug tree still fits the 10,000
char cap at full depth.

--json, --deps, --skyline and --diff output are unchanged. MCP get_structure
renders without hub data, so its tree loses the old size stars and gains no
legend; its HUB FILES section is unchanged.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQw8DzwM4bQnL7mvk6Fsq

* feat(tree): compact tree with line counts, hub addresses and symbols

Builds on the hub stars and legend from #295. All changes apply to the
plain `codemap .` tree; --json, --deps, --skyline and --diff are unchanged.

- Fold same-directory tests into their source: `filegraph.go (+3 tests)` (Go x_test/x_*_test, JS/TS .test/.spec, Python test_x/x_test).
- Count unmatched tests as `+N other tests`, except in a tests tree (test/, tests/, __tests__/, spec/ or no folded test), which lists its files.
- Collapse 4+ siblings that differ only in digit-bearing tokens: `page-*.yml ×9`.
- Collapse a wholly untracked directory (git ls-files --others --directory) to one line with its file count and size.
- Print a single-file directory as `dir/FILE`, at any depth, so .claude/skills/ and plugins/codemap/skills/ render alike.
- Give an overlong name its own row in sorted position instead of forcing a whole directory into one column.
- Show physical line counts at 100+ lines (`hooks.go 2535L`; text files up to 2MB, binaries and assets skipped).
- Read files for line counts and symbol lines only when Lstat says regular (symlinks, FIFOs and devices skipped), through a reader capped at 2MB+1.
- Trailer: `Hubs (top 10 of N)` with full paths, importer counts and line counts, wrapped under 400 chars; `path:line Name` for up to 3 exported top-level functions of the top 5 file hubs, kept only when the reported source line names the symbol; `Next:` commands.
- ast-grep function and type matches now record their line (FileAnalysis.SymbolLines, not serialized).
- Session-start hook: hubs stated once (its own hub block is dropped when the tree prints the legend), and the tree sheds extras before depth via CODEMAP_TREE_LEAN (1: no symbols, top 3 hubs; 2: also no line counts) before fitSessionStart cuts depth. Renders are cached by depth and level.

Measured (`codemap .`, warm, vs origin/main):
- codemap (daemon cache warm): 0.04s -> 0.13s; docling (no daemon): 0.40s -> 1.97s, of which the hub graph scan is ~1.4s, line counts and symbols ~0.2s.
- Output: codemap 307 -> 163 lines, 16.1KB -> 10.3KB; docling 492 -> 456 lines; CodeMachine-CLI 313 -> 324 lines.
- Hook: werkzeug full depth at 8,040 chars (#295: 8,028, full depth); codemap full depth at 8,528 chars (#295: depth 1, 3,369).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQw8DzwM4bQnL7mvk6Fsq

* fix(tree): hub list one per line, drop symbol lines

First-three-exported-in-file-order symbols were arbitrary and Go package hubs got none, so the symbol rows and the ast-grep line recording behind them are gone.
The hub list prints one hub per line, right-aligned count first, so names align and no 400-char wrapping is needed; hook lean level 1 now cuts it to three rows.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWQw8DzwM4bQnL7mvk6Fsq

---------

Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Go importer counts are package-level but rendered and ranked as file-level hubs

2 participants