@@ -26,15 +26,28 @@ describe("createSubAgentWorktree", () => {
2626 const { exec, calls } = recordingExec ( {
2727 "rev-parse" : { stdout : "/repo\n" } ,
2828 worktree : { stdout : "" } ,
29+ stash : { stdout : "" } ,
2930 } ) ;
3031 const result = await createSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
3132 expect ( result . path ) . toBe ( "/repo/.worktrees/abc" ) ;
33+ expect ( result . stashBaseline ) . toEqual ( [ ] ) ;
3234 expect ( calls ) . toEqual ( [
3335 [ "rev-parse" , "--show-toplevel" ] ,
3436 [ "worktree" , "add" , "--detach" , "/repo/.worktrees/abc" , "HEAD" ] ,
37+ [ "stash" , "list" ] ,
3538 ] ) ;
3639 } ) ;
3740
41+ test ( "captures the current stash list as a baseline" , async ( ) => {
42+ const { exec } = recordingExec ( {
43+ "rev-parse" : { stdout : "/repo\n" } ,
44+ worktree : { stdout : "" } ,
45+ stash : { stdout : "stash@{0}: WIP on main: abc1234 pre-existing stash\n" } ,
46+ } ) ;
47+ const result = await createSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
48+ expect ( result . stashBaseline ) . toEqual ( [ "stash@{0}: WIP on main: abc1234 pre-existing stash" ] ) ;
49+ } ) ;
50+
3851 test ( "fails closed when repoCwd is not a git repository" , async ( ) => {
3952 const { exec } = recordingExec ( {
4053 "rev-parse" : { error : new Error ( "not a git repository" ) } ,
@@ -56,15 +69,17 @@ describe("createSubAgentWorktree", () => {
5669} ) ;
5770
5871describe ( "cleanupSubAgentWorktree" , ( ) => {
59- test ( "removes a clean worktree" , async ( ) => {
72+ test ( "removes a clean worktree with no new stash entries " , async ( ) => {
6073 const { exec, calls } = recordingExec ( {
6174 status : { stdout : "" } ,
75+ stash : { stdout : "" } ,
6276 worktree : { stdout : "" } ,
6377 } ) ;
64- const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
78+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ ] , exec ) ;
6579 expect ( result ) . toEqual ( { status : "removed" , path : "/repo/.worktrees/abc" } ) ;
6680 expect ( calls ) . toEqual ( [
6781 [ "status" , "--porcelain" , "--ignored" ] ,
82+ [ "stash" , "list" ] ,
6883 [ "worktree" , "remove" , "/repo/.worktrees/abc" ] ,
6984 ] ) ;
7085 } ) ;
@@ -73,7 +88,7 @@ describe("cleanupSubAgentWorktree", () => {
7388 const { exec, calls } = recordingExec ( {
7489 status : { stdout : "!! dist/output.txt\n" } ,
7590 } ) ;
76- const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
91+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ ] , exec ) ;
7792 expect ( result . status ) . toBe ( "preserved" ) ;
7893 if ( result . status === "preserved" ) {
7994 expect ( result . notice ) . toContain ( "uncommitted changes" ) ;
@@ -85,7 +100,7 @@ describe("cleanupSubAgentWorktree", () => {
85100 const { exec, calls } = recordingExec ( {
86101 status : { stdout : " M src/index.ts\n" } ,
87102 } ) ;
88- const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
103+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ ] , exec ) ;
89104 expect ( result . status ) . toBe ( "preserved" ) ;
90105 expect ( result ) . toMatchObject ( { path : "/repo/.worktrees/abc" } ) ;
91106 if ( result . status === "preserved" ) {
@@ -99,19 +114,45 @@ describe("cleanupSubAgentWorktree", () => {
99114 const { exec } = recordingExec ( {
100115 status : { error : new Error ( "no such directory" ) } ,
101116 } ) ;
102- const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
117+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ ] , exec ) ;
103118 expect ( result . status ) . toBe ( "preserved" ) ;
104119 } ) ;
105120
106121 test ( "preserves the worktree when removal fails" , async ( ) => {
107- const { exec } = recordingExec ( {
122+ const { exec, calls } = recordingExec ( {
108123 status : { stdout : "" } ,
124+ stash : { stdout : "" } ,
109125 worktree : { error : new Error ( "worktree is locked" ) } ,
110126 } ) ;
111- const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , exec ) ;
127+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ ] , exec ) ;
112128 expect ( result . status ) . toBe ( "preserved" ) ;
113129 if ( result . status === "preserved" ) {
114130 expect ( result . notice ) . toContain ( "could not be removed automatically" ) ;
115131 }
116132 } ) ;
133+
134+ test ( "preserves a clean worktree that created a new stash entry" , async ( ) => {
135+ const { exec, calls } = recordingExec ( {
136+ status : { stdout : "" } ,
137+ stash : { stdout : "stash@{0}: WIP on (no branch): abc1234 sub-agent work\n" } ,
138+ } ) ;
139+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ ] , exec ) ;
140+ expect ( result . status ) . toBe ( "preserved" ) ;
141+ if ( result . status === "preserved" ) {
142+ expect ( result . notice ) . toContain ( "stash entry" ) ;
143+ expect ( result . notice ) . toContain ( "stash@{0}" ) ;
144+ }
145+ expect ( calls . some ( ( call ) => call [ 0 ] === "worktree" ) ) . toBe ( false ) ;
146+ } ) ;
147+
148+ test ( "does not flag a stash entry that predates this worktree" , async ( ) => {
149+ const preexisting = "stash@{0}: WIP on main: abc1234 unrelated older stash" ;
150+ const { exec } = recordingExec ( {
151+ status : { stdout : "" } ,
152+ stash : { stdout : `${ preexisting } \n` } ,
153+ worktree : { stdout : "" } ,
154+ } ) ;
155+ const result = await cleanupSubAgentWorktree ( "/repo" , "/repo/.worktrees/abc" , [ preexisting ] , exec ) ;
156+ expect ( result ) . toEqual ( { status : "removed" , path : "/repo/.worktrees/abc" } ) ;
157+ } ) ;
117158} ) ;
0 commit comments