makima-cli 0.2.1__tar.gz → 1.2.2__tar.gz

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.
Files changed (33) hide show
  1. {makima_cli-0.2.1 → makima_cli-1.2.2}/LICENSE +0 -0
  2. {makima_cli-0.2.1 → makima_cli-1.2.2}/PKG-INFO +2 -2
  3. {makima_cli-0.2.1 → makima_cli-1.2.2}/README.md +1 -1
  4. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/__init__.py +0 -0
  5. makima_cli-1.2.2/makima/config.py +18 -0
  6. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/core/__init__.py +0 -0
  7. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/core/agent.py +0 -0
  8. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/core/config_manager.py +52 -1
  9. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/core/context.py +0 -0
  10. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/core/memory.py +0 -0
  11. makima_cli-1.2.2/makima/main.py +14 -0
  12. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/providers/base.py +0 -0
  13. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/providers/groq_provider.py +17 -2
  14. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/setup.py +2 -1
  15. makima_cli-1.2.2/makima/test.py +49 -0
  16. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/tools/__init__.py +0 -0
  17. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/tools/file_tools.py +0 -0
  18. makima_cli-1.2.2/makima/tools/git_tools.py +72 -0
  19. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima/tools/registry.py +0 -0
  20. makima_cli-1.2.2/makima/tools/shell_tools.py +13 -0
  21. makima_cli-1.2.2/makima/ui/__init__.py +0 -0
  22. makima_cli-1.2.2/makima/ui/app.py +175 -0
  23. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima_cli.egg-info/PKG-INFO +2 -2
  24. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima_cli.egg-info/SOURCES.txt +4 -0
  25. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima_cli.egg-info/dependency_links.txt +0 -0
  26. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima_cli.egg-info/entry_points.txt +0 -0
  27. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima_cli.egg-info/requires.txt +0 -0
  28. {makima_cli-0.2.1 → makima_cli-1.2.2}/makima_cli.egg-info/top_level.txt +0 -0
  29. {makima_cli-0.2.1 → makima_cli-1.2.2}/pyproject.toml +1 -1
  30. {makima_cli-0.2.1 → makima_cli-1.2.2}/setup.cfg +0 -0
  31. makima_cli-0.2.1/makima/config.py +0 -20
  32. makima_cli-0.2.1/makima/main.py +0 -8
  33. makima_cli-0.2.1/makima/tools/shell_tools.py +0 -20
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makima-cli
3
- Version: 0.2.1
3
+ Version: 1.2.2
4
4
  Summary: AI coding assistant for your terminal
5
5
  Author: Kanhaiya
6
6
  License: MIT License
@@ -52,7 +52,7 @@ An AI-powered coding assistant that runs in your terminal. Point it at any codeb
52
52
 
53
53
  ## Install
54
54
 
55
- git clone https://github.com/yourusername/makima-cli.git
55
+ git clone https://github.com/kanhaiya-ML/makima-cli.git
56
56
  cd makima-cli
57
57
  pip install -r requirements.txt
58
58
 
@@ -14,7 +14,7 @@ An AI-powered coding assistant that runs in your terminal. Point it at any codeb
14
14
 
15
15
  ## Install
16
16
 
17
- git clone https://github.com/yourusername/makima-cli.git
17
+ git clone https://github.com/kanhaiya-ML/makima-cli.git
18
18
  cd makima-cli
19
19
  pip install -r requirements.txt
20
20
 
@@ -0,0 +1,18 @@
1
+
2
+ import os
3
+ import sys
4
+ import json
5
+ from pathlib import Path
6
+ from dotenv import load_dotenv
7
+ load_dotenv()
8
+
9
+ config_file = Path.home() / ".makima" / "config.json"
10
+ config = {}
11
+ if config_file.exists():
12
+ with open(config_file, "r") as f:
13
+ config = json.load(f)
14
+
15
+ GROQ_API_KEY = config.get("groq_api_key") or os.getenv("GROQ_API_KEY")
16
+ MODEL_NAME = config.get("model") or "qwen/qwen3.6-27b"
17
+ TOKEN_LIMIT = 6000
18
+ PROJECT_ROOT = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
@@ -122,4 +122,55 @@ def handle_config():
122
122
  if choice == "Change model":
123
123
  change_model()
124
124
  elif choice == "Change API key":
