Coverage for sources / agentsmgr / renderers / base.py: 50%

26 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-12-01 15:37 +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''' Coder renderer base class and type definitions. 

22 

23 Defines base class for coder-specific renderers which handle 

24 path resolution, targeting mode validation, and output structure 

25 for different AI coding assistants. 

26''' 

27 

28 

29from . import __ 

30 

31 

32TargetMode: __.typx.TypeAlias = __.typx.Literal[ 

33 'default', 'per-user', 'per-project', 'nowhere' ] 

34ExplicitTargetMode: __.typx.TypeAlias = __.typx.Literal[ 

35 'per-user', 'per-project' ] 

36 

37 

38class RendererBase( __.immut.Object ): 

39 ''' Base class for coder-specific rendering and path resolution. 

40 

41 Provides interface that all coder renderers must implement for 

42 coder-specific behavior including targeting mode validation and 

43 path resolution for output files. 

44 ''' 

45 

46 name: str 

47 modes_available: frozenset[ ExplicitTargetMode ] 

48 mode_default: ExplicitTargetMode 

49 memory_filename: str 

50 

51 def validate_mode( self, mode: ExplicitTargetMode ) -> None: 

52 ''' Validates targeting mode is supported by this coder. 

53 

54 Raises TargetModeNoSupport if mode not supported. 

55 ''' 

56 if mode not in self.modes_available: 

57 raise __.TargetModeNoSupport( self.name, mode ) 

58 

59 def resolve_base_directory( 

60 self, 

61 mode: ExplicitTargetMode, 

62 target: __.Path, 

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

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

65 ) -> __.Path: 

66 ''' Resolves base output directory for this coder. 

67 

68 Determines appropriate output location based on targeting mode, 

69 respecting precedence of environment variables over file 

70 configuration over coder defaults. For per-user mode, checks 

71 environment first, then configuration file overrides, then 

72 falls back to coder-specific defaults. For per-project mode, 

73 constructs path within project structure. 

74 ''' 

75 raise NotImplementedError 

76 

77 def produce_output_structure( 

78 self, item_type: str, category: __.Absential[ str ] = __.absent 

79 ) -> str: 

80 ''' Produces subdirectory structure for item type. 

81 

82 Translates generic item type to coder-specific directory 

83 structure with optional category-based organization. Most 

84 coders use same structure, but some may have different 

85 conventions. Category enables hierarchical organization 

86 (e.g., commands/deploy/kubernetes for nested structure). 

87 ''' 

88 dirname = self.calculate_directory_location( item_type ) 

89 if __.is_absent( category ): return dirname 

90 return f"{dirname}/{category}" 

91 

92 def calculate_directory_location( self, item_type: str ) -> str: 

93 ''' Returns directory name for item type. Override in subclasses. ''' 

94 return item_type 

95 

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

97 ''' Determines template flavor (pioneering coder name) for item type. 

98 

99 Returns the name of the pioneering coder whose template format 

100 should be used for this item type. For example, Claude pioneered 

101 the markdown command format, Gemini pioneered the TOML format. 

102 Each coder specifies which flavor it uses for each item type. 

103 ''' 

104 return 'claude' 

105 

106 def provide_project_symlinks( 

107 self, target: __.Path 

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

109 ''' Provides symlinks required for coder in per-project mode. 

110 

111 Returns sequence of (source, link_path) tuples where source 

112 is the target path and link_path is where the symlink should 

113 be created. Default implementation returns base symlink from 

114 .auxiliary/configuration/coders/{coder_name} to .{coder_name}. 

115 

116 Subclasses may override to provide additional coder-specific 

117 symlinks (e.g., Claude's .mcp.json, OpenCode's opencode.jsonc). 

118 ''' 

119 source = ( 

120 target / '.auxiliary' / 'configuration' / 'coders' / self.name ) 

121 link_path = target / f'.{self.name}' 

122 return [ ( source, link_path ) ] 

123 

124 

125RENDERERS: __.accret.Dictionary[ str, RendererBase ] = ( 

126 __.accret.Dictionary( ) )