mini-code-cli 0.0.7.1__tar.gz → 0.0.8.1__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.
- mini_code_cli-0.0.8.1/PKG-INFO +92 -0
- mini_code_cli-0.0.8.1/README.md +72 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli/agent_loop.py +149 -36
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli/prompts.py +1 -1
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli/tools.py +29 -8
- mini_code_cli-0.0.8.1/mini_code_cli.egg-info/PKG-INFO +92 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/pyproject.toml +1 -1
- mini_code_cli-0.0.7.1/PKG-INFO +0 -45
- mini_code_cli-0.0.7.1/README.md +0 -25
- mini_code_cli-0.0.7.1/mini_code_cli.egg-info/PKG-INFO +0 -45
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/LICENSE +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli/__init__.py +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli/utils.py +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli.egg-info/SOURCES.txt +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli.egg-info/dependency_links.txt +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli.egg-info/entry_points.txt +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/mini_code_cli.egg-info/top_level.txt +0 -0
- {mini_code_cli-0.0.7.1 → mini_code_cli-0.0.8.1}/setup.cfg +0 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mini-code-cli
|
|
3
|
+
Version: 0.0.8.1
|
|
4
|
+
Summary: Minimal Coding CLI
|
|
5
|
+
Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
|
|
6
|
+
License: The MIT License (MIT)
|
|
7
|
+
|
|
8
|
+
Copyright © 2026 Nikolai Rozanov
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
15
|
+
Project-URL: Homepage, https://github.com/ai-nikolai/mini-code-cli
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# A minimal coding agent cli
|
|
22
|
+
|
|
23
|
+
> No bloat, no dependencies, no tracking, no telemetry...
|
|
24
|
+
|
|
25
|
+
Purpose: A no bloat, minimal coding cli, to enjoy experimenting and researching...
|
|
26
|
+
|
|
27
|
+
## Getting started:
|
|
28
|
+
|
|
29
|
+
### Installation (from pypi)
|
|
30
|
+
```bash
|
|
31
|
+
pip3 install mini-code-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Run agent:
|
|
35
|
+
|
|
36
|
+
1. Basic usage: (get local LLM running (e.g. via SGLang or Vllm))
|
|
37
|
+
```bash
|
|
38
|
+
mini-code #run the agent using `localhost:30000/v1` model
|
|
39
|
+
```
|
|
40
|
+
2. Custom API based model:
|
|
41
|
+
```bash
|
|
42
|
+
export MINI_CODE_API_KEY="sk-YOUR_API_KEY"
|
|
43
|
+
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Auto-mode, Agent.md, Shell Enabled mode and a specific allowed dir:
|
|
47
|
+
```bash
|
|
48
|
+
mini-code --enable-shell --auto-mode --agent-md "./skill.md" --allowed-dir "./" --ask-permission --system-prompt "Your awesome new system prompt..."
|
|
49
|
+
```
|
|
50
|
+
- auto-mode: the agent will continue querying the LLM and executing tools, until no more tool calls are possible
|
|
51
|
+
- agent.md: this will read an agent.md file with the specified name (e.g. ./skill.md)
|
|
52
|
+
- shell-enabled: **Be careful**: This will allow the agent to execute shell commands. Specifically, there is no permission checks.
|
|
53
|
+
- allowed-dir: this specifies which dirs the agent can read and write from, that does not stop the shell comands!
|
|
54
|
+
- ask-permission: asks user permission before every tool call.
|
|
55
|
+
- system-prompt: you can override the default prompt by using a string. (Not Agent.md is always appended after the system prompt.)
|
|
56
|
+
|
|
57
|
+
3. Try a prompt:
|
|
58
|
+
```txt
|
|
59
|
+
Write an agent.md file for me that helps an LLM write efficient triton kernels, given a reference implementation.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
4. Get help:
|
|
63
|
+
```bash
|
|
64
|
+
mini-code --help
|
|
65
|
+
```
|
|
66
|
+
```
|
|
67
|
+
Agent loop for interacting with a language model
|
|
68
|
+
|
|
69
|
+
options:
|
|
70
|
+
-h, --help show this help message and exit
|
|
71
|
+
--url URL API host
|
|
72
|
+
--max-tokens MAX_TOKENS
|
|
73
|
+
Maximum tokens for LLM response
|
|
74
|
+
--temperature TEMPERATURE
|
|
75
|
+
Temperature for LLM
|
|
76
|
+
--model MODEL Model name
|
|
77
|
+
--api-key API_KEY API key for authentication. If not set, it tries to find it in env variable: MINI_CODE_API_KEY.
|
|
78
|
+
--cache-dir CACHE_DIR
|
|
79
|
+
The cache dir for the session histories.
|
|
80
|
+
--system-prompt SYSTEM_PROMPT
|
|
81
|
+
Replace system prompt, with a custom system prompt.
|
|
82
|
+
--auto-mode Whether to run the agent in `auto-mode'. Or default: `manual-mode'.
|
|
83
|
+
--agent-md AGENT_MD If the agent should use an agent-md file... (it will added after system message.)
|
|
84
|
+
--prompt PROMPT An initial prompt from the user...
|
|
85
|
+
--ask-permission Ask for permission before any tool call.
|
|
86
|
+
--allowed-dir ALLOWED_DIR
|
|
87
|
+
Allowed directory for file operations
|
|
88
|
+
--enable-shell Allow shell execution. Default: False
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## (C) Nikolai Rozanov, 2026 - Present
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# A minimal coding agent cli
|
|
2
|
+
|
|
3
|
+
> No bloat, no dependencies, no tracking, no telemetry...
|
|
4
|
+
|
|
5
|
+
Purpose: A no bloat, minimal coding cli, to enjoy experimenting and researching...
|
|
6
|
+
|
|
7
|
+
## Getting started:
|
|
8
|
+
|
|
9
|
+
### Installation (from pypi)
|
|
10
|
+
```bash
|
|
11
|
+
pip3 install mini-code-cli
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### Run agent:
|
|
15
|
+
|
|
16
|
+
1. Basic usage: (get local LLM running (e.g. via SGLang or Vllm))
|
|
17
|
+
```bash
|
|
18
|
+
mini-code #run the agent using `localhost:30000/v1` model
|
|
19
|
+
```
|
|
20
|
+
2. Custom API based model:
|
|
21
|
+
```bash
|
|
22
|
+
export MINI_CODE_API_KEY="sk-YOUR_API_KEY"
|
|
23
|
+
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Auto-mode, Agent.md, Shell Enabled mode and a specific allowed dir:
|
|
27
|
+
```bash
|
|
28
|
+
mini-code --enable-shell --auto-mode --agent-md "./skill.md" --allowed-dir "./" --ask-permission --system-prompt "Your awesome new system prompt..."
|
|
29
|
+
```
|
|
30
|
+
- auto-mode: the agent will continue querying the LLM and executing tools, until no more tool calls are possible
|
|
31
|
+
- agent.md: this will read an agent.md file with the specified name (e.g. ./skill.md)
|
|
32
|
+
- shell-enabled: **Be careful**: This will allow the agent to execute shell commands. Specifically, there is no permission checks.
|
|
33
|
+
- allowed-dir: this specifies which dirs the agent can read and write from, that does not stop the shell comands!
|
|
34
|
+
- ask-permission: asks user permission before every tool call.
|
|
35
|
+
- system-prompt: you can override the default prompt by using a string. (Not Agent.md is always appended after the system prompt.)
|
|
36
|
+
|
|
37
|
+
3. Try a prompt:
|
|
38
|
+
```txt
|
|
39
|
+
Write an agent.md file for me that helps an LLM write efficient triton kernels, given a reference implementation.
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
4. Get help:
|
|
43
|
+
```bash
|
|
44
|
+
mini-code --help
|
|
45
|
+
```
|
|
46
|
+
```
|
|
47
|
+
Agent loop for interacting with a language model
|
|
48
|
+
|
|
49
|
+
options:
|
|
50
|
+
-h, --help show this help message and exit
|
|
51
|
+
--url URL API host
|
|
52
|
+
--max-tokens MAX_TOKENS
|
|
53
|
+
Maximum tokens for LLM response
|
|
54
|
+
--temperature TEMPERATURE
|
|
55
|
+
Temperature for LLM
|
|
56
|
+
--model MODEL Model name
|
|
57
|
+
--api-key API_KEY API key for authentication. If not set, it tries to find it in env variable: MINI_CODE_API_KEY.
|
|
58
|
+
--cache-dir CACHE_DIR
|
|
59
|
+
The cache dir for the session histories.
|
|
60
|
+
--system-prompt SYSTEM_PROMPT
|
|
61
|
+
Replace system prompt, with a custom system prompt.
|
|
62
|
+
--auto-mode Whether to run the agent in `auto-mode'. Or default: `manual-mode'.
|
|
63
|
+
--agent-md AGENT_MD If the agent should use an agent-md file... (it will added after system message.)
|
|
64
|
+
--prompt PROMPT An initial prompt from the user...
|
|
65
|
+
--ask-permission Ask for permission before any tool call.
|
|
66
|
+
--allowed-dir ALLOWED_DIR
|
|
67
|
+
Allowed directory for file operations
|
|
68
|
+
--enable-shell Allow shell execution. Default: False
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
## (C) Nikolai Rozanov, 2026 - Present
|
|
@@ -10,13 +10,18 @@ write a similar model.py file but for a LSTM based language model. Make it minim
|
|
|
10
10
|
|
|
11
11
|
import os
|
|
12
12
|
import json
|
|
13
|
+
import signal
|
|
14
|
+
|
|
15
|
+
import time
|
|
16
|
+
import uuid
|
|
17
|
+
|
|
18
|
+
import argparse
|
|
19
|
+
import sys
|
|
13
20
|
|
|
14
21
|
from . import prompts
|
|
15
22
|
from . import tools
|
|
16
23
|
from . import utils
|
|
17
24
|
|
|
18
|
-
import argparse
|
|
19
|
-
import sys
|
|
20
25
|
|
|
21
26
|
# ANSI escape codes for colors and bold
|
|
22
27
|
BOLD = '\033[1m'
|
|
@@ -28,42 +33,69 @@ MAGENTA = '\033[95m'
|
|
|
28
33
|
CYAN = '\033[96m'
|
|
29
34
|
RESET = '\033[0m'
|
|
30
35
|
|
|
31
|
-
def save_history(messages):
|
|
36
|
+
def save_history(messages, args, session_name=""):
|
|
32
37
|
# Save messages to history.json
|
|
33
|
-
|
|
38
|
+
if args.cache_dir:
|
|
39
|
+
cache_dir = args.cache_dir
|
|
40
|
+
else:
|
|
41
|
+
cache_dir = os.path.expanduser("~/.cache/mini_code/sessions/")
|
|
42
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
43
|
+
history_path = os.path.join(cache_dir, f"history_{session_name}.json")
|
|
44
|
+
with open(history_path, 'w') as f:
|
|
34
45
|
json.dump(messages, f, indent=2)
|
|
35
46
|
|
|
47
|
+
def generate_unique_id():
|
|
48
|
+
"""Generate a unique ID based on current time and a random component."""
|
|
49
|
+
timestamp = int(time.time() * 1000000) # microseconds
|
|
50
|
+
return f"{timestamp}-{uuid.uuid4().hex[:8]}"
|
|
36
51
|
|
|
37
52
|
def parse_args():
|
|
38
53
|
parser = argparse.ArgumentParser(description="Agent loop for interacting with a language model")
|
|
54
|
+
# MODEL / LLM related
|
|
39
55
|
parser.add_argument("--url", type=str, default="http://0.0.0.0:30000", help="API host")
|
|
40
|
-
parser.add_argument("--system_prompt", type=str, help="API host")
|
|
41
|
-
parser.add_argument("--api-key", type=str, default=None, help="API key for authentication")
|
|
42
|
-
parser.add_argument("--allowed-dir", type=str, default=".", help="Allowed directory for file operations")
|
|
43
56
|
parser.add_argument("--max-tokens", type=int, help="Maximum tokens for LLM response")
|
|
44
57
|
parser.add_argument("--temperature", type=float, default=0.0, help="Temperature for LLM")
|
|
45
58
|
parser.add_argument("--model", type=str, default="default", help="Model name")
|
|
46
|
-
parser.add_argument("--
|
|
47
|
-
|
|
59
|
+
parser.add_argument("--api-key", type=str, default=None, help="API key for authentication. If not set, it tries to find it in env variable: MINI_CODE_API_KEY.")
|
|
60
|
+
|
|
61
|
+
# Other settings:
|
|
62
|
+
parser.add_argument("--cache-dir", type=str, help="The cache dir for the session histories.")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Agent behaviour related
|
|
66
|
+
parser.add_argument("--system-prompt", type=str, help="Replace system prompt, with a custom system prompt.")
|
|
67
|
+
parser.add_argument("--auto-mode", action="store_true", help="Whether to run the agent in `auto-mode'. Or default: `manual-mode'.")
|
|
68
|
+
parser.add_argument("--agent-md", type=str, help="If the agent should use an agent-md file... (it will added after system message.)")
|
|
69
|
+
parser.add_argument("--prompt", type=str, help="An initial prompt from the user...")
|
|
70
|
+
|
|
71
|
+
# Agent permissions related
|
|
72
|
+
parser.add_argument("--ask-permission", action="store_true", help="Ask for permission before any tool call.")
|
|
73
|
+
parser.add_argument("--allowed-dir", type=str, default=".", help="Allowed directory for file operations")
|
|
74
|
+
parser.add_argument("--enable-shell", action="store_true", help="Allow shell execution. Default: False") #todo
|
|
75
|
+
|
|
48
76
|
|
|
49
77
|
args = parser.parse_args()
|
|
50
78
|
return args
|
|
51
79
|
|
|
52
|
-
|
|
80
|
+
|
|
81
|
+
def print_welcome(allowed_dir, server_url, args, unique_id, cache_dir):
|
|
53
82
|
# Calculate the length of the longest line inside the box
|
|
54
83
|
version = __import__('mini_code_cli').__version__
|
|
55
84
|
|
|
56
85
|
permission_mode = "All tool calls require permission." if args.ask_permission else "Tool calls will execute without asking for permission."
|
|
57
|
-
agent_mode = "Agent is in manual-mode." if args.
|
|
86
|
+
agent_mode = "Agent is in manual-mode." if not args.auto_mode else "Agent is in auto-mode."
|
|
87
|
+
shell_mode = "Shell disabled." if not args.enable_shell else "Shell enabled."
|
|
58
88
|
|
|
59
89
|
lines = [
|
|
60
90
|
f" MiniCodeCLI (v{version})",
|
|
91
|
+
f" The session-id is: {unique_id}" ,
|
|
92
|
+
f" It will be saved under: `{cache_dir}`",
|
|
61
93
|
" Type 'quit' or 'exit' to exit the loop.",
|
|
62
94
|
f" Allowed dir: {allowed_dir}",
|
|
63
95
|
f" Server URL: {server_url}",
|
|
64
96
|
f" {permission_mode}",
|
|
65
97
|
f" {agent_mode}",
|
|
66
|
-
|
|
98
|
+
f" {shell_mode}"
|
|
67
99
|
]
|
|
68
100
|
max_len = max(len(line) for line in lines)
|
|
69
101
|
margin = 2 # Space on each side inside the box
|
|
@@ -78,23 +110,71 @@ def print_welcome(allowed_dir, server_url, args):
|
|
|
78
110
|
bottom = "╚" + "═" * inner_width + "╝"
|
|
79
111
|
print(bottom)
|
|
80
112
|
|
|
81
|
-
def print_help(allowed_dir, server_url, args):
|
|
82
|
-
print_welcome(allowed_dir, server_url, args)
|
|
113
|
+
def print_help(allowed_dir, server_url, args, unique_id, cache_dir):
|
|
114
|
+
print_welcome(allowed_dir, server_url, args, unique_id, cache_dir)
|
|
83
115
|
|
|
84
116
|
help_message = ""
|
|
85
|
-
help_message += "Manual-mode means the agent will ask for user input after each LLM prediction." if args.
|
|
117
|
+
help_message += "Manual-mode means the agent will ask for user input after each LLM prediction." if not args.auto_mode else "Auto-mode means the agent will keep calling the LLM until no further LLM calls are possible."
|
|
86
118
|
|
|
87
119
|
print(f"{BOLD}{GREEN}HELP:{RESET} {help_message}")
|
|
88
120
|
|
|
121
|
+
def call_function_with_timeout(func, tool_params, name):
|
|
122
|
+
"""calls function with params"""
|
|
123
|
+
class TimeoutError(Exception):
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
def timeout_handler(signum, frame):
|
|
127
|
+
raise TimeoutError("Function call timed out")
|
|
128
|
+
|
|
129
|
+
timeout_seconds = 30 # Set your desired timeout in seconds
|
|
130
|
+
signal.signal(signal.SIGALRM, timeout_handler)
|
|
131
|
+
signal.alarm(timeout_seconds)
|
|
132
|
+
try:
|
|
133
|
+
result = func(**tool_params)
|
|
134
|
+
except TimeoutError:
|
|
135
|
+
print(f"{BOLD}{RED}Error:{RESET} Function {name} timed out after {timeout_seconds} seconds.")
|
|
136
|
+
result = "Error:Function {name} timed out after {timeout_seconds} seconds."
|
|
137
|
+
finally:
|
|
138
|
+
signal.alarm(0) # Disable the alarm
|
|
139
|
+
|
|
140
|
+
return result
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
89
144
|
def main():
|
|
90
145
|
args = parse_args()
|
|
91
146
|
|
|
92
|
-
|
|
147
|
+
# Get session unique ID:
|
|
148
|
+
unique_id = generate_unique_id()
|
|
149
|
+
cache_dir = args.cache_dir if args.cache_dir else "~/.cache/mini_code/sessions/"
|
|
150
|
+
|
|
151
|
+
# LLM Related
|
|
93
152
|
server_url = f"{args.url}"
|
|
153
|
+
api_key = None
|
|
154
|
+
if args.api_key:
|
|
155
|
+
api_key = args.api_key
|
|
156
|
+
else:
|
|
157
|
+
api_key = os.environ.get("MINI_CODE_API_KEY", "")
|
|
158
|
+
if api_key:
|
|
159
|
+
print(f"{YELLOW}API Key found at MINI_CODE_API_KEY{RESET}")
|
|
160
|
+
else:
|
|
161
|
+
print(f"{YELLOW}NO API Key found at MINI_CODE_API_KEY. Set it with export MINI_CODE_API_KEY='sk-api-key'{RESET}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# Permissions related
|
|
165
|
+
allowed_dir = os.path.abspath(args.allowed_dir)
|
|
166
|
+
|
|
167
|
+
if not args.enable_shell:
|
|
168
|
+
forbidden_tools = tools.shell_tools
|
|
169
|
+
else:
|
|
170
|
+
forbidden_tools = None
|
|
94
171
|
|
|
95
|
-
|
|
96
|
-
|
|
172
|
+
# TOOLs
|
|
173
|
+
tools_dict = tools.util_get_tools_dict(forbidden_tools=forbidden_tools)
|
|
174
|
+
llm_tools_dict = tools.util_get_tools(forbidden_tools=forbidden_tools)
|
|
97
175
|
|
|
176
|
+
|
|
177
|
+
# System Message and Agent.md
|
|
98
178
|
if args.system_prompt:
|
|
99
179
|
system_prompt = args.system_prompt
|
|
100
180
|
else:
|
|
@@ -102,16 +182,35 @@ def main():
|
|
|
102
182
|
|
|
103
183
|
messages = [{"role": "system", "content": system_prompt}]
|
|
104
184
|
|
|
105
|
-
|
|
185
|
+
if args.agent_md:
|
|
186
|
+
print(f"{YELLOW}AGENT.md '{args.agent_md}' specified.{RESET}")
|
|
187
|
+
|
|
188
|
+
# Check if the file exists
|
|
189
|
+
if not os.path.isfile(args.agent_md):
|
|
190
|
+
print(f"{RED}Error: The AGENT.md '{args.agent_md}' does not exist.{RESET}")
|
|
191
|
+
else:
|
|
192
|
+
# Load the content of agent-md file and add it after system message
|
|
193
|
+
with open(args.agent_md, 'r', encoding='utf-8') as f:
|
|
194
|
+
agent_md_content = f.read()
|
|
195
|
+
print(f"{YELLOW}AGENT.md '{args.agent_md}' loaded.{RESET}")
|
|
196
|
+
|
|
197
|
+
# Append the content as a user message after the system prompt
|
|
198
|
+
messages.append({"role": "user", "content": agent_md_content})
|
|
199
|
+
|
|
200
|
+
print_welcome(allowed_dir, server_url, args, unique_id, cache_dir)
|
|
106
201
|
|
|
202
|
+
# Agent Logic Flags
|
|
107
203
|
llm_response_flag = False
|
|
108
204
|
llm_tool_response_flag = False
|
|
109
205
|
llm_repeat_flag = False
|
|
110
206
|
tool_count = 0
|
|
111
207
|
|
|
208
|
+
user_input = args.prompt
|
|
209
|
+
|
|
112
210
|
while True:
|
|
113
|
-
|
|
114
|
-
|
|
211
|
+
|
|
212
|
+
if llm_tool_response_flag and args.auto_mode: #skip user input and try to call the model again until there no more tool calls.
|
|
213
|
+
print(f"{BOLD}{YELLOW}System:{RESET} Skipping user input. Continuing with LLM calls until no more tool calls. [Tool count={tool_count}]")
|
|
115
214
|
llm_repeat_flag = True
|
|
116
215
|
else:
|
|
117
216
|
# get user input
|
|
@@ -119,23 +218,29 @@ def main():
|
|
|
119
218
|
print(f"{BOLD}{YELLOW}System:{RESET} Called {tool_count} tools in total. Exiting tool loop.")
|
|
120
219
|
|
|
121
220
|
tool_count = 0
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
221
|
+
if not user_input:
|
|
222
|
+
try:
|
|
223
|
+
print("-"*30)
|
|
224
|
+
user_input = input(f"{BOLD}{CYAN}You:{RESET} ")
|
|
225
|
+
if user_input.lower() in ["quit", "exit", "/quit", "/exit"]:
|
|
226
|
+
print("\nThank you. Goodbye! Saving history...")
|
|
227
|
+
save_history(messages, args, unique_id)
|
|
228
|
+
break
|
|
229
|
+
except KeyboardInterrupt:
|
|
126
230
|
print("\nThank you. Goodbye! Saving history...")
|
|
127
|
-
save_history(messages)
|
|
231
|
+
save_history(messages, args, unique_id)
|
|
128
232
|
break
|
|
129
|
-
|
|
130
|
-
print("
|
|
131
|
-
|
|
132
|
-
break
|
|
233
|
+
else:
|
|
234
|
+
print("-"*30)
|
|
235
|
+
print(f"{BOLD}{CYAN}You (preset): {RESET}{user_input}")
|
|
133
236
|
|
|
134
237
|
if user_input == "/help":
|
|
135
|
-
print_help(allowed_dir, server_url, args)
|
|
238
|
+
print_help(allowed_dir, server_url, args, unique_id, cache_dir)
|
|
239
|
+
user_input = ""
|
|
136
240
|
continue
|
|
137
241
|
|
|
138
242
|
messages.append({"role": "user", "content": user_input})
|
|
243
|
+
user_input = ""
|
|
139
244
|
|
|
140
245
|
llm_response_flag = False
|
|
141
246
|
llm_tool_response_flag = False
|
|
@@ -146,7 +251,8 @@ def main():
|
|
|
146
251
|
temperature=args.temperature,
|
|
147
252
|
model=args.model,
|
|
148
253
|
server_url=server_url,
|
|
149
|
-
tools=llm_tools_dict
|
|
254
|
+
tools=llm_tools_dict,
|
|
255
|
+
api_key=api_key,
|
|
150
256
|
)
|
|
151
257
|
|
|
152
258
|
if response_text:
|
|
@@ -195,7 +301,7 @@ def main():
|
|
|
195
301
|
permission_input = input("Type: 'y' or 'yes'\n")
|
|
196
302
|
except KeyboardInterrupt:
|
|
197
303
|
print("\nThank you. Goodbye! Saving history...")
|
|
198
|
-
save_history(messages)
|
|
304
|
+
save_history(messages, args, unique_id)
|
|
199
305
|
sys.exit(0)
|
|
200
306
|
|
|
201
307
|
if not permission_input in ['y', 'yes']:
|
|
@@ -205,7 +311,9 @@ def main():
|
|
|
205
311
|
"content": f"User denied permission for tool {name} and params {params_str}."
|
|
206
312
|
})
|
|
207
313
|
continue
|
|
208
|
-
|
|
314
|
+
|
|
315
|
+
print(f"{BOLD}{YELLOW}Tool Call:{RESET} LLM is trying to call: {name} with {tool_params}.")
|
|
316
|
+
result = call_function_with_timeout(func, tool_params, name)
|
|
209
317
|
# Limiting output to 300 characters for readability
|
|
210
318
|
result_str = str(result)
|
|
211
319
|
if len(result_str) > 300:
|
|
@@ -218,7 +326,12 @@ def main():
|
|
|
218
326
|
"content": str(result)
|
|
219
327
|
})
|
|
220
328
|
else:
|
|
221
|
-
print(f"{BOLD}{RED}
|
|
329
|
+
print(f"{BOLD}{RED}Error:{RESET} Unknown tool: {name}, params {params_str}")
|
|
330
|
+
messages.append({
|
|
331
|
+
"role": "tool",
|
|
332
|
+
"tool_call_id": call_id,
|
|
333
|
+
"content": f"Error: Unknown tool: {name}, params {params_str}"
|
|
334
|
+
})
|
|
222
335
|
else:
|
|
223
336
|
messages.append({
|
|
224
337
|
"role": "assistant",
|
|
@@ -229,7 +342,7 @@ def main():
|
|
|
229
342
|
if not llm_response_flag and not llm_tool_response_flag:
|
|
230
343
|
print(f"{BOLD}{RED}Warning:{RESET} No response was given by the LLM.")
|
|
231
344
|
|
|
232
|
-
save_history(messages)
|
|
345
|
+
save_history(messages, args, unique_id)
|
|
233
346
|
|
|
234
347
|
if __name__ == "__main__":
|
|
235
348
|
main()
|
|
@@ -6,7 +6,7 @@ def system_prompt(tools_dict=None, allowed_dir=None):
|
|
|
6
6
|
|
|
7
7
|
if tools_dict:
|
|
8
8
|
tool_descriptions = "\n".join([f"{name} : {data['description']}" for name, data in tools_dict.items()])
|
|
9
|
-
base_prompt = "You are a helpful AI assistant with access to the following tools:\n"
|
|
9
|
+
base_prompt = "You are a helpful AI assistant with access to the following tools (only):\n"
|
|
10
10
|
end_prompt = (
|
|
11
11
|
"Choose the most appropriate tool for the task at hand. "
|
|
12
12
|
"If a tool fails or you hit a limit, analyze the error and try a different approach. "
|
|
@@ -65,14 +65,27 @@ def read_website(url):
|
|
|
65
65
|
except Exception as e:
|
|
66
66
|
return f"Error: {e}"
|
|
67
67
|
|
|
68
|
-
def
|
|
68
|
+
def run_shell_command(command):
|
|
69
|
+
"""Run a shell command and return its output. Param: command (string)"""
|
|
70
|
+
try:
|
|
71
|
+
result = subprocess.run(command, shell=True, capture_output=True, text=True, check=False)
|
|
72
|
+
output = result.stdout
|
|
73
|
+
if result.stderr:
|
|
74
|
+
output += "\nSTDERR:\n" + result.stderr
|
|
75
|
+
return output
|
|
76
|
+
except Exception as e:
|
|
77
|
+
return f"Error: {e}"
|
|
78
|
+
|
|
79
|
+
def util_get_tools(allowed_tools=None, forbidden_tools=None):
|
|
69
80
|
"""Return a JSON string describing all tool functions in this file for LLM usage."""
|
|
70
81
|
import inspect
|
|
71
82
|
tools = []
|
|
72
|
-
if
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
func_names = [name for name
|
|
83
|
+
func_names = [name for name, obj in globals().items() if callable(obj) and obj.__module__ == __name__ and not name.startswith('util_') and not name.startswith("_")]
|
|
84
|
+
|
|
85
|
+
if allowed_tools:
|
|
86
|
+
func_names = [name for name in func_names if name in allowed_tools]
|
|
87
|
+
if forbidden_tools:
|
|
88
|
+
func_names = [name for name in func_names if name not in forbidden_tools]
|
|
76
89
|
|
|
77
90
|
for name in func_names:
|
|
78
91
|
func = globals()[name]
|
|
@@ -108,17 +121,25 @@ def util_get_tools(tool_names=None):
|
|
|
108
121
|
return tools
|
|
109
122
|
|
|
110
123
|
|
|
111
|
-
def util_get_tools_dict():
|
|
124
|
+
def util_get_tools_dict(allowed_tools=None, forbidden_tools=None):
|
|
112
125
|
"""Return a dictionary mapping tool names to their function objects."""
|
|
113
126
|
result = {}
|
|
114
127
|
func_names = [name for name, obj in globals().items() if callable(obj) and obj.__module__ == __name__ and not name.startswith('util_') and not name.startswith("_")]
|
|
128
|
+
if allowed_tools:
|
|
129
|
+
func_names = [name for name in func_names if name in allowed_tools]
|
|
130
|
+
if forbidden_tools:
|
|
131
|
+
func_names = [name for name in func_names if name not in forbidden_tools]
|
|
132
|
+
|
|
115
133
|
for name in func_names:
|
|
116
134
|
func = globals()[name]
|
|
117
135
|
desc = func.__doc__ if func.__doc__ else f"Function {name}"
|
|
118
136
|
result[name] = {"function": func, "description": desc}
|
|
119
137
|
return result
|
|
120
138
|
|
|
139
|
+
shell_tools = [
|
|
140
|
+
"run_shell_command"
|
|
141
|
+
]
|
|
121
142
|
|
|
122
143
|
if __name__=="__main__":
|
|
123
|
-
|
|
124
|
-
print(json.dumps(tools_dict, indent=4))
|
|
144
|
+
print(util_get_tools())
|
|
145
|
+
# print(json.dumps(tools_dict, indent=4))
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mini-code-cli
|
|
3
|
+
Version: 0.0.8.1
|
|
4
|
+
Summary: Minimal Coding CLI
|
|
5
|
+
Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
|
|
6
|
+
License: The MIT License (MIT)
|
|
7
|
+
|
|
8
|
+
Copyright © 2026 Nikolai Rozanov
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
15
|
+
Project-URL: Homepage, https://github.com/ai-nikolai/mini-code-cli
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# A minimal coding agent cli
|
|
22
|
+
|
|
23
|
+
> No bloat, no dependencies, no tracking, no telemetry...
|
|
24
|
+
|
|
25
|
+
Purpose: A no bloat, minimal coding cli, to enjoy experimenting and researching...
|
|
26
|
+
|
|
27
|
+
## Getting started:
|
|
28
|
+
|
|
29
|
+
### Installation (from pypi)
|
|
30
|
+
```bash
|
|
31
|
+
pip3 install mini-code-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Run agent:
|
|
35
|
+
|
|
36
|
+
1. Basic usage: (get local LLM running (e.g. via SGLang or Vllm))
|
|
37
|
+
```bash
|
|
38
|
+
mini-code #run the agent using `localhost:30000/v1` model
|
|
39
|
+
```
|
|
40
|
+
2. Custom API based model:
|
|
41
|
+
```bash
|
|
42
|
+
export MINI_CODE_API_KEY="sk-YOUR_API_KEY"
|
|
43
|
+
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Auto-mode, Agent.md, Shell Enabled mode and a specific allowed dir:
|
|
47
|
+
```bash
|
|
48
|
+
mini-code --enable-shell --auto-mode --agent-md "./skill.md" --allowed-dir "./" --ask-permission --system-prompt "Your awesome new system prompt..."
|
|
49
|
+
```
|
|
50
|
+
- auto-mode: the agent will continue querying the LLM and executing tools, until no more tool calls are possible
|
|
51
|
+
- agent.md: this will read an agent.md file with the specified name (e.g. ./skill.md)
|
|
52
|
+
- shell-enabled: **Be careful**: This will allow the agent to execute shell commands. Specifically, there is no permission checks.
|
|
53
|
+
- allowed-dir: this specifies which dirs the agent can read and write from, that does not stop the shell comands!
|
|
54
|
+
- ask-permission: asks user permission before every tool call.
|
|
55
|
+
- system-prompt: you can override the default prompt by using a string. (Not Agent.md is always appended after the system prompt.)
|
|
56
|
+
|
|
57
|
+
3. Try a prompt:
|
|
58
|
+
```txt
|
|
59
|
+
Write an agent.md file for me that helps an LLM write efficient triton kernels, given a reference implementation.
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
4. Get help:
|
|
63
|
+
```bash
|
|
64
|
+
mini-code --help
|
|
65
|
+
```
|
|
66
|
+
```
|
|
67
|
+
Agent loop for interacting with a language model
|
|
68
|
+
|
|
69
|
+
options:
|
|
70
|
+
-h, --help show this help message and exit
|
|
71
|
+
--url URL API host
|
|
72
|
+
--max-tokens MAX_TOKENS
|
|
73
|
+
Maximum tokens for LLM response
|
|
74
|
+
--temperature TEMPERATURE
|
|
75
|
+
Temperature for LLM
|
|
76
|
+
--model MODEL Model name
|
|
77
|
+
--api-key API_KEY API key for authentication. If not set, it tries to find it in env variable: MINI_CODE_API_KEY.
|
|
78
|
+
--cache-dir CACHE_DIR
|
|
79
|
+
The cache dir for the session histories.
|
|
80
|
+
--system-prompt SYSTEM_PROMPT
|
|
81
|
+
Replace system prompt, with a custom system prompt.
|
|
82
|
+
--auto-mode Whether to run the agent in `auto-mode'. Or default: `manual-mode'.
|
|
83
|
+
--agent-md AGENT_MD If the agent should use an agent-md file... (it will added after system message.)
|
|
84
|
+
--prompt PROMPT An initial prompt from the user...
|
|
85
|
+
--ask-permission Ask for permission before any tool call.
|
|
86
|
+
--allowed-dir ALLOWED_DIR
|
|
87
|
+
Allowed directory for file operations
|
|
88
|
+
--enable-shell Allow shell execution. Default: False
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## (C) Nikolai Rozanov, 2026 - Present
|
mini_code_cli-0.0.7.1/PKG-INFO
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mini-code-cli
|
|
3
|
-
Version: 0.0.7.1
|
|
4
|
-
Summary: Minimal Coding CLI
|
|
5
|
-
Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
|
|
6
|
-
License: The MIT License (MIT)
|
|
7
|
-
|
|
8
|
-
Copyright © 2026 Nikolai Rozanov
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
15
|
-
Project-URL: Homepage, https://github.com/ai-nikolai/mini-code-cli
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# A minimal coding agent cli
|
|
22
|
-
|
|
23
|
-
> No bloat, no dependencies, no tracking...
|
|
24
|
-
|
|
25
|
-
Purpose: A no bloat, minimal coding cli, to enjoy experimenting and researching...
|
|
26
|
-
|
|
27
|
-
## Getting started:
|
|
28
|
-
1. Installation (from pypi)
|
|
29
|
-
```bash
|
|
30
|
-
pip3 install mini-code-cli
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
2. Run agent: (get LLM running via SGLang or Vllm)
|
|
34
|
-
```bash
|
|
35
|
-
mini-code #run the agent loop
|
|
36
|
-
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash" --api-key "sk-dummy" --allowed-dir ./experiments/
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
3. Get help:
|
|
40
|
-
```bash
|
|
41
|
-
mini-code --help
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## (C) Nikolai Rozanov, 2026 - Present
|
mini_code_cli-0.0.7.1/README.md
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# A minimal coding agent cli
|
|
2
|
-
|
|
3
|
-
> No bloat, no dependencies, no tracking...
|
|
4
|
-
|
|
5
|
-
Purpose: A no bloat, minimal coding cli, to enjoy experimenting and researching...
|
|
6
|
-
|
|
7
|
-
## Getting started:
|
|
8
|
-
1. Installation (from pypi)
|
|
9
|
-
```bash
|
|
10
|
-
pip3 install mini-code-cli
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
2. Run agent: (get LLM running via SGLang or Vllm)
|
|
14
|
-
```bash
|
|
15
|
-
mini-code #run the agent loop
|
|
16
|
-
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash" --api-key "sk-dummy" --allowed-dir ./experiments/
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
3. Get help:
|
|
20
|
-
```bash
|
|
21
|
-
mini-code --help
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## (C) Nikolai Rozanov, 2026 - Present
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: mini-code-cli
|
|
3
|
-
Version: 0.0.7.1
|
|
4
|
-
Summary: Minimal Coding CLI
|
|
5
|
-
Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
|
|
6
|
-
License: The MIT License (MIT)
|
|
7
|
-
|
|
8
|
-
Copyright © 2026 Nikolai Rozanov
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
13
|
-
|
|
14
|
-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
15
|
-
Project-URL: Homepage, https://github.com/ai-nikolai/mini-code-cli
|
|
16
|
-
Requires-Python: >=3.10
|
|
17
|
-
Description-Content-Type: text/markdown
|
|
18
|
-
License-File: LICENSE
|
|
19
|
-
Dynamic: license-file
|
|
20
|
-
|
|
21
|
-
# A minimal coding agent cli
|
|
22
|
-
|
|
23
|
-
> No bloat, no dependencies, no tracking...
|
|
24
|
-
|
|
25
|
-
Purpose: A no bloat, minimal coding cli, to enjoy experimenting and researching...
|
|
26
|
-
|
|
27
|
-
## Getting started:
|
|
28
|
-
1. Installation (from pypi)
|
|
29
|
-
```bash
|
|
30
|
-
pip3 install mini-code-cli
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
2. Run agent: (get LLM running via SGLang or Vllm)
|
|
34
|
-
```bash
|
|
35
|
-
mini-code #run the agent loop
|
|
36
|
-
mini-code --url "https://api.deepseek.com" --model "deepseek-v4-flash" --api-key "sk-dummy" --allowed-dir ./experiments/
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
3. Get help:
|
|
40
|
-
```bash
|
|
41
|
-
mini-code --help
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## (C) Nikolai Rozanov, 2026 - Present
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|