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.
@@ -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
- with open("history.json", "w") as f:
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("--step_by_step", action="store_true", help="Whether to run the agent step by step. Default is to run in 'Permission' mode.")
47
- parser.add_argument("--ask_permission", action="store_true", help="Ask for permission before any tool call.")
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
- def print_welcome(allowed_dir, server_url, args):
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.step_by_step else "Agent is in auto-mode."
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
- " Shell disabled."
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.step_by_step else "Auto-mode means the agent will keep calling the LLM until no further LLM calls are possible."
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
- allowed_dir = os.path.abspath(args.allowed_dir)
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
- tools_dict = tools.util_get_tools_dict()
96
- llm_tools_dict = tools.util_get_tools()
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
- print_welcome(allowed_dir, server_url, args)
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
- if llm_tool_response_flag and not args.step_by_step: #skip user input and try to call the model again until there no more tool calls.
114
- print(f"{BOLD}{YELLOW}System:{RESET} Skipping user input. Continuing with LLM calls until no more tool calls.")
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
- try:
123
- print("-"*30)
124
- user_input = input(f"{BOLD}{CYAN}You:{RESET} ")
125
- if user_input.lower() in ["quit", "exit", "/quit", "/exit"]:
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
- except KeyboardInterrupt:
130
- print("\nThank you. Goodbye! Saving history...")
131
- save_history(messages)
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
- result = func(**tool_params)
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}Warning:{RESET} Unknown tool: {name}, params {params_str}")
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 util_get_tools(tool_names=None):
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 tool_names:
73
- 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("_") and name in tool_names]
74
- else:
75
- 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("_")]
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
- # print(util_get_tools())
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
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "mini-code-cli"
3
3
  # version = { file = "mini_code_cli/__init__.py", attr = "__version__" }
4
- version = "0.0.7.1"
4
+ version = "0.0.8.1"
5
5
  description = "Minimal Coding CLI"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.10"
@@ -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
@@ -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