125
- change_api_key()
125
+ change_api_key()
126
+
127
+
128
+ from makima.tools.git_tools import get_git_diff, generate_commit_message, clean_message
129
+ import subprocess
130
+ import os
131
+
132
+ def commit_only(message):
133
+ message = clean_message(message)
134
+ cmd = f'git add -A && git commit -m "{message}"'
135
+ result = subprocess.run(cmd, shell=True, text=True, capture_output=True)
136
+ if result.returncode == 0:
137
+ return "✓ Committed locally"
138
+ return f"✗ Failed: {result.stderr}"
139
+
140
+
141
+ def analyze_changes():
142
+ diff = get_git_diff()
143
+
144
+ if not diff.strip():
145
+ return None, None
146
+ message = generate_commit_message(diff)
147
+ return diff, message
148
+
149
+
150
+ def handle_commit():
151
+ diff = get_git_diff()
152
+
153
+ if not diff.strip():
154
+ console.print("[yellow]No changes to commit.[/]")
155
+ return
156
+
157
+ commit_message = generate_commit_message(diff=diff)
158
+ console.print(f"[white] {commit_message}")
159
+
160
+ # choice = questionary.select(
161
+ # "What action you want?: ",
162
+ # choices = [
163
+ # "commit_and_push",
164
+ # "commit only",
165
+ # "cancle"
166
+ # ]
167
+ # ).ask()
168
+
169
+ # if choice == "commit_and_push":
170
+ # result = commit_and_push(message=commit_message)
171
+ # console.print(f"[green]{result}[/]")
172
+ # elif choice == "commit only":
173
+ # result = commit_only(diff)
174
+ # console.print(f"[green]{result}[/]")
175
+
176
+
@@ -0,0 +1,14 @@
1
+ from makima.core.agent import run_agent
2
+ import sys
3
+ from makima.ui.app import run_ui
4
+ from makima.setup import setup_api_key, setup_model
5
+
6
+
7
+ def main():
8
+ # run_agent()
9
+ setup_model()
10
+ setup_api_key()
11
+ run_ui()
12
+
13
+ if __name__ == "__main__":
14
+ main()
@@ -1,11 +1,26 @@
1
1
  from groq import Groq
2
2
  from makima.providers.base import LLMProvider
3
- from makima.config import GROQ_API_KEY, MODEL_NAME
3
+ from makima.config import MODEL_NAME
4
+ import os
5
+ import json
6
+ from pathlib import Path
4
7
 
5
8
  class GroqProvider(LLMProvider):
6
9
 
7
10
  def __init__(self):
8
- self.client = Groq(api_key=GROQ_API_KEY)
11
+ # read fresh from config file
12
+ key = self._load_key()
13
+ self.client = Groq(api_key=key)
14
+
15
+ def _load_key(self):
16
+ config_file = Path.home() / ".makima" / "config.json"
17
+ if config_file.exists():
18
+ with open(config_file, "r") as f:
19
+ data = json.load(f)
20
+ key = data.get("groq_api_key")
21
+ if key:
22
+ return key
23
+ return os.getenv("GROQ_API_KEY")
9
24
 
10
25
  def chat(self, messages: list, tools: list) -> object:
