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
156 lines
4.3 KiB
Go
156 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/zzet/gortex/internal/agents"
|
|
"github.com/zzet/gortex/internal/progress"
|
|
)
|
|
|
|
// emitAgentSummary writes the post-install / post-init summary as a styled
|
|
// block: totals strip, configured editors with per-action breakdown, a chip
|
|
// line of editors that weren't detected, and a numbered list of next steps.
|
|
// Used by both `gortex install` and `gortex init` for visual consistency.
|
|
func emitAgentSummary(w io.Writer, results []*agents.Result, opts agents.ApplyOpts, nextSteps []string) {
|
|
var detected []*agents.Result
|
|
var notDetected []string
|
|
var totals actionCounts
|
|
for _, r := range results {
|
|
if r == nil {
|
|
continue
|
|
}
|
|
if !r.Detected {
|
|
notDetected = append(notDetected, r.Name)
|
|
continue
|
|
}
|
|
detected = append(detected, r)
|
|
totals.add(r.Files)
|
|
}
|
|
|
|
fmt.Fprintln(w)
|
|
if !totals.zero() || opts.DryRun {
|
|
fmt.Fprintln(w, " "+totalsStrip(totals, opts.DryRun))
|
|
fmt.Fprintln(w)
|
|
}
|
|
|
|
if len(detected) > 0 {
|
|
fmt.Fprintln(w, " "+progress.Heading("configured", strconv.Itoa(len(detected))))
|
|
for _, r := range detected {
|
|
detail := perAdapterDetail(r.Files)
|
|
fmt.Fprintln(w, " "+progress.Row(r.Name, detail, 14))
|
|
}
|
|
fmt.Fprintln(w)
|
|
}
|
|
|
|
// Non-fatal problems an adapter continued past (e.g. a profile config
|
|
// that failed to write) — surfaced here rather than buried in stderr.
|
|
var warned []*agents.Result
|
|
for _, r := range detected {
|
|
if len(r.Warnings) > 0 {
|
|
warned = append(warned, r)
|
|
}
|
|
}
|
|
if len(warned) > 0 {
|
|
fmt.Fprintln(w, " "+progress.Heading("warnings"))
|
|
for _, r := range warned {
|
|
for _, msg := range r.Warnings {
|
|
fmt.Fprintln(w, " "+progress.Row(r.Name, msg, 14))
|
|
}
|
|
}
|
|
fmt.Fprintln(w)
|
|
}
|
|
|
|
if len(notDetected) > 0 {
|
|
names := progress.SortStrings(notDetected)
|
|
fmt.Fprintln(w, " "+progress.Heading("not detected", strconv.Itoa(len(notDetected))))
|
|
fmt.Fprintln(w, " "+progress.Chips(names, 0))
|
|
fmt.Fprintln(w)
|
|
}
|
|
|
|
if len(nextSteps) > 0 {
|
|
fmt.Fprintln(w, " "+progress.Heading("next steps"))
|
|
for i, s := range nextSteps {
|
|
fmt.Fprintln(w, " "+progress.NumberedStep(i+1, s))
|
|
}
|
|
fmt.Fprintln(w)
|
|
}
|
|
}
|
|
|
|
// actionCounts tallies file actions across all adapters.
|
|
type actionCounts struct {
|
|
create, merge, skip, wouldCreate, wouldMerge int
|
|
}
|
|
|
|
func (c *actionCounts) add(files []agents.FileAction) {
|
|
for _, f := range files {
|
|
switch f.Action {
|
|
case agents.ActionCreate:
|
|
c.create++
|
|
case agents.ActionMerge:
|
|
c.merge++
|
|
case agents.ActionSkip:
|
|
c.skip++
|
|
case agents.ActionWouldCreate:
|
|
c.wouldCreate++
|
|
case agents.ActionWouldMerge:
|
|
c.wouldMerge++
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c actionCounts) zero() bool {
|
|
return c.create+c.merge+c.skip+c.wouldCreate+c.wouldMerge == 0
|
|
}
|
|
|
|
// totalsStrip renders e.g. "21 would-create · 2 would-merge · 12 skip (dry-run)".
|
|
func totalsStrip(c actionCounts, dryRun bool) string {
|
|
var stats []string
|
|
if c.wouldCreate > 0 {
|
|
stats = append(stats, progress.Stat(strconv.Itoa(c.wouldCreate), "would-create", progress.StatGood))
|
|
}
|
|
if c.create > 0 {
|
|
stats = append(stats, progress.Stat(strconv.Itoa(c.create), "created", progress.StatGood))
|
|
}
|
|
if c.wouldMerge > 0 {
|
|
stats = append(stats, progress.Stat(strconv.Itoa(c.wouldMerge), "would-merge", progress.StatNeutral))
|
|
}
|
|
if c.merge > 0 {
|
|
stats = append(stats, progress.Stat(strconv.Itoa(c.merge), "merged", progress.StatNeutral))
|
|
}
|
|
if c.skip > 0 {
|
|
stats = append(stats, progress.Stat(strconv.Itoa(c.skip), "skipped", progress.StatNeutral))
|
|
}
|
|
out := progress.StatStrip(stats...)
|
|
if dryRun {
|
|
out += " " + progress.Caption("dry-run — no files written")
|
|
}
|
|
return out
|
|
}
|
|
|
|
// perAdapterDetail compresses a single adapter's file list into a readable
|
|
// "create 3 · merge 1 · skip 2" string, ordered by significance.
|
|
func perAdapterDetail(files []agents.FileAction) string {
|
|
var c actionCounts
|
|
c.add(files)
|
|
parts := []string{}
|
|
if c.wouldCreate > 0 {
|
|
parts = append(parts, fmt.Sprintf("would-create %d", c.wouldCreate))
|
|
}
|
|
if c.create > 0 {
|
|
parts = append(parts, fmt.Sprintf("created %d", c.create))
|
|
}
|
|
if c.wouldMerge > 0 {
|
|
parts = append(parts, fmt.Sprintf("would-merge %d", c.wouldMerge))
|
|
}
|
|
if c.merge > 0 {
|
|
parts = append(parts, fmt.Sprintf("merged %d", c.merge))
|
|
}
|
|
if c.skip > 0 {
|
|
parts = append(parts, fmt.Sprintf("skipped %d", c.skip))
|
|
}
|
|
return strings.Join(parts, " · ")
|
|
}
|