Coverage for sources/agentsmgr/commands/context.py: 15%
44 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-13 00:43 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-13 00:43 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
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#============================================================================#
21''' Template rendering context normalization and tool mapping.
23 Provides context transformation for template rendering, including
24 hyphen-to-underscore normalization and coder-specific tool mapping.
25'''
28from . import __
31ToolSpecification: __.typx.TypeAlias = (
32 str | dict[ str, __.typx.Any ] )
35_SEMANTIC_TOOLS_CLAUDE: dict[ str, str ] = {
36 'read': 'Read',
37 'edit': 'Edit',
38 'multi-edit': 'MultiEdit',
39 'write': 'Write',
40 'list-directory': 'LS',
41 'glob': 'Glob',
42 'grep': 'Grep',
43 'todo-write': 'TodoWrite',
44 'web-fetch': 'WebFetch',
45 'web-search': 'WebSearch',
46}
49def normalize_render_context(
50 context_data: __.cabc.Mapping[ str, __.typx.Any ],
51 coder_config: __.cabc.Mapping[ str, __.typx.Any ],
52) -> dict[ str, __.typx.Any ]:
53 ''' Normalizes template rendering context with tool mapping.
55 Transforms hyphenated keys to underscored keys, wraps configurations
56 in SimpleNamespace objects for dot-notation access, and maps
57 allowed-tools specifications to coder-specific syntax.
58 '''
59 coder_name = coder_config.get( 'name', 'unknown' )
60 normalized_context = {
61 key.replace( '-', '_' ): value
62 for key, value in context_data.items( ) }
63 if 'allowed_tools' in normalized_context:
64 raw_tools = normalized_context[ 'allowed_tools' ]
65 normalized_context[ 'allowed_tools' ] = (
66 _map_tools_for_coder( raw_tools, coder_name ) )
67 context_namespace = __.types.SimpleNamespace( **normalized_context )
68 coder_namespace = __.types.SimpleNamespace( **coder_config )
69 return {
70 'context': context_namespace,
71 'coder': coder_namespace,
72 }
75def _map_tools_for_coder(
76 tool_specs: __.cabc.Sequence[ ToolSpecification ],
77 coder_name: str,
78) -> list[ str ]:
79 ''' Maps tool specifications to coder-specific syntax.
81 Dispatches to coder-specific mapping function based on coder name.
82 Currently supports Claude; extensible for other coders.
83 '''
84 if coder_name == 'claude':
85 return _map_tools_claude( tool_specs )
86 return [ ]
89def _map_tools_claude(
90 tool_specs: __.cabc.Sequence[ ToolSpecification ]
91) -> list[ str ]:
92 ''' Maps tool specifications to Claude-specific syntax.
94 Handles three specification types:
95 - String literals (semantic names): 'read' → 'Read'
96 - Shell commands: { tool = 'shell', arguments, ... } → 'Bash(...)'
97 - MCP tools: { server, tool } → 'mcp__server__tool'
99 Returns tools sorted alphabetically for consistent output.
100 '''
101 mapped: list[ str ] = [ ]
102 for spec in tool_specs:
103 if isinstance( spec, str ):
104 mapped.append( _map_semantic_tool_claude( spec ) )
105 elif isinstance( spec, dict ):
106 if 'server' in spec:
107 mapped.append( _map_mcp_tool_claude( spec ) )
108 elif spec.get( 'tool' ) == 'shell':
109 mapped.append( _map_shell_tool_claude( spec ) )
110 else:
111 raise __.ToolSpecificationInvalidity( str( spec ) )
112 else:
113 raise __.ToolSpecificationTypeInvalidity( type( spec ).__name__ )
114 return sorted( mapped )
117def _map_semantic_tool_claude( tool_name: str ) -> str:
118 ''' Maps semantic tool name to Claude tool name.
120 Uses lookup table for known semantic names.
121 Raises ToolSpecificationInvalidity for unknown tools.
122 '''
123 if tool_name not in _SEMANTIC_TOOLS_CLAUDE:
124 raise __.ToolSpecificationInvalidity( tool_name )
125 return _SEMANTIC_TOOLS_CLAUDE[ tool_name ]
128def _map_shell_tool_claude( spec: dict[ str, __.typx.Any ] ) -> str:
129 ''' Maps shell command specification to Claude Bash tool syntax.
131 Format: { tool = 'shell', arguments = 'git status' }
132 → 'Bash(git status)'
134 With wildcard: { tool = 'shell', arguments = 'git pull',
135 allow-extra-arguments = true }
136 → 'Bash(git pull:*)'
137 '''
138 arguments = spec.get( 'arguments', '' )
139 allow_extra = spec.get( 'allow-extra-arguments', False )
140 if allow_extra:
141 return f"Bash({arguments}:*)"
142 return f"Bash({arguments})"
145def _map_mcp_tool_claude( spec: dict[ str, __.typx.Any ] ) -> str:
146 ''' Maps MCP tool specification to Claude MCP tool syntax.
148 Format: { server = 'librovore', tool = 'query-inventory' }
149 → 'mcp__librovore__query_inventory'
150 '''
151 server = spec.get( 'server', '' )
152 tool = spec.get( 'tool', '' )
153 tool_normalized = tool.replace( '-', '_' )
154 return f"mcp__{server}__{tool_normalized}"