tass 0.1.5__py3-none-any.whl → 0.1.6__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.
- src/app.py +45 -14
- src/constants.py +23 -13
- {tass-0.1.5.dist-info → tass-0.1.6.dist-info}/METADATA +2 -2
- tass-0.1.6.dist-info/RECORD +10 -0
- tass-0.1.5.dist-info/RECORD +0 -10
- {tass-0.1.5.dist-info → tass-0.1.6.dist-info}/WHEEL +0 -0
- {tass-0.1.5.dist-info → tass-0.1.6.dist-info}/entry_points.txt +0 -0
- {tass-0.1.5.dist-info → tass-0.1.6.dist-info}/licenses/LICENSE +0 -0
src/app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import subprocess
|
|
4
|
+
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
import requests
|
|
6
7
|
from rich.console import Console
|
|
@@ -56,7 +57,7 @@ class TassApp:
|
|
|
56
57
|
console.print(f"[red]Unable to verify new host {self.host}. Continuing with it anyway.[/red]")
|
|
57
58
|
|
|
58
59
|
def summarize(self):
|
|
59
|
-
max_messages =
|
|
60
|
+
max_messages = 20
|
|
60
61
|
if len(self.messages) <= max_messages:
|
|
61
62
|
return
|
|
62
63
|
|
|
@@ -165,22 +166,52 @@ class TassApp:
|
|
|
165
166
|
console.print(" [green]Command succeeded[/green]")
|
|
166
167
|
return "".join(lines)
|
|
167
168
|
|
|
168
|
-
def edit_file(self, path: str,
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
def edit_file(self, path: str, edits: list[dict]) -> str:
|
|
170
|
+
for edit in edits:
|
|
171
|
+
edit["applied"] = False
|
|
171
172
|
|
|
173
|
+
def find_edit(n: int) -> dict | None:
|
|
174
|
+
for edit in edits:
|
|
175
|
+
if edit["line_start"] <= n <= edit["line_end"]:
|
|
176
|
+
return edit
|
|
177
|
+
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
file_exists = Path(path).exists()
|
|
181
|
+
if file_exists:
|
|
182
|
+
with open(path, "r") as f:
|
|
183
|
+
original_content = f.read()
|
|
184
|
+
else:
|
|
185
|
+
original_content = ""
|
|
186
|
+
|
|
187
|
+
final_lines = []
|
|
172
188
|
original_lines = original_content.split("\n")
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
189
|
+
diff_text = f"{'Editing' if file_exists else 'Creating'} {path}"
|
|
190
|
+
for i, line in enumerate(original_lines):
|
|
191
|
+
line_num = i + 1
|
|
192
|
+
edit = find_edit(line_num)
|
|
193
|
+
if not edit:
|
|
194
|
+
final_lines.append(line)
|
|
195
|
+
continue
|
|
196
|
+
|
|
197
|
+
if edit["applied"]:
|
|
198
|
+
continue
|
|
199
|
+
|
|
200
|
+
replace_lines = edit["replace"].split("\n")
|
|
201
|
+
final_lines.extend(replace_lines)
|
|
202
|
+
original_lines = original_content.split("\n")
|
|
203
|
+
replaced_lines = original_lines[edit["line_start"] - 1:edit["line_end"]]
|
|
204
|
+
|
|
205
|
+
prev_line_num = line_num if line_num == 1 else line_num - 1
|
|
206
|
+
line_before = "" if i == 0 else f" {original_lines[i - 1]}\n"
|
|
207
|
+
line_after = "" if i == len(original_lines) - 1 else f"\n {original_lines[i + 1]}"
|
|
208
|
+
replaced_with_minuses = "\n".join([f"-{line}" for line in replaced_lines]) if file_exists else ""
|
|
209
|
+
replace_with_pluses = "\n".join([f"+{line}" for line in edit["replace"].split("\n")])
|
|
210
|
+
diff_text = f"{diff_text}\n\n@@ -{prev_line_num},{len(replaced_lines)} +{prev_line_num},{len(replace_lines)} @@\n{line_before}{replaced_with_minuses}\n{replace_with_pluses}{line_after}"
|
|
211
|
+
edit["applied"] = True
|
|
179
212
|
|
|
180
|
-
replaced_with_minuses = "\n".join([f"-{line}" for line in replaced_lines])
|
|
181
|
-
replace_with_pluses = "\n".join([f"+{line}" for line in replace.split("\n")])
|
|
182
213
|
console.print()
|
|
183
|
-
console.print(Markdown(f"```diff\
|
|
214
|
+
console.print(Markdown(f"```diff\n{diff_text}\n```"))
|
|
184
215
|
answer = console.input("\n[bold]Run?[/] ([bold]Y[/]/n): ").strip().lower()
|
|
185
216
|
if answer not in ("yes", "y", ""):
|
|
186
217
|
reason = console.input("Why not? (optional, press Enter to skip): ").strip()
|
|
@@ -189,7 +220,7 @@ class TassApp:
|
|
|
189
220
|
console.print(" └ Running...")
|
|
190
221
|
try:
|
|
191
222
|
with open(path, "w") as f:
|
|
192
|
-
f.write(
|
|
223
|
+
f.write("\n".join(final_lines))
|
|
193
224
|
except Exception as e:
|
|
194
225
|
console.print(" [red]edit_file failed[/red]")
|
|
195
226
|
console.print(f" [red]{str(e)}[/red]")
|
src/constants.py
CHANGED
|
@@ -37,7 +37,7 @@ TOOLS = [
|
|
|
37
37
|
"type": "function",
|
|
38
38
|
"function": {
|
|
39
39
|
"name": "edit_file",
|
|
40
|
-
"description": "Edits a file.
|
|
40
|
+
"description": "Edits (or creates) a file. Makes multiple replacements in one call. Each edit removes the contents between 'line_start' and 'line_end' inclusive and replaces it with 'replace'. If creating a file, only return a single edit where the line_start and line_end are both 1 and replace is the entire contents of the file.",
|
|
41
41
|
"parameters": {
|
|
42
42
|
"type": "object",
|
|
43
43
|
"properties": {
|
|
@@ -45,20 +45,30 @@ TOOLS = [
|
|
|
45
45
|
"type": "string",
|
|
46
46
|
"description": "Relative path of the file",
|
|
47
47
|
},
|
|
48
|
-
"
|
|
49
|
-
"type": "
|
|
50
|
-
"description": "
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
48
|
+
"edits": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"description": "List of edits to apply. Each edit must contain 'line_start', 'line_end', and 'replace'.",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"properties": {
|
|
54
|
+
"line_start": {
|
|
55
|
+
"type": "integer",
|
|
56
|
+
"description": "The first line to remove (inclusive)",
|
|
57
|
+
},
|
|
58
|
+
"line_end": {
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"description": "The last line to remove (inclusive)",
|
|
61
|
+
},
|
|
62
|
+
"replace": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "The string to replace with. Must have the correct spacing and indentation for all lines.",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
"required": ["line_start", "line_end", "replace"],
|
|
68
|
+
},
|
|
59
69
|
},
|
|
60
70
|
},
|
|
61
|
-
"required": ["path", "
|
|
71
|
+
"required": ["path", "edits"],
|
|
62
72
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
63
73
|
},
|
|
64
74
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tass
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: A terminal assistant that allows you to ask an LLM to run commands.
|
|
5
5
|
Project-URL: Homepage, https://github.com/cetincan0/tass
|
|
6
6
|
Author: Can Cetin
|
|
@@ -33,4 +33,4 @@ tass
|
|
|
33
33
|
|
|
34
34
|
tass has only been tested with gpt-oss-120b using llama.cpp so far, but in theory any LLM with tool calling capabilities should work. By default, it will try connecting to http://localhost:8080. If you want to use another host, set the `TASS_HOST` environment variable.
|
|
35
35
|
|
|
36
|
-
Once it's running, you can ask questions like "
|
|
36
|
+
Once it's running, you can ask questions or give commands like "Create an empty file called test.txt" and it will propose a command to run after user confirmation.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
src/__init__.py,sha256=tu2q9W5_pkq30l3tRMTGahColBAAubbLP6LaB3l3IFg,89
|
|
2
|
+
src/app.py,sha256=SZGIStkRskTraOARKR-sh8hjfQT7EXwJBG-oymIABhU,11466
|
|
3
|
+
src/cli.py,sha256=op3fYcyfek_KqCCiA-Zdlc9jVZSCi036whMmR2ZjjAs,76
|
|
4
|
+
src/constants.py,sha256=2MWn3-tvZjJ2xW68BE7S1V8CgqDuBt3cBG5Bx8ILrKY,4620
|
|
5
|
+
src/utils.py,sha256=rKq34DVmFbsWPy7R6Bfdvv1ztzFLPT4hUd8BFpPHjqs,681
|
|
6
|
+
tass-0.1.6.dist-info/METADATA,sha256=xu-OHc1sIrlDrxXVCUSdPmzYnjVam5tYB96EYJiTWCc,1079
|
|
7
|
+
tass-0.1.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
+
tass-0.1.6.dist-info/entry_points.txt,sha256=pviKuIOuHvaQ7_YiFxatJEY8XYfh3EzVWy4LJh0v-A0,38
|
|
9
|
+
tass-0.1.6.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
10
|
+
tass-0.1.6.dist-info/RECORD,,
|
tass-0.1.5.dist-info/RECORD
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
src/__init__.py,sha256=tu2q9W5_pkq30l3tRMTGahColBAAubbLP6LaB3l3IFg,89
|
|
2
|
-
src/app.py,sha256=Jej2qjyBRqQREpvm6WWvd6cPy9RLzW74rYegeM8ICPI,10224
|
|
3
|
-
src/cli.py,sha256=op3fYcyfek_KqCCiA-Zdlc9jVZSCi036whMmR2ZjjAs,76
|
|
4
|
-
src/constants.py,sha256=gFIMWh38-uyh2XJdiKUsOwAh7yk4jbdfxmeJZ9yl4fw,3847
|
|
5
|
-
src/utils.py,sha256=rKq34DVmFbsWPy7R6Bfdvv1ztzFLPT4hUd8BFpPHjqs,681
|
|
6
|
-
tass-0.1.5.dist-info/METADATA,sha256=HskJ2m7qsulvtK_N5nZ_aJAcq1PH5zGMDnYMm_cjda8,1071
|
|
7
|
-
tass-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
8
|
-
tass-0.1.5.dist-info/entry_points.txt,sha256=pviKuIOuHvaQ7_YiFxatJEY8XYfh3EzVWy4LJh0v-A0,38
|
|
9
|
-
tass-0.1.5.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
10
|
-
tass-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|