Coverage for sources / agentsmgr / renderers / codex.py: 30%

33 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-30 00:03 +0000

1# vim: set filetype=python fileencoding=utf-8: 

2# -*- coding: utf-8 -*- 

3 

4#============================================================================# 

5# # 

6# Licensed under the Apache License, Version 2.0 (the "License"); # 

7# you may not use this file except in compliance with the License. # 

8# You may obtain a copy of the License at # 

9# # 

10# http://www.apache.org/licenses/LICENSE-2.0 # 

11# # 

12# Unless required by applicable law or agreed to in writing, software # 

13# distributed under the License is distributed on an "AS IS" BASIS, # 

14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

15# See the License for the specific language governing permissions and # 

16# limitations under the License. # 

17# # 

18#============================================================================# 

19 

20 

21''' Codex CLI renderer implementation. 

22 

23 Provides path resolution and targeting mode validation for Codex CLI. 

24 Codex CLI only supports per-user configuration as of version 0.44.0. 

25''' 

26 

27 

28from . import __ 

29from .base import RENDERERS, ExplicitTargetMode, RendererBase 

30 

31 

32class CodexRenderer( RendererBase ): 

33 ''' Renderer for Codex CLI coder. 

34 

35 Only supports per-user configuration mode. Codex CLI does not 

36 support per-project configuration as of version 0.44.0. Per-user 

37 mode respects CODEX_HOME environment variable with fallback to 

38 configuration overrides and default location. 

39 ''' 

40 

41 name = 'codex' 

42 modes_available = frozenset( ( 'per-user', ) ) 

43 mode_default = 'per-user' 

44 memory_filename = 'AGENTS.md' 

45 

46 def get_template_flavor( self, item_type: str ) -> str: 

47 ''' Determines template flavor for Codex CLI. 

48 

49 Codex uses same markdown format as Claude for all item types, 

50 so always returns 'claude' flavor. 

51 ''' 

52 return 'claude' 

53 

54 def provide_project_symlinks( 

55 self, target: __.Path 

56 ) -> __.cabc.Sequence[ tuple[ __.Path, __.Path ] ]: 

57 ''' Provides symlinks required for Codex CLI in per-project mode. 

58 

59 Codex does not support per-project mode, so this method 

60 returns empty sequence. Only per-user mode is supported. 

61 ''' 

62 return [ ] 

63 

64 def resolve_base_directory( 

65 self, 

66 mode: ExplicitTargetMode, 

67 target: __.Path, 

68 configuration: __.cabc.Mapping[ str, __.typx.Any ], 

69 environment: __.cabc.Mapping[ str, str ], 

70 ) -> __.Path: 

71 ''' Resolves base output directory for Codex CLI. 

72 

73 Only per-user mode is supported. Respects precedence: CODEX_HOME 

74 environment variable, configuration file override (home key), or 

75 default ~/.codex location. Per-project mode raises error with 

76 explanation of Codex CLI limitation. 

77 ''' 

78 self.validate_mode( mode ) 

79 if mode == 'per-user': 

80 return self._resolve_user_directory( configuration, environment ) 

81 reason = ( 

82 "Codex CLI does not support per-project configuration. " 

83 "Only per-user configuration in ~/.codex or $CODEX_HOME " 

84 "is supported as of version 0.44.0." ) 

85 raise __.TargetModeNoSupport( self.name, mode, reason ) 

86 

87 def _resolve_user_directory( 

88 self, 

89 configuration: __.cabc.Mapping[ str, __.typx.Any ], 

90 environment: __.cabc.Mapping[ str, str ], 

91 ) -> __.Path: 

92 ''' Resolves per-user directory following precedence rules. 

93 

94 Precedence order: 

95 1. CODEX_HOME environment variable 

96 2. Configuration file override (home key for this coder) 

97 3. Default ~/.codex location 

98 ''' 

99 if 'CODEX_HOME' in environment: 

100 directory = __.Path( environment[ 'CODEX_HOME' ] ) 

101 return directory.expanduser( ) 

102 coder_configuration = self._extract_coder_configuration( 

103 configuration ) 

104 if 'home' in coder_configuration: 

105 directory = __.Path( coder_configuration[ 'home' ] ) 

106 return directory.expanduser( ) 

107 return __.Path.home( ) / '.codex' 

108 

109 def _extract_coder_configuration( 

110 self, configuration: __.cabc.Mapping[ str, __.typx.Any ] 

111 ) -> __.cabc.Mapping[ str, __.typx.Any ]: 

112 ''' Extracts configuration for this specific coder. 

113 

114 Looks for coder entry in configuration coders array by name. 

115 ''' 

116 coders = configuration.get( 'coders', ( ) ) 

117 for coder in coders: 

118 if coder.get( 'name' ) == self.name: 

119 return coder 

120 return { } 

121 

122 

123RENDERERS[ 'codex' ] = CodexRenderer( )