@@ -804,30 +804,83 @@ export class JJ {
804804 }
805805
806806 async navigateTop ( ) : Promise < Result < NavigationResult > > {
807- // Use heads(descendants(@)) to find the tip of the current stack
807+ // Check if @ is empty, undescribed, and has no children (already at top ready for work)
808+ const wcResult = await this . run ( [
809+ "log" ,
810+ "-r" ,
811+ "@" ,
812+ "--no-graph" ,
813+ "-T" ,
814+ 'empty ++ "\\t" ++ description.first_line()' ,
815+ ] ) ;
816+ const childrenResult = await this . run ( [
817+ "log" ,
818+ "-r" ,
819+ "@+" ,
820+ "--no-graph" ,
821+ "-T" ,
822+ "change_id.short()" ,
823+ ] ) ;
824+
825+ if ( wcResult . ok && childrenResult . ok ) {
826+ const [ empty , desc = "" ] = wcResult . value . stdout . trim ( ) . split ( "\t" ) ;
827+ const hasChildren = childrenResult . value . stdout . trim ( ) !== "" ;
828+
829+ if ( empty === "true" && desc === "" && ! hasChildren ) {
830+ // Already at top with empty @, return parent info
831+ const parentResult = await this . run ( [
832+ "log" ,
833+ "-r" ,
834+ "@-" ,
835+ "--no-graph" ,
836+ "-T" ,
837+ 'change_id.short() ++ "\\t" ++ description.first_line()' ,
838+ ] ) ;
839+ if ( parentResult . ok ) {
840+ const [ changeId , description ] = parentResult . value . stdout
841+ . trim ( )
842+ . split ( "\t" ) ;
843+ return ok ( { changeId, description : description || "" } ) ;
844+ }
845+ }
846+ }
847+
848+ // Navigate to the head of the stack
808849 const editResult = await this . run ( [ "edit" , "heads(descendants(@))" ] ) ;
809850 if ( ! editResult . ok ) {
810851 if ( editResult . error . message . includes ( "No descendant" ) ) {
811- return err ( createError ( "NAVIGATION_FAILED" , "Already at top of stack" ) ) ;
812- }
813- if ( editResult . error . message . includes ( "more than one revision" ) ) {
852+ // Already at head - fall through to create empty @
853+ } else if ( editResult . error . message . includes ( "more than one revision" ) ) {
814854 return err (
815855 createError (
816856 "NAVIGATION_FAILED" ,
817857 "Stack has multiple heads - cannot determine top" ,
818858 ) ,
819859 ) ;
860+ } else {
861+ return editResult ;
820862 }
821- return editResult ;
822863 }
823864
824- const status = await this . status ( ) ;
825- if ( ! status . ok ) return status ;
865+ // Create a new empty change above the head for new work
866+ const newResult = await this . run ( [ "new" ] ) ;
867+ if ( ! newResult . ok ) return newResult ;
826868
827- return ok ( {
828- changeId : status . value . workingCopy . changeId ,
829- description : status . value . workingCopy . description ,
830- } ) ;
869+ // Get parent info (the actual stack top)
870+ const parentResult = await this . run ( [
871+ "log" ,
872+ "-r" ,
873+ "@-" ,
874+ "--no-graph" ,
875+ "-T" ,
876+ 'change_id.short() ++ "\\t" ++ description.first_line()' ,
877+ ] ) ;
878+ if ( ! parentResult . ok ) return parentResult ;
879+
880+ const [ changeId , description ] = parentResult . value . stdout
881+ . trim ( )
882+ . split ( "\t" ) ;
883+ return ok ( { changeId, description : description || "" } ) ;
831884 }
832885
833886 async navigateBottom ( ) : Promise < Result < NavigationResult > > {
0 commit comments