relay-code 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.
- agent/__init__.py +6 -0
- agent/agent.py +162 -0
- agent/events.py +133 -0
- agent/session.py +71 -0
- client/__init__.py +23 -0
- client/llm_client.py +236 -0
- client/response.py +83 -0
- config/__init__.py +6 -0
- config/config.py +76 -0
- config/credentials.py +54 -0
- config/loader.py +108 -0
- config/oauth.py +137 -0
- context/__init__.py +5 -0
- context/manager.py +92 -0
- main.py +231 -0
- prompts/__init__.py +0 -0
- prompts/system.py +350 -0
- relay_code-0.1.0.dist-info/METADATA +64 -0
- relay_code-0.1.0.dist-info/RECORD +50 -0
- relay_code-0.1.0.dist-info/WHEEL +5 -0
- relay_code-0.1.0.dist-info/entry_points.txt +2 -0
- relay_code-0.1.0.dist-info/licenses/LICENSE +674 -0
- relay_code-0.1.0.dist-info/top_level.txt +9 -0
- tools/__init__.py +14 -0
- tools/base.py +188 -0
- tools/core/__init__.py +44 -0
- tools/core/directories.py +65 -0
- tools/core/edit.py +162 -0
- tools/core/glob.py +92 -0
- tools/core/grep.py +128 -0
- tools/core/read.py +129 -0
- tools/core/shell.py +162 -0
- tools/core/todo.py +135 -0
- tools/core/write.py +76 -0
- tools/memory/__init__.py +0 -0
- tools/memory/memory.py +191 -0
- tools/network/__init__.py +0 -0
- tools/network/fetch.py +72 -0
- tools/network/search.py +72 -0
- tools/registry.py +96 -0
- ui/__init__.py +5 -0
- ui/app.py +949 -0
- ui/format.py +145 -0
- ui/logo.py +46 -0
- ui/renderer.py +580 -0
- ui/theme.py +60 -0
- utils/__init__.py +17 -0
- utils/errors.py +47 -0
- utils/paths.py +44 -0
- utils/text.py +74 -0
ui/format.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, Tuple
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
HEADLINE_KEYS = ("path", "command", "pattern", "url", "query", "action")
|
|
6
|
+
|
|
7
|
+
BULKY_KEYS = frozenset({"content", "old_string", "new_string"})
|
|
8
|
+
|
|
9
|
+
ARG_ORDER = {
|
|
10
|
+
"read": ["path", "offset", "limit"],
|
|
11
|
+
"write": ["path", "create_directories", "content"],
|
|
12
|
+
"edit": ["path", "replace_all", "old_string", "new_string"],
|
|
13
|
+
"shell": ['command', 'timeout', 'cwd'],
|
|
14
|
+
"list_dir": ['path', 'inclide_hidden'],
|
|
15
|
+
"grep": ['path', 'case_insensitive', 'pattern'],
|
|
16
|
+
"glob": ['path', 'pattern'],
|
|
17
|
+
"todo": ['action', 'id', 'content'],
|
|
18
|
+
"memory": ['action', 'key', 'value'],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_EXTENSION_LANGUAGES = {
|
|
22
|
+
".py": "python",
|
|
23
|
+
".js": "javascript",
|
|
24
|
+
".jsx": "jsx",
|
|
25
|
+
".ts": "typescript",
|
|
26
|
+
".tsx": "tsx",
|
|
27
|
+
".json": "json",
|
|
28
|
+
".toml": "toml",
|
|
29
|
+
".yaml": "yaml",
|
|
30
|
+
".yml": "yaml",
|
|
31
|
+
".md": "markdown",
|
|
32
|
+
".sh": "bash",
|
|
33
|
+
".bash": "bash",
|
|
34
|
+
".zsh": "bash",
|
|
35
|
+
".rs": "rust",
|
|
36
|
+
".go": "go",
|
|
37
|
+
".java": "java",
|
|
38
|
+
".kt": "kotlin",
|
|
39
|
+
".swift": "swift",
|
|
40
|
+
".c": "c",
|
|
41
|
+
".h": "c",
|
|
42
|
+
".cpp": "cpp",
|
|
43
|
+
".hpp": "cpp",
|
|
44
|
+
".css": "css",
|
|
45
|
+
".html": "html",
|
|
46
|
+
".xml": "xml",
|
|
47
|
+
".sql": "sql",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
HEADLINE_MAX_WIDTH = 64
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def format_elapsed(seconds: float) -> str:
|
|
54
|
+
if seconds < 1:
|
|
55
|
+
return f"{seconds * 1000:.0f}ms"
|
|
56
|
+
if seconds < 60:
|
|
57
|
+
return f"{seconds:.1f}s"
|
|
58
|
+
minutes, secs = divmod(int(seconds), 60)
|
|
59
|
+
return f"{minutes}m{secs:02d}s"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def guess_language(path: str | None) -> str:
|
|
63
|
+
if not path:
|
|
64
|
+
return "text"
|
|
65
|
+
return _EXTENSION_LANGUAGES.get(Path(path).suffix.lower(), "text")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def ordered_args(tool_name: str, args: dict[str, Any]) -> list[Tuple[str, Any]]:
|
|
69
|
+
preferred = ARG_ORDER.get(tool_name, [])
|
|
70
|
+
ordered: list[Tuple[str, Any]] = []
|
|
71
|
+
seen: set[str] = set()
|
|
72
|
+
|
|
73
|
+
for key in preferred:
|
|
74
|
+
if key in args:
|
|
75
|
+
ordered.append((key, args[key]))
|
|
76
|
+
seen.add(key)
|
|
77
|
+
|
|
78
|
+
ordered.extend((key, args[key]) for key in sorted(args.keys() - seen))
|
|
79
|
+
return ordered
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def summarise_value(key: str, value: Any) -> str:
|
|
83
|
+
if isinstance(value, str) and key in BULKY_KEYS:
|
|
84
|
+
line_count = len(value.splitlines())
|
|
85
|
+
byte_count = len(value.encode("utf-8", errors="replace"))
|
|
86
|
+
return f"{line_count} lines ┈ {byte_count} bytes"
|
|
87
|
+
|
|
88
|
+
if isinstance(value, bool):
|
|
89
|
+
value = str(value)
|
|
90
|
+
|
|
91
|
+
return str(value)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def headline(args: dict[str, Any]) -> tuple[str, str] | None:
|
|
95
|
+
for key in HEADLINE_KEYS:
|
|
96
|
+
value = args.get(key)
|
|
97
|
+
if isinstance(value, str) and value.strip():
|
|
98
|
+
first_line = value.strip().splitlines()[0]
|
|
99
|
+
if len(first_line) > HEADLINE_MAX_WIDTH:
|
|
100
|
+
first_line = f"{first_line[:HEADLINE_MAX_WIDTH - 1]}…"
|
|
101
|
+
return key, first_line
|
|
102
|
+
return None
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def secondary_args(args: dict[str, Any], headline_key: str | None) -> dict[str, Any]:
|
|
106
|
+
return {
|
|
107
|
+
key: value
|
|
108
|
+
for key, value in args.items()
|
|
109
|
+
if key != headline_key and not (key == "cwd" and value == ".")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def extract_read_code(text: str) -> tuple[int, str] | None:
|
|
114
|
+
body = text
|
|
115
|
+
header_match = re.match(
|
|
116
|
+
r"^Showing lines (\d+)-(\d+) of (\d+)[^\n]*\n\n", text, re.IGNORECASE
|
|
117
|
+
)
|
|
118
|
+
if header_match:
|
|
119
|
+
body = text[header_match.end():]
|
|
120
|
+
|
|
121
|
+
code_lines: list[str] = []
|
|
122
|
+
start_line: int | None = None
|
|
123
|
+
|
|
124
|
+
for line in body.splitlines():
|
|
125
|
+
match = re.match(r"^\s*(\d+)\|(.*)$", line)
|
|
126
|
+
if not match:
|
|
127
|
+
return None
|
|
128
|
+
if start_line is None:
|
|
129
|
+
start_line = int(match.group(1))
|
|
130
|
+
code_lines.append(match.group(2))
|
|
131
|
+
|
|
132
|
+
if start_line is None:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
return start_line, "\n".join(code_lines)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def diff_stat(diff: str) -> str:
|
|
139
|
+
added = removed = 0
|
|
140
|
+
for line in diff.splitlines():
|
|
141
|
+
if line.startswith("+") and not line.startswith("+++"):
|
|
142
|
+
added += 1
|
|
143
|
+
elif line.startswith("-") and not line.startswith("---"):
|
|
144
|
+
removed += 1
|
|
145
|
+
return f"+{added} -{removed}"
|
ui/logo.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from rich.text import Text
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
RELAY_VERSION = "0.2"
|
|
5
|
+
|
|
6
|
+
RELAY_LOGO = """\
|
|
7
|
+
⣠⣤⣤⡤⠤⢤⣤⣀⡀⠀⠐⠒⡄⠀⡠⠒⠀⠀⢀⣀⣤⠤⠤⣤⣤⣤⡄
|
|
8
|
+
⠈⠻⣿⡤⠤⡏⠀⠉⠙⠲⣄⠀⢰⢠⠃⢀⡤⠞⠋⠉⠈⢹⠤⢼⣿⠏⠀
|
|
9
|
+
⠀⠀⠘⣿⡅⠓⢒⡤⠤⠀⡈⠱⣄⣼⡴⠋⡀⠀⠤⢤⡒⠓⢬⣿⠃⠀⠀
|
|
10
|
+
⠀⠀⠀⠹⣿⣯⣐⢷⣀⣀⢤⡥⢾⣿⠷⢥⠤⣀⣀⣞⣢⣽⡿⠃⠀⠀⠀
|
|
11
|
+
⠀⠀⠀⠀⠈⢙⣿⠝⠀⢁⠔⡨⡺⡿⡕⢔⠀⡈⠐⠹⣟⠋⠀⠀⠀⠀⠀
|
|
12
|
+
⠀⠀⠀⠀⠀⢼⣟⢦⢶⢅⠜⢰⠃⠀⢹⡌⢢⣸⠦⠴⣿⡇⠀⠀⠀⠀⠀
|
|
13
|
+
⠀⠀⠀⠀⠀⠘⣿⣇⡬⡌⢀⡟⠀⠀⠀⢷⠀⣧⢧⣵⣿⠂⠀⠀⠀⠀⠀
|
|
14
|
+
⠀⠀⠀⠀⠀⠀⠈⢻⠛⠋⠉⠀⠀⠀⠀⠈⠉⠙⢻⡏⠀⠀⠀⠀⠀⠀⠀
|
|
15
|
+
⠀⠀⠀⠀⠀⠀⢰⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠄⠀⠀⠀⠀⠀⠀"""
|
|
16
|
+
|
|
17
|
+
_WHITE = (255, 255, 255)
|
|
18
|
+
_SILVER = (168, 174, 186)
|
|
19
|
+
|
|
20
|
+
_PIXEL = "██"
|
|
21
|
+
_LETTER_GAP = 1
|
|
22
|
+
|
|
23
|
+
def small_wordmark(justify: str = "center") -> Text:
|
|
24
|
+
r, g, b = _SILVER
|
|
25
|
+
return Text(" ".join("relay"), style=f"rgb({r},{g},{b})", justify=justify, no_wrap=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def gradient_logo(justify: str = "center") -> Text:
|
|
29
|
+
rows = RELAY_LOGO.split("\n")
|
|
30
|
+
height = len(rows)
|
|
31
|
+
width = max((len(row) for row in rows), default=1)
|
|
32
|
+
cx = (width - 1) / 2
|
|
33
|
+
cy = (height - 1) / 2
|
|
34
|
+
max_dist = math.hypot(cx, cy) or 1.0
|
|
35
|
+
|
|
36
|
+
text = Text(justify=justify, no_wrap=True)
|
|
37
|
+
for y, row in enumerate(rows):
|
|
38
|
+
for x, char in enumerate(row):
|
|
39
|
+
t = min(math.hypot(x - cx, y - cy) / max_dist, 1.0)
|
|
40
|
+
r = round(_WHITE[0] + (_SILVER[0] - _WHITE[0]) * t)
|
|
41
|
+
g = round(_WHITE[1] + (_SILVER[1] - _WHITE[1]) * t)
|
|
42
|
+
b = round(_WHITE[2] + (_SILVER[2] - _WHITE[2]) * t)
|
|
43
|
+
text.append(char, style=f"rgb({r},{g},{b})")
|
|
44
|
+
if y != height - 1:
|
|
45
|
+
text.append("\n")
|
|
46
|
+
return text
|