VNFPBXF7OPUPHHDUE6I3VAOOQGSTVGMCZKWVG44ZGO6FN6JVFGIQC X3CP3AMLNXAB7GOVTIIVUT2DKKRDISV67IWGVJ5PKUDLO3VMZ7ZAC Q4JPMGETIYLDXTXX3FCR6MYIZDHSZRL35UB2GJGAAAQN3CMRD2WAC KZKLAINJJWZ64T5MUZT34LJVQIKBTKZ6EJGD7C7TTSSDGCHEDPMAC BZ6KQRYDMP4PWYJRL62XXIUXLTBKEASIKSAJIQPZS6DKDSYKA76QC JZRF7OBJNERB4NIB37RSAF3ZK2A4RBWSCFV5OCRXZYVGPSNOWKTAC JAT3DXOLENZZGXE2NYFF3TVQAQIXMMNYO234ETKQGC2CRHJVZERQC I4CMOMXFJ3Y4AY5LPA7MDLWVHJ674IRFYLXCEXCC5ZARLCWSKCAAC pi -e pi-gondolin.ts
pi -e pi-gondolin.tsguru callers ./pkg/path/funcname### Callgraph Commands1. Full project-internal callgraph (best for understanding code flow):```bashcallgraph -algo rta skraak 2>/dev/null | awk -F'\t' '$1 ~ /^skraak/ && $3 ~ /^skraak/' | grep -v '\$' | sed 's/--static-[0-9]*:[0-9]*-->/ →/; s/--dynamic-[0-9]*:[0-9]*-->/ →/' | sort -u```2. Cross-package calls only (who calls across package boundaries):```bashcallgraph -algo rta skraak 2>/dev/null | awk -F'\t' '$1 ~ /^skraak/ && $3 ~ /^skraak/' | grep -v '\$' | awk -F'\t' '{split($1,a,"."); split($3,b,"."); if(a[1]!=b[1]) print}' | sed 's/--static-[0-9]*:[0-9]*-->/ →/; s/--dynamic-[0-9]*:[0-9]*-->/ →/' | sort -u```3. Trace a specific function — "what does CallsClip call?":```bashcallgraph -algo rta skraak 2>/dev/null | grep 'tools.CallsClip\t' | grep -v '\$'```4. Who calls a specific function — "who uses GenerateSpectrogram?":```bashcallgraph -algo rta skraak 2>/dev/null | grep 'GenerateSpectrogram' | grep -v '\$' | grep -v 'GenerateSpectrogram\t.*→.*GenerateSpectrogram'```5. Dynamic (interface/virtual) calls only — find where interfaces dispatch:```bashcallgraph -algo rta skraak 2>/dev/null | grep 'dynamic' | grep '^skraak'```
// TestPackageDependencies enforces the project's package dependency rules// defined in CLAUDE.md. Packages may only import packages below them://// cmd → tools, tui, utils, db// tools → utils, db// tui → tools, utils// utils → (nothing — leaf package)// db → utilsfunc TestPackageDependencies(t *testing.T) {rules := map[string]map[string]bool{"skraak/cmd": {"skraak/tools": true, "skraak/tui": true, "skraak/utils": true, "skraak/db": true},"skraak/tools": {"skraak/utils": true, "skraak/db": true},"skraak/tui": {"skraak/tools": true, "skraak/utils": true},"skraak/utils": {}, // leaf package — no skraak imports allowed"skraak/db": {"skraak/utils": true},}for pkg, allowed := range rules {imports := getInternalImports(t, pkg)for _, imp := range imports {if !allowed[imp] {t.Errorf("%s imports %s — not allowed by dependency rules", pkg, imp)}}}}// TestNoDirectDBImportInUtils checks that no file in utils/ imports skraak/db.// Fast source-level check using go/packages.func TestNoDirectDBImportInUtils(t *testing.T) {importPaths := getInternalImports(t, "skraak/utils")for _, imp := range importPaths {if imp == "skraak/db" {t.Error("utils/ imports skraak/db — forbidden (utils is the leaf package)")}}}// getInternalImports returns the skraak-internal imports for a package using `go list`.func getInternalImports(t *testing.T, pkg string) []string {t.Helper()out, err := exec.Command("go", "list", "-f", "{{range .Imports}}{{.}}\n{{end}}", pkg).Output()if err != nil {t.Fatalf("go list %s: %v", pkg, err)}var internal []stringfor line := range strings.SplitSeq(string(out), "\n") {line = strings.TrimSpace(line)if strings.HasPrefix(line, "skraak/") || line == "skraak" {internal = append(internal, line)}}return internal}
depguard:rules:# Package dependency rules — see CLAUDE.md "Package Organization"# Packages may only import packages below them in the list:# cmd → tools, tui, utils, db# tools → utils, db# tui → tools, utils# db → utils# utils → (nothing — leaf package)utils:files:- "**/utils/*.go"deny:- pkg: "skraak/cmd"desc: "utils is the leaf package"- pkg: "skraak/tools"desc: "utils is the leaf package"- pkg: "skraak/tui"desc: "utils is the leaf package"- pkg: "skraak/db"desc: "utils is the leaf package"db:files:- "**/db/*.go"deny:- pkg: "skraak/cmd"desc: "db may only import utils"- pkg: "skraak/tools"desc: "db may only import utils"- pkg: "skraak/tui"desc: "db may only import utils"tui:files:- "**/tui/*.go"deny:- pkg: "skraak/cmd"desc: "tui must not import cmd"- pkg: "skraak/db"desc: "tui must not import db"tools:files:- "**/tools/*.go"deny:- pkg: "skraak/cmd"desc: "tools must not import cmd"- pkg: "skraak/tui"desc: "tools must not import tui"