mycode-aiagent 0.1.4__tar.gz → 0.2.1__tar.gz
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.
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/PKG-INFO +1 -1
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/__init__.py +1 -1
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/analyzer.py +20 -18
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/backends/__init__.py +3 -2
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/backends/mcp_backend.py +2 -2
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/backends/ricky_backend.py +2 -2
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/cli.py +3 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/mcp_client.py +5 -4
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/utils/prompts.py +11 -6
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/mycode_aiagent.egg-info/PKG-INFO +1 -1
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/pyproject.toml +1 -1
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/README.md +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/__main__.py +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/backends/base.py +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/backends/claude_backend.py +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/backends/openai_backend.py +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/generator.py +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/my_code/utils/__init__.py +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/mycode_aiagent.egg-info/SOURCES.txt +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/mycode_aiagent.egg-info/dependency_links.txt +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/mycode_aiagent.egg-info/entry_points.txt +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/mycode_aiagent.egg-info/requires.txt +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/mycode_aiagent.egg-info/top_level.txt +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/setup.cfg +0 -0
- {mycode_aiagent-0.1.4 → mycode_aiagent-0.2.1}/tests/test_library.py +0 -0
|
@@ -3,7 +3,7 @@ import re
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from .backends import AIBackend
|
|
6
|
-
from .utils.prompts import STYLE_EXTRACTION_PROMPT,
|
|
6
|
+
from .utils.prompts import STYLE_EXTRACTION_PROMPT, STYLE_MERGE_PROMPT
|
|
7
7
|
|
|
8
8
|
_SKIP_EXACT = {"__pycache__", ".git", "node_modules", "dist", "build"}
|
|
9
9
|
|
|
@@ -52,30 +52,32 @@ class StyleAnalyzer:
|
|
|
52
52
|
if not files:
|
|
53
53
|
raise FileNotFoundError(f"No Python files found under {root}")
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
profile = {}
|
|
56
56
|
for path in files:
|
|
57
57
|
rel = path.relative_to(root)
|
|
58
58
|
if verbose:
|
|
59
59
|
print(f" Analyzing {rel} ...")
|
|
60
60
|
obs = self.analyze_file(path)
|
|
61
|
-
if obs:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
if not obs:
|
|
62
|
+
continue
|
|
63
|
+
if not profile:
|
|
64
|
+
profile = obs
|
|
65
|
+
continue
|
|
66
|
+
prompt = STYLE_MERGE_PROMPT.format(
|
|
67
|
+
profile=json.dumps(profile, indent=2),
|
|
68
|
+
observation=json.dumps(obs, indent=2),
|
|
69
|
+
filename=path.name,
|
|
70
|
+
)
|
|
71
|
+
raw = self.backend.ask_to_analyze(prompt)
|
|
72
|
+
try:
|
|
73
|
+
profile = _extract_json(raw)
|
|
74
|
+
except (ValueError, json.JSONDecodeError) as e:
|
|
75
|
+
print(f" [warn] Could not merge style from {path.name}: {e}")
|
|
76
|
+
|
|
77
|
+
if not profile:
|
|
65
78
|
raise RuntimeError("No style data could be extracted from any file.")
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
return observations[0]
|
|
69
|
-
|
|
70
|
-
prompt = STYLE_SUMMARY_PROMPT.format(
|
|
71
|
-
observations=json.dumps(observations, indent=2)
|
|
72
|
-
)
|
|
73
|
-
raw = self.backend.ask_to_analyze(prompt)
|
|
74
|
-
try:
|
|
75
|
-
return _extract_json(raw)
|
|
76
|
-
except (ValueError, json.JSONDecodeError) as e:
|
|
77
|
-
print(f"[warn] Could not synthesize profile, using first observation: {e}")
|
|
78
|
-
return observations[0]
|
|
80
|
+
return profile
|
|
79
81
|
|
|
80
82
|
@staticmethod
|
|
81
83
|
def save_profile(profile: dict, path: Path):
|
|
@@ -15,6 +15,7 @@ def make_backend(
|
|
|
15
15
|
ricky_url: str = "http://localhost:8000/mcp",
|
|
16
16
|
mcp_url: str = "http://localhost:8001/mcp",
|
|
17
17
|
model: str | None = None,
|
|
18
|
+
timeout: int = 120,
|
|
18
19
|
) -> AIBackend:
|
|
19
20
|
if backend == "claude":
|
|
20
21
|
key = api_key or os.environ.get("ANTHROPIC_API_KEY")
|
|
@@ -27,7 +28,7 @@ def make_backend(
|
|
|
27
28
|
raise ValueError("OpenAI backend requires OPENAI_API_KEY or --api-key")
|
|
28
29
|
return OpenAIBackend(api_key=key, **{"model": model} if model else {})
|
|
29
30
|
if backend == "mcp":
|
|
30
|
-
return MCPBackend(url=mcp_url)
|
|
31
|
+
return MCPBackend(url=mcp_url, timeout=timeout)
|
|
31
32
|
if backend == "llama":
|
|
32
|
-
return RickyBackend(url=ricky_url)
|
|
33
|
+
return RickyBackend(url=ricky_url, timeout=timeout)
|
|
33
34
|
raise ValueError(f"Unknown backend {backend!r}. Choose: llama, claude, openai, mcp")
|
|
@@ -3,8 +3,8 @@ from ..mcp_client import MCPClient
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class MCPBackend(AIBackend):
|
|
6
|
-
def __init__(self, url: str = "http://localhost:8001/mcp"):
|
|
7
|
-
self._client = MCPClient(url=url)
|
|
6
|
+
def __init__(self, url: str = "http://localhost:8001/mcp", timeout: int = 120):
|
|
7
|
+
self._client = MCPClient(url=url, timeout=timeout)
|
|
8
8
|
|
|
9
9
|
def ask_for_code(self, prompt: str) -> str:
|
|
10
10
|
return self._client.ask_for_code(prompt)
|
|
@@ -5,8 +5,8 @@ from ..mcp_client import MCPClient
|
|
|
5
5
|
class RickyBackend(AIBackend):
|
|
6
6
|
max_file_chars: int = 1500
|
|
7
7
|
|
|
8
|
-
def __init__(self, url: str = "http://localhost:8000/mcp"):
|
|
9
|
-
self._client = MCPClient(url=url)
|
|
8
|
+
def __init__(self, url: str = "http://localhost:8000/mcp", timeout: int = 120):
|
|
9
|
+
self._client = MCPClient(url=url, timeout=timeout)
|
|
10
10
|
|
|
11
11
|
def ask_for_code(self, prompt: str) -> str:
|
|
12
12
|
return self._client.ask_for_code(prompt)
|
|
@@ -29,6 +29,7 @@ def cmd_analyze(args):
|
|
|
29
29
|
ricky_url=args.ricky_url,
|
|
30
30
|
mcp_url=args.mcp_url,
|
|
31
31
|
model=args.model,
|
|
32
|
+
timeout=args.timeout,
|
|
32
33
|
)
|
|
33
34
|
|
|
34
35
|
print(f"Analyzing codebase at {root} ...")
|
|
@@ -53,6 +54,7 @@ def cmd_generate(args):
|
|
|
53
54
|
ricky_url=args.ricky_url,
|
|
54
55
|
mcp_url=args.mcp_url,
|
|
55
56
|
model=args.model,
|
|
57
|
+
timeout=args.timeout,
|
|
56
58
|
)
|
|
57
59
|
|
|
58
60
|
profile = json.loads(profile_path.read_text(encoding="utf-8"))
|
|
@@ -72,6 +74,7 @@ def main():
|
|
|
72
74
|
parser.add_argument("--model", default=None, help="Override default model for claude/openai backends")
|
|
73
75
|
parser.add_argument("--ricky-url", default="http://localhost:8000/mcp", help="Ricky MCP server URL (llama backend)")
|
|
74
76
|
parser.add_argument("--mcp-url", default="http://localhost:8001/mcp", help="MCP server URL (mcp backend)")
|
|
77
|
+
parser.add_argument("--timeout", type=int, default=120, help="Request timeout in seconds (default: 120)")
|
|
75
78
|
parser.add_argument("--profile", default=str(DEFAULT_PROFILE))
|
|
76
79
|
|
|
77
80
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -24,8 +24,9 @@ class MCPClient:
|
|
|
24
24
|
3. POST /mcp tools/call → invoke a tool
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
-
def __init__(self, url: str = "http://localhost:8001/mcp"):
|
|
27
|
+
def __init__(self, url: str = "http://localhost:8001/mcp", timeout: int = 120):
|
|
28
28
|
self.url = url
|
|
29
|
+
self._timeout = timeout
|
|
29
30
|
self._session_id: str | None = None
|
|
30
31
|
self._tool_name: str | None = None
|
|
31
32
|
self._tool_name_analyze: str | None = None
|
|
@@ -35,7 +36,7 @@ class MCPClient:
|
|
|
35
36
|
self._discover_tools()
|
|
36
37
|
print(f"Connected to MCP server — tools: '{self._tool_name}', '{self._tool_name_analyze}'")
|
|
37
38
|
|
|
38
|
-
def _post(self, method: str, params: dict
|
|
39
|
+
def _post(self, method: str, params: dict) -> dict:
|
|
39
40
|
headers = {
|
|
40
41
|
"Content-Type": "application/json",
|
|
41
42
|
"Accept": "application/json, text/event-stream",
|
|
@@ -49,7 +50,7 @@ class MCPClient:
|
|
|
49
50
|
"method": method,
|
|
50
51
|
"params": params,
|
|
51
52
|
}
|
|
52
|
-
resp = requests.post(self.url, json=payload, headers=headers, timeout=
|
|
53
|
+
resp = requests.post(self.url, json=payload, headers=headers, timeout=self._timeout)
|
|
53
54
|
resp.raise_for_status()
|
|
54
55
|
|
|
55
56
|
if "Mcp-Session-Id" in resp.headers:
|
|
@@ -117,5 +118,5 @@ class MCPClient:
|
|
|
117
118
|
result = self._post("tools/call", {
|
|
118
119
|
"name": self._tool_name_analyze,
|
|
119
120
|
"arguments": {self._analyze_arg: prompt},
|
|
120
|
-
}
|
|
121
|
+
})
|
|
121
122
|
return self._extract_text(result)
|
|
@@ -30,15 +30,20 @@ Source file ({filename}):
|
|
|
30
30
|
```
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
You are a code style analyst.
|
|
33
|
+
STYLE_MERGE_PROMPT = """\
|
|
34
|
+
You are a code style analyst. Update the current style profile by merging in a new file observation.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Rules:
|
|
37
|
+
- Where they agree, keep the value.
|
|
38
|
+
- Where they disagree, pick the more consistent value.
|
|
39
|
+
- Keep representative_snippets to the 3 best examples total.
|
|
40
|
+
- Return ONLY valid JSON using the same schema.
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
Current profile:
|
|
43
|
+
{profile}
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
{
|
|
45
|
+
New observation ({filename}):
|
|
46
|
+
{observation}
|
|
42
47
|
"""
|
|
43
48
|
|
|
44
49
|
CODE_GENERATION_PROMPT = """\
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mycode-aiagent"
|
|
7
|
-
version = "0.1
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Style-aware code generation — analyze any codebase and generate new code that matches its style"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|