Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package cli

import (
"context"
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"

"github.com/sendbird/ccx/internal/clauderegistry"
"github.com/sendbird/ccx/internal/extract"
Expand All @@ -20,6 +22,7 @@ var Commands = []struct {
Desc string
}{
{"urls", "List URLs from the Claude session (interactive on TTY)"},
{"refs", "List PR/Jira references with resolved status (interactive on TTY)"},
{"files", "List file paths touched by the session (interactive on TTY)"},
{"changes", "List file changes made by the session (interactive on TTY)"},
{"images", "List image paths from the session (interactive on TTY)"},
Expand Down Expand Up @@ -75,6 +78,8 @@ func runPlain(command, filePath, sessID, claudeDir string) error {
switch command {
case "urls":
return printItems(extract.SessionURLs(filePath), "urls")
case "refs":
return printRefs(filePath)
case "files":
return printItems(extract.SessionFilePaths(filePath), "files")
case "changes":
Expand All @@ -99,6 +104,8 @@ func runInteractive(command, filePath, sessID, claudeDir string) (*RunResult, er
switch command {
case "urls":
items = extractURLsWithContext(entries, sessID)
case "refs":
items = extractRefsWithContext(entries, sessID)
case "files":
items = extractFilesWithContext(entries, sessID)
case "changes":
Expand Down Expand Up @@ -145,6 +152,8 @@ func printHelp() {
fmt.Fprintf(os.Stderr, " ccx urls Interactive URL picker\n")
fmt.Fprintf(os.Stderr, " ccx urls --plain Plain tab-separated output\n")
fmt.Fprintf(os.Stderr, " ccx urls | fzf Pipe to fzf (auto plain)\n")
fmt.Fprintf(os.Stderr, " ccx refs Interactive PR/Jira reference picker\n")
fmt.Fprintf(os.Stderr, " ccx refs --plain PR/Jira refs with resolved status (tab-separated)\n")
fmt.Fprintf(os.Stderr, " ccx files Interactive file picker\n")
fmt.Fprintf(os.Stderr, " ccx changes Interactive changed-files picker\n")
fmt.Fprintf(os.Stderr, " ccx images Interactive image picker\n")
Expand Down Expand Up @@ -193,6 +202,25 @@ func printItems(items []extract.Item, kind string) error {
return nil
}

func printRefs(filePath string) error {
refs := session.ExtractSessionRefsFromFile(filePath)
if len(refs) == 0 {
return fmt.Errorf("no PR/Jira references found in session")
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
refs = session.ResolveRefs(ctx, refs)
for _, r := range refs {
kind := strings.ToUpper(string(r.Kind))
if len(kind) < 4 {
kind += strings.Repeat(" ", 4-len(kind))
}
status := session.RefStatusText(r)
fmt.Fprintf(os.Stdout, "%s\t%s\t%s\t%s\n", kind, r.Label, status, r.URL)
}
return nil
}

func printChanges(items []extract.ChangeItem) error {
if len(items) == 0 {
return fmt.Errorf("no changes found in session")
Expand Down
14 changes: 14 additions & 0 deletions internal/cli/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,20 @@ func extractURLsWithContext(entries []session.Entry, sessID string) []PickerItem
return items
}

// extractRefsWithContext returns only the session's PR and Jira URL references,
// reusing the URL extractor and filtering to the pr/jira categories. The refs
// picker resolves their status inline the same way the urls picker does.
func extractRefsWithContext(entries []session.Entry, sessID string) []PickerItem {
all := extractURLsWithContext(entries, sessID)
refs := make([]PickerItem, 0, len(all))
for _, it := range all {
if it.Item.Category == "pr" || it.Item.Category == "jira" {
refs = append(refs, it)
}
}
return refs
}

func extractFilesWithContext(entries []session.Entry, sessID string) []PickerItem {
index := make(map[string]int)
var items []PickerItem
Expand Down
6 changes: 5 additions & 1 deletion internal/cli/picker.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ func (m pickerModel) Init() tea.Cmd {
// reuses session.ResolveRef so results are TTL-cached and share the bounded
// concurrency used elsewhere. Non-urls kinds have no PR/Jira links → no-op.
func (m pickerModel) resolveRefsCmd() tea.Cmd {
if m.kind != "urls" {
if m.kind != "urls" && m.kind != "refs" {
return nil
}
var cmds []tea.Cmd
Expand Down Expand Up @@ -807,6 +807,8 @@ func (m pickerModel) View() string {
switch m.kind {
case "urls":
actions = "↵:jump o:open e:$EDITOR"
case "refs":
actions = "↵:jump o:open"
case "files":
actions = "↵:jump e:$EDITOR"
case "changes":
Expand All @@ -824,6 +826,8 @@ func (m pickerModel) View() string {
switch m.kind {
case "urls":
filterHints = hint.Render("is:") + dim.Render("pr gh github jira slack other") + " " + hint.Render("role:") + dim.Render("user asst")
case "refs":
filterHints = hint.Render("is:") + dim.Render("pr jira") + " " + hint.Render("role:") + dim.Render("user asst")
case "files":
filterHints = hint.Render("is:") + dim.Render("read write edit glob grep tool") + " " + hint.Render("role:") + dim.Render("user asst")
case "changes":
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ func main() {
os.Exit(1)
}
os.Exit(0)
case "urls", "files", "changes", "images", "conversation", "info", "help":
case "urls", "refs", "files", "changes", "images", "conversation", "info", "help":
subcmd := os.Args[1]
fs := flag.NewFlagSet(subcmd, flag.ExitOnError)
plain := fs.Bool("plain", false, "force plain text output (no interactive picker)")
Expand Down
Loading