11
26
  return self.client.chat.completions.create(
@@ -28,7 +28,7 @@ def setup_api_key():
28
28
  with open(config_file, "r") as f:
29
29
  data = json.load(f)
30
30
  key = data.get("groq_api_key")
31
- if key:
31
+ if key and validate_key(key):
32
32
  return key
33
33
 
34
34
  import os
@@ -83,6 +83,7 @@ def setup_model():
83
83
 
84
84
  existing["model"] = model
85
85
 
86
+ config_dir.mkdir(exist_ok=True)
86
87
  with open(config_file,"w") as f:
87
88
  json.dump(existing,f)
88
89
 
@@ -0,0 +1,49 @@
1
+ from textual.app import App
2
+ from textual.widgets import Header, Footer, Input, RichLog
3
+ from rich.text import Text
4
+
5
+ from makima.providers.groq_provider import GroqProvider
6
+ from makima.core.memory import Memory
7
+
8
+ SYSTEM_PROMPT = "..."
9
+ class Makima(App):
10
+
11
+ def on_mount(self):
12
+ self.provider = GroqProvider()
13
+ self.memory = Memory(system_prompt=SYSTEM_PROMPT)
14
+
15
+ def compose(self):
16
+ yield Header()
17
+
18
+ yield RichLog(id="chat")
19
+
20
+ yield Input(
21
+ placeholder="Ask Makima...",
22
+ id="input"
23
+ )
24
+
25
+ yield Footer()
26
+
27
+ def on_input_submitted(self, event: Input.Submitted):
28
+ chat = self.query_one("#chat", RichLog)
29
+
30
+ user_input = event.value.strip()
31
+
32
+ if not user_input:
33
+ return
34
+
35
+ chat.write(
36
+ Text(f"You: {user_input}",style="bold cyan")
37
+ )
38
+
39
+
40
+ event.input.value = ""
41
+
42
+ chat.write(
43
+ Text("Makima: Hello! I'm alive", style="bold green")
44
+ )
45
+ # chat.write("[bold green]Makima:[/] Hello! I'm alive")
46
+
47
+ if __name__ == "__main__":
48
+ app = Makima()
49
+ app.run()
@@ -0,0 +1,72 @@
1
+ import subprocess
2
+ from groq import Groq
3
+ from makima.config import GROQ_API_KEY, PROJECT_ROOT
4
+ import re
5
+
6
+
7
+ def get_git_diff():
8
+ diff = subprocess.run("git diff", shell=True, text=True, capture_output=True)
9
+ status = subprocess.run("git status", shell=True, text=True, capture_output=True)
10
+
11
+ return f"STATUS:\n{status.stdout}\n\nDIFF:\n{diff.stdout}"
12
+
13
+
14
+ def generate_commit_message(diff):
15
+ prompt = f"""Generate ONE git commit message for this diff.
16
+ Format: type(scope): description
17
+ Types: feat/fix/refactor/docs/chore
18
+ Rules: max 72 chars, no quotes, no backticks, no explanation
19
+ Return ONLY the commit message line, nothing else.
20
+
21
+ {diff[:3000]}"""
22
+
23
+ try:
24
+ client = Groq(api_key=GROQ_API_KEY)
25
+ result = client.chat.completions.create(
26
+ model="qwen/qwen3.6-27b",
27
+ messages=[{
28
+ "role": "user",
29
+ "content": prompt
30
+ }]
31
+ )
32
+ except Exception as e:
33
+ print(f"Message Generator Failed!\n {e}")
34
+ return f"Failed to generate message: {e}"
35
+
36
+ content = result.choices[0].message.content
37
+ if "</think>" in content:
38
+ content = content.split("</think>")[-1].strip()
39
+ content = re.sub(r'[`\n]', '', content).strip()
40
+ return content
41
+
42
+
43
+ def commit_and_push(message):
44
+ try:
45
+ message = clean_message(message)
46
+ cmd = f'git add -A && git commit -m "{message}" && git push origin main'
47
+ result = subprocess.run(
48
+ cmd,
49
+ shell=True,
50
+ text=True,
51
+ capture_output=True,
52
+ timeout=120,
53
+ cwd=PROJECT_ROOT
54
+ )
55
+ if result.returncode == 0:
56
+ return f"✓ Committed and pushed: {message}"
57
+ else:
58
+ return f"✗ Failed: {result.stderr}"
59
+ except Exception as e:
60
+ return f"✗ Error: {e}"
61
+
62
+
63
+
64
+ def clean_message(message):
65
+ # remove thinking blocks
66
+ if "</think>" in message:
67
+ message = message.split("</think>")[-1].strip()
68
+ # remove backticks, quotes, special shell characters
69
+ message = re.sub(r'[`"\'\n]', '', message)
70
+ # remove any remaining markdown
71
+ message = message.strip()
72
+ return message
@@ -0,0 +1,13 @@
1
+ import subprocess
2
+
3
+
4
+ def run_command(cmd):
5
+ result = subprocess.run(
6
+ cmd,
7
+ shell=True,
8
+ capture_output=True,
9
+ text=True,
10
+ timeout=30
11
+ )
12
+
13
+ return result.stdout + result.stderr
File without changes
@@ -0,0 +1,175 @@
1
+ from textual.app import App, ComposeResult
2
+ from textual.widgets import Header, Footer, Input, RichLog
3
+ from makima.config import PROJECT_ROOT
4
+ from makima.tools import TOOL_REGISTRY
5
+ from makima.tools.registry import TOOL_SCHEMAS
6
+ from makima.core.config_manager import handle_config, analyze_changes, commit_only
7
+ from makima.tools.git_tools import commit_and_push
8
+ from rich.text import Text
9
+ import asyncio
10
+ import json
11
+
12
+ from makima.providers.groq_provider import GroqProvider
13
+ from makima.core.memory import Memory
14
+
15
+ from pathlib import Path
16
+
17
+
18
+ SYSTEM_PROMPT = f"""
19
+ You are a coding assistant. The project is at: {PROJECT_ROOT}
20
+
21
+ Rules:
22
+ - Always use list_files first to find exact paths
23
+ - Always use read_file before editing any file
24
+ - Use full absolute paths always
25
+ - Be concise and direct
26
+ - old_str must be an exact match of multiple lines from the file
27
+ - Never explain what you are about to do. Never say "Let me..." or "I'll first...".
28
+ - Just call the tool immediately and silently.
29
+ - Only speak in plain text when you have a FINAL answer for the user.
30
+ - Call only ONE tool per response. Wait for the tool result before calling the next tool.
31
+ - Never output multiple tool calls in one response.
32
+ """
33
+
34
+
35
+ class MakimaApp(App):
36
+ CSS_PATH = Path(__file__).parent / "app.tcss"
37
+
38
+ def __init__(self):
39
+ super().__init__()
40
+ self.memory = Memory(system_prompt=SYSTEM_PROMPT)
41
+ self.provider = GroqProvider()
42
+ self.pending_commit = None
43
+
44
+
45
+ def compose(self) -> ComposeResult:
46
+ yield Header()
47
+ yield RichLog(id="chat",markup=True, highlight=True)
48
+ yield Input(placeholder="Ask anything... (type 'exit' to quit)")
49
+ yield Footer()
50
+
51
+ def on_mount(self):
52
+ # focus the input so user can type immediately
53
+ self.query_one(Input).focus()
54
+
55
+
56
+ async def on_input_submitted(self, event: Input.Submitted) -> None:
57
+ user_input = event.value.strip()
58
+
59
+ if not user_input:
60
+ return
61
+
62
+ chat = self.query_one(RichLog)
63
+ input_box = self.query_one(Input)
64
+
65
+ chat.write(Text.from_markup(f"[bold blue]You:[/] {user_input}"))
66
+ input_box.clear()
67
+
68
+ if user_input == "exit":
69
+ self.exit()
70
+ return
71
+
72
+ if user_input == "/config":
73
+ # config runs in background thread
74
+ await asyncio.to_thread(handle_config)
75
+ return
76
+
77
+ if user_input == "/commit":
78
+ chat.write("[dim]Analyzing changes...[/]")
79
+ diff, message = await asyncio.to_thread(analyze_changes)
80
+
81
+ if not diff:
82
+ chat.write("[yellow]No changes to commit.[/]")
83
+ return
84
+
85
+ chat.write(Text.from_markup(f"[white]Generated message: {message}[/]"))
86
+ chat.write(Text.from_markup(f"[yellow]Type 'push' to commit and push, 'commit' to commit only, or 'cancel'[/]"))
87
+
88
+ self.pending_commit = {"diff": diff, "message": message}
89
+ return
90
+
91
+ if user_input == "push" and self.pending_commit:
92
+ result = await asyncio.to_thread(commit_and_push, self.pending_commit["message"])
93
+ chat.write(Text.from_markup(f"[green]{result}[/]"))
94
+ self.pending_commit = None
95
+ return
96
+
97
+ if user_input == "commit" and self.pending_commit:
98
+ result = await asyncio.to_thread(commit_only, self.pending_commit["message"])
99
+ chat.write(f"[green]{result}[/]")
100
+ self.pending_commit = None
101
+ return
102
+
103
+ if user_input == "cancel" and self.pending_commit:
104
+ chat.write("[yellow]Commit cancelled.[/]")
105
+ self.pending_commit = None
106
+ return
107
+
108
+
109
+ # show thinking indicator
110
+ chat.write(Text.from_markup("[dim]thinking...[/]"))
111
+
112
+ # run agent in background thread
113
+ response = await asyncio.to_thread(self.process_message, user_input)
114
+
115
+ # remove "thinking..." and show response
116
+ from rich.markdown import Markdown
117
+ chat.write(Text.from_markup(f"[bold green]Agent:[/]"))
118
+ chat.write(Markdown(response))
119
+
120
+
121
+ def process_message(self, user_input):
122
+ self.memory.add_user(user_input)
123
+
124
+ while True:
125
+ stream = self.provider.stream_chat(
126
+ messages=self.memory.get(),
127
+ tools=TOOL_SCHEMAS
128
+ )
129
+ chunks = list(stream)
130
+
131
+ # collect chunks
132
+ text_buffer = ""
133
+ tool_name = ""
134
+ tool_args = ""
135
+ tool_call_id = ""
136
+ is_tool_call = False
137
+
138
+ for chunk in chunks:
139
+ delta = chunk.choices[0].delta
140
+ if delta.content:
141
+ text_buffer += delta.content
142
+ if delta.tool_calls:
143
+ is_tool_call = True
144
+ tc = delta.tool_calls[0]
145
+ if tc.id:
146
+ tool_call_id = tc.id
147
+ if tc.function.name:
148
+ tool_name += tc.function.name
149
+ if tc.function.arguments:
150
+ tool_args += tc.function.arguments
151
+
152
+ if is_tool_call:
153
+ args = json.loads(tool_args)
154
+ tool_fn = TOOL_REGISTRY.get(tool_name)
155
+ result = tool_fn(**args) if tool_fn else "Unknown tool"
156
+
157
+ self.memory.add_raw({
158
+ "role": "assistant",
159
+ "tool_calls": [{
160
+ "id": tool_call_id,
161
+ "type": "function",
162
+ "function": {
163
+ "name": tool_name,
164
+ "arguments": tool_args
165
+ }
166
+ }]
167
+ })
168
+ self.memory.add_tool_result(tool_call_id, str(result))
169
+
170
+ else:
171
+ self.memory.add_assistant(text_buffer)
172
+ return text_buffer
173
+ def run_ui():
174
+ app = MakimaApp()
175
+ app.run()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: makima-cli
3
- Version: 0.2.1
3
+ Version: 1.2.2
4
4
  Summary: AI coding assistant for your terminal
5
5
  Author: Kanhaiya
6
6
  License: MIT License
@@ -52,7 +52,7 @@ An AI-powered coding assistant that runs in your terminal. Point it at any codeb
52
52
 
53
53
  ## Install
54
54
 
55
- git clone https://github.com/yourusername/makima-cli.git
55
+ git clone https://github.com/kanhaiya-ML/makima-cli.git
56
56
  cd makima-cli
57
57
  pip install -r requirements.txt
58
58
 
@@ -5,6 +5,7 @@ makima/__init__.py
5
5
  makima/config.py
6
6
  makima/main.py
7
7
  makima/setup.py
8
+ makima/test.py
8
9
  makima/core/__init__.py
9
10
  makima/core/agent.py
10
11
  makima/core/config_manager.py
@@ -14,8 +15,11 @@ makima/providers/base.py
14
15
  makima/providers/groq_provider.py
15
16
  makima/tools/__init__.py
16
17
  makima/tools/file_tools.py
18
+ makima/tools/git_tools.py
17
19
  makima/tools/registry.py
18
20
  makima/tools/shell_tools.py
21
+ makima/ui/__init__.py
22
+ makima/ui/app.py
19
23
  makima_cli.egg-info/PKG-INFO
20
24
  makima_cli.egg-info/SOURCES.txt
21
25
  makima_cli.egg-info/dependency_links.txt
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "makima-cli"
7
- version = "0.2.1"
7
+ version = "1.2.2"
8
8
  description = "AI coding assistant for your terminal"
9
9
  readme = "README.md"
10
10
  license = {file = "LICENSE"}
File without changes
@@ -1,20 +0,0 @@
1
- import os
2
- import sys
3
- from makima.setup import setup_api_key,setup_model
4
- from dotenv import load_dotenv
5
- load_dotenv()
6
-
7
- GROQ_API_KEY = setup_api_key()
8
- # GROQ_API_KEY = os.getenv("GROQ_API_KEY")
9
- MODEL_NAME = setup_model()
10
- TOKEN_LIMIT = 10000
11
- # PROJECT_ROOT = os.getcwd()
12
- PROJECT_ROOT = sys.argv[1] if len(sys.argv) > 1 else os.getcwd()
13
-
14
- if not GROQ_API_KEY:
15
- print("Error: GROQ_API_KEY not set.")
16
- print("Run this command first:")
17
- print(' Windows: setx GROQ_API_KEY "your-key-here"')
18
- print(' Linux/Mac: export GROQ_API_KEY="your-key-here"')
19
- print("Get a free key at: https://console.groq.com")
20
- sys.exit(1)
@@ -1,8 +0,0 @@
1
- from makima.core.agent import run_agent
2
- import sys
3
-
4
- def main():
5
- run_agent()
6
-
7
- if __name__ == "__main__":
8
- main()
@@ -1,20 +0,0 @@
1
- import subprocess
2
-
3
-
4
- def run_command(cmd):
5
- # print(f"Commands going to Run: {cmd}")
6
- # User_answer = input("Run This? (Y/N): ")
7
- # User_answer = User_answer.lower()
8
-
9
- # if User_answer != "y":
10
- # return "Command cancelled"
11
-
12
- result = subprocess.run(
13
- cmd,
14
- shell=True,
15
- capture_output=True,
16
- text=True,
17
- timeout=30
18
- )
19
-
20
- return result.stdout + result.stderr