charmbracelet / charmbracelet/fang
Support for custom help sections or hooks to extend help output
- Dominant language
- Go
- Stars
- 1.9k
- Forks
- 47
- PR merge metrics
- No merged PRs in 30d
Description
### Description
## Problem
Applications using Fang may need to display additional custom sections in their help output beyond the standard sections (usage, commands, flags, examples). Currently, there's no way to extend Fang's help rendering to include custom content.
## Use Case
CLI applications often need to display additional information in their help output, such as:
- **Environment Variables** - Documentation for environment variables that affect the CLI
- **Learn More** - Additional resources or tips
- **Feedback/Support** - How to report bugs or request features
- **Configuration** - Where config files are located
- **License/Legal** - License or attribution information
Many applications use Cobra's `Annotations` map to store this custom content:
```go
rootCmd := &cobra.Command{
// ...
Annotations: map[string]string{
"help:environment": heredoc.Doc(`
MYAPP_HOST: Specify the URL of the server
MYAPP_TOKEN: Authentication token for API requests
// ... more env vars
`),
"help:feedback": `
Encountered a bug? Open an issue at github.com/org/repo
`,
},
}
```
With a custom help function, these annotations can be rendered as additional sections. However, Fang's `Execute()` function sets its own help function internally, making it impossible to extend the help output without copying and modifying Fang's internal rendering logic.
## Current Limitations
1. `fang.Execute()` sets `root.SetHelpFunc()` internally and then calls `ExecuteContext()`, which blocks
2. The `helpFn` function in `help.go` is not exported, so it can't be wrapped or extended
3. No hooks or callbacks are provided to inject custom content before/after standard help rendering
4. Copying Fang's entire help rendering logic (~481 lines) creates maintenance burden
## Proposed Solutions
**Option 1: Help Section Hook (Preferred)**
Add an option to provide a callback that runs after Fang's standard help rendering:
```go
fang.Execute(ctx, root,
fang.WithHelpAppender(func(w io.Writer, cmd *cobra.Command, styles Styles) {
// Custom rendering logic here
if envHelp, ok := cmd.Annotations["help:environment"]; ok {
fmt.Fprintln(w, styles.Title.Render("ENVIRONMENT VARIABLES"))
fmt.Fprintln(w, envHelp)
}
}),
)
```
**Option 2: Annotation Support**
Built-in support for rendering custom annotation sections:
```go
fang.Execute(ctx, root,
fang.WithCustomSections(map[string]string{
"help:environment": "ENVIRONMENT VARIABLES",
"help:feedback": "FEEDBACK",
}),
)
```
**Option 3: Export `helpFn` and `makeStyles`**
Export the help rendering function so downstream users can wrap it:
```go
// In fang package
func RenderHelp(c *cobra.Command, w io.Writer, styles Styles) {
// existing helpFn logic
}
// In user code
root.SetHelpFunc(func(c *cobra.Command, args []string) {
w := colorprofile.NewWriter(c.OutOrStdout(), os.Environ())
fang.RenderHelp(c, w, styles)
appendCustomSections(w, c, styles)
})
// Then call root.ExecuteContext() directly instead of fang.Execute()
```
## Benefits
- Allows applications to maintain existing help customizations when migrating to Fang
- Provides flexibility for application-specific help content
- Avoids need to fork or copy Fang's rendering logic
- Maintains Fang's excellent styling while adding custom sections
Contributor guide
Research direction
Start by reading fang.Execute() and help.go, especially the internal helpFn and how the help function is installed before ExecuteContext(). Compare the three proposed extension approaches and inspect existing tests or entry points for help rendering. Done should provide a supported way to add custom help content without copying Fang's rendering logic.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100