tzamuncode 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.
- tzamuncode/__init__.py +10 -0
- tzamuncode/agents/__init__.py +1 -0
- tzamuncode/agents/coder.py +144 -0
- tzamuncode/agents/tools.py +159 -0
- tzamuncode/auth/__init__.py +1 -0
- tzamuncode/auth/auth_manager.py +159 -0
- tzamuncode/cli/__init__.py +1 -0
- tzamuncode/cli/agentic_commands.py +131 -0
- tzamuncode/cli/auth_commands.py +125 -0
- tzamuncode/cli/commands.py +203 -0
- tzamuncode/cli/enhanced_chat.py +312 -0
- tzamuncode/cli/interactive_chat.py +323 -0
- tzamuncode/cli/main.py +444 -0
- tzamuncode/cli/realtime_chat.py +965 -0
- tzamuncode/cli/realtime_chat_methods.py +200 -0
- tzamuncode/cli/tui_chat.py +323 -0
- tzamuncode/config/__init__.py +1 -0
- tzamuncode/models/__init__.py +1 -0
- tzamuncode/models/ollama.py +124 -0
- tzamuncode/models/vllm_client.py +121 -0
- tzamuncode/utils/__init__.py +1 -0
- tzamuncode/utils/file_ops.py +59 -0
- tzamuncode/utils/project_scanner.py +193 -0
- tzamuncode-0.1.0.dist-info/METADATA +200 -0
- tzamuncode-0.1.0.dist-info/RECORD +29 -0
- tzamuncode-0.1.0.dist-info/WHEEL +5 -0
- tzamuncode-0.1.0.dist-info/entry_points.txt +3 -0
- tzamuncode-0.1.0.dist-info/licenses/LICENSE +21 -0
- tzamuncode-0.1.0.dist-info/top_level.txt +1 -0
tzamuncode/cli/main.py
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TzamunCode CLI - Main Entry Point
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.markdown import Markdown
|
|
9
|
+
from rich.prompt import Confirm
|
|
10
|
+
from typing import Optional
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
from ..models.ollama import OllamaClient
|
|
14
|
+
from ..utils.file_ops import FileManager
|
|
15
|
+
from .commands import chat_command, generate_command, edit_command, explain_command
|
|
16
|
+
from .agentic_commands import agentic_command, project_command
|
|
17
|
+
|
|
18
|
+
app = typer.Typer(
|
|
19
|
+
name="tzamuncode",
|
|
20
|
+
help="TzamunCode - AI Coding Assistant powered by local models π",
|
|
21
|
+
add_completion=False,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
def version_callback(value: bool):
|
|
27
|
+
if value:
|
|
28
|
+
from .. import __version__
|
|
29
|
+
console.print(f"[bold blue]TzamunCode[/bold blue] version {__version__}")
|
|
30
|
+
console.print("Built with β€οΈ in Saudi Arabia πΈπ¦")
|
|
31
|
+
raise typer.Exit()
|
|
32
|
+
|
|
33
|
+
@app.callback(invoke_without_command=True)
|
|
34
|
+
def main(
|
|
35
|
+
ctx: typer.Context,
|
|
36
|
+
version: Optional[bool] = typer.Option(
|
|
37
|
+
None,
|
|
38
|
+
"--version",
|
|
39
|
+
"-v",
|
|
40
|
+
callback=version_callback,
|
|
41
|
+
is_eager=True,
|
|
42
|
+
help="Show version and exit"
|
|
43
|
+
)
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
TzamunCode - AI Coding Assistant
|
|
47
|
+
|
|
48
|
+
Privacy-first AI coding assistant powered by local models.
|
|
49
|
+
"""
|
|
50
|
+
# If no subcommand provided, show interactive backend selection
|
|
51
|
+
if ctx.invoked_subcommand is None:
|
|
52
|
+
from rich.prompt import Prompt
|
|
53
|
+
from .realtime_chat import RealtimeChat
|
|
54
|
+
from ..auth.auth_manager import AuthManager
|
|
55
|
+
|
|
56
|
+
# Check authentication
|
|
57
|
+
auth = AuthManager()
|
|
58
|
+
if not auth.is_authenticated():
|
|
59
|
+
console.print("\n[bold yellow]π Authentication Required[/bold yellow]\n")
|
|
60
|
+
console.print("[dim]Please login to use TzamunCode[/dim]\n")
|
|
61
|
+
console.print("Run: [cyan]tzamuncode login[/cyan]\n")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
console.print("\n[bold cyan]π TzamunCode AI Assistant[/bold cyan]\n")
|
|
65
|
+
console.print("Select AI Backend:\n")
|
|
66
|
+
console.print(" [bold]1[/bold]. β‘ [cyan]vLLM[/cyan] - Fast inference (Qwen 2.5 7B)")
|
|
67
|
+
console.print(" [bold]2[/bold]. π¦ [green]Ollama[/green] - Powerful models (Qwen 2.5 32B)")
|
|
68
|
+
|
|
69
|
+
choice = Prompt.ask("\nChoose backend", choices=["1", "2"], default="2")
|
|
70
|
+
|
|
71
|
+
if choice == "1":
|
|
72
|
+
console.print("\nβ
Starting with [cyan]vLLM[/cyan] backend...\n")
|
|
73
|
+
chat = RealtimeChat(model="qwen2.5-7b-instruct", use_vllm=True)
|
|
74
|
+
else:
|
|
75
|
+
console.print("\nβ
Starting with [green]Ollama[/green] backend...\n")
|
|
76
|
+
chat = RealtimeChat(model="qwen2.5:32b", use_vllm=False)
|
|
77
|
+
|
|
78
|
+
chat.run()
|
|
79
|
+
|
|
80
|
+
@app.command()
|
|
81
|
+
def chat(
|
|
82
|
+
model: str = typer.Option(
|
|
83
|
+
"qwen2.5:32b",
|
|
84
|
+
"--model",
|
|
85
|
+
"-m",
|
|
86
|
+
help="Model to use for chat"
|
|
87
|
+
),
|
|
88
|
+
system: Optional[str] = typer.Option(
|
|
89
|
+
None,
|
|
90
|
+
"--system",
|
|
91
|
+
"-s",
|
|
92
|
+
help="System prompt"
|
|
93
|
+
),
|
|
94
|
+
vllm: bool = typer.Option(
|
|
95
|
+
False,
|
|
96
|
+
"--vllm",
|
|
97
|
+
help="Use vLLM backend (faster)"
|
|
98
|
+
)
|
|
99
|
+
):
|
|
100
|
+
"""
|
|
101
|
+
Start an interactive chat session with TzamunCode AI.
|
|
102
|
+
|
|
103
|
+
Features:
|
|
104
|
+
- Beautiful UI with welcome screen
|
|
105
|
+
- Type '/' to see all commands
|
|
106
|
+
- '/models' to switch models
|
|
107
|
+
- '/settings' to view settings
|
|
108
|
+
- '/help' for help
|
|
109
|
+
|
|
110
|
+
Examples:
|
|
111
|
+
tzamuncode chat
|
|
112
|
+
tzamuncode chat --model deepseek-coder-v2
|
|
113
|
+
tzc chat -m qwen2.5:32b
|
|
114
|
+
"""
|
|
115
|
+
from .realtime_chat import run_realtime_chat
|
|
116
|
+
from ..auth.auth_manager import AuthManager
|
|
117
|
+
|
|
118
|
+
# Check authentication
|
|
119
|
+
auth = AuthManager()
|
|
120
|
+
if not auth.is_authenticated():
|
|
121
|
+
console.print("\n[bold yellow]π Authentication Required[/bold yellow]\n")
|
|
122
|
+
console.print("[dim]Please login to use TzamunCode[/dim]\n")
|
|
123
|
+
console.print("Run: [cyan]tzamuncode login[/cyan]\n")
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
if vllm and model == 'qwen2.5:32b':
|
|
127
|
+
model = 'qwen2.5-7b-instruct'
|
|
128
|
+
run_realtime_chat(model=model, system=system, use_vllm=vllm)
|
|
129
|
+
|
|
130
|
+
@app.command()
|
|
131
|
+
def generate(
|
|
132
|
+
prompt: str = typer.Argument(..., help="What to generate"),
|
|
133
|
+
model: str = typer.Option(
|
|
134
|
+
"qwen2.5:32b",
|
|
135
|
+
"--model",
|
|
136
|
+
"-m",
|
|
137
|
+
help="Model to use"
|
|
138
|
+
),
|
|
139
|
+
output: Optional[str] = typer.Option(
|
|
140
|
+
None,
|
|
141
|
+
"--output",
|
|
142
|
+
"-o",
|
|
143
|
+
help="Output file or directory"
|
|
144
|
+
)
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Generate code based on a prompt.
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
tzamuncode generate "Create a Flask REST API"
|
|
151
|
+
tzamuncode generate "Add unit tests" --output tests/
|
|
152
|
+
tzc generate "Python web scraper" -o scraper.py
|
|
153
|
+
"""
|
|
154
|
+
generate_command(prompt=prompt, model=model, output=output)
|
|
155
|
+
|
|
156
|
+
@app.command()
|
|
157
|
+
def edit(
|
|
158
|
+
file_path: str = typer.Argument(..., help="File to edit"),
|
|
159
|
+
instruction: str = typer.Argument(..., help="What to change"),
|
|
160
|
+
model: str = typer.Option(
|
|
161
|
+
"qwen2.5:32b",
|
|
162
|
+
"--model",
|
|
163
|
+
"-m",
|
|
164
|
+
help="Model to use"
|
|
165
|
+
),
|
|
166
|
+
apply: bool = typer.Option(
|
|
167
|
+
False,
|
|
168
|
+
"--apply",
|
|
169
|
+
"-a",
|
|
170
|
+
help="Apply changes without confirmation"
|
|
171
|
+
)
|
|
172
|
+
):
|
|
173
|
+
"""
|
|
174
|
+
Edit a file using AI.
|
|
175
|
+
|
|
176
|
+
Examples:
|
|
177
|
+
tzamuncode edit app.py "Add error handling"
|
|
178
|
+
tzamuncode edit . "Add type hints to all functions"
|
|
179
|
+
tzc edit main.py "Refactor to use async" --apply
|
|
180
|
+
"""
|
|
181
|
+
edit_command(file_path=file_path, instruction=instruction, model=model, apply=apply)
|
|
182
|
+
|
|
183
|
+
@app.command()
|
|
184
|
+
def explain(
|
|
185
|
+
file_path: str = typer.Argument(..., help="File to explain"),
|
|
186
|
+
model: str = typer.Option(
|
|
187
|
+
"qwen2.5:32b",
|
|
188
|
+
"--model",
|
|
189
|
+
"-m",
|
|
190
|
+
help="Model to use"
|
|
191
|
+
),
|
|
192
|
+
detailed: bool = typer.Option(
|
|
193
|
+
False,
|
|
194
|
+
"--detailed",
|
|
195
|
+
"-d",
|
|
196
|
+
help="Provide detailed explanation"
|
|
197
|
+
)
|
|
198
|
+
):
|
|
199
|
+
"""
|
|
200
|
+
Explain code in a file.
|
|
201
|
+
|
|
202
|
+
Examples:
|
|
203
|
+
tzamuncode explain complex_function.py
|
|
204
|
+
tzamuncode explain auth.py --detailed
|
|
205
|
+
tzc explain main.py -d
|
|
206
|
+
"""
|
|
207
|
+
explain_command(file_path=file_path, model=model, detailed=detailed)
|
|
208
|
+
|
|
209
|
+
@app.command()
|
|
210
|
+
def code(
|
|
211
|
+
instruction: str = typer.Argument(..., help="What to do"),
|
|
212
|
+
model: str = typer.Option(
|
|
213
|
+
"qwen2.5:32b",
|
|
214
|
+
"--model",
|
|
215
|
+
"-m",
|
|
216
|
+
help="Model to use"
|
|
217
|
+
),
|
|
218
|
+
workspace: Optional[str] = typer.Option(
|
|
219
|
+
None,
|
|
220
|
+
"--workspace",
|
|
221
|
+
"-w",
|
|
222
|
+
help="Project directory (default: current)"
|
|
223
|
+
),
|
|
224
|
+
apply: bool = typer.Option(
|
|
225
|
+
False,
|
|
226
|
+
"--apply",
|
|
227
|
+
"-a",
|
|
228
|
+
help="Auto-apply changes without confirmation"
|
|
229
|
+
)
|
|
230
|
+
):
|
|
231
|
+
"""
|
|
232
|
+
Agentic AI mode - AI autonomously accesses files and writes code.
|
|
233
|
+
|
|
234
|
+
Similar to Claude Code, the AI will:
|
|
235
|
+
- Scan your project structure
|
|
236
|
+
- Read files autonomously
|
|
237
|
+
- Edit multiple files
|
|
238
|
+
- Execute git operations
|
|
239
|
+
- Plan and execute multi-step tasks
|
|
240
|
+
|
|
241
|
+
Examples:
|
|
242
|
+
tzamuncode code "Add authentication to this Flask app"
|
|
243
|
+
tzamuncode code "Refactor all Python files to use async/await"
|
|
244
|
+
tzc code "Add unit tests for all functions" --apply
|
|
245
|
+
"""
|
|
246
|
+
agentic_command(
|
|
247
|
+
instruction=instruction,
|
|
248
|
+
model=model,
|
|
249
|
+
workspace=workspace,
|
|
250
|
+
auto_apply=apply
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
@app.command()
|
|
254
|
+
def project(
|
|
255
|
+
workspace: Optional[str] = typer.Option(
|
|
256
|
+
None,
|
|
257
|
+
"--workspace",
|
|
258
|
+
"-w",
|
|
259
|
+
help="Project directory (default: current)"
|
|
260
|
+
)
|
|
261
|
+
):
|
|
262
|
+
"""
|
|
263
|
+
Analyze and understand project structure.
|
|
264
|
+
|
|
265
|
+
Examples:
|
|
266
|
+
tzamuncode project
|
|
267
|
+
tzamuncode project --workspace /path/to/project
|
|
268
|
+
tzc project
|
|
269
|
+
"""
|
|
270
|
+
project_command(workspace=workspace)
|
|
271
|
+
|
|
272
|
+
@app.command()
|
|
273
|
+
def login(
|
|
274
|
+
username: Optional[str] = typer.Option(
|
|
275
|
+
None,
|
|
276
|
+
"--username",
|
|
277
|
+
"-u",
|
|
278
|
+
help="TzamunAI username"
|
|
279
|
+
),
|
|
280
|
+
api_key: Optional[str] = typer.Option(
|
|
281
|
+
None,
|
|
282
|
+
"--api-key",
|
|
283
|
+
"-k",
|
|
284
|
+
help="TzamunAI API key (alternative to username/password)"
|
|
285
|
+
)
|
|
286
|
+
):
|
|
287
|
+
"""
|
|
288
|
+
Login to TzamunAI (app.tzamun.ai)
|
|
289
|
+
|
|
290
|
+
Examples:
|
|
291
|
+
tzamuncode login
|
|
292
|
+
tzamuncode login --username myuser
|
|
293
|
+
tzamuncode login --api-key YOUR_API_KEY
|
|
294
|
+
"""
|
|
295
|
+
from ..auth.auth_manager import AuthManager
|
|
296
|
+
from rich.prompt import Prompt
|
|
297
|
+
import getpass
|
|
298
|
+
|
|
299
|
+
auth = AuthManager()
|
|
300
|
+
|
|
301
|
+
# Check if already logged in
|
|
302
|
+
if auth.is_authenticated():
|
|
303
|
+
user_info = auth.get_user_info()
|
|
304
|
+
console.print(f"\n[bold yellow]β [/bold yellow] Already logged in as [cyan]{user_info.get('username')}[/cyan]")
|
|
305
|
+
if not Confirm.ask("Logout and login again?"):
|
|
306
|
+
return
|
|
307
|
+
auth.logout()
|
|
308
|
+
|
|
309
|
+
console.print("\n[bold cyan]π Login to TzamunAI[/bold cyan]\n")
|
|
310
|
+
|
|
311
|
+
# API Key login
|
|
312
|
+
if api_key:
|
|
313
|
+
console.print("[dim]Validating API key...[/dim]")
|
|
314
|
+
if auth.login_with_api_key(api_key):
|
|
315
|
+
console.print("\n[bold green]β[/bold green] Successfully logged in with API key!\n")
|
|
316
|
+
else:
|
|
317
|
+
console.print("\n[bold red]β[/bold red] Invalid API key\n")
|
|
318
|
+
return
|
|
319
|
+
|
|
320
|
+
# Username/Password login
|
|
321
|
+
if not username:
|
|
322
|
+
username = Prompt.ask("[cyan]Username[/cyan]")
|
|
323
|
+
|
|
324
|
+
password = getpass.getpass("Password: ")
|
|
325
|
+
|
|
326
|
+
console.print("\n[dim]Authenticating...[/dim]")
|
|
327
|
+
|
|
328
|
+
if auth.login_with_credentials(username, password):
|
|
329
|
+
console.print(f"\n[bold green]β[/bold green] Successfully logged in as [cyan]{username}[/cyan]!\n")
|
|
330
|
+
console.print("[dim]Your session will expire in 30 days[/dim]\n")
|
|
331
|
+
else:
|
|
332
|
+
console.print("\n[bold red]β[/bold red] Login failed. Check your credentials.\n")
|
|
333
|
+
|
|
334
|
+
@app.command()
|
|
335
|
+
def logout():
|
|
336
|
+
"""
|
|
337
|
+
Logout from TzamunAI
|
|
338
|
+
|
|
339
|
+
Examples:
|
|
340
|
+
tzamuncode logout
|
|
341
|
+
"""
|
|
342
|
+
from ..auth.auth_manager import AuthManager
|
|
343
|
+
|
|
344
|
+
auth = AuthManager()
|
|
345
|
+
|
|
346
|
+
if not auth.is_authenticated():
|
|
347
|
+
console.print("\n[dim]Not logged in[/dim]\n")
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
user_info = auth.get_user_info()
|
|
351
|
+
auth.logout()
|
|
352
|
+
|
|
353
|
+
console.print(f"\n[bold green]β[/bold green] Logged out [cyan]{user_info.get('username')}[/cyan]\n")
|
|
354
|
+
|
|
355
|
+
@app.command()
|
|
356
|
+
def whoami():
|
|
357
|
+
"""
|
|
358
|
+
Show current authentication status
|
|
359
|
+
|
|
360
|
+
Examples:
|
|
361
|
+
tzamuncode whoami
|
|
362
|
+
"""
|
|
363
|
+
from ..auth.auth_manager import AuthManager
|
|
364
|
+
|
|
365
|
+
auth = AuthManager()
|
|
366
|
+
|
|
367
|
+
console.print()
|
|
368
|
+
|
|
369
|
+
if not auth.is_authenticated():
|
|
370
|
+
console.print("[bold yellow]Not authenticated[/bold yellow]")
|
|
371
|
+
console.print("\n[dim]Login with:[/dim] [cyan]tzamuncode login[/cyan]\n")
|
|
372
|
+
return
|
|
373
|
+
|
|
374
|
+
user_info = auth.get_user_info()
|
|
375
|
+
|
|
376
|
+
table = Table(
|
|
377
|
+
title="[bold cyan]Authentication Status[/bold cyan]",
|
|
378
|
+
border_style="blue",
|
|
379
|
+
show_header=True,
|
|
380
|
+
header_style="bold white on blue"
|
|
381
|
+
)
|
|
382
|
+
table.add_column("Property", style="bold cyan", width=20)
|
|
383
|
+
table.add_column("Value", style="yellow")
|
|
384
|
+
|
|
385
|
+
table.add_row("Status", "β Authenticated")
|
|
386
|
+
table.add_row("Username", user_info.get('username', 'N/A'))
|
|
387
|
+
table.add_row("Auth Type", user_info.get('type', 'N/A').replace('_', ' ').title())
|
|
388
|
+
|
|
389
|
+
console.print(table)
|
|
390
|
+
console.print()
|
|
391
|
+
|
|
392
|
+
@app.command()
|
|
393
|
+
def info():
|
|
394
|
+
"""
|
|
395
|
+
Show information about TzamunCode and available models.
|
|
396
|
+
"""
|
|
397
|
+
from .. import __version__
|
|
398
|
+
from ..auth.auth_manager import AuthManager
|
|
399
|
+
|
|
400
|
+
auth = AuthManager()
|
|
401
|
+
|
|
402
|
+
console.print(Panel.fit(
|
|
403
|
+
f"""[bold blue]TzamunCode CLI[/bold blue] v{__version__}
|
|
404
|
+
|
|
405
|
+
[bold]AI Coding Assistant powered by local models[/bold]
|
|
406
|
+
|
|
407
|
+
Built by [bold cyan]Tzamun Arabia IT Co.[/bold cyan] πΈπ¦
|
|
408
|
+
|
|
409
|
+
[dim]Privacy-first β’ No cloud dependencies β’ Complete control[/dim]
|
|
410
|
+
""",
|
|
411
|
+
border_style="blue"
|
|
412
|
+
))
|
|
413
|
+
|
|
414
|
+
# Show auth status
|
|
415
|
+
if auth.is_authenticated():
|
|
416
|
+
user_info = auth.get_user_info()
|
|
417
|
+
console.print(f"\n[bold green]β[/bold green] Logged in as [cyan]{user_info.get('username')}[/cyan]")
|
|
418
|
+
else:
|
|
419
|
+
console.print("\n[dim]Not logged in β’ Use[/dim] [cyan]tzamuncode login[/cyan]")
|
|
420
|
+
|
|
421
|
+
# Check Ollama connection
|
|
422
|
+
try:
|
|
423
|
+
client = OllamaClient()
|
|
424
|
+
models = client.list_models()
|
|
425
|
+
|
|
426
|
+
console.print("\n[bold green]β[/bold green] Connected to Ollama")
|
|
427
|
+
console.print(f"[bold]Available models:[/bold] {len(models)}")
|
|
428
|
+
|
|
429
|
+
for model in models[:5]: # Show first 5
|
|
430
|
+
console.print(f" β’ {model}")
|
|
431
|
+
|
|
432
|
+
if len(models) > 5:
|
|
433
|
+
console.print(f" ... and {len(models) - 5} more")
|
|
434
|
+
|
|
435
|
+
except Exception as e:
|
|
436
|
+
console.print(f"\n[bold red]β[/bold red] Could not connect to Ollama: {e}")
|
|
437
|
+
console.print("[dim]Make sure Ollama is running: ollama serve[/dim]")
|
|
438
|
+
|
|
439
|
+
def run():
|
|
440
|
+
"""Entry point for the CLI"""
|
|
441
|
+
app()
|
|
442
|
+
|
|
443
|
+
if __name__ == "__main__":
|
|
444
|
+
run()
|