mirror of
https://github.com/IS1DI/x0gp.git
synced 2026-07-19 05:47:02 +00:00
Add graphify, tests and supabase migrations & config
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
// Package main is the graphify utility. It walks the codebase, parses Go AST,
|
||||
// and outputs a highly compact symbol summary to server/CODEBASE.md.
|
||||
// This is used for token savings and helping developers/AI agents quickly map out imports and structures.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PackageSummary struct {
|
||||
Name string
|
||||
Path string
|
||||
Doc string
|
||||
Structs []string
|
||||
Interfaces []string
|
||||
Functions []string
|
||||
Methods []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootDir := "."
|
||||
outputFile := "CODEBASE.md"
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("# x0gp Go Codebase Map (Generated)\n\n")
|
||||
buf.WriteString("This file contains a compiled map of all packages, structures, interfaces, and public functions in the `server` directory. It is optimized for token-efficiency so AI agents and humans can understand the codebase layout without reading all files.\n\n")
|
||||
|
||||
pkgs := make(map[string]*PackageSummary)
|
||||
fset := token.NewFileSet()
|
||||
|
||||
err := filepath.WalkDir(rootDir, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.IsDir() {
|
||||
// Skip hidden and build dirs
|
||||
name := d.Name()
|
||||
if (name != "." && strings.HasPrefix(name, ".")) || name == "vendor" || name == "bin" || name == "docs" || name == "web" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(d.Name(), ".go") || strings.HasSuffix(d.Name(), "_test.go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
pkgSum, exists := pkgs[dir]
|
||||
if !exists {
|
||||
pkgSum = &PackageSummary{
|
||||
Path: dir,
|
||||
}
|
||||
pkgs[dir] = pkgSum
|
||||
}
|
||||
|
||||
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil // ignore unparseable files defensively
|
||||
}
|
||||
|
||||
pkgSum.Name = node.Name.Name
|
||||
if node.Doc != nil && pkgSum.Doc == "" {
|
||||
pkgSum.Doc = strings.TrimSpace(node.Doc.Text())
|
||||
}
|
||||
|
||||
// Inspect AST for declarations
|
||||
ast.Inspect(node, func(n ast.Node) bool {
|
||||
switch decl := n.(type) {
|
||||
case *ast.GenDecl:
|
||||
for _, spec := range decl.Specs {
|
||||
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||
if !ast.IsExported(typeSpec.Name.Name) {
|
||||
continue
|
||||
}
|
||||
switch typeType := typeSpec.Type.(type) {
|
||||
case *ast.StructType:
|
||||
structDesc := fmt.Sprintf("- **struct %s**", typeSpec.Name.Name)
|
||||
if typeType.Fields != nil && len(typeType.Fields.List) > 0 {
|
||||
fields := []string{}
|
||||
for _, f := range typeType.Fields.List {
|
||||
for _, id := range f.Names {
|
||||
if ast.IsExported(id.Name) {
|
||||
fields = append(fields, id.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(fields) > 0 {
|
||||
structDesc += fmt.Sprintf(" { %s }", strings.Join(fields, ", "))
|
||||
}
|
||||
}
|
||||
pkgSum.Structs = append(pkgSum.Structs, structDesc)
|
||||
case *ast.InterfaceType:
|
||||
methods := []string{}
|
||||
if typeType.Methods != nil {
|
||||
for _, m := range typeType.Methods.List {
|
||||
for _, id := range m.Names {
|
||||
if ast.IsExported(id.Name) {
|
||||
methods = append(methods, id.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pkgSum.Interfaces = append(pkgSum.Interfaces, fmt.Sprintf("- **interface %s** { %s }", typeSpec.Name.Name, strings.Join(methods, ", ")))
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
if !ast.IsExported(decl.Name.Name) {
|
||||
return true
|
||||
}
|
||||
// Format function signature
|
||||
params := []string{}
|
||||
if decl.Type.Params != nil {
|
||||
for _, p := range decl.Type.Params.List {
|
||||
for range p.Names {
|
||||
params = append(params, formatType(p.Type))
|
||||
}
|
||||
if len(p.Names) == 0 {
|
||||
params = append(params, formatType(p.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
results := []string{}
|
||||
if decl.Type.Results != nil {
|
||||
for _, r := range decl.Type.Results.List {
|
||||
for range r.Names {
|
||||
results = append(results, formatType(r.Type))
|
||||
}
|
||||
if len(r.Names) == 0 {
|
||||
results = append(results, formatType(r.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sig := fmt.Sprintf("%s(%s)", decl.Name.Name, strings.Join(params, ", "))
|
||||
if len(results) > 0 {
|
||||
if len(results) == 1 {
|
||||
sig += " " + results[0]
|
||||
} else {
|
||||
sig += " (" + strings.Join(results, ", ") + ")"
|
||||
}
|
||||
}
|
||||
|
||||
if decl.Recv != nil && len(decl.Recv.List) > 0 {
|
||||
recvType := formatType(decl.Recv.List[0].Type)
|
||||
pkgSum.Methods = append(pkgSum.Methods, fmt.Sprintf("- func (%s) %s", recvType, sig))
|
||||
} else {
|
||||
pkgSum.Functions = append(pkgSum.Functions, fmt.Sprintf("- func %s", sig))
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error gathering package summaries: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Write out gathered summaries sorted/grouped by path
|
||||
// (we can sort keys or print them grouped by internal vs cmd)
|
||||
paths := []string{}
|
||||
for p := range pkgs {
|
||||
paths = append(paths, p)
|
||||
}
|
||||
// Simple sort: cmd/ first, then internal/
|
||||
// We want to present it nicely
|
||||
for _, path := range paths {
|
||||
sum := pkgs[path]
|
||||
cleanPath := filepath.ToSlash(path)
|
||||
buf.WriteString(fmt.Sprintf("## Package `%s` (`%s`)\n\n", sum.Name, cleanPath))
|
||||
if sum.Doc != "" {
|
||||
buf.WriteString(fmt.Sprintf("%s\n\n", sum.Doc))
|
||||
}
|
||||
|
||||
if len(sum.Interfaces) > 0 {
|
||||
buf.WriteString("### Interfaces\n")
|
||||
for _, item := range sum.Interfaces {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(sum.Structs) > 0 {
|
||||
buf.WriteString("### Structs\n")
|
||||
for _, item := range sum.Structs {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(sum.Functions) > 0 {
|
||||
buf.WriteString("### Functions\n")
|
||||
for _, item := range sum.Functions {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(sum.Methods) > 0 {
|
||||
buf.WriteString("### Methods\n")
|
||||
for _, item := range sum.Methods {
|
||||
buf.WriteString(item + "\n")
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
buf.WriteString("---\n\n")
|
||||
}
|
||||
|
||||
err = os.WriteFile(outputFile, buf.Bytes(), 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing CODEBASE.md: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Generated %s successfully!\n", outputFile)
|
||||
}
|
||||
|
||||
func formatType(expr ast.Expr) string {
|
||||
switch t := expr.(type) {
|
||||
case *ast.Ident:
|
||||
return t.Name
|
||||
case *ast.StarExpr:
|
||||
return "*" + formatType(t.X)
|
||||
case *ast.SelectorExpr:
|
||||
return formatType(t.X) + "." + t.Sel.Name
|
||||
case *ast.ArrayType:
|
||||
return "[]" + formatType(t.Elt)
|
||||
case *ast.MapType:
|
||||
return "map[" + formatType(t.Key) + "]" + formatType(t.Value)
|
||||
case *ast.InterfaceType:
|
||||
return "interface{}"
|
||||
case *ast.FuncType:
|
||||
return "func"
|
||||
default:
|
||||
return "?"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user