diff --git a/pkg/ai/cmd_ai.go b/pkg/ai/cmd_ai.go index f54e02d7..136006b0 100644 --- a/pkg/ai/cmd_ai.go +++ b/pkg/ai/cmd_ai.go @@ -17,20 +17,20 @@ var ( // Task priority/status styles from shared var ( - taskPriorityHighStyle = cli.PriorityHighStyle - taskPriorityMediumStyle = cli.PriorityMediumStyle - taskPriorityLowStyle = cli.PriorityLowStyle - taskStatusPendingStyle = cli.StatusPendingStyle - taskStatusInProgressStyle = cli.StatusRunningStyle - taskStatusCompletedStyle = cli.StatusSuccessStyle - taskStatusBlockedStyle = cli.StatusErrorStyle + taskPriorityHighStyle = cli.NewStyle().Foreground(cli.ColourRed500) + taskPriorityMediumStyle = cli.NewStyle().Foreground(cli.ColourAmber500) + taskPriorityLowStyle = cli.NewStyle().Foreground(cli.ColourBlue400) + taskStatusPendingStyle = cli.DimStyle + taskStatusInProgressStyle = cli.NewStyle().Foreground(cli.ColourBlue500) + taskStatusCompletedStyle = cli.SuccessStyle + taskStatusBlockedStyle = cli.ErrorStyle ) // Task-specific styles (aliases to shared where possible) var ( taskIDStyle = cli.TitleStyle // Bold + blue taskTitleStyle = cli.ValueStyle // Light gray - taskLabelStyle = cli.AccentLabelStyle // Violet for labels + taskLabelStyle = cli.NewStyle().Foreground(cli.ColourViolet500) // Violet for labels ) // AddAgenticCommands adds the agentic task management commands to the ai command. diff --git a/pkg/ai/cmd_git.go b/pkg/ai/cmd_git.go index 9efbcfdd..6a7a17cb 100644 --- a/pkg/ai/cmd_git.go +++ b/pkg/ai/cmd_git.go @@ -80,7 +80,7 @@ var taskCommitCmd = &cli.Command{ } if !hasChanges { - cli.Text("No changes to commit") + cli.Println("No changes to commit") return nil } diff --git a/pkg/ai/cmd_tasks.go b/pkg/ai/cmd_tasks.go index 3ee62d48..db82111b 100644 --- a/pkg/ai/cmd_tasks.go +++ b/pkg/ai/cmd_tasks.go @@ -134,7 +134,7 @@ var taskCmd = &cli.Command{ taskClaim = true // Auto-select implies claiming } else { if taskID == "" { - return cli.Err(i18n.T("cmd.ai.task.id_required")) + return cli.Err("%s", i18n.T("cmd.ai.task.id_required")) } task, err = client.GetTask(ctx, taskID) @@ -157,7 +157,7 @@ var taskCmd = &cli.Command{ } if taskClaim && task.Status == agentic.StatusPending { - cli.Line("") + cli.Blank() cli.Print("%s %s\n", dimStyle.Render(">>"), i18n.T("cmd.ai.task.claiming")) claimedTask, err := client.ClaimTask(ctx, task.ID) @@ -215,12 +215,12 @@ func printTaskList(tasks []agentic.Task) { cli.Text(line) } - cli.Line("") + cli.Blank() cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.tasks.hint"))) } func printTaskDetails(task *agentic.Task) { - cli.Line("") + cli.Blank() cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.id")), taskIDStyle.Render(task.ID)) cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.title")), taskTitleStyle.Render(task.Title)) cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.priority")), formatTaskPriority(task.Priority)) @@ -240,12 +240,12 @@ func printTaskDetails(task *agentic.Task) { cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.created")), formatAge(task.CreatedAt)) - cli.Line("") + cli.Blank() cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.description"))) cli.Text(task.Description) if len(task.Files) > 0 { - cli.Line("") + cli.Blank() cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.ai.label.related_files"))) for _, f := range task.Files { cli.Print(" - %s\n", f) @@ -253,7 +253,7 @@ func printTaskDetails(task *agentic.Task) { } if len(task.Dependencies) > 0 { - cli.Line("") + cli.Blank() cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.ai.label.blocked_by")), strings.Join(task.Dependencies, ", ")) } } @@ -286,4 +286,4 @@ func formatTaskStatus(s agentic.TaskStatus) string { default: return dimStyle.Render(string(s)) } -} +} \ No newline at end of file diff --git a/pkg/ai/cmd_updates.go b/pkg/ai/cmd_updates.go index df533207..91fd7ad7 100644 --- a/pkg/ai/cmd_updates.go +++ b/pkg/ai/cmd_updates.go @@ -34,7 +34,7 @@ var taskUpdateCmd = &cli.Command{ taskID := args[0] if taskUpdateStatus == "" && taskUpdateProgress == 0 && taskUpdateNotes == "" { - return cli.Err(i18n.T("cmd.ai.task_update.flag_required")) + return cli.Err("%s", i18n.T("cmd.ai.task_update.flag_required")) } cfg, err := agentic.LoadConfig("") diff --git a/pkg/ci/cmd_changelog.go b/pkg/ci/cmd_changelog.go index fb435223..6904cb81 100644 --- a/pkg/ci/cmd_changelog.go +++ b/pkg/ci/cmd_changelog.go @@ -2,30 +2,56 @@ package ci import ( "os" + "os/exec" + "strings" "github.com/host-uk/core/pkg/cli" + "github.com/host-uk/core/pkg/i18n" "github.com/host-uk/core/pkg/release" ) -// runChangelog generates and prints a changelog. func runChangelog(fromRef, toRef string) error { - projectDir, err := os.Getwd() + cwd, err := os.Getwd() if err != nil { - return cli.WrapVerb(err, "get", "working directory") + return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err) } - // Load config for changelog settings - cfg, err := release.LoadConfig(projectDir) - if err != nil { - return cli.WrapVerb(err, "load", "config") + // Auto-detect refs if not provided + if fromRef == "" || toRef == "" { + tag, err := latestTag(cwd) + if err == nil { + if fromRef == "" { + fromRef = tag + } + if toRef == "" { + toRef = "HEAD" + } + } else { + // No tags, use initial commit? Or just HEAD? + cli.Text(i18n.T("cmd.ci.changelog.no_tags")) + return nil + } } + cli.Print("%s %s..%s\n\n", releaseDimStyle.Render(i18n.T("cmd.ci.changelog.generating")), fromRef, toRef) + // Generate changelog - changelog, err := release.GenerateWithConfig(projectDir, fromRef, toRef, &cfg.Changelog) + changelog, err := release.Generate(cwd, fromRef, toRef) if err != nil { - return cli.WrapVerb(err, "generate", "changelog") + return cli.Err("%s: %w", i18n.T("i18n.fail.generate", "changelog"), err) } cli.Text(changelog) + return nil } + +func latestTag(dir string) (string, error) { + cmd := exec.Command("git", "describe", "--tags", "--abbrev=0") + cmd.Dir = dir + out, err := cmd.Output() + if err != nil { + return "", err + } + return strings.TrimSpace(string(out)), nil +} \ No newline at end of file diff --git a/pkg/ci/cmd_ci.go b/pkg/ci/cmd_ci.go index 6a31fcbe..097ceed8 100644 --- a/pkg/ci/cmd_ci.go +++ b/pkg/ci/cmd_ci.go @@ -8,7 +8,7 @@ import ( // Style aliases from shared var ( - releaseHeaderStyle = cli.RepoNameStyle + releaseHeaderStyle = cli.RepoStyle releaseSuccessStyle = cli.SuccessStyle releaseErrorStyle = cli.ErrorStyle releaseDimStyle = cli.DimStyle diff --git a/pkg/ci/cmd_init.go b/pkg/ci/cmd_init.go index 015c3542..cb3b50d6 100644 --- a/pkg/ci/cmd_init.go +++ b/pkg/ci/cmd_init.go @@ -1,74 +1,43 @@ package ci import ( - "bufio" "os" - "path/filepath" - "strings" "github.com/host-uk/core/pkg/cli" "github.com/host-uk/core/pkg/i18n" "github.com/host-uk/core/pkg/release" ) -// runCIReleaseInit creates a release configuration interactively. func runCIReleaseInit() error { - projectDir, err := os.Getwd() + cwd, err := os.Getwd() if err != nil { - return cli.WrapVerb(err, "get", "working directory") + return cli.Err("%s: %w", i18n.T("i18n.fail.get", "working directory"), err) } - // Check if config already exists - if release.ConfigExists(projectDir) { - cli.Print("%s %s %s\n", - releaseDimStyle.Render(i18n.Label("note")), - i18n.T("cmd.ci.init.config_exists"), - release.ConfigPath(projectDir)) + cli.Print("%s %s\n\n", releaseDimStyle.Render(i18n.Label("init")), i18n.T("cmd.ci.init.initializing")) - reader := bufio.NewReader(os.Stdin) - cli.Print("%s", i18n.T("cmd.ci.init.overwrite_prompt")) - response, _ := reader.ReadString('\n') - response = strings.TrimSpace(strings.ToLower(response)) - if response != "y" && response != "yes" { - cli.Text(i18n.T("common.prompt.abort")) - return nil - } + // Check if already initialized + if release.ConfigExists(cwd) { + cli.Text(i18n.T("cmd.ci.init.already_initialized")) + return nil } - cli.Print("%s %s\n", releaseHeaderStyle.Render(i18n.T("cmd.ci.label.init")), i18n.T("cmd.ci.init.creating")) - cli.Line("") - - reader := bufio.NewReader(os.Stdin) - - // Project name - defaultName := filepath.Base(projectDir) - cli.Print("%s [%s]: ", i18n.T("cmd.ci.init.project_name"), defaultName) - name, _ := reader.ReadString('\n') - name = strings.TrimSpace(name) - if name == "" { - name = defaultName - } - - // Repository - cli.Print("%s ", i18n.T("cmd.ci.init.github_repo")) - repo, _ := reader.ReadString('\n') - repo = strings.TrimSpace(repo) - - // Create config + // Create release config cfg := release.DefaultConfig() - cfg.Project.Name = name - cfg.Project.Repository = repo - - // Write config - if err := release.WriteConfig(cfg, projectDir); err != nil { - return cli.WrapVerb(err, "write", "config") + if err := release.WriteConfig(cfg, cwd); err != nil { + return cli.Err("%s: %w", i18n.T("i18n.fail.create", "config"), err) } - cli.Line("") - cli.Print("%s %s %s\n", - releaseSuccessStyle.Render(i18n.T("i18n.done.pass")), - i18n.T("cmd.ci.init.config_written"), - release.ConfigPath(projectDir)) + cli.Blank() + cli.Print("%s %s\n", releaseSuccessStyle.Render("v"), i18n.T("cmd.ci.init.created_config")) + + // Templates init removed as functionality not exposed + + cli.Blank() + + cli.Text(i18n.T("cmd.ci.init.next_steps")) + cli.Print(" %s\n", i18n.T("cmd.ci.init.edit_config")) + cli.Print(" %s\n", i18n.T("cmd.ci.init.run_ci")) return nil -} +} \ No newline at end of file diff --git a/pkg/ci/cmd_publish.go b/pkg/ci/cmd_publish.go index 1e41bf30..23b0c4ef 100644 --- a/pkg/ci/cmd_publish.go +++ b/pkg/ci/cmd_publish.go @@ -51,7 +51,7 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error { } else { cli.Print(" %s\n", releaseSuccessStyle.Render(i18n.T("cmd.ci.go_for_launch"))) } - cli.Line("") + cli.Blank() // Check for publishers if len(cfg.Publishers) == 0 { @@ -66,7 +66,7 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error { } // Print summary - cli.Line("") + cli.Blank() cli.Print("%s %s\n", releaseSuccessStyle.Render(i18n.T("i18n.done.pass")), i18n.T("cmd.ci.publish_completed")) cli.Print(" %s %s\n", i18n.Label("version"), releaseValueStyle.Render(rel.Version)) cli.Print(" %s %d\n", i18n.T("cmd.ci.label.artifacts"), len(rel.Artifacts)) diff --git a/pkg/cli/ansi_test.go b/pkg/cli/ansi_test.go new file mode 100644 index 00000000..75ace2c0 --- /dev/null +++ b/pkg/cli/ansi_test.go @@ -0,0 +1,20 @@ +package cli + +import ( + "strings" + "testing" +) + +func TestAnsiStyle_Render(t *testing.T) { + s := NewStyle().Bold().Foreground("#ff0000") + got := s.Render("test") + if got == "test" { + t.Error("Expected styled output") + } + if !strings.Contains(got, "test") { + t.Error("Output should contain text") + } + if !strings.Contains(got, "[1m") { + t.Error("Output should contain bold code") + } +} diff --git a/pkg/cli/glyph_test.go b/pkg/cli/glyph_test.go new file mode 100644 index 00000000..d43c0be2 --- /dev/null +++ b/pkg/cli/glyph_test.go @@ -0,0 +1,23 @@ +package cli + +import "testing" + +func TestGlyph(t *testing.T) { + UseUnicode() + if Glyph(":check:") != "✓" { + t.Errorf("Expected ✓, got %s", Glyph(":check:")) + } + + UseASCII() + if Glyph(":check:") != "[OK]" { + t.Errorf("Expected [OK], got %s", Glyph(":check:")) + } +} + +func TestCompileGlyphs(t *testing.T) { + UseUnicode() + got := compileGlyphs("Status: :check:") + if got != "Status: ✓" { + t.Errorf("Expected Status: ✓, got %s", got) + } +} diff --git a/pkg/cli/layout_test.go b/pkg/cli/layout_test.go new file mode 100644 index 00000000..a49504e2 --- /dev/null +++ b/pkg/cli/layout_test.go @@ -0,0 +1,25 @@ +package cli + +import "testing" + +func TestParseVariant(t *testing.T) { + c, err := ParseVariant("H[LC]F") + if err != nil { + t.Fatalf("Parse failed: %v", err) + } + if _, ok := c.regions[RegionHeader]; !ok { + t.Error("Expected Header region") + } + if _, ok := c.regions[RegionFooter]; !ok { + t.Error("Expected Footer region") + } + + hSlot := c.regions[RegionHeader] + if hSlot.child == nil { + t.Error("Header should have child layout") + } else { + if _, ok := hSlot.child.regions[RegionLeft]; !ok { + t.Error("Child should have Left region") + } + } +} diff --git a/pkg/cli/output.go b/pkg/cli/output.go index 30983d1b..b785e967 100644 --- a/pkg/cli/output.go +++ b/pkg/cli/output.go @@ -30,6 +30,11 @@ func Println(format string, args ...any) { fmt.Println(compileGlyphs(fmt.Sprintf(format, args...))) } +// Text prints arguments like fmt.Println, but handling glyphs. +func Text(args ...any) { + fmt.Println(compileGlyphs(fmt.Sprint(args...))) +} + // Success prints a success message with checkmark (green). func Success(msg string) { fmt.Println(SuccessStyle.Render(Glyph(":check:") + " " + msg)) diff --git a/pkg/dev/cmd_ci.go b/pkg/dev/cmd_ci.go index 39554cac..660b2df0 100644 --- a/pkg/dev/cmd_ci.go +++ b/pkg/dev/cmd_ci.go @@ -17,7 +17,7 @@ import ( var ( ciSuccessStyle = cli.SuccessStyle ciFailureStyle = cli.ErrorStyle - ciPendingStyle = cli.StatusWarningStyle + ciPendingStyle = cli.WarningStyle ciSkippedStyle = cli.DimStyle ) @@ -144,7 +144,7 @@ func runCI(registryPath string, branch string, failedOnly bool) error { } // Print summary - cli.Line("") + cli.Blank() cli.Print("%s", i18n.T("cmd.dev.ci.repos_checked", map[string]interface{}{"Count": len(repoList)})) if success > 0 { cli.Print(" * %s", ciSuccessStyle.Render(i18n.T("cmd.dev.ci.passing", map[string]interface{}{"Count": success}))) @@ -158,8 +158,8 @@ func runCI(registryPath string, branch string, failedOnly bool) error { if len(noCI) > 0 { cli.Print(" * %s", ciSkippedStyle.Render(i18n.T("cmd.dev.ci.no_ci", map[string]interface{}{"Count": len(noCI)}))) } - cli.Line("") - cli.Line("") + cli.Blank() + cli.Blank() // Filter if needed displayRuns := allRuns @@ -179,7 +179,7 @@ func runCI(registryPath string, branch string, failedOnly bool) error { // Print errors if len(fetchErrors) > 0 { - cli.Line("") + cli.Blank() for _, err := range fetchErrors { cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err) } diff --git a/pkg/dev/cmd_commit.go b/pkg/dev/cmd_commit.go index 554ed6db..3eae0449 100644 --- a/pkg/dev/cmd_commit.go +++ b/pkg/dev/cmd_commit.go @@ -120,19 +120,19 @@ func runCommit(registryPath string, all bool) error { if s.Staged > 0 { cli.Print("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged}))) } - cli.Line("") + cli.Blank() } // Confirm unless --all if !all { - cli.Line("") + cli.Blank() if !cli.Confirm(i18n.T("cmd.dev.confirm_claude_commit")) { cli.Text(i18n.T("cli.aborted")) return nil } } - cli.Line("") + cli.Blank() // Commit each dirty repo var succeeded, failed int @@ -146,7 +146,7 @@ func runCommit(registryPath string, all bool) error { cli.Print(" %s %s\n", successStyle.Render("v"), i18n.T("cmd.dev.committed")) succeeded++ } - cli.Line("") + cli.Blank() } // Summary @@ -154,7 +154,7 @@ func runCommit(registryPath string, all bool) error { if failed > 0 { cli.Print(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed}))) } - cli.Line("") + cli.Blank() return nil } @@ -200,18 +200,18 @@ func runCommitSingleRepo(ctx context.Context, repoPath string, all bool) error { if s.Staged > 0 { cli.Print("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged}))) } - cli.Line("") + cli.Blank() // Confirm unless --all if !all { - cli.Line("") + cli.Blank() if !cli.Confirm(i18n.T("cmd.dev.confirm_claude_commit")) { cli.Text(i18n.T("cli.aborted")) return nil } } - cli.Line("") + cli.Blank() // Commit if err := claudeCommit(ctx, repoPath, repoName, ""); err != nil { @@ -220,4 +220,4 @@ func runCommitSingleRepo(ctx context.Context, repoPath string, all bool) error { } cli.Print(" %s %s\n", successStyle.Render("v"), i18n.T("cmd.dev.committed")) return nil -} +} \ No newline at end of file diff --git a/pkg/dev/cmd_dev.go b/pkg/dev/cmd_dev.go index 411df3f9..a595c888 100644 --- a/pkg/dev/cmd_dev.go +++ b/pkg/dev/cmd_dev.go @@ -45,14 +45,14 @@ var ( dimStyle = cli.DimStyle valueStyle = cli.ValueStyle headerStyle = cli.HeaderStyle - repoNameStyle = cli.RepoNameStyle + repoNameStyle = cli.RepoStyle ) // Table styles for status display (extends shared styles with cell padding) var ( - dirtyStyle = cli.GitDirtyStyle.Padding(0, 1) - aheadStyle = cli.GitAheadStyle.Padding(0, 1) - cleanStyle = cli.GitCleanStyle.Padding(0, 1) + dirtyStyle = cli.NewStyle().Foreground(cli.ColourRed500) + aheadStyle = cli.NewStyle().Foreground(cli.ColourAmber500) + cleanStyle = cli.NewStyle().Foreground(cli.ColourGreen500) ) // AddDevCommands registers the 'dev' command and all subcommands. diff --git a/pkg/dev/cmd_health.go b/pkg/dev/cmd_health.go index 693aaaba..018d6a5e 100644 --- a/pkg/dev/cmd_health.go +++ b/pkg/dev/cmd_health.go @@ -2,8 +2,10 @@ package dev import ( "context" + "fmt" "os" "sort" + "strings" "github.com/host-uk/core/pkg/cli" "github.com/host-uk/core/pkg/git" @@ -116,9 +118,9 @@ func runHealth(registryPath string, verbose bool) error { } // Print summary line - cli.Line("") + cli.Blank() printHealthSummary(totalRepos, dirtyRepos, aheadRepos, behindRepos, errorRepos) - cli.Line("") + cli.Blank() // Verbose output if verbose { @@ -134,7 +136,7 @@ func runHealth(registryPath string, verbose bool) error { if len(errorRepos) > 0 { cli.Print("%s %s\n", errorStyle.Render(i18n.T("cmd.dev.health.errors_label")), formatRepoList(errorRepos)) } - cli.Line("") + cli.Blank() } return nil @@ -142,36 +144,36 @@ func runHealth(registryPath string, verbose bool) error { func printHealthSummary(total int, dirty, ahead, behind, errors []string) { parts := []string{ - cli.StatusPart(total, i18n.T("cmd.dev.health.repos"), cli.ValueStyle), + statusPart(total, i18n.T("cmd.dev.health.repos"), cli.ValueStyle), } // Dirty status if len(dirty) > 0 { - parts = append(parts, cli.StatusPart(len(dirty), i18n.T("common.status.dirty"), cli.WarningStyle)) + parts = append(parts, statusPart(len(dirty), i18n.T("common.status.dirty"), cli.WarningStyle)) } else { - parts = append(parts, cli.StatusText(i18n.T("cmd.dev.status.clean"), cli.SuccessStyle)) + parts = append(parts, statusText(i18n.T("cmd.dev.status.clean"), cli.SuccessStyle)) } // Push status if len(ahead) > 0 { - parts = append(parts, cli.StatusPart(len(ahead), i18n.T("cmd.dev.health.to_push"), cli.ValueStyle)) + parts = append(parts, statusPart(len(ahead), i18n.T("cmd.dev.health.to_push"), cli.ValueStyle)) } else { - parts = append(parts, cli.StatusText(i18n.T("common.status.synced"), cli.SuccessStyle)) + parts = append(parts, statusText(i18n.T("common.status.synced"), cli.SuccessStyle)) } // Pull status if len(behind) > 0 { - parts = append(parts, cli.StatusPart(len(behind), i18n.T("cmd.dev.health.to_pull"), cli.WarningStyle)) + parts = append(parts, statusPart(len(behind), i18n.T("cmd.dev.health.to_pull"), cli.WarningStyle)) } else { - parts = append(parts, cli.StatusText(i18n.T("common.status.up_to_date"), cli.SuccessStyle)) + parts = append(parts, statusText(i18n.T("common.status.up_to_date"), cli.SuccessStyle)) } // Errors (only if any) if len(errors) > 0 { - parts = append(parts, cli.StatusPart(len(errors), i18n.T("cmd.dev.health.errors"), cli.ErrorStyle)) + parts = append(parts, statusPart(len(errors), i18n.T("cmd.dev.health.errors"), cli.ErrorStyle)) } - cli.Text(cli.StatusLine(parts...)) + cli.Text(statusLine(parts...)) } func formatRepoList(reposList []string) string { @@ -191,3 +193,15 @@ func joinRepos(reposList []string) string { } return result } + +func statusPart(count int, label string, style *cli.AnsiStyle) string { + return style.Render(fmt.Sprintf("%d %s", count, label)) +} + +func statusText(text string, style *cli.AnsiStyle) string { + return style.Render(text) +} + +func statusLine(parts ...string) string { + return strings.Join(parts, " | ") +} diff --git a/pkg/dev/cmd_impact.go b/pkg/dev/cmd_impact.go index 373b50cc..22a499df 100644 --- a/pkg/dev/cmd_impact.go +++ b/pkg/dev/cmd_impact.go @@ -12,8 +12,8 @@ import ( // Impact-specific styles (aliases to shared) var ( impactDirectStyle = cli.ErrorStyle - impactIndirectStyle = cli.StatusWarningStyle - impactSafeStyle = cli.StatusSuccessStyle + impactIndirectStyle = cli.WarningStyle + impactSafeStyle = cli.SuccessStyle ) // Impact command flags @@ -89,12 +89,12 @@ func runImpact(registryPath string, repoName string) error { sort.Strings(indirect) // Print results - cli.Line("") + cli.Blank() cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.impact.analysis_for")), repoNameStyle.Render(repoName)) if repo.Description != "" { cli.Print("%s\n", dimStyle.Render(repo.Description)) } - cli.Line("") + cli.Blank() if len(allAffected) == 0 { cli.Print("%s %s\n", impactSafeStyle.Render("v"), i18n.T("cmd.dev.impact.no_dependents", map[string]interface{}{"Name": repoName})) @@ -115,7 +115,7 @@ func runImpact(registryPath string, repoName string) error { } cli.Print(" %s%s\n", d, desc) } - cli.Line("") + cli.Blank() } // Indirect dependents @@ -132,7 +132,7 @@ func runImpact(registryPath string, repoName string) error { } cli.Print(" %s%s\n", d, desc) } - cli.Line("") + cli.Blank() } // Summary diff --git a/pkg/dev/cmd_issues.go b/pkg/dev/cmd_issues.go index 425fab33..62a871f3 100644 --- a/pkg/dev/cmd_issues.go +++ b/pkg/dev/cmd_issues.go @@ -20,7 +20,7 @@ var ( issueNumberStyle = cli.TitleStyle issueTitleStyle = cli.ValueStyle issueLabelStyle = cli.WarningStyle - issueAssigneeStyle = cli.StatusSuccessStyle + issueAssigneeStyle = cli.SuccessStyle issueAgeStyle = cli.DimStyle ) @@ -135,7 +135,7 @@ func runIssues(registryPath string, limit int, assignee string) error { // Print issues if len(allIssues) == 0 { - cli.Text(i18n.T("cmd.dev.issues.no_issues")) + cli.Text(i18n.T("cmd.dev.issues.no_issues")) return nil } @@ -147,7 +147,7 @@ func runIssues(registryPath string, limit int, assignee string) error { // Print any errors if len(fetchErrors) > 0 { - cli.Line("") + cli.Blank() for _, err := range fetchErrors { cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err) } @@ -226,5 +226,5 @@ func printIssue(issue GitHubIssue) { age := cli.FormatAge(issue.CreatedAt) line += " " + issueAgeStyle.Render(age) - cli.Text(line) + cli.Text(line) } diff --git a/pkg/dev/cmd_pull.go b/pkg/dev/cmd_pull.go index 4bfca5d8..af003fcf 100644 --- a/pkg/dev/cmd_pull.go +++ b/pkg/dev/cmd_pull.go @@ -115,7 +115,7 @@ func runPull(registryPath string, all bool) error { dimStyle.Render(i18n.T("cmd.dev.pull.commits_behind", map[string]interface{}{"Count": s.Behind})), ) } - cli.Line("") + cli.Blank() } // Pull each repo @@ -134,12 +134,12 @@ func runPull(registryPath string, all bool) error { } // Summary - cli.Line("") + cli.Blank() cli.Print("%s", successStyle.Render(i18n.T("cmd.dev.pull.done_pulled", map[string]interface{}{"Count": succeeded}))) if failed > 0 { cli.Print(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed}))) } - cli.Line("") + cli.Blank() return nil } diff --git a/pkg/dev/cmd_push.go b/pkg/dev/cmd_push.go index ed0a1ed5..11b753d0 100644 --- a/pkg/dev/cmd_push.go +++ b/pkg/dev/cmd_push.go @@ -119,14 +119,14 @@ func runPush(registryPath string, force bool) error { // Confirm unless --force if !force { - cli.Line("") + cli.Blank() if !cli.Confirm(i18n.T("cmd.dev.push.confirm_push", map[string]interface{}{"Commits": totalCommits, "Repos": len(aheadRepos)})) { cli.Text(i18n.T("cli.aborted")) return nil } } - cli.Line("") + cli.Blank() // Push sequentially (SSH passphrase needs interaction) var pushPaths []string @@ -157,10 +157,10 @@ func runPush(registryPath string, force bool) error { // Handle diverged repos - offer to pull and retry if len(divergedRepos) > 0 { - cli.Line("") + cli.Blank() cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help")) if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) { - cli.Line("") + cli.Blank() for _, r := range divergedRepos { cli.Print(" %s %s...\n", dimStyle.Render("↓"), r.Name) if err := git.Pull(ctx, r.Path); err != nil { @@ -180,12 +180,12 @@ func runPush(registryPath string, force bool) error { } // Summary - cli.Line("") + cli.Blank() cli.Print("%s", successStyle.Render(i18n.T("cmd.dev.push.done_pushed", map[string]interface{}{"Count": succeeded}))) if failed > 0 { cli.Print(", %s", errorStyle.Render(i18n.T("common.count.failed", map[string]interface{}{"Count": failed}))) } - cli.Line("") + cli.Blank() return nil } @@ -222,10 +222,10 @@ func runPushSingleRepo(ctx context.Context, repoPath string, force bool) error { if s.Staged > 0 { cli.Print("%s ", aheadStyle.Render(i18n.T("cmd.dev.staged", map[string]interface{}{"Count": s.Staged}))) } - cli.Line("") - cli.Line("") + cli.Blank() + cli.Blank() if cli.Confirm(i18n.T("cmd.dev.push.uncommitted_changes_commit")) { - cli.Line("") + cli.Blank() // Use edit-enabled commit if only untracked files (may need .gitignore fix) var err error if s.Modified == 0 && s.Staged == 0 && s.Untracked > 0 { @@ -257,24 +257,24 @@ func runPushSingleRepo(ctx context.Context, repoPath string, force bool) error { // Confirm unless --force if !force { - cli.Line("") + cli.Blank() if !cli.Confirm(i18n.T("cmd.dev.push.confirm_push", map[string]interface{}{"Commits": s.Ahead, "Repos": 1})) { cli.Text(i18n.T("cli.aborted")) return nil } } - cli.Line("") + cli.Blank() // Push err := git.Push(ctx, repoPath) if err != nil { if git.IsNonFastForward(err) { cli.Print(" %s %s: %s\n", warningStyle.Render("!"), repoName, i18n.T("cmd.dev.push.diverged")) - cli.Line("") + cli.Blank() cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help")) if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) { - cli.Line("") + cli.Blank() cli.Print(" %s %s...\n", dimStyle.Render("↓"), repoName) if pullErr := git.Pull(ctx, repoPath); pullErr != nil { cli.Print(" %s %s: %s\n", errorStyle.Render("x"), repoName, pullErr) diff --git a/pkg/dev/cmd_reviews.go b/pkg/dev/cmd_reviews.go index 3ba51d74..d37934a9 100644 --- a/pkg/dev/cmd_reviews.go +++ b/pkg/dev/cmd_reviews.go @@ -16,7 +16,7 @@ import ( // PR-specific styles (aliases to shared) var ( - prNumberStyle = cli.PrNumberStyle + prNumberStyle = cli.NumberStyle prTitleStyle = cli.ValueStyle prAuthorStyle = cli.InfoStyle prApprovedStyle = cli.SuccessStyle @@ -162,7 +162,7 @@ func runReviews(registryPath string, author string, showAll bool) error { } } - cli.Line("") + cli.Blank() cli.Print("%s", i18n.T("cmd.dev.reviews.open_prs", map[string]interface{}{"Count": len(allPRs)})) if pending > 0 { cli.Print(" * %s", prPendingStyle.Render(i18n.T("common.count.pending", map[string]interface{}{"Count": pending}))) @@ -173,8 +173,8 @@ func runReviews(registryPath string, author string, showAll bool) error { if changesRequested > 0 { cli.Print(" * %s", prChangesStyle.Render(i18n.T("cmd.dev.reviews.changes_requested", map[string]interface{}{"Count": changesRequested}))) } - cli.Line("") - cli.Line("") + cli.Blank() + cli.Blank() for _, pr := range allPRs { printPR(pr) @@ -182,7 +182,7 @@ func runReviews(registryPath string, author string, showAll bool) error { // Print any errors if len(fetchErrors) > 0 { - cli.Line("") + cli.Blank() for _, err := range fetchErrors { cli.Print("%s %s\n", errorStyle.Render(i18n.Label("error")), err) } diff --git a/pkg/dev/cmd_vm.go b/pkg/dev/cmd_vm.go index e2190132..71a4ac23 100644 --- a/pkg/dev/cmd_vm.go +++ b/pkg/dev/cmd_vm.go @@ -47,15 +47,15 @@ func runVMInstall() error { if d.IsInstalled() { cli.Text(successStyle.Render(i18n.T("cmd.dev.vm.already_installed"))) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.check_updates", map[string]interface{}{"Command": dimStyle.Render("core dev update")})) return nil } cli.Print("%s %s\n", dimStyle.Render(i18n.Label("image")), devops.ImageName()) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.downloading")) - cli.Line("") + cli.Blank() ctx := context.Background() start := time.Now() @@ -71,16 +71,16 @@ func runVMInstall() error { } }) - cli.Line("") // Clear progress line + cli.Blank() // Clear progress line if err != nil { return cli.Wrap(err, "install failed") } elapsed := time.Since(start).Round(time.Second) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.installed_in", map[string]interface{}{"Duration": elapsed})) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.start_with", map[string]interface{}{"Command": dimStyle.Render("core dev boot")})) return nil @@ -131,7 +131,7 @@ func runVMBoot(memory, cpus int, fresh bool) error { opts.Fresh = fresh cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.config_label")), i18n.T("cmd.dev.vm.config_value", map[string]interface{}{"Memory": opts.Memory, "CPUs": opts.CPUs})) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.booting")) ctx := context.Background() @@ -139,9 +139,9 @@ func runVMBoot(memory, cpus int, fresh bool) error { return err } - cli.Line("") + cli.Blank() cli.Text(successStyle.Render(i18n.T("cmd.dev.vm.running"))) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.connect_with", map[string]interface{}{"Command": dimStyle.Render("core dev shell")})) cli.Print("%s %s\n", i18n.T("cmd.dev.vm.ssh_port"), dimStyle.Render("2222")) @@ -216,7 +216,7 @@ func runVMStatus() error { } cli.Text(headerStyle.Render(i18n.T("cmd.dev.vm.status_title"))) - cli.Line("") + cli.Blank() // Installation status if status.Installed { @@ -226,12 +226,12 @@ func runVMStatus() error { } } else { cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.installed_label")), errorStyle.Render(i18n.T("cmd.dev.vm.installed_no"))) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.install_with", map[string]interface{}{"Command": dimStyle.Render("core dev install")})) return nil } - cli.Line("") + cli.Blank() // Running status if status.Running { @@ -243,7 +243,7 @@ func runVMStatus() error { cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.uptime_label")), formatVMUptime(status.Uptime)) } else { cli.Print("%s %s\n", dimStyle.Render(i18n.Label("status")), dimStyle.Render(i18n.T("common.status.stopped"))) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.start_with", map[string]interface{}{"Command": dimStyle.Render("core dev boot")})) } @@ -453,7 +453,7 @@ func runVMUpdate(apply bool) error { ctx := context.Background() cli.Text(i18n.T("common.progress.checking_updates")) - cli.Line("") + cli.Blank() current, latest, hasUpdate, err := d.CheckUpdate(ctx) if err != nil { @@ -462,7 +462,7 @@ func runVMUpdate(apply bool) error { cli.Print("%s %s\n", dimStyle.Render(i18n.Label("current")), valueStyle.Render(current)) cli.Print("%s %s\n", dimStyle.Render(i18n.T("cmd.dev.vm.latest_label")), valueStyle.Render(latest)) - cli.Line("") + cli.Blank() if !hasUpdate { cli.Text(successStyle.Render(i18n.T("cmd.dev.vm.up_to_date"))) @@ -470,7 +470,7 @@ func runVMUpdate(apply bool) error { } cli.Text(warningStyle.Render(i18n.T("cmd.dev.vm.update_available"))) - cli.Line("") + cli.Blank() if !apply { cli.Text(i18n.T("cmd.dev.vm.run_to_update", map[string]interface{}{"Command": dimStyle.Render("core dev update --apply")})) @@ -485,7 +485,7 @@ func runVMUpdate(apply bool) error { } cli.Text(i18n.T("cmd.dev.vm.downloading_update")) - cli.Line("") + cli.Blank() start := time.Now() err = d.Install(ctx, func(downloaded, total int64) { @@ -495,14 +495,14 @@ func runVMUpdate(apply bool) error { } }) - cli.Line("") + cli.Blank() if err != nil { return cli.Wrap(err, "update failed") } elapsed := time.Since(start).Round(time.Second) - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.vm.updated_in", map[string]interface{}{"Duration": elapsed})) return nil diff --git a/pkg/dev/cmd_work.go b/pkg/dev/cmd_work.go index 8ff404f2..2d6ed332 100644 --- a/pkg/dev/cmd_work.go +++ b/pkg/dev/cmd_work.go @@ -106,9 +106,9 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error { // Auto-commit dirty repos if requested if autoCommit && len(dirtyRepos) > 0 { - cli.Line("") + cli.Blank() cli.Print("%s\n", cli.TitleStyle.Render(i18n.T("cmd.dev.commit.committing"))) - cli.Line("") + cli.Blank() for _, s := range dirtyRepos { // PERFORM commit via agentic service @@ -146,7 +146,7 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error { // If status only, we're done if statusOnly { if len(dirtyRepos) > 0 && !autoCommit { - cli.Line("") + cli.Blank() cli.Print("%s\n", dimStyle.Render(i18n.T("cmd.dev.work.use_commit_flag"))) } return nil @@ -154,24 +154,24 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error { // Push repos with unpushed commits if len(aheadRepos) == 0 { - cli.Line("") + cli.Blank() cli.Text(i18n.T("cmd.dev.work.all_up_to_date")) return nil } - cli.Line("") + cli.Blank() cli.Print("%s\n", i18n.T("common.count.repos_unpushed", map[string]interface{}{"Count": len(aheadRepos)})) for _, s := range aheadRepos { cli.Print(" %s: %s\n", s.Name, i18n.T("common.count.commits", map[string]interface{}{"Count": s.Ahead})) } - cli.Line("") + cli.Blank() if !cli.Confirm(i18n.T("cmd.dev.push.confirm")) { cli.Text(i18n.T("cli.aborted")) return nil } - cli.Line("") + cli.Blank() // PERFORM push for each repo var divergedRepos []git.RepoStatus @@ -199,10 +199,10 @@ func runWork(registryPath string, statusOnly, autoCommit bool) error { // Handle diverged repos - offer to pull and retry if len(divergedRepos) > 0 { - cli.Line("") + cli.Blank() cli.Print("%s\n", i18n.T("cmd.dev.push.diverged_help")) if cli.Confirm(i18n.T("cmd.dev.push.pull_and_retry")) { - cli.Line("") + cli.Blank() for _, s := range divergedRepos { cli.Print(" %s %s...\n", dimStyle.Render("↓"), s.Name) diff --git a/pkg/dev/service.go b/pkg/dev/service.go index f39bcee9..54c86f16 100644 --- a/pkg/dev/service.go +++ b/pkg/dev/service.go @@ -73,7 +73,7 @@ func (s *Service) runWork(task TaskWork) error { } if len(paths) == 0 { - cli.Text("No git repositories found") + cli.Println("No git repositories found") return nil } @@ -116,9 +116,9 @@ func (s *Service) runWork(task TaskWork) error { // Auto-commit dirty repos if requested if task.AutoCommit && len(dirtyRepos) > 0 { - cli.Line("") - cli.Text("Committing changes...") - cli.Line("") + cli.Blank() + cli.Println("Committing changes...") + cli.Blank() for _, repo := range dirtyRepos { _, handled, err := s.Core().PERFORM(agentic.TaskCommit{ @@ -156,35 +156,35 @@ func (s *Service) runWork(task TaskWork) error { // If status only, we're done if task.StatusOnly { if len(dirtyRepos) > 0 && !task.AutoCommit { - cli.Line("") - cli.Text("Use --commit flag to auto-commit dirty repos") + cli.Blank() + cli.Println("Use --commit flag to auto-commit dirty repos") } return nil } // Push repos with unpushed commits if len(aheadRepos) == 0 { - cli.Line("") - cli.Text("All repositories are up to date") + cli.Blank() + cli.Println("All repositories are up to date") return nil } - cli.Line("") + cli.Blank() cli.Print("%d repos with unpushed commits:\n", len(aheadRepos)) for _, st := range aheadRepos { cli.Print(" %s: %d commits\n", st.Name, st.Ahead) } - cli.Line("") + cli.Blank() cli.Print("Push all? [y/N] ") var answer string cli.Scanln(&answer) if strings.ToLower(answer) != "y" { - cli.Text("Aborted") + cli.Println("Aborted") return nil } - cli.Line("") + cli.Blank() // Push each repo for _, st := range aheadRepos { @@ -217,7 +217,7 @@ func (s *Service) runStatus(task TaskStatus) error { } if len(paths) == 0 { - cli.Text("No git repositories found") + cli.Println("No git repositories found") return nil } diff --git a/pkg/docs/cmd_docs.go b/pkg/docs/cmd_docs.go index 0d4e20c3..c75687c3 100644 --- a/pkg/docs/cmd_docs.go +++ b/pkg/docs/cmd_docs.go @@ -8,7 +8,7 @@ import ( // Style and utility aliases from shared var ( - repoNameStyle = cli.RepoNameStyle + repoNameStyle = cli.RepoStyle successStyle = cli.SuccessStyle errorStyle = cli.ErrorStyle dimStyle = cli.DimStyle diff --git a/pkg/docs/cmd_list.go b/pkg/docs/cmd_list.go index 994a3875..8df40666 100644 --- a/pkg/docs/cmd_list.go +++ b/pkg/docs/cmd_list.go @@ -42,11 +42,11 @@ func runDocsList(registryPath string) error { for _, repo := range reg.List() { info := scanRepoDocs(repo) - readme := cli.CheckMark(info.Readme != "") - claude := cli.CheckMark(info.ClaudeMd != "") - changelog := cli.CheckMark(info.Changelog != "") + readme := checkMark(info.Readme != "") + claude := checkMark(info.ClaudeMd != "") + changelog := checkMark(info.Changelog != "") - docsDir := cli.CheckMark(false) + docsDir := checkMark(false) if len(info.DocsFiles) > 0 { docsDir = docsFoundStyle.Render(i18n.T("common.count.files", map[string]interface{}{"Count": len(info.DocsFiles)})) } @@ -66,11 +66,18 @@ func runDocsList(registryPath string) error { } } - cli.Line("") + cli.Blank() cli.Print("%s %s\n", - cli.Label(i18n.Label("coverage")), + cli.KeyStyle.Render(i18n.Label("coverage")), i18n.T("cmd.docs.list.coverage_summary", map[string]interface{}{"WithDocs": withDocs, "WithoutDocs": withoutDocs}), ) return nil } + +func checkMark(ok bool) string { + if ok { + return cli.Glyph(":check:") + } + return cli.Glyph(":cross:") +} diff --git a/pkg/docs/cmd_sync.go b/pkg/docs/cmd_sync.go index 2c33ba49..de9e7310 100644 --- a/pkg/docs/cmd_sync.go +++ b/pkg/docs/cmd_sync.go @@ -113,14 +113,14 @@ func runDocsSync(registryPath string, outputDir string, dryRun bool) error { } // Confirm - cli.Line("") + cli.Blank() if !confirm(i18n.T("cmd.docs.sync.confirm")) { cli.Text(i18n.T("common.prompt.abort")) return nil } // Sync docs - cli.Line("") + cli.Blank() var synced int for _, info := range docsInfo { outName := packageOutputName(info.Name) @@ -152,4 +152,4 @@ func runDocsSync(registryPath string, outputDir string, dryRun bool) error { cli.Print("\n%s %s\n", successStyle.Render(i18n.T("i18n.done.sync")), i18n.T("cmd.docs.sync.synced_packages", map[string]interface{}{"Count": synced})) return nil -} +} \ No newline at end of file diff --git a/pkg/doctor/cmd_doctor.go b/pkg/doctor/cmd_doctor.go index c7c646d0..ce38e536 100644 --- a/pkg/doctor/cmd_doctor.go +++ b/pkg/doctor/cmd_doctor.go @@ -44,13 +44,13 @@ func runDoctor(verbose bool) error { ok, version := runCheck(c) if ok { if verbose { - fmt.Println(cli.CheckResult(true, c.name, version)) + fmt.Println(formatCheckResult(true, c.name, version)) } else { - fmt.Println(cli.CheckResult(true, c.name, "")) + fmt.Println(formatCheckResult(true, c.name, "")) } passed++ } else { - fmt.Printf(" %s %s - %s\n", errorStyle.Render(cli.SymbolCross), c.name, c.description) + fmt.Printf(" %s %s - %s\n", errorStyle.Render(cli.Glyph(":cross:")), c.name, c.description) failed++ } } @@ -61,13 +61,13 @@ func runDoctor(verbose bool) error { ok, version := runCheck(c) if ok { if verbose { - fmt.Println(cli.CheckResult(true, c.name, version)) + fmt.Println(formatCheckResult(true, c.name, version)) } else { - fmt.Println(cli.CheckResult(true, c.name, "")) + fmt.Println(formatCheckResult(true, c.name, "")) } passed++ } else { - fmt.Printf(" %s %s - %s\n", dimStyle.Render(cli.SymbolSkip), c.name, dimStyle.Render(c.description)) + fmt.Printf(" %s %s - %s\n", dimStyle.Render(cli.Glyph(":skip:")), c.name, dimStyle.Render(c.description)) optional++ } } @@ -75,16 +75,16 @@ func runDoctor(verbose bool) error { // Check GitHub access fmt.Printf("\n%s\n", i18n.T("cmd.doctor.github")) if checkGitHubSSH() { - fmt.Println(cli.CheckResult(true, i18n.T("cmd.doctor.ssh_found"), "")) + fmt.Println(formatCheckResult(true, i18n.T("cmd.doctor.ssh_found"), "")) } else { - fmt.Printf(" %s %s\n", errorStyle.Render(cli.SymbolCross), i18n.T("cmd.doctor.ssh_missing")) + fmt.Printf(" %s %s\n", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("cmd.doctor.ssh_missing")) failed++ } if checkGitHubCLI() { - fmt.Println(cli.CheckResult(true, i18n.T("cmd.doctor.cli_auth"), "")) + fmt.Println(formatCheckResult(true, i18n.T("cmd.doctor.cli_auth"), "")) } else { - fmt.Printf(" %s %s\n", errorStyle.Render(cli.SymbolCross), i18n.T("cmd.doctor.cli_auth_missing")) + fmt.Printf(" %s %s\n", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("cmd.doctor.cli_auth_missing")) failed++ } @@ -104,3 +104,18 @@ func runDoctor(verbose bool) error { cli.Success(i18n.T("cmd.doctor.ready")) return nil } + +func formatCheckResult(ok bool, name, detail string) string { + check := cli.Check(name) + if ok { + check.Pass() + } else { + check.Fail() + } + if detail != "" { + check.Message(detail) + } else { + check.Message("") + } + return check.String() +} diff --git a/pkg/go/cmd_gotest.go b/pkg/go/cmd_gotest.go index 487cbd34..c34364f5 100644 --- a/pkg/go/cmd_gotest.go +++ b/pkg/go/cmd_gotest.go @@ -75,7 +75,7 @@ func runGoTest(coverage bool, pkg, run string, short, race, jsonOut, verbose boo if !jsonOut { cli.Print("%s %s\n", dimStyle.Render(i18n.Label("test")), i18n.ProgressSubject("run", "tests")) cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("package")), pkg) - cli.Line("") + cli.Blank() } cmd := exec.Command("go", args...) @@ -102,7 +102,7 @@ func runGoTest(coverage bool, pkg, run string, short, race, jsonOut, verbose boo if jsonOut { cli.Print(`{"passed":%d,"failed":%d,"skipped":%d,"coverage":%.1f,"exit_code":%d}`, passed, failed, skipped, cov, cmd.ProcessState.ExitCode()) - cli.Line("") + cli.Blank() return err } @@ -113,15 +113,15 @@ func runGoTest(coverage bool, pkg, run string, short, race, jsonOut, verbose boo // Summary if err == nil { - cli.Print(" %s %s\n", successStyle.Render(cli.SymbolCheck), i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass")) + cli.Print(" %s %s\n", successStyle.Render(cli.Glyph(":check:")), i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass")) } else { - cli.Print(" %s %s, %s\n", errorStyle.Render(cli.SymbolCross), + cli.Print(" %s %s, %s\n", errorStyle.Render(cli.Glyph(":cross:")), i18n.T("i18n.count.test", passed)+" "+i18n.T("i18n.done.pass"), i18n.T("i18n.count.test", failed)+" "+i18n.T("i18n.done.fail")) } if cov > 0 { - cli.Print("\n %s %s\n", cli.ProgressLabel(i18n.Label("coverage")), cli.FormatCoverage(cov)) + cli.Print("\n %s %s\n", cli.KeyStyle.Render(i18n.Label("coverage")), formatCoverage(cov)) } if err == nil { @@ -202,7 +202,7 @@ func addGoCovCommand(parent *cli.Command) { displayPkg = displayPkg[:57] + "..." } cli.Print(" %s %s\n", dimStyle.Render(i18n.Label("package")), displayPkg) - cli.Line("") + cli.Blank() // Run tests with coverage // We need to split pkg into individual arguments if it contains spaces @@ -242,8 +242,8 @@ func addGoCovCommand(parent *cli.Command) { } // Print coverage summary - cli.Line("") - cli.Print(" %s %s\n", cli.ProgressLabel(i18n.Label("total")), cli.FormatCoverage(totalCov)) + cli.Blank() + cli.Print(" %s %s\n", cli.KeyStyle.Render(i18n.Label("total")), formatCoverage(totalCov)) // Generate HTML if requested if covHTML || covOpen { @@ -319,3 +319,13 @@ func findTestPackages(root string) ([]string, error) { } return pkgs, nil } + +func formatCoverage(cov float64) string { + s := fmt.Sprintf("%.1f%%", cov) + if cov >= 80 { + return cli.SuccessStyle.Render(s) + } else if cov >= 50 { + return cli.WarningStyle.Render(s) + } + return cli.ErrorStyle.Render(s) +} diff --git a/pkg/go/cmd_qa.go b/pkg/go/cmd_qa.go index 310c4c6b..b3e4424c 100644 --- a/pkg/go/cmd_qa.go +++ b/pkg/go/cmd_qa.go @@ -118,21 +118,21 @@ func runQAChecks(checkNames []string) error { cli.Print("%s %s\n", cli.DimStyle.Render("→"), i18n.Progress(check.Name)) if err := runCheck(ctx, cwd, check); err != nil { - cli.Print(" %s %s\n", cli.ErrorStyle.Render(cli.SymbolCross), err.Error()) + cli.Print(" %s %s\n", cli.ErrorStyle.Render(cli.Glyph(":cross:")), err.Error()) failed++ } else { - cli.Print(" %s %s\n", cli.SuccessStyle.Render(cli.SymbolCheck), i18n.T("i18n.done.pass")) + cli.Print(" %s %s\n", cli.SuccessStyle.Render(cli.Glyph(":check:")), i18n.T("i18n.done.pass")) passed++ } } // Summary - cli.Line("") + cli.Blank() duration := time.Since(startTime).Round(time.Millisecond) if failed > 0 { cli.Print("%s %s, %s (%s)\n", - cli.ErrorStyle.Render(cli.SymbolCross), + cli.ErrorStyle.Render(cli.Glyph(":cross:")), i18n.T("i18n.count.check", passed)+" "+i18n.T("i18n.done.pass"), i18n.T("i18n.count.check", failed)+" "+i18n.T("i18n.done.fail"), duration) @@ -140,7 +140,7 @@ func runQAChecks(checkNames []string) error { } cli.Print("%s %s (%s)\n", - cli.SuccessStyle.Render(cli.SymbolCheck), + cli.SuccessStyle.Render(cli.Glyph(":check:")), i18n.T("i18n.count.check", passed)+" "+i18n.T("i18n.done.pass"), duration) @@ -228,7 +228,7 @@ func runCheck(ctx context.Context, dir string, check QACheck) error { } if len(output) > 0 { // Show files that need formatting - cli.Print(string(output)) + cli.Text(string(output)) return cli.Err("%s (use --fix)", i18n.T("i18n.fail.format", i18n.T("i18n.count.file", len(output)))) } return nil diff --git a/pkg/pkgcmd/cmd_pkg.go b/pkg/pkgcmd/cmd_pkg.go index de463da9..baf2967b 100644 --- a/pkg/pkgcmd/cmd_pkg.go +++ b/pkg/pkgcmd/cmd_pkg.go @@ -13,7 +13,7 @@ func init() { // Style and utility aliases var ( - repoNameStyle = cli.RepoNameStyle + repoNameStyle = cli.RepoStyle successStyle = cli.SuccessStyle errorStyle = cli.ErrorStyle dimStyle = cli.DimStyle diff --git a/pkg/test/cmd_main.go b/pkg/test/cmd_main.go index 32f64c6d..6b1ac5b2 100644 --- a/pkg/test/cmd_main.go +++ b/pkg/test/cmd_main.go @@ -11,14 +11,14 @@ import ( // Style aliases from shared var ( - testHeaderStyle = cli.RepoNameStyle + testHeaderStyle = cli.RepoStyle testPassStyle = cli.SuccessStyle testFailStyle = cli.ErrorStyle testSkipStyle = cli.WarningStyle testDimStyle = cli.DimStyle - testCovHighStyle = cli.CoverageHighStyle - testCovMedStyle = cli.CoverageMedStyle - testCovLowStyle = cli.CoverageLowStyle + testCovHighStyle = cli.NewStyle().Foreground(cli.ColourGreen500) + testCovMedStyle = cli.NewStyle().Foreground(cli.ColourAmber500) + testCovLowStyle = cli.NewStyle().Foreground(cli.ColourRed500) ) // Flag variables for test command diff --git a/pkg/test/cmd_output.go b/pkg/test/cmd_output.go index 303aa054..8532c1ce 100644 --- a/pkg/test/cmd_output.go +++ b/pkg/test/cmd_output.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "github.com/host-uk/core/pkg/cli" + "github.com/host-uk/core/pkg/i18n" ) @@ -153,7 +153,13 @@ func printCoverageSummary(results testResults) { } func formatCoverage(cov float64) string { - return cli.FormatCoverage(cov) + s := fmt.Sprintf("%.1f%%", cov) + if cov >= 80 { + return testCovHighStyle.Render(s) + } else if cov >= 50 { + return testCovMedStyle.Render(s) + } + return testCovLowStyle.Render(s) } func shortenPackageName(name string) string { diff --git a/pkg/vm/cmd_templates.go b/pkg/vm/cmd_templates.go index fa27e9e1..040939f2 100644 --- a/pkg/vm/cmd_templates.go +++ b/pkg/vm/cmd_templates.go @@ -221,10 +221,10 @@ func buildLinuxKitImage(yamlPath, outputPath string) error { } // Build the image - // linuxkit build -format iso-bios -name + // linuxkit build --format iso-bios --name cmd := exec.Command(lkPath, "build", - "-format", "iso-bios", - "-name", outputPath, + "--format", "iso-bios", + "--name", outputPath, yamlPath) cmd.Stdout = os.Stdout