mirror of
https://github.com/tiennm99/awesome-coding-agents.git
synced 2026-05-14 03:52:39 +00:00
237d7acd6a
Go updater that fetches AI agent coding tool repo stats via GitHub GraphQL (batched, one query), sorts by star count, appends a daily snapshot to data/history.jsonl, and regenerates README.md from templates/readme.tmpl. Daily workflow at .github/workflows/update.yml refreshes rankings and commits changes. Seed list in data/agents.yml covers 19 tracked repos.
97 lines
2.0 KiB
Go
97 lines
2.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
)
|
|
|
|
type Row struct {
|
|
Rank int
|
|
NameWithOwner string
|
|
URL string
|
|
Stars int
|
|
Delta7d int
|
|
HasDelta bool
|
|
Language string
|
|
PushedAt string
|
|
Description string
|
|
Category string
|
|
}
|
|
|
|
func renderReadme(tmplPath, outPath string, stats []Stat, deltas map[string]int) error {
|
|
rows := make([]Row, len(stats))
|
|
for i, s := range stats {
|
|
delta, has := deltas[s.NameWithOwner]
|
|
rows[i] = Row{
|
|
Rank: i + 1,
|
|
NameWithOwner: s.NameWithOwner,
|
|
URL: s.URL,
|
|
Stars: s.Stars,
|
|
Delta7d: delta,
|
|
HasDelta: has,
|
|
Language: s.Language,
|
|
PushedAt: s.PushedAt.Format("2006-01-02"),
|
|
Description: sanitizeCell(s.Description),
|
|
Category: s.Category,
|
|
}
|
|
}
|
|
|
|
funcs := template.FuncMap{
|
|
"formatDelta": func(d int, has bool) string {
|
|
if !has {
|
|
return "—"
|
|
}
|
|
if d > 0 {
|
|
return fmt.Sprintf("+%d", d)
|
|
}
|
|
if d < 0 {
|
|
return fmt.Sprintf("%d", d)
|
|
}
|
|
return "0"
|
|
},
|
|
"formatStars": func(n int) string {
|
|
switch {
|
|
case n < 1000:
|
|
return fmt.Sprintf("%d", n)
|
|
case n < 1_000_000:
|
|
return fmt.Sprintf("%.1fk", float64(n)/1000)
|
|
default:
|
|
return fmt.Sprintf("%.1fM", float64(n)/1_000_000)
|
|
}
|
|
},
|
|
}
|
|
|
|
tmpl, err := template.New("").Funcs(funcs).ParseFiles(tmplPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
f, err := os.Create(outPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
name := tmplPath
|
|
if i := strings.LastIndex(tmplPath, "/"); i >= 0 {
|
|
name = tmplPath[i+1:]
|
|
}
|
|
|
|
return tmpl.ExecuteTemplate(f, name, map[string]any{
|
|
"Rows": rows,
|
|
"UpdatedAt": time.Now().UTC().Format("2006-01-02 15:04 UTC"),
|
|
"Total": len(rows),
|
|
})
|
|
}
|
|
|
|
// sanitizeCell escapes pipe and newline characters so descriptions stay in one table cell.
|
|
func sanitizeCell(s string) string {
|
|
s = strings.ReplaceAll(s, "|", "\\|")
|
|
s = strings.ReplaceAll(s, "\n", " ")
|
|
s = strings.ReplaceAll(s, "\r", " ")
|
|
return strings.TrimSpace(s)
|
|
}
|