66 "io"
77 "net/http"
88 "os"
9+ "os/exec"
910 "regexp"
11+ "runtime"
1012 "sort"
1113 "strings"
1214 "sync"
@@ -31,33 +33,39 @@ type ChangelogEntry struct {
3133type Source struct {
3234 Name string
3335 DisplayName string
36+ URL string
3437 FetchFunc func () ([]ChangelogEntry , error )
3538}
3639
3740var sources = map [string ]Source {
3841 "claude" : {
3942 Name : "claude" ,
4043 DisplayName : "Claude Code" ,
44+ URL : "https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md" ,
4145 FetchFunc : fetchClaudeChangelog ,
4246 },
4347 "codex" : {
4448 Name : "codex" ,
4549 DisplayName : "OpenAI Codex" ,
50+ URL : "https://github.com/openai/codex/releases" ,
4651 FetchFunc : fetchCodexChangelog ,
4752 },
4853 "opencode" : {
4954 Name : "opencode" ,
5055 DisplayName : "OpenCode" ,
56+ URL : "https://github.com/sst/opencode/releases" ,
5157 FetchFunc : fetchOpenCodeChangelog ,
5258 },
5359 "gemini" : {
5460 Name : "gemini" ,
5561 DisplayName : "Gemini CLI" ,
62+ URL : "https://github.com/google-gemini/gemini-cli/releases" ,
5663 FetchFunc : fetchGeminiChangelog ,
5764 },
5865 "copilot" : {
5966 Name : "copilot" ,
6067 DisplayName : "GitHub Copilot CLI" ,
68+ URL : "https://github.com/github/copilot-cli/blob/main/changelog.md" ,
6169 FetchFunc : fetchCopilotChangelog ,
6270 },
6371}
@@ -83,22 +91,40 @@ func main() {
8391 }
8492
8593 if args [0 ] == "latest" {
86- var jsonOutput bool
94+ var jsonOutput , webOpen bool
8795 for i := 1 ; i < len (args ); i ++ {
88- if args [i ] == "-json" || args [i ] == "--json" {
96+ switch args [i ] {
97+ case "-json" , "--json" :
8998 jsonOutput = true
99+ case "-web" , "--web" :
100+ webOpen = true
90101 }
91102 }
103+ if webOpen {
104+ for _ , src := range sources {
105+ openBrowser (src .URL )
106+ }
107+ os .Exit (0 )
108+ }
92109 runLatestCommand (jsonOutput )
93110 os .Exit (0 )
94111 }
95112
96113 if args [0 ] == "status" {
97- var jsonOutput bool
114+ var jsonOutput , webOpen bool
98115 for i := 1 ; i < len (args ); i ++ {
99- if args [i ] == "-json" || args [i ] == "--json" {
116+ switch args [i ] {
117+ case "-json" , "--json" :
100118 jsonOutput = true
119+ case "-web" , "--web" :
120+ webOpen = true
121+ }
122+ }
123+ if webOpen {
124+ for _ , src := range sources {
125+ openBrowser (src .URL )
101126 }
127+ os .Exit (0 )
102128 }
103129 runStatusCommand (jsonOutput )
104130 os .Exit (0 )
@@ -115,7 +141,7 @@ func main() {
115141 os .Exit (1 )
116142 }
117143
118- var jsonOutput , mdOutput , listVersions bool
144+ var jsonOutput , mdOutput , listVersions , webOpen bool
119145 var targetVersion string
120146
121147 for i := 1 ; i < len (args ); i ++ {
@@ -126,6 +152,8 @@ func main() {
126152 mdOutput = true
127153 case "-list" , "--list" :
128154 listVersions = true
155+ case "-web" , "--web" :
156+ webOpen = true
129157 case "-version" , "--version" :
130158 if i + 1 < len (args ) {
131159 targetVersion = args [i + 1 ]
@@ -134,6 +162,11 @@ func main() {
134162 }
135163 }
136164
165+ if webOpen {
166+ openBrowser (source .URL )
167+ os .Exit (0 )
168+ }
169+
137170 entries , err := source .FetchFunc ()
138171 if err != nil {
139172 fmt .Fprintf (os .Stderr , "Error fetching changelog: %v\n " , err )
@@ -196,6 +229,7 @@ func printUsage() {
196229 fmt .Fprintf (os .Stderr , " -md Output as markdown\n " )
197230 fmt .Fprintf (os .Stderr , " -list List all versions\n " )
198231 fmt .Fprintf (os .Stderr , " -version <ver> Get specific version\n " )
232+ fmt .Fprintf (os .Stderr , " -web Open changelog source in browser\n " )
199233 fmt .Fprintf (os .Stderr , " -v, --version Show aic version\n " )
200234 fmt .Fprintf (os .Stderr , " -h, --help Show this help\n \n " )
201235 fmt .Fprintf (os .Stderr , "Examples:\n " )
@@ -205,6 +239,8 @@ func printUsage() {
205239 fmt .Fprintf (os .Stderr , " aic gemini -version 0.21.0 # Specific Gemini version\n " )
206240 fmt .Fprintf (os .Stderr , " aic latest # All releases in last 24h\n " )
207241 fmt .Fprintf (os .Stderr , " aic status # Status table of all tools\n " )
242+ fmt .Fprintf (os .Stderr , " aic claude -web # Open Claude changelog in browser\n " )
243+ fmt .Fprintf (os .Stderr , " aic status -web # Open all changelogs in browser\n " )
208244}
209245
210246func runLatestCommand (jsonOutput bool ) {
@@ -450,6 +486,21 @@ func truncateString(s string, maxLen int) string {
450486 return s [:maxLen - 3 ] + "..."
451487}
452488
489+ func openBrowser (url string ) {
490+ var cmd * exec.Cmd
491+ switch runtime .GOOS {
492+ case "darwin" :
493+ cmd = exec .Command ("open" , url )
494+ case "windows" :
495+ cmd = exec .Command ("cmd" , "/c" , "start" , url )
496+ default : // linux, freebsd, etc.
497+ cmd = exec .Command ("xdg-open" , url )
498+ }
499+ if err := cmd .Start (); err != nil {
500+ fmt .Fprintf (os .Stderr , "Error opening browser: %v\n " , err )
501+ }
502+ }
503+
453504func formatRelativeTime (t time.Time ) string {
454505 if t .IsZero () {
455506 return "-"
0 commit comments