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
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""
|
|
2
|
+
vLLM Client for TzamunCode CLI
|
|
3
|
+
Provides faster inference using vLLM OpenAI-compatible API
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from typing import Iterator, List, Dict, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class VLLMClient:
|
|
11
|
+
"""Client for vLLM server using OpenAI-compatible API"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, base_url: str = "http://localhost:8000", model: str = "deepseek-coder-7b"):
|
|
14
|
+
self.base_url = base_url.rstrip('/')
|
|
15
|
+
self.model = model
|
|
16
|
+
self.api_url = f"{self.base_url}/v1"
|
|
17
|
+
|
|
18
|
+
def chat(self, messages: List[Dict[str, str]], stream: bool = True) -> Iterator[str]:
|
|
19
|
+
"""
|
|
20
|
+
Send chat request to vLLM server
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
messages: List of message dicts with 'role' and 'content'
|
|
24
|
+
stream: Whether to stream the response
|
|
25
|
+
|
|
26
|
+
Yields:
|
|
27
|
+
Response chunks as strings
|
|
28
|
+
"""
|
|
29
|
+
url = f"{self.api_url}/chat/completions"
|
|
30
|
+
|
|
31
|
+
payload = {
|
|
32
|
+
"model": self.model,
|
|
33
|
+
"messages": messages,
|
|
34
|
+
"stream": stream,
|
|
35
|
+
"temperature": 0.7,
|
|
36
|
+
"max_tokens": 2000, # Reduced to fit within 4096 context with system prompt
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
response = requests.post(
|
|
41
|
+
url,
|
|
42
|
+
json=payload,
|
|
43
|
+
stream=stream,
|
|
44
|
+
timeout=60
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if response.status_code != 200:
|
|
48
|
+
error_detail = response.text
|
|
49
|
+
yield f"\nβ vLLM Error ({response.status_code}): {error_detail}\n"
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
response.raise_for_status()
|
|
53
|
+
|
|
54
|
+
if stream:
|
|
55
|
+
for line in response.iter_lines():
|
|
56
|
+
if line:
|
|
57
|
+
line = line.decode('utf-8')
|
|
58
|
+
if line.startswith('data: '):
|
|
59
|
+
data = line[6:] # Remove 'data: ' prefix
|
|
60
|
+
if data.strip() == '[DONE]':
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
import json
|
|
65
|
+
chunk = json.loads(data)
|
|
66
|
+
if 'choices' in chunk and len(chunk['choices']) > 0:
|
|
67
|
+
delta = chunk['choices'][0].get('delta', {})
|
|
68
|
+
content = delta.get('content', '')
|
|
69
|
+
if content:
|
|
70
|
+
yield content
|
|
71
|
+
except json.JSONDecodeError:
|
|
72
|
+
continue
|
|
73
|
+
else:
|
|
74
|
+
result = response.json()
|
|
75
|
+
if 'choices' in result and len(result['choices']) > 0:
|
|
76
|
+
content = result['choices'][0]['message']['content']
|
|
77
|
+
yield content
|
|
78
|
+
|
|
79
|
+
except requests.exceptions.RequestException as e:
|
|
80
|
+
yield f"\nβ Error connecting to vLLM server: {e}\n"
|
|
81
|
+
yield "Make sure vLLM server is running on http://localhost:8000\n"
|
|
82
|
+
|
|
83
|
+
def list_models(self) -> List[str]:
|
|
84
|
+
"""List available models from vLLM server"""
|
|
85
|
+
try:
|
|
86
|
+
url = f"{self.api_url}/models"
|
|
87
|
+
response = requests.get(url, timeout=5)
|
|
88
|
+
response.raise_for_status()
|
|
89
|
+
|
|
90
|
+
data = response.json()
|
|
91
|
+
if 'data' in data:
|
|
92
|
+
return [model['id'] for model in data['data']]
|
|
93
|
+
return [self.model]
|
|
94
|
+
except:
|
|
95
|
+
return [self.model]
|
|
96
|
+
|
|
97
|
+
def chat_stream(self, messages: List[Dict[str, str]]) -> Iterator[str]:
|
|
98
|
+
"""
|
|
99
|
+
Stream chat responses (alias for chat with stream=True)
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
messages: List of message dicts with 'role' and 'content'
|
|
103
|
+
|
|
104
|
+
Yields:
|
|
105
|
+
Response chunks as strings
|
|
106
|
+
"""
|
|
107
|
+
yield from self.chat(messages, stream=True)
|
|
108
|
+
|
|
109
|
+
def generate(self, prompt: str, stream: bool = True) -> Iterator[str]:
|
|
110
|
+
"""
|
|
111
|
+
Generate completion from prompt
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
prompt: Input prompt
|
|
115
|
+
stream: Whether to stream the response
|
|
116
|
+
|
|
117
|
+
Yields:
|
|
118
|
+
Response chunks
|
|
119
|
+
"""
|
|
120
|
+
messages = [{"role": "user", "content": prompt}]
|
|
121
|
+
yield from self.chat(messages, stream=stream)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Utilities module"""
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
File operations utilities for TzamunCode
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
import difflib
|
|
8
|
+
|
|
9
|
+
class FileManager:
|
|
10
|
+
"""Manage file operations"""
|
|
11
|
+
|
|
12
|
+
def read_file(self, path: str) -> str:
|
|
13
|
+
"""Read file content"""
|
|
14
|
+
file_path = Path(path)
|
|
15
|
+
if not file_path.exists():
|
|
16
|
+
raise FileNotFoundError(f"File not found: {path}")
|
|
17
|
+
return file_path.read_text()
|
|
18
|
+
|
|
19
|
+
def write_file(self, path: str, content: str) -> None:
|
|
20
|
+
"""Write content to file"""
|
|
21
|
+
file_path = Path(path)
|
|
22
|
+
|
|
23
|
+
# Create parent directories if they don't exist
|
|
24
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
25
|
+
|
|
26
|
+
file_path.write_text(content)
|
|
27
|
+
|
|
28
|
+
def show_diff(self, path: str, new_content: str) -> str:
|
|
29
|
+
"""Show diff between current and new content"""
|
|
30
|
+
try:
|
|
31
|
+
old_content = self.read_file(path)
|
|
32
|
+
except FileNotFoundError:
|
|
33
|
+
# New file
|
|
34
|
+
return f"+++ {path} (new file)\n" + "\n".join(f"+{line}" for line in new_content.splitlines())
|
|
35
|
+
|
|
36
|
+
old_lines = old_content.splitlines(keepends=True)
|
|
37
|
+
new_lines = new_content.splitlines(keepends=True)
|
|
38
|
+
|
|
39
|
+
diff = difflib.unified_diff(
|
|
40
|
+
old_lines,
|
|
41
|
+
new_lines,
|
|
42
|
+
fromfile=f"a/{path}",
|
|
43
|
+
tofile=f"b/{path}",
|
|
44
|
+
lineterm=""
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return "".join(diff)
|
|
48
|
+
|
|
49
|
+
def file_exists(self, path: str) -> bool:
|
|
50
|
+
"""Check if file exists"""
|
|
51
|
+
return Path(path).exists()
|
|
52
|
+
|
|
53
|
+
def list_files(self, directory: str, pattern: str = "*") -> list:
|
|
54
|
+
"""List files in directory matching pattern"""
|
|
55
|
+
dir_path = Path(directory)
|
|
56
|
+
if not dir_path.is_dir():
|
|
57
|
+
raise NotADirectoryError(f"Not a directory: {directory}")
|
|
58
|
+
|
|
59
|
+
return [str(p) for p in dir_path.rglob(pattern) if p.is_file()]
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project scanner for understanding codebase structure
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, List, Set
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ProjectScanner:
|
|
11
|
+
"""Scan and understand project structure"""
|
|
12
|
+
|
|
13
|
+
# Common files to ignore
|
|
14
|
+
IGNORE_DIRS = {
|
|
15
|
+
'.git', '__pycache__', 'node_modules', 'venv', 'env',
|
|
16
|
+
'.venv', 'dist', 'build', '.pytest_cache', '.mypy_cache',
|
|
17
|
+
'coverage', '.coverage', 'htmlcov'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
IGNORE_FILES = {
|
|
21
|
+
'.pyc', '.pyo', '.pyd', '.so', '.dll', '.dylib',
|
|
22
|
+
'.class', '.o', '.obj', '.exe'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Language detection
|
|
26
|
+
LANGUAGE_EXTENSIONS = {
|
|
27
|
+
'.py': 'Python',
|
|
28
|
+
'.js': 'JavaScript',
|
|
29
|
+
'.ts': 'TypeScript',
|
|
30
|
+
'.jsx': 'React',
|
|
31
|
+
'.tsx': 'React TypeScript',
|
|
32
|
+
'.go': 'Go',
|
|
33
|
+
'.rs': 'Rust',
|
|
34
|
+
'.java': 'Java',
|
|
35
|
+
'.cpp': 'C++',
|
|
36
|
+
'.c': 'C',
|
|
37
|
+
'.rb': 'Ruby',
|
|
38
|
+
'.php': 'PHP',
|
|
39
|
+
'.swift': 'Swift',
|
|
40
|
+
'.kt': 'Kotlin',
|
|
41
|
+
'.sh': 'Shell',
|
|
42
|
+
'.md': 'Markdown',
|
|
43
|
+
'.json': 'JSON',
|
|
44
|
+
'.yaml': 'YAML',
|
|
45
|
+
'.yml': 'YAML',
|
|
46
|
+
'.toml': 'TOML',
|
|
47
|
+
'.html': 'HTML',
|
|
48
|
+
'.css': 'CSS',
|
|
49
|
+
'.scss': 'SCSS',
|
|
50
|
+
'.sql': 'SQL',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
def __init__(self, workspace: Path):
|
|
54
|
+
self.workspace = Path(workspace)
|
|
55
|
+
|
|
56
|
+
def scan(self) -> Dict:
|
|
57
|
+
"""
|
|
58
|
+
Scan project and return structure information
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dict with project metadata
|
|
62
|
+
"""
|
|
63
|
+
structure = {
|
|
64
|
+
'total_files': 0,
|
|
65
|
+
'total_dirs': 0,
|
|
66
|
+
'languages': set(),
|
|
67
|
+
'key_files': [],
|
|
68
|
+
'file_tree': [],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for root, dirs, files in os.walk(self.workspace):
|
|
72
|
+
# Filter ignored directories
|
|
73
|
+
dirs[:] = [d for d in dirs if d not in self.IGNORE_DIRS]
|
|
74
|
+
|
|
75
|
+
root_path = Path(root)
|
|
76
|
+
rel_root = root_path.relative_to(self.workspace)
|
|
77
|
+
|
|
78
|
+
structure['total_dirs'] += len(dirs)
|
|
79
|
+
|
|
80
|
+
for file in files:
|
|
81
|
+
# Skip ignored files
|
|
82
|
+
if any(file.endswith(ext) for ext in self.IGNORE_FILES):
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
structure['total_files'] += 1
|
|
86
|
+
|
|
87
|
+
file_path = root_path / file
|
|
88
|
+
rel_path = file_path.relative_to(self.workspace)
|
|
89
|
+
|
|
90
|
+
# Detect language
|
|
91
|
+
ext = file_path.suffix
|
|
92
|
+
if ext in self.LANGUAGE_EXTENSIONS:
|
|
93
|
+
structure['languages'].add(self.LANGUAGE_EXTENSIONS[ext])
|
|
94
|
+
|
|
95
|
+
# Identify key files
|
|
96
|
+
if self._is_key_file(file):
|
|
97
|
+
structure['key_files'].append(str(rel_path))
|
|
98
|
+
|
|
99
|
+
# Add to file tree (limit depth)
|
|
100
|
+
if len(rel_path.parts) <= 3:
|
|
101
|
+
structure['file_tree'].append(str(rel_path))
|
|
102
|
+
|
|
103
|
+
structure['languages'] = sorted(list(structure['languages']))
|
|
104
|
+
|
|
105
|
+
return structure
|
|
106
|
+
|
|
107
|
+
def _is_key_file(self, filename: str) -> bool:
|
|
108
|
+
"""Check if file is a key project file"""
|
|
109
|
+
key_files = {
|
|
110
|
+
'README.md', 'README.rst', 'README.txt',
|
|
111
|
+
'setup.py', 'setup.cfg', 'pyproject.toml',
|
|
112
|
+
'requirements.txt', 'Pipfile', 'poetry.lock',
|
|
113
|
+
'package.json', 'package-lock.json', 'yarn.lock',
|
|
114
|
+
'Cargo.toml', 'go.mod', 'pom.xml', 'build.gradle',
|
|
115
|
+
'Makefile', 'Dockerfile', 'docker-compose.yml',
|
|
116
|
+
'.gitignore', 'LICENSE', 'CONTRIBUTING.md',
|
|
117
|
+
'main.py', 'app.py', 'index.js', 'main.go',
|
|
118
|
+
'__init__.py', '__main__.py'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return filename in key_files
|
|
122
|
+
|
|
123
|
+
def find_files(self, pattern: str) -> List[str]:
|
|
124
|
+
"""Find files matching a pattern"""
|
|
125
|
+
matches = []
|
|
126
|
+
|
|
127
|
+
for root, dirs, files in os.walk(self.workspace):
|
|
128
|
+
dirs[:] = [d for d in dirs if d not in self.IGNORE_DIRS]
|
|
129
|
+
|
|
130
|
+
root_path = Path(root)
|
|
131
|
+
|
|
132
|
+
for file in files:
|
|
133
|
+
if pattern in file:
|
|
134
|
+
file_path = root_path / file
|
|
135
|
+
rel_path = file_path.relative_to(self.workspace)
|
|
136
|
+
matches.append(str(rel_path))
|
|
137
|
+
|
|
138
|
+
return matches
|
|
139
|
+
|
|
140
|
+
def get_project_type(self) -> str:
|
|
141
|
+
"""Detect project type"""
|
|
142
|
+
project_types = []
|
|
143
|
+
|
|
144
|
+
# Check root directory
|
|
145
|
+
try:
|
|
146
|
+
root_files = set(os.listdir(self.workspace))
|
|
147
|
+
except:
|
|
148
|
+
return 'Unknown'
|
|
149
|
+
|
|
150
|
+
# Check for multiple project types
|
|
151
|
+
if 'package.json' in root_files:
|
|
152
|
+
project_types.append('Node.js')
|
|
153
|
+
|
|
154
|
+
if 'requirements.txt' in root_files or 'setup.py' in root_files or 'pyproject.toml' in root_files:
|
|
155
|
+
project_types.append('Python')
|
|
156
|
+
|
|
157
|
+
if 'Cargo.toml' in root_files:
|
|
158
|
+
project_types.append('Rust')
|
|
159
|
+
|
|
160
|
+
if 'go.mod' in root_files:
|
|
161
|
+
project_types.append('Go')
|
|
162
|
+
|
|
163
|
+
if 'pom.xml' in root_files or 'build.gradle' in root_files:
|
|
164
|
+
project_types.append('Java')
|
|
165
|
+
|
|
166
|
+
# Check for frontend frameworks
|
|
167
|
+
if 'package.json' in root_files:
|
|
168
|
+
try:
|
|
169
|
+
import json
|
|
170
|
+
pkg_path = self.workspace / 'package.json'
|
|
171
|
+
with open(pkg_path) as f:
|
|
172
|
+
pkg = json.load(f)
|
|
173
|
+
deps = {**pkg.get('dependencies', {}), **pkg.get('devDependencies', {})}
|
|
174
|
+
|
|
175
|
+
if 'react' in deps:
|
|
176
|
+
project_types.append('React')
|
|
177
|
+
elif 'vue' in deps:
|
|
178
|
+
project_types.append('Vue')
|
|
179
|
+
elif 'angular' in deps or '@angular/core' in deps:
|
|
180
|
+
project_types.append('Angular')
|
|
181
|
+
elif 'next' in deps:
|
|
182
|
+
project_types.append('Next.js')
|
|
183
|
+
except:
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
# Check subdirectories for additional context
|
|
187
|
+
if 'backend' in root_files and 'frontend' in root_files:
|
|
188
|
+
return 'Full-Stack (' + ', '.join(project_types) + ')' if project_types else 'Full-Stack'
|
|
189
|
+
|
|
190
|
+
if project_types:
|
|
191
|
+
return ', '.join(project_types)
|
|
192
|
+
|
|
193
|
+
return 'Unknown'
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tzamuncode
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: TzamunCode - AI Coding Assistant powered by local models
|
|
5
|
+
Author-email: "Tzamun Arabia IT Co." <info@tzamun.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://tzamun.com
|
|
8
|
+
Project-URL: Documentation, https://docs.tzamun.com/tzamuncode
|
|
9
|
+
Project-URL: Repository, https://github.com/tzamun/tzamuncode-cli
|
|
10
|
+
Keywords: ai,coding,assistant,ollama,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: typer>=0.9.0
|
|
21
|
+
Requires-Dist: rich>=13.0.0
|
|
22
|
+
Requires-Dist: textual>=0.40.0
|
|
23
|
+
Requires-Dist: readchar>=4.0.0
|
|
24
|
+
Requires-Dist: langchain>=0.1.0
|
|
25
|
+
Requires-Dist: langchain-community>=0.0.10
|
|
26
|
+
Requires-Dist: openai>=1.0.0
|
|
27
|
+
Requires-Dist: requests>=2.31.0
|
|
28
|
+
Requires-Dist: gitpython>=3.1.40
|
|
29
|
+
Requires-Dist: pathspec>=0.11.0
|
|
30
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
31
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
# TzamunCode CLI π
|
|
40
|
+
|
|
41
|
+
> AI Coding Assistant powered by local models - Built in Saudi Arabia πΈπ¦
|
|
42
|
+
|
|
43
|
+
**TzamunCode** is a privacy-first AI coding assistant that runs entirely on your local infrastructure using Ollama and vLLM. No cloud dependencies, no API costs, complete control.
|
|
44
|
+
|
|
45
|
+
## β¨ Features
|
|
46
|
+
|
|
47
|
+
- π€ **Agentic AI** - Multi-step planning and execution
|
|
48
|
+
- π **Code Generation** - Create files, functions, and entire projects
|
|
49
|
+
- βοΈ **Multi-file Editing** - Edit multiple files in one operation
|
|
50
|
+
- π§ **Tool Calling** - Git operations, file search, command execution
|
|
51
|
+
- π― **Context Aware** - Understands your project structure
|
|
52
|
+
- π **Privacy First** - Everything runs locally
|
|
53
|
+
- β‘ **Fast** - Powered by vLLM for optimized inference
|
|
54
|
+
- π **Multi-model** - Use any Ollama model (15+ available)
|
|
55
|
+
|
|
56
|
+
## π Quick Start
|
|
57
|
+
|
|
58
|
+
### Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Clone the repository
|
|
62
|
+
git clone https://github.com/tzamun/tzamuncode-cli.git
|
|
63
|
+
cd tzamuncode-cli
|
|
64
|
+
|
|
65
|
+
# Install
|
|
66
|
+
pip install -e .
|
|
67
|
+
|
|
68
|
+
# Or install from PyPI (when published)
|
|
69
|
+
pip install tzamuncode
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Prerequisites
|
|
73
|
+
|
|
74
|
+
- Python 3.9+
|
|
75
|
+
- Ollama running locally (http://localhost:11434)
|
|
76
|
+
- At least one Ollama model installed
|
|
77
|
+
|
|
78
|
+
### Basic Usage
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
# Start interactive chat
|
|
82
|
+
tzamuncode chat
|
|
83
|
+
|
|
84
|
+
# Generate code
|
|
85
|
+
tzamuncode generate "Create a Flask REST API with authentication"
|
|
86
|
+
|
|
87
|
+
# Edit a file
|
|
88
|
+
tzamuncode edit app.py "Add error handling to all routes"
|
|
89
|
+
|
|
90
|
+
# Explain code
|
|
91
|
+
tzamuncode explain main.py
|
|
92
|
+
|
|
93
|
+
# Quick alias
|
|
94
|
+
tzc chat
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## π Documentation
|
|
98
|
+
|
|
99
|
+
### Commands
|
|
100
|
+
|
|
101
|
+
#### `chat` - Interactive Chat
|
|
102
|
+
```bash
|
|
103
|
+
tzamuncode chat
|
|
104
|
+
tzamuncode chat --model qwen2.5:32b
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### `generate` - Code Generation
|
|
108
|
+
```bash
|
|
109
|
+
tzamuncode generate "Create a Python web scraper"
|
|
110
|
+
tzamuncode generate "Add unit tests for user.py" --output tests/
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### `edit` - File Editing
|
|
114
|
+
```bash
|
|
115
|
+
tzamuncode edit app.py "Refactor to use async/await"
|
|
116
|
+
tzamuncode edit . "Add type hints to all functions"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
#### `explain` - Code Explanation
|
|
120
|
+
```bash
|
|
121
|
+
tzamuncode explain complex_function.py
|
|
122
|
+
tzamuncode explain --detailed auth.py
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
#### `review` - Code Review
|
|
126
|
+
```bash
|
|
127
|
+
tzamuncode review pull_request.diff
|
|
128
|
+
tzamuncode review --strict src/
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Configuration
|
|
132
|
+
|
|
133
|
+
Create `~/.tzamuncode/config.yaml`:
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
# Default model
|
|
137
|
+
model: qwen2.5:32b
|
|
138
|
+
|
|
139
|
+
# Ollama settings
|
|
140
|
+
ollama:
|
|
141
|
+
base_url: http://localhost:11434
|
|
142
|
+
timeout: 120
|
|
143
|
+
|
|
144
|
+
# vLLM settings (optional, for faster inference)
|
|
145
|
+
vllm:
|
|
146
|
+
enabled: true
|
|
147
|
+
base_url: http://localhost:8000
|
|
148
|
+
model: deepseek-coder-7b
|
|
149
|
+
|
|
150
|
+
# Preferences
|
|
151
|
+
preferences:
|
|
152
|
+
show_diff: true
|
|
153
|
+
auto_apply: false
|
|
154
|
+
max_context: 64000
|
|
155
|
+
temperature: 0.7
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## ποΈ Architecture
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
162
|
+
β TzamunCode CLI β
|
|
163
|
+
β (Typer + Rich UI) β
|
|
164
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
165
|
+
β
|
|
166
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
167
|
+
β Agentic Layer (LangChain) β
|
|
168
|
+
β - Multi-step planning β
|
|
169
|
+
β - Tool calling β
|
|
170
|
+
β - Context management β
|
|
171
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
172
|
+
β
|
|
173
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
174
|
+
β AI Backend β
|
|
175
|
+
β - Ollama (15+ models) β
|
|
176
|
+
β - vLLM (fast inference) β
|
|
177
|
+
βββββββββββββββββββββββββββββββββββββββ
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## π€ Contributing
|
|
181
|
+
|
|
182
|
+
We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
183
|
+
|
|
184
|
+
## π License
|
|
185
|
+
|
|
186
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
187
|
+
|
|
188
|
+
## π Built by Tzamun Arabia IT Co.
|
|
189
|
+
|
|
190
|
+
**TzamunCode** is part of the Tzamun AI ecosystem:
|
|
191
|
+
- **TzamunAI** - AI platform with 15+ models
|
|
192
|
+
- **TzamunERP** - ERPNext + AI integration
|
|
193
|
+
- **Auxly** - AI coding assistant for IDEs
|
|
194
|
+
- **AccessHub** - Privileged Access Management
|
|
195
|
+
|
|
196
|
+
Visit [tzamun.com](https://tzamun.com) to learn more.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
Made with β€οΈ in Saudi Arabia πΈπ¦
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
tzamuncode/__init__.py,sha256=cNcSNEPzBmnBV86T-NDk_EgBLbfV2VlWy0vgxXLRSTU,229
|
|
2
|
+
tzamuncode/agents/__init__.py,sha256=ZcQU1197DpPw2Gq3dWH99E6nsgOv0F6dojKO4BbV6kQ,39
|
|
3
|
+
tzamuncode/agents/coder.py,sha256=852XLqSSFZpgIpIrbLuTn4BHFSES2DJaAXQfFJjXRVY,3977
|
|
4
|
+
tzamuncode/agents/tools.py,sha256=xIiQGhdUXEui3Wy6zyl0g6JgR9wUOwsrxlKRUm_Sx9Y,5253
|
|
5
|
+
tzamuncode/auth/__init__.py,sha256=BLT9ylnDYj8iyIS_Mb-IZrd5Br7eS1OjiPTTOb019mM,43
|
|
6
|
+
tzamuncode/auth/auth_manager.py,sha256=y-YWaVrfhjzW33IXP3dw1eqtTOW56jo9iB-Ef8pznps,4970
|
|
7
|
+
tzamuncode/cli/__init__.py,sha256=2GidVBgUv1bt9mf4sS1a6kLP5p9xyxT_YMjG4PpGIAk,32
|
|
8
|
+
tzamuncode/cli/agentic_commands.py,sha256=9xyDJhKZiT6__7N-FYfGyFAC_hS_xnqgWU6RQMQerKY,4390
|
|
9
|
+
tzamuncode/cli/auth_commands.py,sha256=6fhnm3Ta6QggcjiDq9On8hd0NrKbDQZq5f195m9l1n8,3999
|
|
10
|
+
tzamuncode/cli/commands.py,sha256=hGQvyFo1aYzExrZaXmxrbJSHJU9dC-2OzVG0lM-2Lqg,6521
|
|
11
|
+
tzamuncode/cli/enhanced_chat.py,sha256=9FaGPOKRXIIGIvdZHlhdxAMwKgVA9qorM7YDmSuzmBE,11193
|
|
12
|
+
tzamuncode/cli/interactive_chat.py,sha256=K-mpagYwYtVAEfvTYIOb5r_srydB7EG1k6jgATuXBsk,10649
|
|
13
|
+
tzamuncode/cli/main.py,sha256=bX7WHyjLa_zeiTowBDtDB0WXbTSg79flm6iFBnjlVvM,12726
|
|
14
|
+
tzamuncode/cli/realtime_chat.py,sha256=a8JCU2KwoJiQmXS89h8zDdaDPl1f-QQzSZIrasZ6WFU,40202
|
|
15
|
+
tzamuncode/cli/realtime_chat_methods.py,sha256=N4iJWFV6bLIG7SUuGjZTB_RzqntIxdu5Svy54rpARLQ,7186
|
|
16
|
+
tzamuncode/cli/tui_chat.py,sha256=EbAUFE8vHG2S7LN9qWVkzay3vTvQuXPFXwGZ0CRprHI,10612
|
|
17
|
+
tzamuncode/config/__init__.py,sha256=702VfKiaLtKlGiTc2655Fgb1twtivhPaXZKk67wqfK0,27
|
|
18
|
+
tzamuncode/models/__init__.py,sha256=obVaZ746k9ZW9OBOF4I2ZLwN293d11iyLjEyj2oZSMk,36
|
|
19
|
+
tzamuncode/models/ollama.py,sha256=Szwi9XMuSl1jD6Q3ffLrWqPlrU39ra5lA62NVESsCE0,4040
|
|
20
|
+
tzamuncode/models/vllm_client.py,sha256=rJzjYNOYoK09SStDEkt6YIm-Gm5XzL7UGRec2ArR5ZI,4309
|
|
21
|
+
tzamuncode/utils/__init__.py,sha256=pwnlYHOEveP6R5snPZnGlTE7erq2-eNyYx1aSf_O77c,23
|
|
22
|
+
tzamuncode/utils/file_ops.py,sha256=di50l5oV04o_P-iQcB8UrOcIf8TVEU6LikA8etpYp7U,1892
|
|
23
|
+
tzamuncode/utils/project_scanner.py,sha256=SvfxtbSn5nBvuqJNgIRVjVGlvTymfIvG0GxVHxYcA0c,6367
|
|
24
|
+
tzamuncode-0.1.0.dist-info/licenses/LICENSE,sha256=SKMpoBdfYod6hLsPmTRIIPBFkDfdDNqkSWVd357RTSc,1077
|
|
25
|
+
tzamuncode-0.1.0.dist-info/METADATA,sha256=rT8-usbRGWHygFttCf8X9_Eb4NRvjle0JIRBXd1Xir8,5652
|
|
26
|
+
tzamuncode-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
27
|
+
tzamuncode-0.1.0.dist-info/entry_points.txt,sha256=XUYoWwk091ZyGE9oB2K6iZqix2H4efQM7YNhOA7FSHU,85
|
|
28
|
+
tzamuncode-0.1.0.dist-info/top_level.txt,sha256=0phBwTnCSCYPBHwWuJdyJ_3WmVWEoCKIhqT4IIwDyPc,11
|
|
29
|
+
tzamuncode-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tzamun Arabia IT Co.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tzamuncode
|