diff --git a/CHANGELOG.md b/CHANGELOG.md index 59713cb..b9eac61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,3 +69,10 @@ Initial implementation. - Interactive screens now gate on a real terminal check (ioctl) instead of "is a character device": `traust-cli skills < /dev/null` used to open the picker and hang forever, because /dev/null is a character device. +- Cobra help output respects the terminal width: flag usages wrap with + continuations aligned under each description (FlagUsagesWrapped at the + measured width), Long descriptions re-wrap, the trailing use-hint breaks + on narrow terminals, cobra's built-in completion commands are hidden from + the command list (still invocable), over-long command summaries were + shortened, and the init --forge default URL moved out of the flag line + (an unbreakable token that could not fit pflag's description column). diff --git a/cmd/env.go b/cmd/env.go index de564a2..19d9ad2 100644 --- a/cmd/env.go +++ b/cmd/env.go @@ -12,7 +12,7 @@ var envJSON bool var envCmd = &cobra.Command{ Use: "env", - Short: "Print the environment the CLI injects into child processes", + Short: "Print the injected child environment", Long: `The CLI does not write to your shell profile. It injects the harness venv PATH, TRAUST_CONFIG_HOME and HARNESS_BIN_DIR into every process it spawns. This command prints that environment — source it if you want the diff --git a/cmd/init.go b/cmd/init.go index 823cf38..2057ae1 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -46,7 +46,7 @@ TRAUST_CONFIG_HOME and the venv PATH into the processes it spawns. Pass func init() { rootCmd.AddCommand(initCmd) initCmd.Flags().StringVar(&initOpts.workspace, "workspace", "", "managed workspace root (default ~/.traust)") - initCmd.Flags().StringVar(&initOpts.forge, "forge", bootstrap.DefaultForge, "git URL to clone traust from") + initCmd.Flags().StringVar(&initOpts.forge, "forge", "", "git URL to clone traust from (default: the public traust repository)") initCmd.Flags().StringVar(&initOpts.ref, "ref", "latest", "tag or branch to pin (or \"latest\" for the newest stable tag)") initCmd.Flags().StringVar(&initOpts.configHome, "config-home", "", "TRAUST_CONFIG_HOME (default /config)") initCmd.Flags().StringVar(&initOpts.toolchain, "toolchain", "none", "scanner toolchain profile (none, secure-code-audit, vuln-scan, triage, …)") @@ -56,6 +56,14 @@ func init() { initCmd.Flags().BoolVarP(&initOpts.yes, "yes", "y", false, "non-interactive: accept all defaults") } +// defaultForge is the wizard's placeholder for the forge URL. +func defaultForge() string { + if initOpts.forge != "" { + return initOpts.forge + } + return bootstrap.DefaultForge +} + func runInit(cmd *cobra.Command, args []string) { ctx := context.Background() styles := theme.Default() @@ -79,7 +87,7 @@ func runInit(cmd *cobra.Command, args []string) { if defaultHarness == "" { defaultHarness = "claude" } - answers, err := tui.RunInitWizard(workspace, initOpts.forge, detected, defaultHarness) + answers, err := tui.RunInitWizard(workspace, defaultForge(), detected, defaultHarness) if err != nil { fatalf(1, "init wizard: %v", err) } diff --git a/cmd/ledger.go b/cmd/ledger.go index a3366b1..044903b 100644 --- a/cmd/ledger.go +++ b/cmd/ledger.go @@ -40,7 +40,7 @@ var ledgerWhoamiCmd = &cobra.Command{ var ledgerFindingsCmd = &cobra.Command{ Use: "findings [layer-id]", - Short: "List resolved finding dispositions (one layer or all)", + Short: "List resolved finding dispositions", Run: func(cmd *cobra.Command, args []string) { v := newLedgerViews() layer := "" diff --git a/cmd/root.go b/cmd/root.go index b43a513..bf57e47 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -37,6 +37,7 @@ journals.`, // Execute runs the root command. func Execute() { + installWrappedUsageTemplate() if err := rootCmd.Execute(); err != nil { styles := theme.Default() fmt.Fprintln(os.Stderr, styles.Err.Render("error: "+err.Error())) @@ -44,6 +45,48 @@ func Execute() { } } +// installWrappedUsageTemplate makes cobra's help respect the terminal width. +// The default template emits flag usages unwrapped, so long descriptions +// hard-wrap at the terminal's right edge with continuations at column zero +// while the flags themselves are indented. pflag already knows how to wrap +// (FlagUsagesWrapped aligns continuations under each description); the +// template is patched in place rather than copied, so it tracks cobra's +// default. Long descriptions and the trailing use-hint are wrapped the same +// way, and cobra's built-in completion commands are hidden (their summaries +// are too long for narrow terminals and they remain invocable). Templates +// inherit from the root, so every subcommand is covered. +func installWrappedUsageTemplate() { + width := tui.TerminalWidth() + if width < 60 { + width = 60 + } + tmpl := rootCmd.UsageTemplate() + patched := strings.ReplaceAll(tmpl, + ".FlagUsages | trimTrailingWhitespaces", + fmt.Sprintf(".FlagUsagesWrapped %d | trimTrailingWhitespaces", width)) + if width < 78 { + patched = strings.ReplaceAll(patched, + "for more information about a command.", + "for more\ninformation about a command.") + } + if patched != tmpl { + rootCmd.SetUsageTemplate(patched) + } + rootCmd.CompletionOptions.HiddenDefaultCmd = true + wrapCommandLongs(rootCmd, width) +} + +// wrapCommandLongs re-wraps every command's Long description to the current +// width; cobra renders them verbatim. +func wrapCommandLongs(cmd *cobra.Command, width int) { + if cmd.Long != "" { + cmd.Long = tui.Wrap(cmd.Long, width) + } + for _, sub := range cmd.Commands() { + wrapCommandLongs(sub, width) + } +} + func init() { rootCmd.SetVersionTemplate(theme.Default().App.Render(" traust-cli ") + " {{.Version}}\n") } diff --git a/cmd/run.go b/cmd/run.go index e2b66cc..6e96ae3 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -32,7 +32,7 @@ var runFlags struct { var runCmd = &cobra.Command{ Use: "run [args…]", - Short: "Execute a skill headlessly through your agent harness", + Short: "Run a skill headlessly through your harness", Long: `Composes the skill's SKILL.md into a prompt, resolves the model-floor default from the registry, and drives the harness of your choice in headless mode. The agent always runs from the harness root — never inside the audit