Skip to content

Commit 95a8468

Browse files
authored
Add codemap-full release and blast radius bundle (#70)
* Add codemap-full release and blast radius bundle * Address review feedback for bundled releases
1 parent bce0d8d commit 95a8468

15 files changed

Lines changed: 1297 additions & 35 deletions

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ jobs:
101101
with:
102102
distribution: goreleaser
103103
version: latest
104-
args: release ${{ (github.event_name == 'workflow_dispatch' && inputs.dry_run) && '--snapshot --clean' || '--clean' }}
104+
args: release --config .goreleaser.full.yml ${{ (github.event_name == 'workflow_dispatch' && inputs.dry_run) && '--snapshot --clean' || '--clean' }}
105105
env:
106106
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
107107
HOMEBREW_TAP_TOKEN: ${{ secrets.SCOOP_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ __pycache__/
1212
.codemap/
1313
/codemap-dev
1414
/dev_codemap
15+
/bundled-tools/
1516
firebase-debug.log
1617
firebalse-debug.log
1718
coverage.out

.goreleaser.full.yml

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
version: 2
2+
3+
project_name: codemap
4+
5+
before:
6+
hooks:
7+
- go mod tidy
8+
- ./scripts/download-bundled-astgrep.sh
9+
10+
builds:
11+
- id: codemap
12+
main: .
13+
binary: codemap
14+
env:
15+
- CGO_ENABLED=0
16+
ldflags:
17+
- -s -w
18+
goos:
19+
- darwin
20+
- linux
21+
- windows
22+
goarch:
23+
- amd64
24+
- arm64
25+
ignore:
26+
- goos: windows
27+
goarch: arm64
28+
29+
archives:
30+
- id: default
31+
ids:
32+
- codemap
33+
formats:
34+
- tar.gz
35+
format_overrides:
36+
- goos: windows
37+
formats:
38+
- zip
39+
files:
40+
- README.md
41+
- LICENSE*
42+
- src: scanner/sg-rules/*
43+
dst: sg-rules
44+
- id: full
45+
ids:
46+
- codemap
47+
name_template: '{{ .ProjectName }}-full_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ with .Arm }}v{{ . }}{{ end }}{{ with .Mips }}_{{ . }}{{ end }}{{ if not (eq .Amd64 "v1") }}{{ .Amd64 }}{{ end }}'
48+
formats:
49+
- tar.gz
50+
format_overrides:
51+
- goos: windows
52+
formats:
53+
- zip
54+
files:
55+
- README.md
56+
- LICENSE*
57+
- src: scanner/sg-rules/*
58+
dst: sg-rules
59+
- src: bundled-tools/{{ .Os }}_{{ .Arch }}/ast-grep{{ if eq .Os "windows" }}.exe{{ end }}
60+
strip_parent: true
61+
- src: bundled-tools/{{ .Os }}_{{ .Arch }}/sg{{ if eq .Os "windows" }}.exe{{ end }}
62+
strip_parent: true
63+
64+
checksum:
65+
name_template: 'checksums.txt'
66+
67+
changelog:
68+
sort: asc
69+
filters:
70+
exclude:
71+
- '^docs:'
72+
- '^test:'
73+
- '^chore:'
74+
75+
release:
76+
github:
77+
owner: JordanCoin
78+
name: codemap
79+
80+
brews:
81+
- repository:
82+
owner: JordanCoin
83+
name: homebrew-tap
84+
token: "{{ .Env.HOMEBREW_TAP_TOKEN }}"
85+
ids:
86+
- default
87+
directory: .
88+
homepage: https://github.com/JordanCoin/codemap
89+
description: Generate a brain map of your codebase for LLM context
90+
license: MIT
91+
dependencies:
92+
- name: ast-grep
93+
install: |
94+
bin.install "codemap"
95+
(pkgshare/"sg-rules").install Dir["sg-rules/*.yml"] if Dir.exist?("sg-rules")
96+
caveats: |
97+
The --deps mode uses ast-grep for code analysis.
98+
test: |
99+
system "#{bin}/codemap", "--help"
100+
101+
scoops:
102+
- repository:
103+
owner: JordanCoin
104+
name: scoop-codemap
105+
token: "{{ .Env.SCOOP_TOKEN }}"
106+
ids:
107+
- default
108+
homepage: https://github.com/JordanCoin/codemap
109+
description: Generate a brain map of your codebase for LLM context
110+
license: MIT
111+
depends:
112+
- ast-grep
113+
114+
winget:
115+
- name: codemap
116+
ids:
117+
- default
118+
publisher: JordanCoin
119+
short_description: Generate a brain map of your codebase for LLM context
120+
license: MIT
121+
publisher_url: https://github.com/JordanCoin
122+
publisher_support_url: https://github.com/JordanCoin/codemap/issues
123+
package_identifier: JordanCoin.codemap
124+
homepage: https://github.com/JordanCoin/codemap
125+
description: |
126+
codemap generates a compact, structured "brain map" of your codebase
127+
that LLMs can instantly understand. One command gives instant
128+
architectural context without burning tokens.
129+
license_url: https://github.com/JordanCoin/codemap/blob/main/LICENSE
130+
tags:
131+
- cli
132+
- developer-tools
133+
- ai
134+
- llm
135+
- code-analysis
136+
repository:
137+
owner: JordanCoin
138+
name: winget-pkgs
139+
branch: "JordanCoin.codemap-{{.Version}}"
140+
token: "{{ .Env.SCOOP_TOKEN }}"
141+
pull_request:
142+
enabled: true
143+
base:
144+
owner: microsoft
145+
name: winget-pkgs
146+
branch: master

.goreleaser.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ builds:
2727

2828
archives:
2929
- id: default
30+
ids:
31+
- codemap
3032
formats:
3133
- tar.gz
3234
format_overrides:

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,40 @@ scoop install codemap
2323

2424
> Other options: [Releases](https://github.com/JordanCoin/codemap/releases) | `go install` | Build from source
2525
26+
## Tarball / CI Install
27+
28+
If you install `codemap` from a release tarball, also install `ast-grep` separately for `--deps`.
29+
The tarball includes `codemap` and the bundled rules, but not the `ast-grep` executable.
30+
31+
Example for Alpine-based CI:
32+
33+
```bash
34+
apk add --no-cache curl jq bash python3 py3-pip
35+
36+
ARCH=$(uname -m)
37+
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; elif [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi
38+
39+
CODEMAP_VERSION=$(curl -fsSL https://api.github.com/repos/JordanCoin/codemap/releases/latest | jq -r '.tag_name' | tr -d 'v')
40+
curl -fsSL "https://github.com/JordanCoin/codemap/releases/download/v${CODEMAP_VERSION}/codemap_${CODEMAP_VERSION}_linux_${ARCH}.tar.gz" \
41+
| tar xz -C /usr/local/bin/ codemap
42+
43+
python3 -m pip install --no-cache-dir ast-grep-cli
44+
```
45+
46+
If you want a self-contained archive for CI/CD, use the `codemap-full` release artifact instead.
47+
It includes `codemap`, `ast-grep`, and `sg` in one archive so `--deps` works after extraction.
48+
49+
```bash
50+
apk add --no-cache curl jq bash
51+
52+
ARCH=$(uname -m)
53+
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; elif [ "$ARCH" = "aarch64" ]; then ARCH="arm64"; fi
54+
55+
CODEMAP_VERSION=$(curl -fsSL https://api.github.com/repos/JordanCoin/codemap/releases/latest | jq -r '.tag_name' | tr -d 'v')
56+
curl -fsSL "https://github.com/JordanCoin/codemap/releases/download/v${CODEMAP_VERSION}/codemap-full_${CODEMAP_VERSION}_linux_${ARCH}.tar.gz" \
57+
| tar xz -C /usr/local/bin/ codemap ast-grep sg
58+
```
59+
2660
## Recommended Setup (Hooks + Daemon + Config)
2761

2862
No repo clone is required for normal users.
@@ -182,6 +216,23 @@ Uses a shallow clone to a temp directory (fast, no history, auto-cleanup). If yo
182216

183217
> Powered by [ast-grep](https://ast-grep.github.io/). Install via `brew install ast-grep` for `--deps` mode.
184218
219+
## Blast Radius Bundle
220+
221+
If you want a compact review bundle for another LLM, combine the three high-signal views:
222+
223+
```bash
224+
codemap --json --diff --ref main .
225+
codemap --json --deps --diff --ref main .
226+
codemap --json --importers path/to/file .
227+
```
228+
229+
For a reusable wrapper that emits either Markdown or a single JSON object:
230+
231+
```bash
232+
bash scripts/codemap-blast-radius.sh --markdown --ref main .
233+
bash scripts/codemap-blast-radius.sh --json --ref main .
234+
```
235+
185236
## Claude Integration
186237

187238
**Hooks (Recommended)** — Automatic context at session start, before/after edits, and more.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/modelcontextprotocol/go-sdk v1.1.0
99
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
1010
golang.org/x/term v0.37.0
11+
gopkg.in/yaml.v3 v3.0.1
1112
)
1213

1314
require (
@@ -33,5 +34,4 @@ require (
3334
golang.org/x/oauth2 v0.30.0 // indirect
3435
golang.org/x/sys v0.38.0 // indirect
3536
golang.org/x/text v0.3.8 // indirect
36-
gopkg.in/yaml.v3 v3.0.1 // indirect
3737
)

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
6868
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
6969
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
7070
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
71+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
7172
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7273
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7374
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

main.go

Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ func main() {
305305

306306
// Importers mode - check file impact
307307
if *importersMode != "" {
308-
runImportersMode(absRoot, *importersMode)
308+
runImportersMode(absRoot, *importersMode, *jsonMode)
309309
return
310310
}
311311

@@ -407,13 +407,20 @@ func runDepsMode(absRoot, root string, jsonMode bool, diffRef string, changedFil
407407
} else {
408408
analyses, err = scanForDepsWithHint(root)
409409
if err != nil {
410-
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
411-
fmt.Fprintln(os.Stderr, "")
412-
fmt.Fprintln(os.Stderr, "The --deps feature requires ast-grep. Install it with:")
413-
fmt.Fprintln(os.Stderr, " brew install ast-grep # macOS/Linux (installs as 'sg')")
414-
fmt.Fprintln(os.Stderr, " cargo install ast-grep # via Rust (installs as 'ast-grep')")
415-
fmt.Fprintln(os.Stderr, " pipx install ast-grep # via Python (installs as 'ast-grep')")
416-
fmt.Fprintln(os.Stderr, "")
410+
if errors.Is(err, scanner.ErrAstGrepNotFound) {
411+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
412+
fmt.Fprintln(os.Stderr, "")
413+
fmt.Fprintln(os.Stderr, "The --deps feature requires ast-grep. Install it with:")
414+
fmt.Fprintln(os.Stderr, " brew install ast-grep # macOS/Linux (installs as 'sg')")
415+
fmt.Fprintln(os.Stderr, " cargo install ast-grep # installs as 'ast-grep'")
416+
fmt.Fprintln(os.Stderr, " pipx install ast-grep # installs as 'ast-grep'")
417+
fmt.Fprintln(os.Stderr, " python3 -m pip install ast-grep-cli")
418+
fmt.Fprintln(os.Stderr, "")
419+
fmt.Fprintln(os.Stderr, "Standard release tarballs ship codemap without the ast-grep binary.")
420+
fmt.Fprintln(os.Stderr, "Use a codemap-full archive for self-contained CI installs, or install ast-grep separately.")
421+
} else {
422+
fmt.Fprintf(os.Stderr, "Error scanning dependencies: %v\n", err)
423+
}
417424
os.Exit(1)
418425
}
419426
externalDeps = scanner.ReadExternalDeps(absRoot)
@@ -529,11 +536,10 @@ func runWatchMode(root string, verbose bool) {
529536
fmt.Printf(" Events logged: %d\n", len(events))
530537
}
531538

532-
func runImportersMode(root, file string) {
539+
func buildImportersReport(root, file string) (scanner.ImportersReport, error) {
533540
fg, err := scanner.BuildFileGraph(root)
534541
if err != nil {
535-
fmt.Fprintf(os.Stderr, "Error building file graph: %v\n", err)
536-
os.Exit(1)
542+
return scanner.ImportersReport{}, err
537543
}
538544

539545
// Handle absolute paths - convert to relative
@@ -544,8 +550,41 @@ func runImportersMode(root, file string) {
544550
}
545551

546552
importers := fg.Importers[file]
553+
imports := fg.Imports[file]
554+
report := scanner.ImportersReport{
555+
Root: root,
556+
Mode: "importers",
557+
File: file,
558+
Importers: append([]string(nil), importers...),
559+
Imports: append([]string(nil), imports...),
560+
ImporterCount: len(importers),
561+
IsHub: len(importers) >= 3,
562+
}
563+
564+
for _, imp := range imports {
565+
if fg.IsHub(imp) {
566+
report.HubImports = append(report.HubImports, imp)
567+
}
568+
}
569+
570+
return report, nil
571+
}
572+
573+
func runImportersMode(root, file string, jsonMode bool) {
574+
report, err := buildImportersReport(root, file)
575+
if err != nil {
576+
fmt.Fprintf(os.Stderr, "Error building file graph: %v\n", err)
577+
os.Exit(1)
578+
}
579+
580+
if jsonMode {
581+
_ = json.NewEncoder(os.Stdout).Encode(report)
582+
return
583+
}
584+
585+
importers := report.Importers
547586
if len(importers) >= 3 {
548-
fmt.Printf("⚠️ HUB FILE: %s\n", file)
587+
fmt.Printf("⚠️ HUB FILE: %s\n", report.File)
549588
fmt.Printf(" Imported by %d files - changes have wide impact!\n", len(importers))
550589
fmt.Println()
551590
fmt.Println(" Dependents:")
@@ -557,26 +596,18 @@ func runImportersMode(root, file string) {
557596
fmt.Printf(" • %s\n", imp)
558597
}
559598
} else if len(importers) > 0 {
560-
fmt.Printf("📍 File: %s\n", file)
599+
fmt.Printf("📍 File: %s\n", report.File)
561600
fmt.Printf(" Imported by %d file(s)\n", len(importers))
562601
for _, imp := range importers {
563602
fmt.Printf(" • %s\n", imp)
564603
}
565604
}
566605

567-
// Also check if this file imports any hubs
568-
imports := fg.Imports[file]
569-
var hubImports []string
570-
for _, imp := range imports {
571-
if fg.IsHub(imp) {
572-
hubImports = append(hubImports, imp)
573-
}
574-
}
575-
if len(hubImports) > 0 {
606+
if len(report.HubImports) > 0 {
576607
if len(importers) == 0 {
577-
fmt.Printf("📍 File: %s\n", file)
608+
fmt.Printf("📍 File: %s\n", report.File)
578609
}
579-
fmt.Printf(" Imports %d hub(s): %s\n", len(hubImports), strings.Join(hubImports, ", "))
610+
fmt.Printf(" Imports %d hub(s): %s\n", len(report.HubImports), strings.Join(report.HubImports, ", "))
580611
}
581612
}
582613

0 commit comments

Comments
 (0)