@@ -13,7 +13,7 @@ import {
1313 ToolPermissionChoice ,
1414} from './rovoDevApiClientInterfaces' ;
1515import { RovoDevTelemetryProvider } from './rovoDevTelemetryProvider' ;
16- import { RovoDevPrompt , TechnicalPlan } from './rovoDevTypes' ;
16+ import { RovoDevContextItem , RovoDevPrompt , TechnicalPlan } from './rovoDevTypes' ;
1717import { RovoDevProviderMessage , RovoDevProviderMessageType } from './rovoDevWebviewProviderMessages' ;
1818
1919interface TypedWebview < MessageOut , MessageIn > extends Webview {
@@ -333,15 +333,13 @@ export class RovoDevChatProvider {
333333
334334 case 'user-prompt' :
335335 if ( this . _replayInProgress ) {
336+ const { text, context } = this . parseUserPromptReplay ( response . content || '' ) ;
336337 this . _currentPrompt = {
337- text : response . content ,
338+ text : text ,
338339 enable_deep_plan : false ,
339- context : [ ] ,
340+ context : context ,
340341 } ;
341- return this . signalPromptSent (
342- { text : response . content , enable_deep_plan : false , context : [ ] } ,
343- true ,
344- ) ;
342+ return this . signalPromptSent ( { text, enable_deep_plan : false , context } , true ) ;
345343 }
346344 return Promise . resolve ( false ) ;
347345
@@ -542,4 +540,53 @@ export class RovoDevChatProvider {
542540 'The previous response interrupted prematurely because of an error. Continue processing the previous prompt from the point where it was interrupted.' ,
543541 } ) ;
544542 }
543+
544+ // Rovo Dev CLI inserts context into the response during replay
545+ // we need to parse it out to reconstruct the prompt
546+ // TODO: get a proper solution for this from the CLI team :)
547+ private parseUserPromptReplay ( source : string ) : { text : string ; context : RovoDevContextItem [ ] } {
548+ // Let's target the specific pattern from `/replay` to minimize the risk of
549+ // accidentally matching something in the user's prompt.
550+ const contextRegex =
551+ / < c o n t e x t > \n W h e n r e l e v a n t , u s e t h e c o n t e x t b e l o w t o b e t t e r r e s p o n d t o t h e m e s s a g e a b o v e ( [ \s \S ] * ?) < \/ c o n t e x t > $ / g;
552+ const contextMatch = contextRegex . exec ( source ) ;
553+
554+ if ( ! contextMatch ) {
555+ return { text : source . trim ( ) , context : [ ] } ;
556+ }
557+
558+ const contextContent = contextMatch [ 1 ] ;
559+ const context : RovoDevContextItem [ ] = [ ] ;
560+
561+ // Parse individual file entries within context
562+ const fileRegex = / < f i l e p a t h = " ( [ ^ " ] + ) " [ ^ > ] * > \s * ( [ ^ < ] * ) \s * < \/ f i l e > / g;
563+ let fileMatch ;
564+
565+ while ( ( fileMatch = fileRegex . exec ( contextContent ) ) !== null ) {
566+ const filePath = fileMatch [ 1 ] ;
567+
568+ // Parse selection info if available (format: "path" selection="start-end")
569+ const selectionMatch = fileMatch [ 0 ] . match ( / s e l e c t i o n = " ( \d + - \d + ) " / ) ;
570+ let selection : { start : number ; end : number } | undefined ;
571+
572+ if ( selectionMatch ) {
573+ const [ start , end ] = selectionMatch [ 1 ] . split ( '-' ) . map ( Number ) ;
574+ selection = { start, end } ;
575+ }
576+
577+ // Create context item for each file
578+ context . push ( {
579+ isFocus : false ,
580+ enabled : true ,
581+ file : {
582+ name : filePath . split ( '/' ) . pop ( ) || filePath ,
583+ absolutePath : filePath ,
584+ relativePath : filePath . split ( '/' ) . pop ( ) || filePath , // Use basename as relative path
585+ } ,
586+ selection : selection ,
587+ } ) ;
588+ }
589+
590+ return { text : source . replace ( contextRegex , '' ) . trim ( ) , context } ;
591+ }
545592}
0 commit comments