emcd-agents 1.0a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentsmgr/__/__init__.py +25 -0
- agentsmgr/__/imports.py +56 -0
- agentsmgr/__/nomina.py +32 -0
- agentsmgr/__init__.py +40 -0
- agentsmgr/__main__.py +28 -0
- agentsmgr/_typedecls/dulwich/__init__.pyi +1 -0
- agentsmgr/_typedecls/dulwich/porcelain.pyi +11 -0
- agentsmgr/_typedecls/dulwich/repo.pyi +28 -0
- agentsmgr/cli.py +70 -0
- agentsmgr/commands/__.py +32 -0
- agentsmgr/commands/__init__.py +26 -0
- agentsmgr/commands/base.py +153 -0
- agentsmgr/commands/context.py +154 -0
- agentsmgr/commands/detection.py +56 -0
- agentsmgr/commands/generator.py +242 -0
- agentsmgr/commands/memorylinks.py +112 -0
- agentsmgr/commands/operations.py +103 -0
- agentsmgr/commands/population.py +181 -0
- agentsmgr/commands/userdata.py +206 -0
- agentsmgr/commands/validation.py +109 -0
- agentsmgr/core.py +75 -0
- agentsmgr/data/.gitignore +0 -0
- agentsmgr/data/configuration/general.toml +25 -0
- agentsmgr/exceptions.py +213 -0
- agentsmgr/py.typed +0 -0
- agentsmgr/renderers/__.py +28 -0
- agentsmgr/renderers/__init__.py +32 -0
- agentsmgr/renderers/base.py +88 -0
- agentsmgr/renderers/claude.py +102 -0
- agentsmgr/renderers/codex.py +105 -0
- agentsmgr/renderers/opencode.py +103 -0
- agentsmgr/results.py +103 -0
- agentsmgr/sources/__.py +28 -0
- agentsmgr/sources/__init__.py +37 -0
- agentsmgr/sources/base.py +104 -0
- agentsmgr/sources/git.py +249 -0
- agentsmgr/sources/local.py +48 -0
- emcd_agents-1.0a0.dist-info/METADATA +303 -0
- emcd_agents-1.0a0.dist-info/RECORD +42 -0
- emcd_agents-1.0a0.dist-info/WHEEL +4 -0
- emcd_agents-1.0a0.dist-info/entry_points.txt +2 -0
- emcd_agents-1.0a0.dist-info/licenses/LICENSE.txt +202 -0
agentsmgr/__/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
''' Common constants, imports, and utilities. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from .imports import *
|
|
25
|
+
from .nomina import *
|
agentsmgr/__/imports.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
''' Common imports used throughout the package. '''
|
|
22
|
+
|
|
23
|
+
# ruff: noqa: F401
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
import abc
|
|
27
|
+
import asyncio
|
|
28
|
+
import collections.abc as cabc
|
|
29
|
+
import contextlib as ctxl
|
|
30
|
+
import dataclasses as dcls
|
|
31
|
+
import enum
|
|
32
|
+
import functools as funct
|
|
33
|
+
import os
|
|
34
|
+
import shutil
|
|
35
|
+
import sys
|
|
36
|
+
import tempfile
|
|
37
|
+
import types
|
|
38
|
+
|
|
39
|
+
from logging import getLogger as provide_scribe
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
import accretive as accret
|
|
44
|
+
import appcore
|
|
45
|
+
import appcore.exceptions
|
|
46
|
+
import appcore.state
|
|
47
|
+
import dynadoc as ddoc
|
|
48
|
+
import frigid as immut
|
|
49
|
+
import tomli
|
|
50
|
+
import typing_extensions as typx
|
|
51
|
+
# --- BEGIN: Injected by Copier ---
|
|
52
|
+
import tyro
|
|
53
|
+
# --- END: Injected by Copier ---
|
|
54
|
+
|
|
55
|
+
from absence import Absential, absent, is_absent
|
|
56
|
+
from appcore import cli as appcore_cli
|
agentsmgr/__/nomina.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
''' Common names and type aliases. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from . import imports as __
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
ComparisonResult: __.typx.TypeAlias = bool | __.types.NotImplementedType
|
|
28
|
+
NominativeArguments: __.typx.TypeAlias = __.cabc.Mapping[ str, __.typx.Any ]
|
|
29
|
+
PositionalArguments: __.typx.TypeAlias = __.cabc.Sequence[ __.typx.Any ]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
package_name = __name__.split( '.', maxsplit = 1 )[ 0 ]
|
agentsmgr/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
''' Common collection of AI agents configuration and data. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from . import __
|
|
25
|
+
# --- BEGIN: Injected by Copier ---
|
|
26
|
+
from . import exceptions
|
|
27
|
+
# --- END: Injected by Copier ---
|
|
28
|
+
from . import sources
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__version__ = '1.0a0'
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main( ):
|
|
35
|
+
''' Entrypoint. '''
|
|
36
|
+
from .cli import execute
|
|
37
|
+
execute( )
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# TODO: Reclassify package modules as immutable and concealed.
|
agentsmgr/__main__.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
''' Entrypoint. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Note: Use absolute import for PyInstaller happiness.
|
|
25
|
+
from agentsmgr.cli import execute
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if '__main__' == __name__: execute( )
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Minimal type stubs for dulwich
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Type stubs for dulwich.repo
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
class GitCommit:
|
|
6
|
+
commit_time: int
|
|
7
|
+
object: bytes | None
|
|
8
|
+
|
|
9
|
+
class GitRefs:
|
|
10
|
+
def as_dict(self, prefix: bytes) -> Dict[bytes, bytes]: ...
|
|
11
|
+
def __contains__(self, key: bytes) -> bool: ...
|
|
12
|
+
def __getitem__(self, key: bytes) -> bytes: ...
|
|
13
|
+
|
|
14
|
+
class Repo:
|
|
15
|
+
def __init__(self, path: str) -> None: ...
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def refs(self) -> GitRefs: ...
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def object_store(self) -> Any: ...
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def path(self) -> str: ...
|
|
25
|
+
|
|
26
|
+
def __getitem__(self, key: bytes) -> GitCommit: ...
|
|
27
|
+
|
|
28
|
+
def open_index(self) -> Any: ...
|
agentsmgr/cli.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
''' Command-line interface. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from . import __
|
|
25
|
+
from . import commands as _commands
|
|
26
|
+
from . import core as _core
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Application( __.appcore_cli.Application ):
|
|
30
|
+
''' Agent configuration management CLI. '''
|
|
31
|
+
|
|
32
|
+
display: _core.DisplayOptions = __.dcls.field(
|
|
33
|
+
default_factory = _core.DisplayOptions )
|
|
34
|
+
command: __.typx.Union[
|
|
35
|
+
__.typx.Annotated[
|
|
36
|
+
_commands.DetectCommand,
|
|
37
|
+
__.tyro.conf.subcommand( 'detect', prefix_name = False ),
|
|
38
|
+
],
|
|
39
|
+
__.typx.Annotated[
|
|
40
|
+
_commands.PopulateCommand,
|
|
41
|
+
__.tyro.conf.subcommand( 'populate', prefix_name = False ),
|
|
42
|
+
],
|
|
43
|
+
__.typx.Annotated[
|
|
44
|
+
_commands.ValidateCommand,
|
|
45
|
+
__.tyro.conf.subcommand( 'validate', prefix_name = False ),
|
|
46
|
+
],
|
|
47
|
+
] = __.dcls.field( default_factory = _commands.DetectCommand )
|
|
48
|
+
|
|
49
|
+
async def execute( self, auxdata: _core.Globals ) -> None: # pyright: ignore[reportIncompatibleMethodOverride]
|
|
50
|
+
''' Executes the specified command. '''
|
|
51
|
+
await self.command( auxdata )
|
|
52
|
+
|
|
53
|
+
async def prepare( self, exits: __.ctxl.AsyncExitStack ) -> _core.Globals:
|
|
54
|
+
''' Prepares agentsmgr-specific global state with display options. '''
|
|
55
|
+
auxdata_base = await super( ).prepare( exits )
|
|
56
|
+
nomargs = {
|
|
57
|
+
field.name: getattr( auxdata_base, field.name )
|
|
58
|
+
for field in __.dcls.fields( auxdata_base )
|
|
59
|
+
if not field.name.startswith( '_' ) }
|
|
60
|
+
return _core.Globals( display = self.display, **nomargs )
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def execute( ) -> None:
|
|
64
|
+
''' Entrypoint for CLI execution. '''
|
|
65
|
+
config = ( __.tyro.conf.HelptextFromCommentsOff, )
|
|
66
|
+
try: __.asyncio.run( __.tyro.cli( Application, config = config )( ) )
|
|
67
|
+
except SystemExit: raise
|
|
68
|
+
except BaseException:
|
|
69
|
+
# TODO: Log exception.
|
|
70
|
+
raise SystemExit( 1 ) from None
|
agentsmgr/commands/__.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
''' Centralized imports for commands subpackage. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ruff: noqa: F403
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
from ..__ import *
|
|
28
|
+
from ..core import *
|
|
29
|
+
from ..exceptions import *
|
|
30
|
+
from ..renderers import *
|
|
31
|
+
from ..results import *
|
|
32
|
+
from ..sources import *
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
''' Command implementations for agentsmgr CLI. '''
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from .detection import DetectCommand
|
|
25
|
+
from .population import PopulateCommand
|
|
26
|
+
from .validation import ValidateCommand
|
|
@@ -0,0 +1,153 @@
|
|
|
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
|
+
''' Shared infrastructure for command implementations.
|
|
22
|
+
|
|
23
|
+
This module provides common utilities used across multiple command
|
|
24
|
+
implementations, including error handling, configuration management,
|
|
25
|
+
and data location resolution.
|
|
26
|
+
'''
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
import yaml as _yaml
|
|
30
|
+
|
|
31
|
+
from . import __
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
CoderConfiguration: __.typx.TypeAlias = __.cabc.Mapping[ str, __.typx.Any ]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def intercept_errors( ) -> __.cabc.Callable[
|
|
38
|
+
[ __.cabc.Callable[
|
|
39
|
+
..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ] ] ],
|
|
40
|
+
__.cabc.Callable[
|
|
41
|
+
..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ] ]
|
|
42
|
+
]:
|
|
43
|
+
''' Decorator for CLI command handlers to intercept and render errors.
|
|
44
|
+
|
|
45
|
+
Provides clean separation between business logic and error handling:
|
|
46
|
+
|
|
47
|
+
**Purpose**: Enables command implementations to focus purely on
|
|
48
|
+
business logic while the decorator handles all error presentation
|
|
49
|
+
concerns.
|
|
50
|
+
|
|
51
|
+
**Responsibilities**:
|
|
52
|
+
|
|
53
|
+
- Intercepts Omnierror exceptions from command execution
|
|
54
|
+
- Renders errors in appropriate format (markdown for CLI)
|
|
55
|
+
- Ensures proper exit code handling (SystemExit with code 1)
|
|
56
|
+
|
|
57
|
+
**Pattern**: Commands implement business logic and raise exceptions;
|
|
58
|
+
decorator handles presentation and process termination. This
|
|
59
|
+
separation ensures commands remain testable and focused.
|
|
60
|
+
|
|
61
|
+
**Type narrowing note**: The isinstance(auxdata, _core.Globals)
|
|
62
|
+
checks in individual command execute methods serve type narrowing
|
|
63
|
+
purposes and must be retained for proper type checking.
|
|
64
|
+
'''
|
|
65
|
+
def decorator(
|
|
66
|
+
function: __.cabc.Callable[
|
|
67
|
+
..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ] ]
|
|
68
|
+
) -> __.cabc.Callable[
|
|
69
|
+
..., __.cabc.Coroutine[ __.typx.Any, __.typx.Any, None ]
|
|
70
|
+
]:
|
|
71
|
+
@__.funct.wraps( function )
|
|
72
|
+
async def wrapper(
|
|
73
|
+
self: __.typx.Any,
|
|
74
|
+
auxdata: __.typx.Any,
|
|
75
|
+
*posargs: __.typx.Any,
|
|
76
|
+
**nomargs: __.typx.Any,
|
|
77
|
+
) -> None:
|
|
78
|
+
try: return await function( self, auxdata, *posargs, **nomargs )
|
|
79
|
+
except __.Omnierror as exception:
|
|
80
|
+
if isinstance( auxdata, __.Globals ):
|
|
81
|
+
await __.render_and_print_result(
|
|
82
|
+
exception, auxdata.display, auxdata.exits )
|
|
83
|
+
else:
|
|
84
|
+
for line in exception.render_as_markdown( ):
|
|
85
|
+
print( line, file = __.sys.stderr )
|
|
86
|
+
raise SystemExit( 1 ) from None
|
|
87
|
+
return wrapper
|
|
88
|
+
return decorator
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
async def retrieve_configuration(
|
|
92
|
+
target: __.Path
|
|
93
|
+
) -> __.cabc.Mapping[ str, __.typx.Any ]:
|
|
94
|
+
''' Loads and validates configuration from Copier answers file.
|
|
95
|
+
|
|
96
|
+
Unified configuration loading used by multiple command
|
|
97
|
+
implementations. Reads from standard Copier answers location
|
|
98
|
+
and validates required fields.
|
|
99
|
+
'''
|
|
100
|
+
answers_file = (
|
|
101
|
+
target / ".auxiliary/configuration/copier-answers--agents.yaml" )
|
|
102
|
+
if not answers_file.exists( ):
|
|
103
|
+
raise __.ConfigurationAbsence( target )
|
|
104
|
+
try: content = answers_file.read_text( encoding = 'utf-8' )
|
|
105
|
+
except ( OSError, IOError ) as exception:
|
|
106
|
+
raise __.ConfigurationAbsence( ) from exception
|
|
107
|
+
try:
|
|
108
|
+
configuration: __.cabc.Mapping[ str, __.typx.Any ] = (
|
|
109
|
+
_yaml.safe_load( content ) )
|
|
110
|
+
except _yaml.YAMLError as exception:
|
|
111
|
+
raise __.ConfigurationInvalidity( exception ) from exception
|
|
112
|
+
if not isinstance( configuration, __.cabc.Mapping ):
|
|
113
|
+
raise __.ConfigurationInvalidity( )
|
|
114
|
+
await validate_configuration( configuration )
|
|
115
|
+
return configuration
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
async def validate_configuration(
|
|
119
|
+
configuration: __.cabc.Mapping[ str, __.typx.Any ]
|
|
120
|
+
) -> None:
|
|
121
|
+
''' Validates required configuration fields are present and non-empty. '''
|
|
122
|
+
if not configuration.get( 'coders' ):
|
|
123
|
+
raise __.ConfigurationInvalidity( )
|
|
124
|
+
if not configuration.get( 'languages' ):
|
|
125
|
+
raise __.ConfigurationInvalidity( )
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def retrieve_data_location( source_spec: str ) -> __.Path:
|
|
129
|
+
''' Resolves data source specification to local filesystem path.
|
|
130
|
+
|
|
131
|
+
Supports local paths, Git repositories, and remote sources through
|
|
132
|
+
pluggable source handlers. Uses registered handlers to resolve
|
|
133
|
+
various URL schemes to local filesystem paths.
|
|
134
|
+
'''
|
|
135
|
+
return __.resolve_source_location( source_spec )
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def retrieve_variant_answers_file(
|
|
139
|
+
auxdata: __.Globals, variant: str
|
|
140
|
+
) -> __.Path:
|
|
141
|
+
''' Retrieves path to variant answers file in test data directory.
|
|
142
|
+
|
|
143
|
+
Validates file existence and raises ConfigurationAbsence if not
|
|
144
|
+
found.
|
|
145
|
+
'''
|
|
146
|
+
data_directory = auxdata.provide_data_location( )
|
|
147
|
+
project_root = data_directory.parent
|
|
148
|
+
answers_file = (
|
|
149
|
+
project_root / 'tests' / 'data' / 'profiles'
|
|
150
|
+
/ f"answers-{variant}.yaml" )
|
|
151
|
+
if not answers_file.exists( ):
|
|
152
|
+
raise __.ConfigurationAbsence( )
|
|
153
|
+
return answers_file
|
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
''' Template rendering context normalization and tool mapping.
|
|
22
|
+
|
|
23
|
+
Provides context transformation for template rendering, including
|
|
24
|
+
hyphen-to-underscore normalization and coder-specific tool mapping.
|
|
25
|
+
'''
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
from . import __
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
ToolSpecification: __.typx.TypeAlias = (
|
|
32
|
+
str | dict[ str, __.typx.Any ] )
|
|
33
|
+
|
|
34
|
+
|
|
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
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def 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.
|
|
54
|
+
|
|
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
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _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.
|
|
80
|
+
|
|
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 [ ]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _map_tools_claude(
|
|
90
|
+
tool_specs: __.cabc.Sequence[ ToolSpecification ]
|
|
91
|
+
) -> list[ str ]:
|
|
92
|
+
''' Maps tool specifications to Claude-specific syntax.
|
|
93
|
+
|
|
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'
|
|
98
|
+
|
|
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 )
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _map_semantic_tool_claude( tool_name: str ) -> str:
|
|
118
|
+
''' Maps semantic tool name to Claude tool name.
|
|
119
|
+
|
|
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 ]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _map_shell_tool_claude( spec: dict[ str, __.typx.Any ] ) -> str:
|
|
129
|
+
''' Maps shell command specification to Claude Bash tool syntax.
|
|
130
|
+
|
|
131
|
+
Format: { tool = 'shell', arguments = 'git status' }
|
|
132
|
+
→ 'Bash(git status)'
|
|
133
|
+
|
|
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})"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _map_mcp_tool_claude( spec: dict[ str, __.typx.Any ] ) -> str:
|
|
146
|
+
''' Maps MCP tool specification to Claude MCP tool syntax.
|
|
147
|
+
|
|
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}"
|