a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
169 lines
5.2 KiB
Go
169 lines
5.2 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"sort"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/zzet/gortex/internal/daemon"
|
|
"github.com/zzet/gortex/internal/progress"
|
|
"github.com/zzet/gortex/internal/tui"
|
|
)
|
|
|
|
var statusIndex string
|
|
|
|
var statusCmd = &cobra.Command{
|
|
Use: "status",
|
|
Short: "Show the daemon's index status: tracked repos, node/edge counts, workspaces",
|
|
RunE: runStatus,
|
|
}
|
|
|
|
func init() {
|
|
statusCmd.Flags().StringVar(&statusIndex, "index", ".", "repository path (informational; status reports the daemon's whole tracked set)")
|
|
rootCmd.AddCommand(statusCmd)
|
|
}
|
|
|
|
func runStatus(cmd *cobra.Command, _ []string) error {
|
|
if !daemon.IsRunning() {
|
|
return fmt.Errorf("no gortex daemon is running — start it with `gortex daemon start --detach`, then track a repo with `gortex track <path>`")
|
|
}
|
|
return runStatusViaDaemon(cmd)
|
|
}
|
|
|
|
// runStatusViaDaemon prints the daemon's aggregate status across every
|
|
// tracked repository.
|
|
func runStatusViaDaemon(cmd *cobra.Command) error {
|
|
c, err := daemon.Dial(daemon.Handshake{Mode: daemon.ModeControl, ClientName: "cli"})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer c.Close()
|
|
resp, err := c.Control(daemon.ControlStatus, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !resp.OK {
|
|
return fmt.Errorf("status rejected: %s %s", resp.ErrorCode, resp.ErrorMsg)
|
|
}
|
|
var st daemon.StatusResponse
|
|
if err := json.Unmarshal(resp.Result, &st); err != nil {
|
|
return fmt.Errorf("parse status: %w", err)
|
|
}
|
|
|
|
w := cmd.OutOrStdout()
|
|
emitStatusBanner(cmd.ErrOrStderr(), "daemon view", "Aggregate status across every tracked repository.")
|
|
|
|
if progress.IsTTY(cmd.ErrOrStderr()) && !noProgress {
|
|
emitDaemonStatusCard(cmd.ErrOrStderr(), st)
|
|
} else {
|
|
fmt.Fprintf(w, "daemon %s (pid %d, uptime %s)\n",
|
|
st.Version, st.PID, time.Duration(st.UptimeSeconds)*time.Second)
|
|
fmt.Fprintf(w, "sessions %d\n", st.Sessions)
|
|
if st.MemoryBytes > 0 {
|
|
fmt.Fprintf(w, "memory %.1f MB\n", float64(st.MemoryBytes)/(1024*1024))
|
|
}
|
|
if st.ToolPreset != "" {
|
|
line := st.ToolPreset
|
|
if st.ToolPresetMode != "" {
|
|
line += "/" + st.ToolPresetMode
|
|
}
|
|
if st.LearnedTools > 0 {
|
|
line += fmt.Sprintf(" (+%d learned)", st.LearnedTools)
|
|
}
|
|
fmt.Fprintf(w, "tools %s\n", line)
|
|
}
|
|
}
|
|
if len(st.TrackedRepos) == 0 {
|
|
fmt.Fprintln(w, "tracked repos: (none — run `gortex track <path>` to add one)")
|
|
return nil
|
|
}
|
|
|
|
// One-line workspace rollup so the workspace boundary state is visible
|
|
// in the compact view: a single sentence when every repo is its own
|
|
// default workspace, an explicit count when the user grouped some.
|
|
if len(st.Workspaces) > 0 {
|
|
multiRepoWorkspaces := 0
|
|
for _, ws := range st.Workspaces {
|
|
if len(ws.Repos) > 1 {
|
|
multiRepoWorkspaces++
|
|
}
|
|
}
|
|
if multiRepoWorkspaces == 0 {
|
|
fmt.Fprintf(w, "workspaces %d (one per repo, default)\n", len(st.Workspaces))
|
|
} else {
|
|
fmt.Fprintf(w, "workspaces %d (%d shared, %d default singletons)\n",
|
|
len(st.Workspaces), multiRepoWorkspaces, len(st.Workspaces)-multiRepoWorkspaces)
|
|
}
|
|
}
|
|
|
|
fmt.Fprintln(w, "tracked repos:")
|
|
sort.Slice(st.TrackedRepos, func(i, j int) bool {
|
|
return st.TrackedRepos[i].Prefix < st.TrackedRepos[j].Prefix
|
|
})
|
|
// Workspace column only appears when the user has explicit
|
|
// declarations — otherwise every row just repeats the repo prefix.
|
|
showWS := false
|
|
for _, r := range st.TrackedRepos {
|
|
if r.Workspace != "" && r.Workspace != r.Prefix {
|
|
showWS = true
|
|
break
|
|
}
|
|
}
|
|
for _, r := range st.TrackedRepos {
|
|
if showWS {
|
|
ws := r.Workspace
|
|
if r.WorkspaceProject != "" && r.WorkspaceProject != ws {
|
|
ws = ws + "/" + r.WorkspaceProject
|
|
}
|
|
fmt.Fprintf(w, " %-24s [%-12s] %s (%d files, %d nodes, %d edges)\n",
|
|
r.Prefix, ws, r.Path, r.Files, r.Nodes, r.Edges)
|
|
} else {
|
|
fmt.Fprintf(w, " %-24s %s (%d files, %d nodes, %d edges)\n",
|
|
r.Prefix, r.Path, r.Files, r.Nodes, r.Edges)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// emitStatusBanner prints the shared status banner on stderr (so stdout
|
|
// remains a clean key/value stream for scripts piping `gortex status`).
|
|
// TTY-only; non-TTY callers see nothing on stderr.
|
|
func emitStatusBanner(w io.Writer, mode, subtitle string) {
|
|
if !progress.IsTTY(w) || noProgress {
|
|
return
|
|
}
|
|
banner := tui.Banner{
|
|
Title: "gortex status — " + mode,
|
|
Subtitle: subtitle,
|
|
}.Render()
|
|
fmt.Fprintln(w)
|
|
fmt.Fprintln(w, banner)
|
|
fmt.Fprintln(w)
|
|
}
|
|
|
|
// emitDaemonStatusCard renders the daemon view as a styled header card:
|
|
// pid, uptime, version, sessions, memory in a single stat strip.
|
|
func emitDaemonStatusCard(w io.Writer, st daemon.StatusResponse) {
|
|
uptime := (time.Duration(st.UptimeSeconds) * time.Second).Truncate(time.Second)
|
|
stats := []string{
|
|
progress.Stat(st.Version, "version", progress.StatNeutral),
|
|
progress.Stat(strconv.Itoa(st.PID), "pid", progress.StatNeutral),
|
|
progress.Stat(uptime.String(), "uptime", progress.StatGood),
|
|
progress.Stat(strconv.Itoa(st.Sessions), "sessions", progress.StatNeutral),
|
|
}
|
|
if st.MemoryBytes > 0 {
|
|
stats = append(stats,
|
|
progress.Stat(fmt.Sprintf("%.1f MB", float64(st.MemoryBytes)/(1024*1024)),
|
|
"memory", progress.StatNeutral))
|
|
}
|
|
fmt.Fprintln(w, " "+progress.StyleOK.Render("●")+" "+
|
|
progress.StyleStrong.Render("daemon up"))
|
|
fmt.Fprintln(w, " "+progress.StatStrip(stats...))
|
|
fmt.Fprintln(w)
|
|
}
|