@@ -93,7 +93,7 @@ test('agent-branch-finish handles Claude-root worktrees when inferring base from
9393 result = runCmd ( 'git' , [ 'worktree' , 'add' , auxWorktree , 'main' ] , repoDir ) ;
9494 assert . equal ( result . status , 0 , result . stderr || result . stdout ) ;
9595
96- const finish = runBranchFinish ( [ '--branch' , agentBranch ] , repoDir ) ;
96+ const finish = runBranchFinish ( [ '--branch' , agentBranch , '--no-cleanup' ] , repoDir ) ;
9797 assert . equal ( finish . status , 0 , finish . stderr || finish . stdout ) ;
9898 assert . match ( finish . stdout , new RegExp ( `Merged '${ escapeRegexLiteral ( agentBranch ) } ' into 'main'` ) ) ;
9999
@@ -325,7 +325,7 @@ test('agent-branch-finish auto-syncs source branch when behind origin/dev', () =
325325 result = runCmd ( 'git' , [ 'checkout' , 'agent/test-finish-sync-guard' ] , repoDir ) ;
326326 assert . equal ( result . status , 0 , result . stderr ) ;
327327
328- const finish = runBranchFinish ( [ '--branch' , 'agent/test-finish-sync-guard' ] , repoDir ) ;
328+ const finish = runBranchFinish ( [ '--branch' , 'agent/test-finish-sync-guard' , '--no-cleanup' ] , repoDir ) ;
329329 assert . equal ( finish . status , 0 , finish . stderr || finish . stdout ) ;
330330 assert . match ( finish . stderr , / a g e n t - s y n c - g u a r d / ) ;
331331 assert . match ( finish . stderr , / A u t o - s y n c i n g ' a g e n t \/ t e s t - f i n i s h - s y n c - g u a r d ' o n t o o r i g i n \/ d e v b e f o r e f i n i s h / ) ;
@@ -373,7 +373,7 @@ test('agent-branch-finish removes stale source-probe worktrees before creating a
373373 assert . equal ( result . status , 0 , result . stderr || result . stdout ) ;
374374 fs . writeFileSync ( path . join ( sourceProbePath , 'agent-stale-source-probe.txt' ) , 'stale probe dirty change\n' , 'utf8' ) ;
375375
376- const finish = runBranchFinish ( [ '--branch' , 'agent/test-stale-source-probe' ] , repoDir ) ;
376+ const finish = runBranchFinish ( [ '--branch' , 'agent/test-stale-source-probe' , '--no-cleanup' ] , repoDir ) ;
377377 assert . equal ( finish . status , 0 , finish . stderr || finish . stdout ) ;
378378 assert . match ( finish . stderr , / R e m o v i n g s t a l e s o u r c e - p r o b e w o r k t r e e f o r ' a g e n t \/ t e s t - s t a l e - s o u r c e - p r o b e ' / ) ;
379379 assert . equal ( fs . existsSync ( sourceProbePath ) , false , 'stale source-probe worktree should be removed before finish continues' ) ;
@@ -1078,6 +1078,150 @@ exit 1
10781078} ) ;
10791079
10801080
1081+ test ( 'agent-branch-finish defaults bare PR finish to wait, cleanup, and base refresh' , ( ) => {
1082+ const repoDir = initRepo ( ) ;
1083+ seedCommit ( repoDir ) ;
1084+ attachOriginRemote ( repoDir ) ;
1085+
1086+ let result = runNode ( [ 'setup' , '--target' , repoDir , '--no-global-install' ] , repoDir ) ;
1087+ assert . equal ( result . status , 0 , result . stderr || result . stdout ) ;
1088+ result = runCmd ( 'git' , [ 'add' , '.' ] , repoDir ) ;
1089+ assert . equal ( result . status , 0 , result . stderr ) ;
1090+ result = runCmd ( 'git' , [ 'commit' , '-m' , 'apply gx setup' ] , repoDir , {
1091+ ALLOW_COMMIT_ON_PROTECTED_BRANCH : '1' ,
1092+ } ) ;
1093+ assert . equal ( result . status , 0 , result . stderr ) ;
1094+ result = runCmd ( 'git' , [ 'push' , 'origin' , 'dev' ] , repoDir ) ;
1095+ assert . equal ( result . status , 0 , result . stderr ) ;
1096+
1097+ result = runCmd ( 'git' , [ 'checkout' , '-b' , 'agent/test-pr-default-wait-cleanup' ] , repoDir ) ;
1098+ assert . equal ( result . status , 0 , result . stderr ) ;
1099+ commitFile ( repoDir , 'agent-pr-default.txt' , 'agent default wait cleanup\n' , 'agent default wait cleanup change' ) ;
1100+
1101+ const auxWorktree = path . join ( path . dirname ( repoDir ) , 'aux-default-pr-dev' ) ;
1102+ result = runCmd ( 'git' , [ 'worktree' , 'add' , auxWorktree , 'dev' ] , repoDir ) ;
1103+ assert . equal ( result . status , 0 , result . stderr || result . stdout ) ;
1104+
1105+ const ghMergeState = path . join ( repoDir , '.finish-gh-default-merge-attempts' ) ;
1106+ const { fakePath : fakeGhPath } = createFakeGhScript ( `
1107+ if [[ "$1" == "pr" && "$2" == "create" ]]; then
1108+ exit 0
1109+ fi
1110+ if [[ "$1" == "pr" && "$2" == "view" ]]; then
1111+ if [[ " $* " == *" --json url "* ]]; then
1112+ echo "https://example.test/pr/default"
1113+ exit 0
1114+ fi
1115+ if [[ " $* " == *" --json state,mergedAt,url "* ]]; then
1116+ attempts=0
1117+ if [[ -f "${ '${GUARDEX_TEST_GH_MERGE_STATE}' } " ]]; then
1118+ attempts="$(cat "${ '${GUARDEX_TEST_GH_MERGE_STATE}' } ")"
1119+ fi
1120+ if [[ "$attempts" -ge 2 ]]; then
1121+ echo -e "MERGED\\x1f2026-05-11T00:00:00Z\\x1fhttps://example.test/pr/default"
1122+ else
1123+ echo -e "OPEN\\x1f\\x1fhttps://example.test/pr/default"
1124+ fi
1125+ exit 0
1126+ fi
1127+ echo "unexpected gh pr view args: $*" >&2
1128+ exit 1
1129+ fi
1130+ if [[ "$1" == "pr" && "$2" == "merge" ]]; then
1131+ attempts=0
1132+ if [[ -f "${ '${GUARDEX_TEST_GH_MERGE_STATE}' } " ]]; then
1133+ attempts="$(cat "${ '${GUARDEX_TEST_GH_MERGE_STATE}' } ")"
1134+ fi
1135+ attempts=$((attempts + 1))
1136+ echo "$attempts" > "${ '${GUARDEX_TEST_GH_MERGE_STATE}' } "
1137+ if [[ "$attempts" -lt 2 ]]; then
1138+ echo "Required status check \\"test (node 22)\\" is expected." >&2
1139+ exit 1
1140+ fi
1141+ git push origin "$3:dev" >/dev/null 2>&1
1142+ exit 0
1143+ fi
1144+ echo "unexpected gh args: $*" >&2
1145+ exit 1
1146+ ` ) ;
1147+
1148+ const finish = runBranchFinish (
1149+ [
1150+ '--branch' ,
1151+ 'agent/test-pr-default-wait-cleanup' ,
1152+ '--mode' ,
1153+ 'pr' ,
1154+ '--wait-timeout-seconds' ,
1155+ '60' ,
1156+ '--wait-poll-seconds' ,
1157+ '0' ,
1158+ ] ,
1159+ repoDir ,
1160+ {
1161+ GUARDEX_GH_BIN : fakeGhPath ,
1162+ GUARDEX_TEST_GH_MERGE_STATE : ghMergeState ,
1163+ } ,
1164+ ) ;
1165+ assert . equal ( finish . status , 0 , finish . stderr || finish . stdout ) ;
1166+ assert . equal ( fs . readFileSync ( ghMergeState , 'utf8' ) . trim ( ) , '2' , 'bare PR finish should wait and retry merge by default' ) ;
1167+ assert . match ( finish . stdout , / M e r g e d ' a g e n t \/ t e s t - p r - d e f a u l t - w a i t - c l e a n u p ' i n t o ' d e v ' v i a p r f l o w a n d c l e a n e d s o u r c e b r a n c h \/ w o r k t r e e \. / ) ;
1168+ assert . match ( finish . stdout , / R e f r e s h e d l o c a l d e v w o r k t r e e w i t h ' g i t p u l l - - f f - o n l y o r i g i n d e v ' : / ) ;
1169+ assert . equal ( fs . existsSync ( auxWorktree ) , true , 'default cleanup should keep the checked-out dev worktree' ) ;
1170+ assert . equal (
1171+ fs . existsSync ( path . join ( repoDir , 'agent-pr-default.txt' ) ) ,
1172+ true ,
1173+ 'clean local dev checkout should be refreshed after PR merge' ,
1174+ ) ;
1175+
1176+ result = runCmd ( 'git' , [ 'show-ref' , '--verify' , '--quiet' , 'refs/heads/agent/test-pr-default-wait-cleanup' ] , repoDir ) ;
1177+ assert . notEqual ( result . status , 0 , 'default cleanup should delete the local agent branch' ) ;
1178+ result = runCmd ( 'git' , [ 'ls-remote' , '--heads' , 'origin' , 'agent/test-pr-default-wait-cleanup' ] , repoDir ) ;
1179+ assert . equal ( result . stdout . trim ( ) , '' , 'default cleanup should delete the remote agent branch' ) ;
1180+ } ) ;
1181+
1182+
1183+ test ( 'agent-branch-finish warns instead of pulling dirty local base worktree' , ( ) => {
1184+ const repoDir = initRepo ( ) ;
1185+ seedCommit ( repoDir ) ;
1186+ attachOriginRemote ( repoDir ) ;
1187+
1188+ let result = runNode ( [ 'setup' , '--target' , repoDir , '--no-global-install' ] , repoDir ) ;
1189+ assert . equal ( result . status , 0 , result . stderr || result . stdout ) ;
1190+ result = runCmd ( 'git' , [ 'add' , '.' ] , repoDir ) ;
1191+ assert . equal ( result . status , 0 , result . stderr ) ;
1192+ result = runCmd ( 'git' , [ 'commit' , '-m' , 'apply gx setup' ] , repoDir , {
1193+ ALLOW_COMMIT_ON_PROTECTED_BRANCH : '1' ,
1194+ } ) ;
1195+ assert . equal ( result . status , 0 , result . stderr ) ;
1196+ result = runCmd ( 'git' , [ 'push' , 'origin' , 'dev' ] , repoDir ) ;
1197+ assert . equal ( result . status , 0 , result . stderr ) ;
1198+
1199+ result = runCmd ( 'git' , [ 'checkout' , '-b' , 'agent/test-dirty-base-refresh-warning' ] , repoDir ) ;
1200+ assert . equal ( result . status , 0 , result . stderr ) ;
1201+ commitFile ( repoDir , 'agent-dirty-base-refresh.txt' , 'agent dirty base refresh\n' , 'agent dirty base refresh change' ) ;
1202+
1203+ const auxWorktree = path . join ( path . dirname ( repoDir ) , 'aux-dirty-dev' ) ;
1204+ result = runCmd ( 'git' , [ 'worktree' , 'add' , auxWorktree , 'dev' ] , repoDir ) ;
1205+ assert . equal ( result . status , 0 , result . stderr || result . stdout ) ;
1206+ fs . writeFileSync ( path . join ( auxWorktree , 'package.json' ) , '{"dirty":true}\n' , 'utf8' ) ;
1207+
1208+ const finish = runBranchFinish (
1209+ [ '--branch' , 'agent/test-dirty-base-refresh-warning' , '--base' , 'dev' , '--direct-only' , '--no-cleanup' ] ,
1210+ repoDir ,
1211+ ) ;
1212+ assert . equal ( finish . status , 0 , finish . stderr || finish . stdout ) ;
1213+ assert . match (
1214+ finish . stderr ,
1215+ / W a r n i n g : l o c a l d e v w o r k t r e e i s d i r t y ; s k i p p i n g ' g i t p u l l - - f f - o n l y o r i g i n d e v ' f o r / ,
1216+ ) ;
1217+ assert . equal (
1218+ fs . existsSync ( path . join ( auxWorktree , 'agent-dirty-base-refresh.txt' ) ) ,
1219+ false ,
1220+ 'dirty local dev worktree should not be pulled implicitly' ,
1221+ ) ;
1222+ } ) ;
1223+
1224+
10811225test ( 'cleanup command removes merged agent branch/worktree and remote ref' , ( ) => {
10821226 const repoDir = initRepo ( ) ;
10831227 seedCommit ( repoDir ) ;
0 commit comments