@@ -13,6 +13,29 @@ vi.mock("@roo-code/cloud", () => ({
1313 getRooCodeApiUrl : ( ) => "https://test.api.com" ,
1414} ) )
1515
16+ // Mock the modes import
17+ vi . mock ( "../../../shared/modes" , ( ) => ( {
18+ modes : [
19+ {
20+ slug : "architect" ,
21+ name : "🏗️ Architect" ,
22+ roleDefinition : "You are an architect" ,
23+ whenToUse : "Use for planning" ,
24+ description : "Plan and design" ,
25+ groups : [ "read" , "edit" ] ,
26+ customInstructions : "Plan first" ,
27+ } ,
28+ {
29+ slug : "code" ,
30+ name : "💻 Code" ,
31+ roleDefinition : "You are a coder" ,
32+ whenToUse : "Use for coding" ,
33+ description : "Write code" ,
34+ groups : [ "read" , "edit" , "command" ] ,
35+ } ,
36+ ] ,
37+ } ) )
38+
1639describe ( "RemoteConfigLoader" , ( ) => {
1740 let loader : RemoteConfigLoader
1841
@@ -332,4 +355,124 @@ describe("RemoteConfigLoader", () => {
332355 Date . now = originalDateNow
333356 } )
334357 } )
358+
359+ describe ( "built-in modes integration" , ( ) => {
360+ it ( "should include built-in modes when API returns empty" , async ( ) => {
361+ const mockModesYaml = `items: []`
362+ const mockMcpsYaml = `items: []`
363+
364+ mockedAxios . get . mockImplementation ( ( url : string ) => {
365+ if ( url . includes ( "/modes" ) ) {
366+ return Promise . resolve ( { data : mockModesYaml } )
367+ }
368+ if ( url . includes ( "/mcps" ) ) {
369+ return Promise . resolve ( { data : mockMcpsYaml } )
370+ }
371+ return Promise . reject ( new Error ( "Unknown URL" ) )
372+ } )
373+
374+ const items = await loader . loadAllItems ( )
375+
376+ // Should include 2 built-in modes (architect and code from mock)
377+ expect ( items ) . toHaveLength ( 2 )
378+ expect ( items [ 0 ] ) . toEqual ( {
379+ type : "mode" ,
380+ id : "architect" ,
381+ name : "🏗️ Architect" ,
382+ description : "Plan and design" ,
383+ author : "Roo Code" ,
384+ tags : [ "built-in" , "core" ] ,
385+ content : expect . stringContaining ( "slug: architect" ) ,
386+ } )
387+ expect ( items [ 1 ] ) . toEqual ( {
388+ type : "mode" ,
389+ id : "code" ,
390+ name : "💻 Code" ,
391+ description : "Write code" ,
392+ author : "Roo Code" ,
393+ tags : [ "built-in" , "core" ] ,
394+ content : expect . stringContaining ( "slug: code" ) ,
395+ } )
396+ } )
397+
398+ it ( "should merge API modes with built-in modes" , async ( ) => {
399+ const mockModesYaml = `items:
400+ - id: "api-mode"
401+ name: "API Mode"
402+ description: "A mode from API"
403+ content: "test content"`
404+
405+ const mockMcpsYaml = `items: []`
406+
407+ mockedAxios . get . mockImplementation ( ( url : string ) => {
408+ if ( url . includes ( "/modes" ) ) {
409+ return Promise . resolve ( { data : mockModesYaml } )
410+ }
411+ if ( url . includes ( "/mcps" ) ) {
412+ return Promise . resolve ( { data : mockMcpsYaml } )
413+ }
414+ return Promise . reject ( new Error ( "Unknown URL" ) )
415+ } )
416+
417+ const items = await loader . loadAllItems ( )
418+
419+ // Should include 2 built-in modes + 1 API mode = 3 total
420+ expect ( items ) . toHaveLength ( 3 )
421+
422+ // Check that we have both built-in and API modes
423+ const modeIds = items . map ( item => item . id )
424+ expect ( modeIds ) . toContain ( "architect" )
425+ expect ( modeIds ) . toContain ( "code" )
426+ expect ( modeIds ) . toContain ( "api-mode" )
427+ } )
428+
429+ it ( "should allow API modes to override built-in modes" , async ( ) => {
430+ const mockModesYaml = `items:
431+ - id: "architect"
432+ name: "Custom Architect"
433+ description: "Overridden architect mode"
434+ content: "custom content"`
435+
436+ const mockMcpsYaml = `items: []`
437+
438+ mockedAxios . get . mockImplementation ( ( url : string ) => {
439+ if ( url . includes ( "/modes" ) ) {
440+ return Promise . resolve ( { data : mockModesYaml } )
441+ }
442+ if ( url . includes ( "/mcps" ) ) {
443+ return Promise . resolve ( { data : mockMcpsYaml } )
444+ }
445+ return Promise . reject ( new Error ( "Unknown URL" ) )
446+ } )
447+
448+ const items = await loader . loadAllItems ( )
449+
450+ // Should have 2 modes: overridden architect + built-in code
451+ expect ( items ) . toHaveLength ( 2 )
452+
453+ const architectMode = items . find ( item => item . id === "architect" )
454+ expect ( architectMode ) . toEqual ( {
455+ type : "mode" ,
456+ id : "architect" ,
457+ name : "Custom Architect" ,
458+ description : "Overridden architect mode" ,
459+ content : "custom content" ,
460+ } )
461+
462+ const codeMode = items . find ( item => item . id === "code" )
463+ expect ( codeMode ?. name ) . toBe ( "💻 Code" ) // Should be built-in version
464+ } )
465+
466+ it ( "should fallback to built-in modes when API fails" , async ( ) => {
467+ // Mock API to fail
468+ mockedAxios . get . mockRejectedValue ( new Error ( "API failure" ) )
469+
470+ const items = await loader . loadAllItems ( )
471+
472+ // Should still return built-in modes
473+ expect ( items ) . toHaveLength ( 2 )
474+ expect ( items [ 0 ] . id ) . toBe ( "architect" )
475+ expect ( items [ 1 ] . id ) . toBe ( "code" )
476+ } )
477+ } )
335478} )
0 commit comments