refactor(cli): improve help UX — add examples support, show help on arg errors
- Add EXAMPLES section to leaf and sub help templates (gh CLI style) - Add example to attachment download command - Simplify attachment download description - Show help output when required args are missing (error first, then help) - Replace cobra.ExactArgs with custom exactArgs that prints help on failure Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d6a5ba4d5e
commit
4c0dbbf1c8
10 changed files with 70 additions and 33 deletions
|
|
@ -29,7 +29,7 @@ var agentListCmd = &cobra.Command{
|
|||
var agentGetCmd = &cobra.Command{
|
||||
Use: "get <id>",
|
||||
Short: "Get agent details",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentGet,
|
||||
}
|
||||
|
||||
|
|
@ -42,28 +42,28 @@ var agentCreateCmd = &cobra.Command{
|
|||
var agentUpdateCmd = &cobra.Command{
|
||||
Use: "update <id>",
|
||||
Short: "Update an agent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentUpdate,
|
||||
}
|
||||
|
||||
var agentArchiveCmd = &cobra.Command{
|
||||
Use: "archive <id>",
|
||||
Short: "Archive an agent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentArchive,
|
||||
}
|
||||
|
||||
var agentRestoreCmd = &cobra.Command{
|
||||
Use: "restore <id>",
|
||||
Short: "Restore an archived agent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentRestore,
|
||||
}
|
||||
|
||||
var agentTasksCmd = &cobra.Command{
|
||||
Use: "tasks <id>",
|
||||
Short: "List tasks for an agent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentTasks,
|
||||
}
|
||||
|
||||
|
|
@ -77,14 +77,14 @@ var agentSkillsCmd = &cobra.Command{
|
|||
var agentSkillsListCmd = &cobra.Command{
|
||||
Use: "list <agent-id>",
|
||||
Short: "List skills assigned to an agent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentSkillsList,
|
||||
}
|
||||
|
||||
var agentSkillsSetCmd = &cobra.Command{
|
||||
Use: "set <agent-id>",
|
||||
Short: "Set skills for an agent (replaces all current assignments)",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runAgentSkillsSet,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,13 @@ var attachmentCmd = &cobra.Command{
|
|||
var attachmentDownloadCmd = &cobra.Command{
|
||||
Use: "download <attachment-id>",
|
||||
Short: "Download an attachment to a local file",
|
||||
Long: "Fetches the attachment metadata from the API, then downloads the file using its signed URL. Prints the local file path on success.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Long: "Download an attachment by its ID to a local file.",
|
||||
Example: ` # Download an image attachment to the current directory
|
||||
$ multica attachment download abc123
|
||||
|
||||
# Download to a specific directory
|
||||
$ multica attachment download abc123 -o /tmp/images`,
|
||||
Args: exactArgs(1),
|
||||
RunE: runAttachmentDownload,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ var configSetCmd = &cobra.Command{
|
|||
Use: "set <key> <value>",
|
||||
Short: "Set a CLI configuration value",
|
||||
Long: "Supported keys: server_url, app_url, workspace_id",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Args: exactArgs(2),
|
||||
RunE: runConfigSet,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ var issueListCmd = &cobra.Command{
|
|||
var issueGetCmd = &cobra.Command{
|
||||
Use: "get <id>",
|
||||
Short: "Get issue details",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueGet,
|
||||
}
|
||||
|
||||
|
|
@ -41,21 +41,21 @@ var issueCreateCmd = &cobra.Command{
|
|||
var issueUpdateCmd = &cobra.Command{
|
||||
Use: "update <id>",
|
||||
Short: "Update an issue",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueUpdate,
|
||||
}
|
||||
|
||||
var issueAssignCmd = &cobra.Command{
|
||||
Use: "assign <id>",
|
||||
Short: "Assign an issue to a member or agent",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueAssign,
|
||||
}
|
||||
|
||||
var issueStatusCmd = &cobra.Command{
|
||||
Use: "status <id> <status>",
|
||||
Short: "Change issue status",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Args: exactArgs(2),
|
||||
RunE: runIssueStatus,
|
||||
}
|
||||
|
||||
|
|
@ -69,21 +69,21 @@ var issueCommentCmd = &cobra.Command{
|
|||
var issueCommentListCmd = &cobra.Command{
|
||||
Use: "list <issue-id>",
|
||||
Short: "List comments on an issue",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueCommentList,
|
||||
}
|
||||
|
||||
var issueCommentAddCmd = &cobra.Command{
|
||||
Use: "add <issue-id>",
|
||||
Short: "Add a comment to an issue",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueCommentAdd,
|
||||
}
|
||||
|
||||
var issueCommentDeleteCmd = &cobra.Command{
|
||||
Use: "delete <comment-id>",
|
||||
Short: "Delete a comment",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueCommentDelete,
|
||||
}
|
||||
|
||||
|
|
@ -92,14 +92,14 @@ var issueCommentDeleteCmd = &cobra.Command{
|
|||
var issueRunsCmd = &cobra.Command{
|
||||
Use: "runs <issue-id>",
|
||||
Short: "List execution history for an issue",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueRuns,
|
||||
}
|
||||
|
||||
var issueRunMessagesCmd = &cobra.Command{
|
||||
Use: "run-messages <task-id>",
|
||||
Short: "List messages for an execution",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runIssueRunMessages,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ var repoCheckoutCmd = &cobra.Command{
|
|||
Use: "checkout <url>",
|
||||
Short: "Check out a repository into the working directory",
|
||||
Long: "Creates a git worktree from the daemon's bare clone cache. Used by agents to check out repos on demand.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runRepoCheckout,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,28 +25,28 @@ var runtimeListCmd = &cobra.Command{
|
|||
var runtimeUsageCmd = &cobra.Command{
|
||||
Use: "usage <runtime-id>",
|
||||
Short: "Get token usage for a runtime",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runRuntimeUsage,
|
||||
}
|
||||
|
||||
var runtimeActivityCmd = &cobra.Command{
|
||||
Use: "activity <runtime-id>",
|
||||
Short: "Get hourly task activity for a runtime",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runRuntimeActivity,
|
||||
}
|
||||
|
||||
var runtimePingCmd = &cobra.Command{
|
||||
Use: "ping <runtime-id>",
|
||||
Short: "Ping a runtime to check connectivity",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runRuntimePing,
|
||||
}
|
||||
|
||||
var runtimeUpdateCmd = &cobra.Command{
|
||||
Use: "update <runtime-id>",
|
||||
Short: "Initiate a CLI update on a runtime",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runRuntimeUpdate,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ var skillListCmd = &cobra.Command{
|
|||
var skillGetCmd = &cobra.Command{
|
||||
Use: "get <id>",
|
||||
Short: "Get skill details (includes files)",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runSkillGet,
|
||||
}
|
||||
|
||||
|
|
@ -41,14 +41,14 @@ var skillCreateCmd = &cobra.Command{
|
|||
var skillUpdateCmd = &cobra.Command{
|
||||
Use: "update <id>",
|
||||
Short: "Update a skill",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runSkillUpdate,
|
||||
}
|
||||
|
||||
var skillDeleteCmd = &cobra.Command{
|
||||
Use: "delete <id>",
|
||||
Short: "Delete a skill",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runSkillDelete,
|
||||
}
|
||||
|
||||
|
|
@ -68,21 +68,21 @@ var skillFilesCmd = &cobra.Command{
|
|||
var skillFilesListCmd = &cobra.Command{
|
||||
Use: "list <skill-id>",
|
||||
Short: "List files for a skill",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runSkillFilesList,
|
||||
}
|
||||
|
||||
var skillFilesUpsertCmd = &cobra.Command{
|
||||
Use: "upsert <skill-id>",
|
||||
Short: "Create or update a skill file",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runSkillFilesUpsert,
|
||||
}
|
||||
|
||||
var skillFilesDeleteCmd = &cobra.Command{
|
||||
Use: "delete <skill-id> <file-id>",
|
||||
Short: "Delete a skill file",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Args: exactArgs(2),
|
||||
RunE: runSkillFilesDelete,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,14 +41,14 @@ var workspaceMembersCmd = &cobra.Command{
|
|||
var workspaceWatchCmd = &cobra.Command{
|
||||
Use: "watch <workspace-id>",
|
||||
Short: "Add a workspace to the daemon watch list",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runWatch,
|
||||
}
|
||||
|
||||
var workspaceUnwatchCmd = &cobra.Command{
|
||||
Use: "unwatch <workspace-id>",
|
||||
Short: "Remove a workspace from the daemon watch list",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: exactArgs(1),
|
||||
RunE: runUnwatch,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,26 @@ const (
|
|||
groupAdditional = "additional"
|
||||
)
|
||||
|
||||
// errSilent is returned when the error message has already been printed.
|
||||
var errSilent = fmt.Errorf("")
|
||||
|
||||
// exactArgs returns a cobra.PositionalArgs that validates the arg count
|
||||
// and prints help on failure, so users see usage context with the error.
|
||||
func exactArgs(n int) cobra.PositionalArgs {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) != n {
|
||||
if n == 1 {
|
||||
fmt.Fprintf(cmd.ErrOrStderr(), "Error: accepts 1 arg, received %d\n\n", len(args))
|
||||
} else {
|
||||
fmt.Fprintf(cmd.ErrOrStderr(), "Error: accepts %d args, received %d\n\n", n, len(args))
|
||||
}
|
||||
cmd.Help()
|
||||
return errSilent
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// initHelp configures the root command to use gh-style help output.
|
||||
func initHelp(root *cobra.Command) {
|
||||
root.SetHelpTemplate(rootHelpTemplate)
|
||||
|
|
@ -120,6 +140,11 @@ COMMANDS
|
|||
{{formatCommandList .Commands}}
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
{{- if .Example}}
|
||||
|
||||
EXAMPLES
|
||||
{{.Example}}
|
||||
{{- end}}
|
||||
|
||||
LEARN MORE
|
||||
Use ` + "`{{.CommandPath}} <command> --help`" + ` for more information about a command.
|
||||
|
|
@ -136,6 +161,11 @@ FLAGS
|
|||
{{- end}}
|
||||
INHERITED FLAGS
|
||||
--help Show help for command
|
||||
{{- if .Example}}
|
||||
|
||||
EXAMPLES
|
||||
{{.Example}}
|
||||
{{- end}}
|
||||
|
||||
LEARN MORE
|
||||
Use ` + "`multica <command> <subcommand> --help`" + ` for more information about a command.
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ func init() {
|
|||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||
if err != errSilent {
|
||||
fmt.Fprintln(os.Stderr, "Error:", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue