diff --git a/internal/cli/cluster.go b/internal/cli/cluster.go index 021327b3..cea1b271 100644 --- a/internal/cli/cluster.go +++ b/internal/cli/cluster.go @@ -193,6 +193,7 @@ func runClusterInfo( p.Field("expires in", "never (static-secret fallback)") } + p.Newline() p.Successf("Ready for `tracebloc dataset push`.") return nil } diff --git a/internal/cli/dataset.go b/internal/cli/dataset.go index 54c69f18..80f7eba2 100644 --- a/internal/cli/dataset.go +++ b/internal/cli/dataset.go @@ -615,6 +615,7 @@ contributors train against it without ever seeing the raw files.`)) // 8. Dry-run stop. Acknowledged success, plus a reminder of the // live-only steps (stage + ingest) the customer just skipped. if a.DryRun { + a.Printer.Newline() a.Printer.Successf("Dry-run complete — your dataset and cluster check out; nothing was created.") a.Printer.Hintf("A real run continues with step 3 (stage your files) and step 4 (run the ingestion).") if a.OutputJSON { diff --git a/internal/cli/dataset_rm.go b/internal/cli/dataset_rm.go index 60d009a6..a7c398f7 100644 --- a/internal/cli/dataset_rm.go +++ b/internal/cli/dataset_rm.go @@ -151,6 +151,7 @@ undone — re-pushing the data is the only way back.`) // 5. Dry-run stop. if a.DryRun { + p.Newline() p.Successf("Dry-run — nothing was deleted.") return nil } @@ -193,6 +194,7 @@ undone — re-pushing the data is the only way back.`) return &exitError{code: 7, err: fmt.Errorf("teardown failed: %w", err)} } + p.Newline() p.Successf("Deleted %s.%s and %d PVC path(s).", plan.Database, plan.Table, len(res.RemovedPaths)) p.Infof("The dataset's catalog metadata will be removed automatically — no further action needed.") return nil diff --git a/internal/cli/root.go b/internal/cli/root.go index 736873af..f1047d66 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -92,11 +92,12 @@ what's planned next.`, p := printerFor(cmd) p.Banner("tracebloc", "interactive data ingestion for your cluster") p.Section("Get started") - p.Infof("tracebloc dataset push ./data — stage + ingest a dataset (guided if you omit flags)") + p.Infof("tracebloc dataset push — stage + ingest a dataset interactively (or use --help to see flags)") p.Infof("tracebloc dataset list — list datasets ingested in the cluster") p.Infof("tracebloc dataset rm — delete a pushed dataset (its table + files)") p.Infof("tracebloc cluster info — check the CLI can reach your cluster") p.Infof("tracebloc ingest validate f.yaml — validate an ingest.yaml locally") + p.Newline() p.Hintf("Add --help to any command for the full flag list.") return nil } diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 68b72198..e87122f1 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -163,6 +163,13 @@ func (p *Printer) PromptHint(format string, a ...any) { p.out("\n %s\n", p.paint(fmt.Sprintf(format, a...), color.FgCyan)) } +// Newline emits a single blank line. Used to detach a closing line or +// call-to-action (e.g. cluster info's "Ready" line, a dry-run / deletion +// result) from the field block above it, so it doesn't get lost. +func (p *Printer) Newline() { + p.out("\n") +} + // PromptHeader prints a bold-white label before a user-input prompt. func (p *Printer) PromptHeader(label string) { p.out("\n %s\n", p.paint(label, color.Bold, color.FgWhite))