@@ -255,13 +255,14 @@ func (m model) View() string {
255255 tableWidth := 97
256256
257257 // Title line with help right-aligned
258- // Display widths: title="ccs · claude code search"=25, help="↑/↓ Enter Ctrl+J/K Esc"=22
259- titlePadding := tableWidth - 2 - 25 - 22 + 1 // 2 for indent, +1 to shift right
258+ title := fmt .Sprintf ("ccs · claude code search · %s" , version )
259+ help := "↑/↓ Enter Ctrl+J/K Esc"
260+ titlePadding := tableWidth - 2 - len (title ) - len (help )
260261 if titlePadding < 1 {
261262 titlePadding = 1
262263 }
263- b .WriteString (fmt .Sprintf (" \033 [1;36mccs\033 [0m \033 [90m· claude code search%s↑/↓ Enter Ctrl+J/K Esc \033 [0m\n " ,
264- strings .Repeat (" " , titlePadding )))
264+ b .WriteString (fmt .Sprintf (" \033 [1;36mccs\033 [0m \033 [90m· claude code search · %s%s%s \033 [0m\n " ,
265+ version , strings .Repeat (" " , titlePadding ), help ))
265266
266267 // Search line with count right-aligned
267268 count := fmt .Sprintf ("(%d/%d)" , len (m .filtered ), len (m .items ))
@@ -547,7 +548,11 @@ func extractText(content json.RawMessage) string {
547548 return ""
548549}
549550
550- func parseConversationFile (path string ) (* Conversation , error ) {
551+
552+
553+
554+
555+ func parseConversationFile (path string , cutoff time.Time , maxSize int64 ) (* Conversation , error ) {
551556 info , err := os .Stat (path )
552557 if err != nil {
553558 return nil , err
@@ -557,6 +562,16 @@ func parseConversationFile(path string) (*Conversation, error) {
557562 return nil , nil
558563 }
559564
565+ // Skip files larger than maxSize (0 means no limit)
566+ if maxSize > 0 && info .Size () > maxSize {
567+ return nil , nil
568+ }
569+
570+ // Skip files not modified since cutoff (file mtime check)
571+ if ! cutoff .IsZero () && info .ModTime ().Before (cutoff ) {
572+ return nil , nil
573+ }
574+
560575 sessionID := strings .TrimSuffix (info .Name (), ".jsonl" )
561576 conv := & Conversation {SessionID : sessionID }
562577
@@ -570,8 +585,10 @@ func parseConversationFile(path string) (*Conversation, error) {
570585 scanner .Buffer (make ([]byte , 1024 * 1024 ), 10 * 1024 * 1024 )
571586
572587 for scanner .Scan () {
588+ lineBytes := scanner .Bytes ()
589+
573590 var raw RawMessage
574- if err := json .Unmarshal (scanner . Bytes () , & raw ); err != nil {
591+ if err := json .Unmarshal (lineBytes , & raw ); err != nil {
575592 continue
576593 }
577594
@@ -615,7 +632,7 @@ func parseConversationFile(path string) (*Conversation, error) {
615632 return conv , nil
616633}
617634
618- func getConversations () ([]Conversation , error ) {
635+ func getConversations (cutoff time. Time , maxSize int64 ) ([]Conversation , error ) {
619636 projectsDir := getProjectsDir ()
620637
621638 var files []string
@@ -632,24 +649,30 @@ func getConversations() ([]Conversation, error) {
632649 return nil , err
633650 }
634651
635- var wg sync.WaitGroup
652+ // Worker pool to limit concurrent file operations
653+ const numWorkers = 8
654+ jobs := make (chan string , len (files ))
636655 results := make (chan * Conversation , len (files ))
637- sem := make (chan struct {}, 20 )
638656
639- for _ , file := range files {
657+ var wg sync.WaitGroup
658+ for i := 0 ; i < numWorkers ; i ++ {
640659 wg .Add (1 )
641- go func (path string ) {
660+ go func () {
642661 defer wg .Done ()
643- sem <- struct {}{}
644- defer func () { <- sem }()
645-
646- conv , err := parseConversationFile (path )
647- if err == nil && conv != nil {
648- results <- conv
662+ for path := range jobs {
663+ conv , err := parseConversationFile (path , cutoff , maxSize )
664+ if err == nil && conv != nil {
665+ results <- conv
666+ }
649667 }
650- }(file )
668+ }()
651669 }
652670
671+ for _ , file := range files {
672+ jobs <- file
673+ }
674+ close (jobs )
675+
653676 go func () {
654677 wg .Wait ()
655678 close (results )
@@ -728,12 +751,17 @@ Arguments:
728751 -- claude-flags Flags to pass to 'claude --resume' (after --)
729752
730753Flags:
731- -h, --help Show this help message
732- -v, --version Show version
733- --dump [query] Debug: print all search items (with optional highlighting)
754+ -h, --help Show this help message
755+ -v, --version Show version
756+ --max-age=N Only search last N days (default: 60, 0 = no limit)
757+ --max-size=N Max file size in MB (default: 1024, 0 = no limit)
758+ --all Include everything (same as --max-age=0 --max-size=0)
759+ --dump [query] Debug: print all search items (with optional highlighting)
734760
735761Examples:
736- ccs Search all conversations
762+ ccs Search last 60 days, files <1GB (default)
763+ ccs --max-age=7 Search last 7 days only
764+ ccs --all Search everything (all time, all files)
737765 ccs buyer Search with initial query "buyer"
738766 ccs -- --plan Resume with plan mode
739767 ccs buyer -- --plan Search "buyer", resume with plan mode
@@ -763,14 +791,39 @@ func main() {
763791 }
764792 }
765793
794+ // Parse flags
795+ maxAgeDays := 60 // Default to 60 days
796+ maxSizeMB := int64 (1024 ) // Default to 1GB
797+ for _ , arg := range args {
798+ if arg == "--all" {
799+ maxAgeDays = 0
800+ maxSizeMB = 0
801+ } else if strings .HasPrefix (arg , "--max-age=" ) {
802+ val := strings .TrimPrefix (arg , "--max-age=" )
803+ fmt .Sscanf (val , "%d" , & maxAgeDays )
804+ } else if strings .HasPrefix (arg , "--max-size=" ) {
805+ val := strings .TrimPrefix (arg , "--max-size=" )
806+ fmt .Sscanf (val , "%d" , & maxSizeMB )
807+ }
808+ }
809+
810+ // Convert to bytes (0 means no limit)
811+ maxSize := maxSizeMB * 1024 * 1024
812+
813+ // Calculate cutoff time (0 means no limit)
814+ var cutoff time.Time
815+ if maxAgeDays > 0 {
816+ cutoff = time .Now ().AddDate (0 , 0 , - maxAgeDays )
817+ }
818+
766819 // Debug mode - dump search lines
767820 for i , arg := range args {
768821 if arg == "--dump" {
769822 filter := ""
770823 if i + 1 < len (args ) && ! strings .HasPrefix (args [i + 1 ], "-" ) {
771824 filter = args [i + 1 ]
772825 }
773- conversations , _ := getConversations ()
826+ conversations , _ := getConversations (cutoff , maxSize )
774827 items := buildItems (conversations )
775828 for _ , item := range items {
776829 line := item .searchText
@@ -791,6 +844,10 @@ func main() {
791844 claudeFlags = args [i + 1 :]
792845 break
793846 }
847+ // Skip our flags when looking for filter query
848+ if arg == "--all" || strings .HasPrefix (arg , "--max-age=" ) || strings .HasPrefix (arg , "--max-size=" ) {
849+ continue
850+ }
794851 if ! strings .HasPrefix (arg , "-" ) && filterQuery == "" {
795852 filterQuery = arg
796853 }
@@ -804,7 +861,7 @@ func main() {
804861 }
805862
806863 fmt .Fprint (os .Stderr , "Loading conversations..." )
807- conversations , err := getConversations ()
864+ conversations , err := getConversations (cutoff , maxSize )
808865 if err != nil {
809866 fmt .Fprintf (os .Stderr , "\r Error loading conversations: %v\n " , err )
810867 os .Exit (1 )
0 commit comments