@@ -14,21 +14,14 @@ import (
1414 "github.com/entireio/cli/cmd/entire/cli/logging"
1515 "github.com/entireio/cli/cmd/entire/cli/paths"
1616 "github.com/entireio/cli/cmd/entire/cli/session"
17+ "github.com/entireio/cli/cmd/entire/cli/settings"
1718 "github.com/entireio/cli/cmd/entire/cli/strategy"
1819 "github.com/go-git/go-git/v6/plumbing"
1920 "github.com/spf13/cobra"
2021)
2122
22- func newCleanCmd () * cobra.Command {
23- var forceFlag bool
24- var allFlag bool
25- var dryRunFlag bool
26- var sessionFlag string
27-
28- cmd := & cobra.Command {
29- Use : "clean" ,
30- Short : "Clean up Entire session data" ,
31- Long : `Clean up Entire session data for the current HEAD commit.
23+ func cleanLongDescription (ctx context.Context ) string {
24+ description := `Clean up Entire session data for the current HEAD commit.
3225
3326By default, cleans session state and shadow branches for the current HEAD:
3427 - Session state files (.git/entire-sessions/<session-id>.json)
@@ -37,13 +30,34 @@ By default, cleans session state and shadow branches for the current HEAD:
3730Use --all to clean all Entire session data across the repository:
3831 - All session state files (.git/entire-sessions/)
3932 - All shadow branches
40- - Temporary files (.entire/tmp/)
41- The entire/checkpoints/v1 branch itself is preserved.
33+ - Temporary files (.entire/tmp/)`
34+
35+ s , err := settings .Load (ctx )
36+ if err == nil && s .IsCheckpointsV2Enabled () {
37+ description += fmt .Sprintf (`
38+ - Archived v2 full transcripts older than the configured %d-day retention window` , s .GetFullTranscriptGenerationRetentionDays ())
39+ }
40+
41+ description += `
4242
4343Use --session <id> to clean a specific session only.
4444
4545Without --force, prompts for confirmation before deleting.
46- Use --dry-run to preview what would be deleted without prompting.` ,
46+ Use --dry-run to preview what would be deleted without prompting.`
47+
48+ return description
49+ }
50+
51+ func newCleanCmd () * cobra.Command {
52+ var forceFlag bool
53+ var allFlag bool
54+ var dryRunFlag bool
55+ var sessionFlag string
56+
57+ cmd := & cobra.Command {
58+ Use : "clean" ,
59+ Short : "Clean up Entire session data" ,
60+ Long : cleanLongDescription (context .Background ()),
4761 RunE : func (cmd * cobra.Command , _ []string ) error {
4862 ctx := cmd .Context ()
4963
@@ -258,12 +272,29 @@ func runCleanSession(ctx context.Context, cmd *cobra.Command, strat *strategy.Ma
258272
259273// runCleanAll cleans all session data across the repository.
260274func runCleanAll (ctx context.Context , cmd * cobra.Command , force , dryRun bool ) error {
275+ s , err := settings .Load (ctx )
276+ if err != nil {
277+ fmt .Fprintf (cmd .ErrOrStderr (), "Warning: failed to load settings: %v\n " , err )
278+ s = & settings.EntireSettings {}
279+ }
280+
261281 // List all items (sessions, shadow branches) — not just orphaned ones
262282 items , err := strategy .ListAllItems (ctx )
263283 if err != nil {
264284 return fmt .Errorf ("failed to list items: %w" , err )
265285 }
266286
287+ if s .IsCheckpointsV2Enabled () {
288+ v2Items , warnings , err := strategy .ListEligibleV2Generations (ctx , s )
289+ if err != nil {
290+ return fmt .Errorf ("failed to list v2 generations: %w" , err )
291+ }
292+ items = append (items , v2Items ... )
293+ for _ , warning := range warnings {
294+ fmt .Fprintf (cmd .ErrOrStderr (), "Warning: %s\n " , warning )
295+ }
296+ }
297+
267298 // List temp files — skip active-session filter since --all deletes those sessions
268299 tempFiles , err := listAllTempFiles (ctx )
269300 if err != nil {
@@ -274,6 +305,29 @@ func runCleanAll(ctx context.Context, cmd *cobra.Command, force, dryRun bool) er
274305 return runCleanAllWithItems (ctx , cmd , force , dryRun , items , tempFiles )
275306}
276307
308+ // printSection prints a titled list of items if the slice is non-empty.
309+ func printSection (w io.Writer , title string , items []string ) {
310+ if len (items ) == 0 {
311+ return
312+ }
313+ fmt .Fprintf (w , "%s (%d):\n " , title , len (items ))
314+ for _ , item := range items {
315+ fmt .Fprintf (w , " %s\n " , item )
316+ }
317+ fmt .Fprintln (w )
318+ }
319+
320+ // printResultSection prints a titled list with a leading newline, for post-deletion output.
321+ func printResultSection (w io.Writer , title string , items []string ) {
322+ if len (items ) == 0 {
323+ return
324+ }
325+ fmt .Fprintf (w , "\n %s (%d):\n " , title , len (items ))
326+ for _ , item := range items {
327+ fmt .Fprintf (w , " %s\n " , item )
328+ }
329+ }
330+
277331// runCleanAllWithItems is the core logic for cleaning all items.
278332// Separated for testability — tests pass a cmd without a TTY and use force or dryRun to avoid prompts.
279333func runCleanAllWithItems (ctx context.Context , cmd * cobra.Command , force , dryRun bool , items []strategy.CleanupItem , tempFiles []string ) error {
@@ -286,7 +340,7 @@ func runCleanAllWithItems(ctx context.Context, cmd *cobra.Command, force, dryRun
286340 }
287341
288342 // Group items by type for display
289- var branches , states , checkpoints []strategy.CleanupItem
343+ var branches , states , checkpoints , v2Generations []strategy.CleanupItem
290344 for _ , item := range items {
291345 switch item .Type {
292346 case strategy .CleanupTypeShadowBranch :
@@ -295,6 +349,8 @@ func runCleanAllWithItems(ctx context.Context, cmd *cobra.Command, force, dryRun
295349 states = append (states , item )
296350 case strategy .CleanupTypeCheckpoint :
297351 checkpoints = append (checkpoints , item )
352+ case strategy .CleanupTypeV2Generation :
353+ v2Generations = append (v2Generations , item )
298354 }
299355 }
300356
@@ -303,37 +359,11 @@ func runCleanAllWithItems(ctx context.Context, cmd *cobra.Command, force, dryRun
303359 totalItems := len (items ) + len (tempFiles )
304360 fmt .Fprintf (w , "Found %d %s to clean:\n \n " , totalItems , itemWord (totalItems ))
305361
306- if len (branches ) > 0 {
307- fmt .Fprintf (w , "Shadow branches (%d):\n " , len (branches ))
308- for _ , item := range branches {
309- fmt .Fprintf (w , " %s\n " , item .ID )
310- }
311- fmt .Fprintln (w )
312- }
313-
314- if len (states ) > 0 {
315- fmt .Fprintf (w , "Session states (%d):\n " , len (states ))
316- for _ , item := range states {
317- fmt .Fprintf (w , " %s\n " , item .ID )
318- }
319- fmt .Fprintln (w )
320- }
321-
322- if len (checkpoints ) > 0 {
323- fmt .Fprintf (w , "Checkpoint metadata (%d):\n " , len (checkpoints ))
324- for _ , item := range checkpoints {
325- fmt .Fprintf (w , " %s\n " , item .ID )
326- }
327- fmt .Fprintln (w )
328- }
329-
330- if len (tempFiles ) > 0 {
331- fmt .Fprintf (w , "Temp files (%d):\n " , len (tempFiles ))
332- for _ , file := range tempFiles {
333- fmt .Fprintf (w , " %s\n " , file )
334- }
335- fmt .Fprintln (w )
336- }
362+ printSection (w , "Shadow branches" , cleanupItemIDs (branches ))
363+ printSection (w , "Session states" , cleanupItemIDs (states ))
364+ printSection (w , "Checkpoint metadata" , cleanupItemIDs (checkpoints ))
365+ printSection (w , "Archived v2 generations" , cleanupItemIDs (v2Generations ))
366+ printSection (w , "Temp files" , tempFiles )
337367
338368 if dryRun {
339369 fmt .Fprintln (w , "Run without --dry-run to delete these items." )
@@ -370,64 +400,27 @@ func runCleanAllWithItems(ctx context.Context, cmd *cobra.Command, force, dryRun
370400 deletedTempFiles , failedTempFiles := deleteTempFiles (ctx , tempFiles )
371401
372402 // Report results
373- totalDeleted := len (result .ShadowBranches ) + len (result .SessionStates ) + len (result .Checkpoints ) + len (deletedTempFiles )
374- totalFailed := len (result .FailedBranches ) + len (result .FailedStates ) + len (result .FailedCheckpoints ) + len (failedTempFiles )
403+ totalDeleted := len (result .ShadowBranches ) + len (result .SessionStates ) + len (result .Checkpoints ) + len (result . V2Generations ) + len ( deletedTempFiles )
404+ totalFailed := len (result .FailedBranches ) + len (result .FailedStates ) + len (result .FailedCheckpoints ) + len (result . FailedV2Refs ) + len ( failedTempFiles )
375405
376406 if totalDeleted > 0 {
377407 fmt .Fprintf (w , "✓ Deleted %d %s:\n " , totalDeleted , itemWord (totalDeleted ))
378408
379- if len (result .ShadowBranches ) > 0 {
380- fmt .Fprintf (w , "\n Shadow branches (%d):\n " , len (result .ShadowBranches ))
381- for _ , branch := range result .ShadowBranches {
382- fmt .Fprintf (w , " %s\n " , branch )
383- }
384- }
409+ printResultSection (w , "Shadow branches" , result .ShadowBranches )
410+ printResultSection (w , "Session states" , result .SessionStates )
411+ printResultSection (w , "Checkpoints" , result .Checkpoints )
412+ printResultSection (w , "Archived v2 generations" , result .V2Generations )
385413
386- if len (result .SessionStates ) > 0 {
387- fmt .Fprintf (w , "\n Session states (%d):\n " , len (result .SessionStates ))
388- for _ , state := range result .SessionStates {
389- fmt .Fprintf (w , " %s\n " , state )
390- }
391- }
392-
393- if len (result .Checkpoints ) > 0 {
394- fmt .Fprintf (w , "\n Checkpoints (%d):\n " , len (result .Checkpoints ))
395- for _ , cp := range result .Checkpoints {
396- fmt .Fprintf (w , " %s\n " , cp )
397- }
398- }
399-
400- if len (deletedTempFiles ) > 0 {
401- fmt .Fprintf (w , "\n Temp files (%d):\n " , len (deletedTempFiles ))
402- for _ , file := range deletedTempFiles {
403- fmt .Fprintf (w , " %s\n " , file )
404- }
405- }
414+ printResultSection (w , "Temp files" , deletedTempFiles )
406415 }
407416
408417 if totalFailed > 0 {
409418 fmt .Fprintf (errW , "\n Failed to delete %d %s:\n " , totalFailed , itemWord (totalFailed ))
410419
411- if len (result .FailedBranches ) > 0 {
412- fmt .Fprintf (errW , "\n Shadow branches:\n " )
413- for _ , branch := range result .FailedBranches {
414- fmt .Fprintf (errW , " %s\n " , branch )
415- }
416- }
417-
418- if len (result .FailedStates ) > 0 {
419- fmt .Fprintf (errW , "\n Session states:\n " )
420- for _ , state := range result .FailedStates {
421- fmt .Fprintf (errW , " %s\n " , state )
422- }
423- }
424-
425- if len (result .FailedCheckpoints ) > 0 {
426- fmt .Fprintf (errW , "\n Checkpoints:\n " )
427- for _ , cp := range result .FailedCheckpoints {
428- fmt .Fprintf (errW , " %s\n " , cp )
429- }
430- }
420+ printResultSection (errW , "Shadow branches" , result .FailedBranches )
421+ printResultSection (errW , "Session states" , result .FailedStates )
422+ printResultSection (errW , "Checkpoints" , result .FailedCheckpoints )
423+ printResultSection (errW , "Archived v2 generations" , result .FailedV2Refs )
431424
432425 if len (failedTempFiles ) > 0 {
433426 fmt .Fprintf (errW , "\n Temp files:\n " )
@@ -442,6 +435,15 @@ func runCleanAllWithItems(ctx context.Context, cmd *cobra.Command, force, dryRun
442435 return nil
443436}
444437
438+ // cleanupItemIDs extracts IDs from a slice of CleanupItems.
439+ func cleanupItemIDs (items []strategy.CleanupItem ) []string {
440+ ids := make ([]string , len (items ))
441+ for i , item := range items {
442+ ids [i ] = item .ID
443+ }
444+ return ids
445+ }
446+
445447// listAllTempFiles returns all files in .entire/tmp/ without filtering.
446448// Used by --all since those sessions are being deleted anyway.
447449func listAllTempFiles (ctx context.Context ) ([]string , error ) {
0 commit comments