mycode-aiagent 0.1.0__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.
- my_code/__init__.py +16 -0
- my_code/__main__.py +4 -0
- my_code/analyzer.py +84 -0
- my_code/backends/__init__.py +33 -0
- my_code/backends/base.py +13 -0
- my_code/backends/claude_backend.py +32 -0
- my_code/backends/mcp_backend.py +14 -0
- my_code/backends/openai_backend.py +33 -0
- my_code/backends/ricky_backend.py +15 -0
- my_code/cli.py +93 -0
- my_code/generator.py +13 -0
- my_code/ricky_client.py +112 -0
- my_code/utils/__init__.py +0 -0
- my_code/utils/prompts.py +58 -0
- mycode_aiagent-0.1.0.dist-info/METADATA +341 -0
- mycode_aiagent-0.1.0.dist-info/RECORD +19 -0
- mycode_aiagent-0.1.0.dist-info/WHEEL +5 -0
- mycode_aiagent-0.1.0.dist-info/entry_points.txt +2 -0
- mycode_aiagent-0.1.0.dist-info/top_level.txt +1 -0
my_code/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
2
|
+
|
|
3
|
+
from .analyzer import StyleAnalyzer
|
|
4
|
+
from .generator import generate_code
|
|
5
|
+
from .backends import AIBackend, ClaudeBackend, OpenAIBackend, RickyBackend, MCPBackend, make_backend
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"StyleAnalyzer",
|
|
9
|
+
"generate_code",
|
|
10
|
+
"AIBackend",
|
|
11
|
+
"ClaudeBackend",
|
|
12
|
+
"OpenAIBackend",
|
|
13
|
+
"RickyBackend",
|
|
14
|
+
"MCPBackend",
|
|
15
|
+
"make_backend",
|
|
16
|
+
]
|
my_code/__main__.py
ADDED
my_code/analyzer.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from .backends import AIBackend
|
|
6
|
+
from .utils.prompts import STYLE_EXTRACTION_PROMPT, STYLE_SUMMARY_PROMPT
|
|
7
|
+
|
|
8
|
+
_SKIP_DIRS = {"__pycache__", ".git", ".venv", "venv", "node_modules", "dist", "build"}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _collect_python_files(root: Path) -> list[Path]:
|
|
12
|
+
files = []
|
|
13
|
+
for path in root.rglob("*.py"):
|
|
14
|
+
if not any(part in _SKIP_DIRS for part in path.parts):
|
|
15
|
+
files.append(path)
|
|
16
|
+
return sorted(files)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _extract_json(text: str) -> dict:
|
|
20
|
+
# Claude/OpenAI return clean JSON; llama wraps it in prose
|
|
21
|
+
try:
|
|
22
|
+
return json.loads(text.strip())
|
|
23
|
+
except json.JSONDecodeError:
|
|
24
|
+
pass
|
|
25
|
+
match = re.search(r"\{.*\}", text, re.DOTALL)
|
|
26
|
+
if not match:
|
|
27
|
+
raise ValueError(f"No JSON found in response:\n{text[:300]}")
|
|
28
|
+
return json.loads(match.group())
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class StyleAnalyzer:
|
|
32
|
+
def __init__(self, backend: AIBackend):
|
|
33
|
+
self.backend = backend
|
|
34
|
+
|
|
35
|
+
def analyze_file(self, path: Path) -> dict:
|
|
36
|
+
source = path.read_text(encoding="utf-8", errors="ignore")[:self.backend.max_file_chars]
|
|
37
|
+
if not source.strip():
|
|
38
|
+
return {}
|
|
39
|
+
prompt = STYLE_EXTRACTION_PROMPT.format(filename=path.name, source=source)
|
|
40
|
+
raw = self.backend.ask_to_analyze(prompt)
|
|
41
|
+
try:
|
|
42
|
+
return _extract_json(raw)
|
|
43
|
+
except (ValueError, json.JSONDecodeError) as e:
|
|
44
|
+
print(f" [warn] Could not parse style from {path.name}: {e}")
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
def analyze_codebase(self, root: Path, verbose: bool = False) -> dict:
|
|
48
|
+
files = _collect_python_files(root)
|
|
49
|
+
if not files:
|
|
50
|
+
raise FileNotFoundError(f"No Python files found under {root}")
|
|
51
|
+
|
|
52
|
+
observations = []
|
|
53
|
+
for path in files:
|
|
54
|
+
rel = path.relative_to(root)
|
|
55
|
+
if verbose:
|
|
56
|
+
print(f" Analyzing {rel} ...")
|
|
57
|
+
obs = self.analyze_file(path)
|
|
58
|
+
if obs:
|
|
59
|
+
observations.append(obs)
|
|
60
|
+
|
|
61
|
+
if not observations:
|
|
62
|
+
raise RuntimeError("No style data could be extracted from any file.")
|
|
63
|
+
|
|
64
|
+
if len(observations) == 1:
|
|
65
|
+
return observations[0]
|
|
66
|
+
|
|
67
|
+
prompt = STYLE_SUMMARY_PROMPT.format(
|
|
68
|
+
observations=json.dumps(observations, indent=2)
|
|
69
|
+
)
|
|
70
|
+
raw = self.backend.ask_to_analyze(prompt)
|
|
71
|
+
try:
|
|
72
|
+
return _extract_json(raw)
|
|
73
|
+
except (ValueError, json.JSONDecodeError) as e:
|
|
74
|
+
print(f"[warn] Could not synthesize profile, using first observation: {e}")
|
|
75
|
+
return observations[0]
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def save_profile(profile: dict, path: Path):
|
|
79
|
+
path.write_text(json.dumps(profile, indent=2), encoding="utf-8")
|
|
80
|
+
print(f"Style profile saved to {path}")
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def load_profile(path: Path) -> dict:
|
|
84
|
+
return json.loads(path.read_text(encoding="utf-8"))
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from .base import AIBackend
|
|
4
|
+
from .claude_backend import ClaudeBackend
|
|
5
|
+
from .openai_backend import OpenAIBackend
|
|
6
|
+
from .ricky_backend import RickyBackend
|
|
7
|
+
from .mcp_backend import MCPBackend
|
|
8
|
+
|
|
9
|
+
__all__ = ["AIBackend", "ClaudeBackend", "OpenAIBackend", "RickyBackend", "MCPBackend", "make_backend"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def make_backend(
|
|
13
|
+
backend: str = "llama",
|
|
14
|
+
api_key: str | None = None,
|
|
15
|
+
ricky_url: str = "http://localhost:8000/mcp",
|
|
16
|
+
mcp_url: str = "http://localhost:8001/mcp",
|
|
17
|
+
model: str | None = None,
|
|
18
|
+
) -> AIBackend:
|
|
19
|
+
if backend == "claude":
|
|
20
|
+
key = api_key or os.environ.get("ANTHROPIC_API_KEY")
|
|
21
|
+
if not key:
|
|
22
|
+
raise ValueError("Claude backend requires ANTHROPIC_API_KEY or --api-key")
|
|
23
|
+
return ClaudeBackend(api_key=key, **{"model": model} if model else {})
|
|
24
|
+
if backend == "openai":
|
|
25
|
+
key = api_key or os.environ.get("OPENAI_API_KEY")
|
|
26
|
+
if not key:
|
|
27
|
+
raise ValueError("OpenAI backend requires OPENAI_API_KEY or --api-key")
|
|
28
|
+
return OpenAIBackend(api_key=key, **{"model": model} if model else {})
|
|
29
|
+
if backend == "mcp":
|
|
30
|
+
return MCPBackend(url=mcp_url)
|
|
31
|
+
if backend == "llama":
|
|
32
|
+
return RickyBackend(url=ricky_url)
|
|
33
|
+
raise ValueError(f"Unknown backend {backend!r}. Choose: llama, claude, openai, mcp")
|
my_code/backends/base.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from .base import AIBackend
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ClaudeBackend(AIBackend):
|
|
5
|
+
def __init__(self, api_key: str, model: str = "claude-opus-4-7"):
|
|
6
|
+
try:
|
|
7
|
+
import anthropic
|
|
8
|
+
except ImportError:
|
|
9
|
+
raise ImportError("Claude backend requires 'anthropic': pip install 'my-code[claude]'")
|
|
10
|
+
self._client = anthropic.Anthropic(api_key=api_key)
|
|
11
|
+
self._model = model
|
|
12
|
+
|
|
13
|
+
def _call(self, system: str, prompt: str) -> str:
|
|
14
|
+
response = self._client.messages.create(
|
|
15
|
+
model=self._model,
|
|
16
|
+
max_tokens=2048,
|
|
17
|
+
system=system,
|
|
18
|
+
messages=[{"role": "user", "content": prompt}],
|
|
19
|
+
)
|
|
20
|
+
return response.content[0].text
|
|
21
|
+
|
|
22
|
+
def ask_for_code(self, prompt: str) -> str:
|
|
23
|
+
return self._call(
|
|
24
|
+
"You are an expert Python developer. Return only Python code, no explanation.",
|
|
25
|
+
prompt,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def ask_to_analyze(self, prompt: str) -> str:
|
|
29
|
+
return self._call(
|
|
30
|
+
"You are a code style analyst. Return only valid JSON, no explanation.",
|
|
31
|
+
prompt,
|
|
32
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .base import AIBackend
|
|
2
|
+
|
|
3
|
+
_MSG = "MCPBackend is not yet configured. Set the MCP server URL and implement this backend."
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MCPBackend(AIBackend):
|
|
7
|
+
def __init__(self, url: str = "http://localhost:8001/mcp"):
|
|
8
|
+
self.url = url
|
|
9
|
+
|
|
10
|
+
def ask_for_code(self, prompt: str) -> str:
|
|
11
|
+
raise NotImplementedError(_MSG)
|
|
12
|
+
|
|
13
|
+
def ask_to_analyze(self, prompt: str) -> str:
|
|
14
|
+
raise NotImplementedError(_MSG)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from .base import AIBackend
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OpenAIBackend(AIBackend):
|
|
5
|
+
def __init__(self, api_key: str, model: str = "gpt-4o"):
|
|
6
|
+
try:
|
|
7
|
+
import openai
|
|
8
|
+
except ImportError:
|
|
9
|
+
raise ImportError("OpenAI backend requires 'openai': pip install 'my-code[openai]'")
|
|
10
|
+
self._client = openai.OpenAI(api_key=api_key)
|
|
11
|
+
self._model = model
|
|
12
|
+
|
|
13
|
+
def _call(self, system: str, prompt: str) -> str:
|
|
14
|
+
response = self._client.chat.completions.create(
|
|
15
|
+
model=self._model,
|
|
16
|
+
messages=[
|
|
17
|
+
{"role": "system", "content": system},
|
|
18
|
+
{"role": "user", "content": prompt},
|
|
19
|
+
],
|
|
20
|
+
)
|
|
21
|
+
return response.choices[0].message.content
|
|
22
|
+
|
|
23
|
+
def ask_for_code(self, prompt: str) -> str:
|
|
24
|
+
return self._call(
|
|
25
|
+
"You are an expert Python developer. Return only Python code, no explanation.",
|
|
26
|
+
prompt,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def ask_to_analyze(self, prompt: str) -> str:
|
|
30
|
+
return self._call(
|
|
31
|
+
"You are a code style analyst. Return only valid JSON, no explanation.",
|
|
32
|
+
prompt,
|
|
33
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .base import AIBackend
|
|
2
|
+
from ..ricky_client import RickyClient
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RickyBackend(AIBackend):
|
|
6
|
+
max_file_chars: int = 1500
|
|
7
|
+
|
|
8
|
+
def __init__(self, url: str = "http://localhost:8000/mcp"):
|
|
9
|
+
self._client = RickyClient(url=url)
|
|
10
|
+
|
|
11
|
+
def ask_for_code(self, prompt: str) -> str:
|
|
12
|
+
return self._client.ask_for_code(prompt)
|
|
13
|
+
|
|
14
|
+
def ask_to_analyze(self, prompt: str) -> str:
|
|
15
|
+
return self._client.ask_to_analyze(prompt)
|
my_code/cli.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Style-aware code generation agent.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
my-code [--backend llama|claude|openai] analyze <dir>
|
|
6
|
+
my-code [--backend llama|claude|openai] --api-key <key> generate "<task>"
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from .analyzer import StyleAnalyzer
|
|
15
|
+
from .backends import make_backend
|
|
16
|
+
from .generator import generate_code
|
|
17
|
+
|
|
18
|
+
DEFAULT_PROFILE = Path("style_profile.json")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def cmd_analyze(args):
|
|
22
|
+
root = Path(args.codebase).resolve()
|
|
23
|
+
if not root.exists():
|
|
24
|
+
sys.exit(f"Error: directory not found: {root}")
|
|
25
|
+
|
|
26
|
+
backend = make_backend(
|
|
27
|
+
backend=args.backend,
|
|
28
|
+
api_key=args.api_key,
|
|
29
|
+
ricky_url=args.ricky_url,
|
|
30
|
+
mcp_url=args.mcp_url,
|
|
31
|
+
model=args.model,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
print(f"Analyzing codebase at {root} ...")
|
|
35
|
+
analyzer = StyleAnalyzer(backend)
|
|
36
|
+
profile = analyzer.analyze_codebase(root, verbose=args.verbose)
|
|
37
|
+
|
|
38
|
+
out = Path(args.profile)
|
|
39
|
+
StyleAnalyzer.save_profile(profile, out)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def cmd_generate(args):
|
|
43
|
+
profile_path = Path(args.profile)
|
|
44
|
+
if not profile_path.exists():
|
|
45
|
+
sys.exit(
|
|
46
|
+
f"Error: style profile not found at {profile_path}. "
|
|
47
|
+
"Run 'analyze' first."
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
backend = make_backend(
|
|
51
|
+
backend=args.backend,
|
|
52
|
+
api_key=args.api_key,
|
|
53
|
+
ricky_url=args.ricky_url,
|
|
54
|
+
mcp_url=args.mcp_url,
|
|
55
|
+
model=args.model,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
profile = json.loads(profile_path.read_text(encoding="utf-8"))
|
|
59
|
+
|
|
60
|
+
print("Generating code ...\n")
|
|
61
|
+
code = generate_code(task=args.task, backend=backend, profile=profile)
|
|
62
|
+
print(code)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def main():
|
|
66
|
+
parser = argparse.ArgumentParser(description="Style-aware code agent")
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"--backend", default="llama", choices=["llama", "claude", "openai", "mcp"],
|
|
69
|
+
help="AI backend to use (default: llama)",
|
|
70
|
+
)
|
|
71
|
+
parser.add_argument("--api-key", default=None, help="API key (claude/openai); falls back to env var")
|
|
72
|
+
parser.add_argument("--model", default=None, help="Override default model for claude/openai backends")
|
|
73
|
+
parser.add_argument("--ricky-url", default="http://localhost:8000/mcp", help="Ricky MCP server URL (llama backend)")
|
|
74
|
+
parser.add_argument("--mcp-url", default="http://localhost:8001/mcp", help="MCP server URL (mcp backend)")
|
|
75
|
+
parser.add_argument("--profile", default=str(DEFAULT_PROFILE))
|
|
76
|
+
|
|
77
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
78
|
+
|
|
79
|
+
p_analyze = sub.add_parser("analyze", help="Analyze a codebase and save a style profile")
|
|
80
|
+
p_analyze.add_argument("codebase", help="Path to the codebase directory")
|
|
81
|
+
p_analyze.add_argument("-v", "--verbose", action="store_true")
|
|
82
|
+
p_analyze.set_defaults(func=cmd_analyze)
|
|
83
|
+
|
|
84
|
+
p_gen = sub.add_parser("generate", help="Generate code matching the saved style profile")
|
|
85
|
+
p_gen.add_argument("task", help="Description of what to write")
|
|
86
|
+
p_gen.set_defaults(func=cmd_generate)
|
|
87
|
+
|
|
88
|
+
args = parser.parse_args()
|
|
89
|
+
args.func(args)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
main()
|
my_code/generator.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from .backends.base import AIBackend
|
|
4
|
+
from .utils.prompts import CODE_GENERATION_PROMPT
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def generate_code(task: str, backend: AIBackend, profile: dict) -> str:
|
|
8
|
+
"""Generate Python code matching the given style profile."""
|
|
9
|
+
prompt = CODE_GENERATION_PROMPT.format(
|
|
10
|
+
style_profile=json.dumps(profile, indent=2),
|
|
11
|
+
task=task,
|
|
12
|
+
)
|
|
13
|
+
return backend.ask_for_code(prompt)
|
my_code/ricky_client.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import uuid
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
# Ricky's own instructions: give it explicit, complete prompts.
|
|
7
|
+
_PROTOCOL = "2024-11-05"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _parse_sse_result(text: str) -> dict:
|
|
11
|
+
"""Extract the first JSON-RPC result/error from an SSE response body."""
|
|
12
|
+
for line in text.splitlines():
|
|
13
|
+
if line.startswith("data:"):
|
|
14
|
+
payload = line[5:].strip()
|
|
15
|
+
if payload and payload != "[DONE]":
|
|
16
|
+
return json.loads(payload)
|
|
17
|
+
raise ValueError(f"No data event in SSE body:\n{text[:400]}")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RickyClient:
|
|
21
|
+
"""MCP client for ricky using the Streamable HTTP transport.
|
|
22
|
+
|
|
23
|
+
Flow:
|
|
24
|
+
1. POST /mcp initialize → SSE body + Mcp-Session-Id header
|
|
25
|
+
2. POST /mcp <any call> → SSE body (Mcp-Session-Id in every request)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self, url: str = "http://localhost:8000/mcp"):
|
|
29
|
+
self.url = url
|
|
30
|
+
self._session_id: str | None = None
|
|
31
|
+
self._tool_name: str | None = None
|
|
32
|
+
self._tool_name_analyze: str | None = None
|
|
33
|
+
self._initialize()
|
|
34
|
+
self._discover_tools()
|
|
35
|
+
print(f"Connected to ricky — tools: '{self._tool_name}', '{self._tool_name_analyze}'")
|
|
36
|
+
|
|
37
|
+
def _post(self, method: str, params: dict, timeout: int = 120) -> dict:
|
|
38
|
+
headers = {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"Accept": "application/json, text/event-stream",
|
|
41
|
+
}
|
|
42
|
+
if self._session_id:
|
|
43
|
+
headers["Mcp-Session-Id"] = self._session_id
|
|
44
|
+
|
|
45
|
+
payload = {
|
|
46
|
+
"jsonrpc": "2.0",
|
|
47
|
+
"id": str(uuid.uuid4()),
|
|
48
|
+
"method": method,
|
|
49
|
+
"params": params,
|
|
50
|
+
}
|
|
51
|
+
resp = requests.post(self.url, json=payload, headers=headers, timeout=timeout)
|
|
52
|
+
resp.raise_for_status()
|
|
53
|
+
|
|
54
|
+
if "Mcp-Session-Id" in resp.headers:
|
|
55
|
+
self._session_id = resp.headers["Mcp-Session-Id"]
|
|
56
|
+
|
|
57
|
+
msg = _parse_sse_result(resp.text)
|
|
58
|
+
if "error" in msg:
|
|
59
|
+
raise RuntimeError(f"MCP error ({method}): {msg['error']}")
|
|
60
|
+
return msg.get("result", {})
|
|
61
|
+
|
|
62
|
+
def _initialize(self):
|
|
63
|
+
self._post("initialize", {
|
|
64
|
+
"protocolVersion": _PROTOCOL,
|
|
65
|
+
"capabilities": {},
|
|
66
|
+
"clientInfo": {"name": "style-agent", "version": "1.0"},
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
def _discover_tools(self):
|
|
70
|
+
tools = self._post("tools/list", {}).get("tools", [])
|
|
71
|
+
if not tools:
|
|
72
|
+
raise RuntimeError("Ricky exposes no tools")
|
|
73
|
+
for t in tools:
|
|
74
|
+
name = t["name"]
|
|
75
|
+
if "analyze" in name and self._tool_name_analyze is None:
|
|
76
|
+
self._tool_name_analyze = name
|
|
77
|
+
elif self._tool_name is None:
|
|
78
|
+
self._tool_name = name
|
|
79
|
+
if not self._tool_name:
|
|
80
|
+
raise RuntimeError("Ricky exposes no coding tool")
|
|
81
|
+
if not self._tool_name_analyze:
|
|
82
|
+
raise RuntimeError("Ricky exposes no analyze tool")
|
|
83
|
+
|
|
84
|
+
def _parse_content(self, result: dict) -> str:
|
|
85
|
+
content = result.get("content", [])
|
|
86
|
+
if isinstance(content, list):
|
|
87
|
+
for block in content:
|
|
88
|
+
if block.get("type") == "text":
|
|
89
|
+
text = block.get("text", "")
|
|
90
|
+
# Ricky wraps llama.cpp output: {"result": "<completion JSON>"}
|
|
91
|
+
try:
|
|
92
|
+
outer = json.loads(text)
|
|
93
|
+
inner_str = outer.get("result", text)
|
|
94
|
+
inner = json.loads(inner_str)
|
|
95
|
+
return inner["choices"][0]["text"]
|
|
96
|
+
except (json.JSONDecodeError, KeyError, TypeError):
|
|
97
|
+
return text
|
|
98
|
+
return str(content)
|
|
99
|
+
|
|
100
|
+
def ask_for_code(self, prompt: str) -> str:
|
|
101
|
+
result = self._post("tools/call", {
|
|
102
|
+
"name": self._tool_name,
|
|
103
|
+
"arguments": {"query": prompt},
|
|
104
|
+
})
|
|
105
|
+
return self._parse_content(result)
|
|
106
|
+
|
|
107
|
+
def ask_to_analyze(self, prompt: str) -> str:
|
|
108
|
+
result = self._post("tools/call", {
|
|
109
|
+
"name": self._tool_name_analyze,
|
|
110
|
+
"arguments": {"query": prompt},
|
|
111
|
+
}, timeout=600)
|
|
112
|
+
return self._parse_content(result)
|
|
File without changes
|
my_code/utils/prompts.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
STYLE_EXTRACTION_PROMPT = """\
|
|
2
|
+
You are a code style analyst. Analyze the following Python source file and return a JSON object describing its style. Be precise and concise.
|
|
3
|
+
|
|
4
|
+
Return ONLY valid JSON with this exact schema:
|
|
5
|
+
{{
|
|
6
|
+
"naming": {{
|
|
7
|
+
"functions": "<snake_case|camelCase|PascalCase|other>",
|
|
8
|
+
"classes": "<snake_case|camelCase|PascalCase|other>",
|
|
9
|
+
"variables": "<snake_case|camelCase|PascalCase|other>",
|
|
10
|
+
"constants": "<UPPER_SNAKE_CASE|other>",
|
|
11
|
+
"notes": "<any notable patterns, e.g. verb_noun for functions, single-letter loop vars, etc.>"
|
|
12
|
+
}},
|
|
13
|
+
"structure": {{
|
|
14
|
+
"import_style": "<grouped|flat|alphabetical|none>",
|
|
15
|
+
"class_method_order": "<init_first|alphabetical|public_then_private|none>",
|
|
16
|
+
"preferred_length": "<short|medium|long>",
|
|
17
|
+
"module_layout": "<description of top-level ordering>"
|
|
18
|
+
}},
|
|
19
|
+
"comments": {{
|
|
20
|
+
"docstring_style": "<Google|NumPy|reStructuredText|plain|none>",
|
|
21
|
+
"inline_density": "<sparse|moderate|heavy|none>",
|
|
22
|
+
"docstring_sections": [<list of sections used, e.g. "Args", "Returns", "Raises", "Example">]
|
|
23
|
+
}},
|
|
24
|
+
"representative_snippets": [<1-3 short verbatim code snippets that best show the style>]
|
|
25
|
+
}}
|
|
26
|
+
|
|
27
|
+
Source file ({filename}):
|
|
28
|
+
```python
|
|
29
|
+
{source}
|
|
30
|
+
```
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
STYLE_SUMMARY_PROMPT = """\
|
|
34
|
+
You are a code style analyst. Below are JSON style observations extracted from multiple files in a codebase. Synthesize them into a single authoritative style profile JSON.
|
|
35
|
+
|
|
36
|
+
Use the same schema. For fields where files disagree, pick the majority or most consistent value. Add a "confidence" field (low/medium/high) per section. Keep representative_snippets to the 3 best examples across all files.
|
|
37
|
+
|
|
38
|
+
Return ONLY valid JSON.
|
|
39
|
+
|
|
40
|
+
Observations:
|
|
41
|
+
{observations}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
CODE_GENERATION_PROMPT = """\
|
|
45
|
+
You are an expert Python developer. Write code that matches the style profile below exactly.
|
|
46
|
+
|
|
47
|
+
Style profile:
|
|
48
|
+
{style_profile}
|
|
49
|
+
|
|
50
|
+
Task:
|
|
51
|
+
{task}
|
|
52
|
+
|
|
53
|
+
Rules:
|
|
54
|
+
- Match naming conventions, docstring style, and comment density from the profile exactly.
|
|
55
|
+
- Follow the module and class structure patterns.
|
|
56
|
+
- Use the representative snippets as a style reference.
|
|
57
|
+
- Return ONLY the Python code, no explanation.
|
|
58
|
+
"""
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mycode-aiagent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Style-aware code generation — analyze any codebase and generate new code that matches its style
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/RyanAbbottData/MyCode
|
|
7
|
+
Project-URL: Repository, https://github.com/RyanAbbottData/MyCode
|
|
8
|
+
Project-URL: Issues, https://github.com/RyanAbbottData/MyCode/issues
|
|
9
|
+
Keywords: code generation,style analysis,llm,ai,developer tools
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: requests
|
|
22
|
+
Provides-Extra: claude
|
|
23
|
+
Requires-Dist: anthropic>=0.40; extra == "claude"
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.0; extra == "openai"
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: anthropic>=0.40; extra == "all"
|
|
28
|
+
Requires-Dist: openai>=1.0; extra == "all"
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
__ __ ____ _
|
|
32
|
+
| \/ |_ _ / ___|___ __| | ___
|
|
33
|
+
| |\/| | | | | | / _ \ / _` |/ _ \
|
|
34
|
+
| | | | |_| | |__| (_) | (_| | __/
|
|
35
|
+
|_| |_|\__, |\____\___/ \__,_|\___|
|
|
36
|
+
|___/
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
> **Style-aware code generation.** Analyze any codebase to extract its coding style, then generate new code that matches it exactly.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## What it does
|
|
44
|
+
|
|
45
|
+
MyCode learns how a developer or team writes code — naming conventions, type annotation style, import grouping, docstring format, error handling patterns — and uses that style profile to generate new code that feels like it was written by the same hand.
|
|
46
|
+
|
|
47
|
+
It is built to slot into larger agentic systems: the analyzer and generator are clean library functions, and the backend is swappable (local LLM, Claude, or OpenAI).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Requirements
|
|
52
|
+
|
|
53
|
+
- Python 3.10+
|
|
54
|
+
- A running AI backend (see [Backends](#backends))
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Clone and install in editable mode
|
|
62
|
+
git clone <repo-url>
|
|
63
|
+
cd MyCode
|
|
64
|
+
pip install -e .
|
|
65
|
+
|
|
66
|
+
# For Claude backend support
|
|
67
|
+
pip install -e ".[claude]"
|
|
68
|
+
|
|
69
|
+
# For OpenAI backend support
|
|
70
|
+
pip install -e ".[openai]"
|
|
71
|
+
|
|
72
|
+
# For all backends
|
|
73
|
+
pip install -e ".[all]"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The `my-code` CLI command is registered automatically on install.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Backends
|
|
81
|
+
|
|
82
|
+
MyCode delegates inference to a pluggable backend. Choose one based on what you have available.
|
|
83
|
+
|
|
84
|
+
| Backend | Flag | Requirement |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| Local LLM | `--backend llama` (default) | MCP server running at `localhost:8000` |
|
|
87
|
+
| Anthropic Claude | `--backend claude` | `ANTHROPIC_API_KEY` env var or `--api-key` |
|
|
88
|
+
| OpenAI | `--backend openai` | `OPENAI_API_KEY` env var or `--api-key` |
|
|
89
|
+
| Custom MCP server | `--backend mcp` | Any MCP server at `--mcp-url` |
|
|
90
|
+
|
|
91
|
+
### Setting up a local LLM
|
|
92
|
+
|
|
93
|
+
The `llama` backend expects an MCP server at `http://localhost:8000/mcp` that exposes two tools: one for code generation and one for analysis. Any MCP-compatible wrapper around a local model will work. Here is a recommended setup using [llama.cpp](https://github.com/ggerganov/llama.cpp):
|
|
94
|
+
|
|
95
|
+
**1. Download a model**
|
|
96
|
+
|
|
97
|
+
A code-focused model works best. Good options:
|
|
98
|
+
- [CodeLlama-7B-Instruct](https://huggingface.co/TheBloke/CodeLlama-7B-Instruct-GGUF) — fast, runs on most hardware
|
|
99
|
+
- [CodeLlama-13B-Instruct](https://huggingface.co/TheBloke/CodeLlama-13B-Instruct-GGUF) — better quality, needs ~10 GB VRAM
|
|
100
|
+
- [DeepSeek-Coder-6.7B-Instruct](https://huggingface.co/TheBloke/deepseek-coder-6.7B-instruct-GGUF) — strong alternative
|
|
101
|
+
|
|
102
|
+
Download a `.gguf` quantized file (Q4_K_M is a good balance of size and quality).
|
|
103
|
+
|
|
104
|
+
**2. Start the llama.cpp server**
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Install llama.cpp (or use a pre-built binary)
|
|
108
|
+
pip install llama-cpp-python[server]
|
|
109
|
+
|
|
110
|
+
# Start the OpenAI-compatible server
|
|
111
|
+
python -m llama_cpp.server \
|
|
112
|
+
--model ./models/codellama-7b-instruct.Q4_K_M.gguf \
|
|
113
|
+
--host 0.0.0.0 \
|
|
114
|
+
--port 8000 \
|
|
115
|
+
--n_ctx 4096
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**3. Wrap it with an MCP server**
|
|
119
|
+
|
|
120
|
+
The `llama` backend communicates over MCP, not directly with the llama.cpp HTTP API. You need a thin MCP wrapper that exposes two tools:
|
|
121
|
+
- A **code generation tool** (name must not contain `"analyze"`)
|
|
122
|
+
- An **analysis tool** (name must contain `"analyze"`)
|
|
123
|
+
|
|
124
|
+
Both tools accept a `query` string and return the model's completion. A minimal FastMCP wrapper example:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# llm_mcp_server.py
|
|
128
|
+
from fastmcp import FastMCP
|
|
129
|
+
import requests
|
|
130
|
+
|
|
131
|
+
mcp = FastMCP("local-llm")
|
|
132
|
+
LLM_URL = "http://localhost:8000/v1/completions"
|
|
133
|
+
|
|
134
|
+
def _complete(prompt: str) -> str:
|
|
135
|
+
resp = requests.post(LLM_URL, json={
|
|
136
|
+
"prompt": prompt,
|
|
137
|
+
"max_tokens": 1024,
|
|
138
|
+
"temperature": 0.1,
|
|
139
|
+
})
|
|
140
|
+
return resp.json()["choices"][0]["text"]
|
|
141
|
+
|
|
142
|
+
@mcp.tool()
|
|
143
|
+
def generate_code(query: str) -> str:
|
|
144
|
+
return _complete(query)
|
|
145
|
+
|
|
146
|
+
@mcp.tool()
|
|
147
|
+
def analyze_code(query: str) -> str:
|
|
148
|
+
return _complete(query)
|
|
149
|
+
|
|
150
|
+
if __name__ == "__main__":
|
|
151
|
+
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
pip install fastmcp
|
|
156
|
+
python llm_mcp_server.py
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**4. Point MyCode at it**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
my-code --backend llama --ricky-url http://localhost:8000/mcp analyze .
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Or set a custom URL if your server runs on a different port:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
my-code --backend llama --ricky-url http://localhost:9000/mcp analyze .
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## CLI Usage
|
|
174
|
+
|
|
175
|
+
### Step 1 — Analyze a codebase
|
|
176
|
+
|
|
177
|
+
Point MyCode at any directory. It reads every `.py` file and builds a style profile.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
my-code analyze ./path/to/codebase
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
With verbose output:
|
|
184
|
+
```bash
|
|
185
|
+
my-code analyze ./path/to/codebase --verbose
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Using a different backend:
|
|
189
|
+
```bash
|
|
190
|
+
my-code --backend claude analyze ./path/to/codebase
|
|
191
|
+
my-code --backend openai --api-key sk-... analyze ./path/to/codebase
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
The profile is saved to `style_profile.json` by default. Specify a different path with `--profile`:
|
|
195
|
+
```bash
|
|
196
|
+
my-code analyze ./path/to/codebase --profile ./profiles/my_team.json
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Step 2 — Generate code
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
my-code generate "write a function that parses a CSV file and returns a list of dicts"
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
MyCode loads `style_profile.json` and instructs the backend to produce code that matches the analyzed style — naming, annotations, docstrings, structure and all.
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# Use a specific profile
|
|
209
|
+
my-code generate "write a retry decorator" --profile ./profiles/my_team.json
|
|
210
|
+
|
|
211
|
+
# Use Claude to generate
|
|
212
|
+
my-code --backend claude generate "write a binary search function"
|
|
213
|
+
|
|
214
|
+
# Override the model
|
|
215
|
+
my-code --backend claude --model claude-sonnet-4-6 generate "write a rate limiter"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## CLI Reference
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
my-code [OPTIONS] COMMAND
|
|
224
|
+
|
|
225
|
+
Options:
|
|
226
|
+
--backend {llama,claude,openai,mcp} AI backend to use (default: llama)
|
|
227
|
+
--api-key TEXT API key for claude/openai backends
|
|
228
|
+
--model TEXT Override the default model
|
|
229
|
+
--ricky-url TEXT Local LLM MCP server URL (default: http://localhost:8000/mcp)
|
|
230
|
+
--mcp-url TEXT Custom MCP server URL (default: http://localhost:8001/mcp)
|
|
231
|
+
--profile TEXT Path to style profile JSON (default: style_profile.json)
|
|
232
|
+
|
|
233
|
+
Commands:
|
|
234
|
+
analyze PATH Analyze a codebase and write a style profile
|
|
235
|
+
--verbose Print each file as it is analyzed
|
|
236
|
+
|
|
237
|
+
generate TASK Generate code matching the saved style profile
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Python API
|
|
243
|
+
|
|
244
|
+
MyCode is a first-class library. All CLI functionality is available programmatically.
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
from my_code import StyleAnalyzer, generate_code, make_backend
|
|
248
|
+
from pathlib import Path
|
|
249
|
+
|
|
250
|
+
# Create a backend
|
|
251
|
+
backend = make_backend() # local LLM (default)
|
|
252
|
+
backend = make_backend("claude") # Claude (reads ANTHROPIC_API_KEY)
|
|
253
|
+
backend = make_backend("openai", api_key="sk-...") # OpenAI
|
|
254
|
+
|
|
255
|
+
# Analyze a codebase
|
|
256
|
+
analyzer = StyleAnalyzer(backend)
|
|
257
|
+
profile = analyzer.analyze_codebase(Path("./my_project"), verbose=True)
|
|
258
|
+
|
|
259
|
+
# Save and reload the profile
|
|
260
|
+
StyleAnalyzer.save_profile(profile, Path("style.json"))
|
|
261
|
+
profile = StyleAnalyzer.load_profile(Path("style.json"))
|
|
262
|
+
|
|
263
|
+
# Generate code
|
|
264
|
+
code = generate_code(
|
|
265
|
+
task="write a function that validates an email address",
|
|
266
|
+
backend=backend,
|
|
267
|
+
profile=profile,
|
|
268
|
+
)
|
|
269
|
+
print(code)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Bring your own backend
|
|
273
|
+
|
|
274
|
+
Implement `AIBackend` to connect any model:
|
|
275
|
+
|
|
276
|
+
```python
|
|
277
|
+
from my_code import AIBackend, StyleAnalyzer, generate_code
|
|
278
|
+
|
|
279
|
+
class MyBackend(AIBackend):
|
|
280
|
+
max_file_chars = 4000 # how much of each file to send for analysis
|
|
281
|
+
|
|
282
|
+
def ask_for_code(self, prompt: str) -> str:
|
|
283
|
+
# call your model, return the generated code as a string
|
|
284
|
+
...
|
|
285
|
+
|
|
286
|
+
def ask_to_analyze(self, prompt: str) -> str:
|
|
287
|
+
# call your model, return a JSON string describing the style
|
|
288
|
+
...
|
|
289
|
+
|
|
290
|
+
backend = MyBackend()
|
|
291
|
+
analyzer = StyleAnalyzer(backend)
|
|
292
|
+
profile = analyzer.analyze_codebase(Path("."))
|
|
293
|
+
code = generate_code("write a logging helper", backend, profile)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Deep Analysis
|
|
299
|
+
|
|
300
|
+
For a richer style profile, `scripts/deep_analyze.py` runs six focused queries (naming, error handling, string formatting, module structure, docstrings, and representative snippets) and synthesizes them into a single detailed profile.
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
# Run from the project root; writes style_profile.json
|
|
304
|
+
python scripts/deep_analyze.py
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
This is slower than the standard `analyze` command but produces a more detailed profile, which leads to better code generation.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Running Tests
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
python tests/test_library.py
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
The test suite uses a `MockBackend` so no live AI backend is required. It exercises the full analyze → generate pipeline.
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Project Structure
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
my_code/
|
|
325
|
+
├── analyzer.py # StyleAnalyzer — scans files, builds style profile
|
|
326
|
+
├── generator.py # generate_code() — formats prompt and calls backend
|
|
327
|
+
├── ricky_client.py # Low-level MCP client for the local LLM server
|
|
328
|
+
├── cli.py # CLI entry point (my-code command)
|
|
329
|
+
├── backends/
|
|
330
|
+
│ ├── base.py # AIBackend abstract base class
|
|
331
|
+
│ ├── ricky_backend.py # Local LLM backend (connects via MCP)
|
|
332
|
+
│ ├── claude_backend.py
|
|
333
|
+
│ ├── openai_backend.py
|
|
334
|
+
│ └── mcp_backend.py # Placeholder for custom MCP backends
|
|
335
|
+
└── utils/
|
|
336
|
+
└── prompts.py # Prompt templates for extraction, summary, generation
|
|
337
|
+
scripts/
|
|
338
|
+
└── deep_analyze.py # Multi-query deep style analysis
|
|
339
|
+
tests/
|
|
340
|
+
└── test_library.py # Smoke tests (no live backend required)
|
|
341
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
my_code/__init__.py,sha256=YCTHYVmOKP6TmIFDxnQDcc84K4N8AwqB6wGjn-H5vOU,372
|
|
2
|
+
my_code/__main__.py,sha256=-vt2T7-_AnWJ8BTBSmJKPXmOZtpe6TkNXghJJ6dIP_o,63
|
|
3
|
+
my_code/analyzer.py,sha256=-6auv6bbxe10ep_Y2T9uiq2qM7CLTU1M2nwRE7kN_rU,2850
|
|
4
|
+
my_code/cli.py,sha256=H1QXPLLbsrqEkdxcjASrr4xwHbs8uGVOU81E7Rzr4U4,2985
|
|
5
|
+
my_code/generator.py,sha256=R3FPVDNy-8YAMjyZyuLygLH913M2WAGE7q4BqvFdDXU,401
|
|
6
|
+
my_code/ricky_client.py,sha256=irve8roLPQBtsEZMDBXKHL4JWt2Mv65ZMNYGlYGbLWw,4187
|
|
7
|
+
my_code/backends/__init__.py,sha256=FjtrGB7y_3O_s1O81G5gICUUVcMHtnarY1BXP-uGmuU,1328
|
|
8
|
+
my_code/backends/base.py,sha256=9hc6HaNcJSX8twRZM897teGONQWe3CcoltwzIjhdrkA,268
|
|
9
|
+
my_code/backends/claude_backend.py,sha256=O37BnJXnOTzvRPIsWl07WtYElVH-Ov2cJKKt2T3jS_0,1082
|
|
10
|
+
my_code/backends/mcp_backend.py,sha256=g2SAqMmL-_A5NEEouNE1l7XdiVyw3iMku-Ko4F3ZHp8,421
|
|
11
|
+
my_code/backends/openai_backend.py,sha256=SneHFNQQKhq4hKZgN7WniyVgP9lJd6wK1vZaOIokCWw,1110
|
|
12
|
+
my_code/backends/ricky_backend.py,sha256=EVXgWyjBEr5b9tY6wvTxQLUgXu70agPSuByo30TVKYM,440
|
|
13
|
+
my_code/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
+
my_code/utils/prompts.py,sha256=azaC61hc9eaTSnGiFD0botw7-HoTMzQKawJ9X1YEa0w,2194
|
|
15
|
+
mycode_aiagent-0.1.0.dist-info/METADATA,sha256=5apqZyJtbt_4iSDs89O6pu3fxUI1r2-eyoY6NKLmoS0,10916
|
|
16
|
+
mycode_aiagent-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
17
|
+
mycode_aiagent-0.1.0.dist-info/entry_points.txt,sha256=-bGlti5BHHrdHW2XhcQW-W-gufCQek9PsczygeFZkXs,45
|
|
18
|
+
mycode_aiagent-0.1.0.dist-info/top_level.txt,sha256=xAInreiHP8EP1o4MxvRhovZW9pZuinc3miY3ZNAnlJc,8
|
|
19
|
+
mycode_aiagent-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
my_code
|