feat: add branch, notification, org, open, whoami commands
Ports five commands from tea that fgj-sid was missing:
- fgj branch {list,rename,delete} — list branches with protection
status, rename, and delete with confirmation.
- fgj notification {list,read} — list user notifications (unread by
default, --all for everything), mark individual threads read.
- fgj org {list,create,delete} — manage organizations on the host.
Create accepts --description/--full-name/--website/--location and
--visibility (public/limited/private).
- fgj open [number] — open the repo, issue, or PR in a browser.
Auto-detects issue-vs-PR via GetIssue. Falls back to printing the
URL when stdout is not a TTY or --url is passed.
- fgj whoami — display authenticated user + host.
All commands follow the established pattern (parseRepo + config.Load +
api.NewClientFromConfig + ios), support --json where list semantics
apply, and share a new loadClient helper for host-scoped (non-repo)
commands. Tested live against forgejo.zerova.net.
Refs audit recommendation.md §'v0.5.0 — Missing resources'.
This commit is contained in:
parent
d4b5b79541
commit
17ca49d0c5
5 changed files with 665 additions and 0 deletions
137
cmd/notification.go
Normal file
137
cmd/notification.go
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/sdk/gitea"
|
||||
"forgejo.zerova.net/public/fgj-sid/internal/api"
|
||||
"forgejo.zerova.net/public/fgj-sid/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var notificationCmd = &cobra.Command{
|
||||
Use: "notification",
|
||||
Aliases: []string{"notifications", "n"},
|
||||
Short: "Manage user notifications",
|
||||
Long: "List and mark notifications for the authenticated user.",
|
||||
}
|
||||
|
||||
var notificationListCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "List notifications",
|
||||
Long: "List notifications for the authenticated user. Shows unread by default.",
|
||||
Example: ` # List unread notifications
|
||||
fgj notification list
|
||||
|
||||
# Include read and pinned notifications
|
||||
fgj notification list --all
|
||||
|
||||
# Limit number of results
|
||||
fgj notification list -L 50
|
||||
|
||||
# Output as JSON
|
||||
fgj notification list --json`,
|
||||
RunE: runNotificationList,
|
||||
}
|
||||
|
||||
var notificationReadCmd = &cobra.Command{
|
||||
Use: "read <id>",
|
||||
Aliases: []string{"r"},
|
||||
Short: "Mark a notification as read",
|
||||
Long: "Mark a single notification thread as read by its ID.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: runNotificationRead,
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(notificationCmd)
|
||||
notificationCmd.AddCommand(notificationListCmd)
|
||||
notificationCmd.AddCommand(notificationReadCmd)
|
||||
|
||||
notificationListCmd.Flags().Bool("all", false, "Include read and pinned notifications (not just unread)")
|
||||
notificationListCmd.Flags().IntP("limit", "L", 30, "Maximum number of notifications to list")
|
||||
addJSONFlags(notificationListCmd, "Output as JSON")
|
||||
}
|
||||
|
||||
func runNotificationList(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
all, _ := cmd.Flags().GetBool("all")
|
||||
limit, _ := cmd.Flags().GetInt("limit")
|
||||
if limit <= 0 {
|
||||
limit = 30
|
||||
}
|
||||
|
||||
opt := gitea.ListNotificationOptions{
|
||||
ListOptions: gitea.ListOptions{PageSize: limit},
|
||||
Status: []gitea.NotifyStatus{gitea.NotifyStatusUnread},
|
||||
}
|
||||
if all {
|
||||
opt.Status = []gitea.NotifyStatus{gitea.NotifyStatusUnread, gitea.NotifyStatusRead, gitea.NotifyStatusPinned}
|
||||
}
|
||||
|
||||
threads, _, err := client.ListNotifications(opt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list notifications: %w", err)
|
||||
}
|
||||
|
||||
if wantJSON(cmd) {
|
||||
return outputJSON(cmd, threads)
|
||||
}
|
||||
|
||||
if len(threads) == 0 {
|
||||
fmt.Fprintln(ios.Out, "No notifications.")
|
||||
return nil
|
||||
}
|
||||
|
||||
tp := ios.NewTablePrinter()
|
||||
tp.AddHeader("ID", "REPO", "TYPE", "STATE", "TITLE")
|
||||
for _, t := range threads {
|
||||
repo := ""
|
||||
if t.Repository != nil {
|
||||
repo = t.Repository.FullName
|
||||
}
|
||||
subjType, subjState, title := "", "", ""
|
||||
if t.Subject != nil {
|
||||
subjType = string(t.Subject.Type)
|
||||
subjState = string(t.Subject.State)
|
||||
title = t.Subject.Title
|
||||
}
|
||||
tp.AddRow(fmt.Sprintf("%d", t.ID), repo, subjType, subjState, title)
|
||||
}
|
||||
return tp.Render()
|
||||
}
|
||||
|
||||
func runNotificationRead(cmd *cobra.Command, args []string) error {
|
||||
id, err := parseIssueArg(args[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid notification id %q: %w", args[0], err)
|
||||
}
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := api.NewClientFromConfig(cfg, "", getDetectedHost(), getCwd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, _, err := client.ReadNotification(id, gitea.NotifyStatusRead); err != nil {
|
||||
return fmt.Errorf("failed to mark notification %d as read: %w", id, err)
|
||||
}
|
||||
|
||||
cs := ios.ColorScheme()
|
||||
fmt.Fprintf(ios.Out, "%s Marked notification %d as read\n", cs.SuccessIcon(), id)
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue