mini-code-cli 0.1.0.0__py3-none-any.whl → 0.1.0.2__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.
- mini_code_cli/agent_loop.py +76 -4
- mini_code_cli/persistence.py +107 -2
- mini_code_cli/ui.py +65 -0
- {mini_code_cli-0.1.0.0.dist-info → mini_code_cli-0.1.0.2.dist-info}/METADATA +17 -2
- {mini_code_cli-0.1.0.0.dist-info → mini_code_cli-0.1.0.2.dist-info}/RECORD +9 -9
- {mini_code_cli-0.1.0.0.dist-info → mini_code_cli-0.1.0.2.dist-info}/WHEEL +0 -0
- {mini_code_cli-0.1.0.0.dist-info → mini_code_cli-0.1.0.2.dist-info}/entry_points.txt +0 -0
- {mini_code_cli-0.1.0.0.dist-info → mini_code_cli-0.1.0.2.dist-info}/licenses/LICENSE +0 -0
- {mini_code_cli-0.1.0.0.dist-info → mini_code_cli-0.1.0.2.dist-info}/top_level.txt +0 -0
mini_code_cli/agent_loop.py
CHANGED
|
@@ -52,6 +52,7 @@ from .persistence import (
|
|
|
52
52
|
load_summary_from_dir,
|
|
53
53
|
load_usage_from_dir,
|
|
54
54
|
find_last_session_dir,
|
|
55
|
+
save_last_session_pointer,
|
|
55
56
|
accumulate_usage,
|
|
56
57
|
generate_unique_id,
|
|
57
58
|
_restore_dir,
|
|
@@ -368,7 +369,15 @@ class AgentSession:
|
|
|
368
369
|
restore_session = getattr(self.args, "restore_session", None)
|
|
369
370
|
self.loaded_last_session_dir = None
|
|
370
371
|
if getattr(self.args, "load_last_session", False) and not restore_session:
|
|
371
|
-
|
|
372
|
+
# Resolve the most recent session: the .mini pointer recorded under
|
|
373
|
+
# --allowed-dir is consulted first (an explicit record of the last
|
|
374
|
+
# session worked in), falling back to the timestamp heuristic over
|
|
375
|
+
# the cache dir.
|
|
376
|
+
detected = find_last_session_dir(
|
|
377
|
+
self.cache_dir,
|
|
378
|
+
allowed_dir=self.allowed_dir,
|
|
379
|
+
use_pointer=True,
|
|
380
|
+
)
|
|
372
381
|
if detected:
|
|
373
382
|
restore_session = detected
|
|
374
383
|
self.loaded_last_session_dir = detected
|
|
@@ -581,6 +590,17 @@ class AgentSession:
|
|
|
581
590
|
print_welcome(self.allowed_dir, self.server_url, self.args,
|
|
582
591
|
self.unique_id, self.cache_dir, self.model)
|
|
583
592
|
|
|
593
|
+
# Announce the session's unique id as an assistant-styled message so the
|
|
594
|
+
# user can see / copy the id this conversation is being saved under.
|
|
595
|
+
self._print_session_id()
|
|
596
|
+
|
|
597
|
+
# Record this session as the "latest" so a future --load-last-session can
|
|
598
|
+
# resume it without scanning the cache dir. This lives in the top-level
|
|
599
|
+
# loop's own directory (cache_dir/unique_id), and only the interactive
|
|
600
|
+
# run() writes it -- a sub-agent child drives via _drive_auto and would
|
|
601
|
+
# otherwise overwrite the pointer with its own buried path.
|
|
602
|
+
self._save_last_session_pointer()
|
|
603
|
+
|
|
584
604
|
# Report the outcome of --load-last-session up front, so the user sees
|
|
585
605
|
# whether we resumed an earlier conversation (under a brand-new id) or
|
|
586
606
|
# started fresh because no previous session was found.
|
|
@@ -612,6 +632,7 @@ class AgentSession:
|
|
|
612
632
|
# 1) Get the user's turn: the one-shot preset, or a fresh line.
|
|
613
633
|
if pending is not None:
|
|
614
634
|
self.console.rule(title="Preset prompt", char="=", fg="cyan")
|
|
635
|
+
self.console.short_divider()
|
|
615
636
|
self.console.turn("You", pending, accent="cyan")
|
|
616
637
|
user_input = pending
|
|
617
638
|
pending = None
|
|
@@ -649,6 +670,26 @@ class AgentSession:
|
|
|
649
670
|
# Ctrl-C mid-loop just saves and re-prompts rather than crashing.
|
|
650
671
|
self._persist()
|
|
651
672
|
|
|
673
|
+
def _print_session_id(self):
|
|
674
|
+
"""Print the session's unique id as a minimal "Assistant" turn.
|
|
675
|
+
|
|
676
|
+
Instead of a banner/header the id is rendered as an assistant-styled
|
|
677
|
+
(green) turn carrying *only* the id in brackets, so the user can see /
|
|
678
|
+
copy the id this conversation is being saved under without the noise of
|
|
679
|
+
the extra "saved under" line.
|
|
680
|
+
"""
|
|
681
|
+
self.console.turn("Assistant", f"[{self.unique_id}]", accent="green")
|
|
682
|
+
|
|
683
|
+
def _save_last_session_pointer(self):
|
|
684
|
+
"""Record this session's directory in --allowed-dir/.mini.
|
|
685
|
+
|
|
686
|
+
Writes the .mini pointer that a later --load-last-session consults first
|
|
687
|
+
(see save_last_session_pointer). Best-effort: never raises, so a pointer
|
|
688
|
+
hiccup can never break the interactive session.
|
|
689
|
+
"""
|
|
690
|
+
save_last_session_pointer(self.allowed_dir,
|
|
691
|
+
os.path.join(self.cache_dir, self.unique_id))
|
|
692
|
+
|
|
652
693
|
# -- autonomous drive (used by sub-agents) ------------------------------
|
|
653
694
|
|
|
654
695
|
def _drive_auto(self, initial_task, max_steps=None):
|
|
@@ -815,6 +856,7 @@ class AgentSession:
|
|
|
815
856
|
|
|
816
857
|
def _read_user_line(self):
|
|
817
858
|
"""Read one turn from the user. Returns the text to send, or None to quit."""
|
|
859
|
+
self.console.short_divider()
|
|
818
860
|
user_input = self.console.read_line(style_line_prompt("You:"))
|
|
819
861
|
if user_input is None: # EOF / Ctrl-C at the prompt
|
|
820
862
|
self.console.note("Thank you. Goodbye! Saving history...", "info")
|
|
@@ -864,7 +906,7 @@ class AgentSession:
|
|
|
864
906
|
self.unique_id, self.cache_dir, self.model)
|
|
865
907
|
return True
|
|
866
908
|
if cmd in self.CLEAR_COMMANDS:
|
|
867
|
-
self.
|
|
909
|
+
self._clear_and_show_last()
|
|
868
910
|
return True
|
|
869
911
|
self.console.note(f"Unknown command {stripped!r}. Try /help.", "warn")
|
|
870
912
|
return True
|
|
@@ -892,12 +934,42 @@ class AgentSession:
|
|
|
892
934
|
return
|
|
893
935
|
self._reset_history(target["turn"], target)
|
|
894
936
|
|
|
937
|
+
def _clear_and_show_last(self):
|
|
938
|
+
"""Handle /clear: wipe the screen and re-print the last turn.
|
|
939
|
+
|
|
940
|
+
The conversation itself is unchanged; we just redraw the most recent
|
|
941
|
+
exchange so the user re-orients after clearing the view. If there is no
|
|
942
|
+
assistant reply yet, the last user turn is shown instead.
|
|
943
|
+
"""
|
|
944
|
+
self.console.clear_screen()
|
|
945
|
+
last_role = last_text = None
|
|
946
|
+
for m in reversed(self.messages):
|
|
947
|
+
role = m.get("role")
|
|
948
|
+
content = m.get("content")
|
|
949
|
+
if role == "assistant" and not m.get("tool_calls") and content:
|
|
950
|
+
last_role, last_text = "Assistant", content
|
|
951
|
+
break
|
|
952
|
+
if role == "user" and content:
|
|
953
|
+
last_role, last_text = "You", content
|
|
954
|
+
break
|
|
955
|
+
if last_role is not None:
|
|
956
|
+
self.console.divider()
|
|
957
|
+
accent = "green" if last_role == "Assistant" else "cyan"
|
|
958
|
+
self.console.turn(last_role, last_text, accent=accent)
|
|
959
|
+
|
|
895
960
|
# -- tool execution ----------------------------------------------------
|
|
896
961
|
|
|
897
962
|
def _run_tool_calls(self, tool_calls):
|
|
898
963
|
_make_tool_call_note(self.console, len(tool_calls))
|
|
899
|
-
|
|
900
|
-
|
|
964
|
+
# Show a "… calling tool" spinner (in-place, non-scrolling) while the
|
|
965
|
+
# tool(s) execute, mirroring the thinking spinner that covers the LLM
|
|
966
|
+
# call. Always stopped (finally) so it can never leak into the next turn.
|
|
967
|
+
self.console.start_tool_call()
|
|
968
|
+
try:
|
|
969
|
+
for call in tool_calls:
|
|
970
|
+
self._run_one_tool_call(call)
|
|
971
|
+
finally:
|
|
972
|
+
self.console.stop_tool_call()
|
|
901
973
|
|
|
902
974
|
def _run_one_tool_call(self, call):
|
|
903
975
|
call_id = call.get("id", "")
|
mini_code_cli/persistence.py
CHANGED
|
@@ -24,6 +24,7 @@ raising.
|
|
|
24
24
|
import json
|
|
25
25
|
import os
|
|
26
26
|
import re
|
|
27
|
+
import time
|
|
27
28
|
|
|
28
29
|
from . import ui
|
|
29
30
|
from .config import DEFAULT_CACHE_LOCATION
|
|
@@ -76,8 +77,112 @@ def generate_unique_id():
|
|
|
76
77
|
return f"{timestamp}-{uuid.uuid4().hex[:8]}"
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
# latest-session pointer (.mini)
|
|
82
|
+
# ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
# The .mini directory (inside the user's --allowed-dir) holds a tiny pointer to
|
|
85
|
+
# the most recent session. --load-last-session consults this pointer *first*
|
|
86
|
+
# (an explicit opt-in the user's environment recorded) before falling back to
|
|
87
|
+
# the timestamp heuristic in find_last_session_dir().
|
|
88
|
+
#
|
|
89
|
+
# NOTE: .mini is also the skills feature's directory. The pointer is a single
|
|
90
|
+
# dedicated file inside it (LAST_SESSION_FILENAME), so the two never collide and
|
|
91
|
+
# the skills code (which only reads <skill-name>/ + SKILLS.md) is unaffected.
|
|
92
|
+
SKILLS_DIRNAME = ".mini"
|
|
93
|
+
LAST_SESSION_FILENAME = "last_session.json"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _last_session_dir_store(allowed_dir):
|
|
97
|
+
"""Return the .mini directory for ``allowed_dir`` (creating it if needed).
|
|
98
|
+
|
|
99
|
+
Best-effort: never raises. Returns None if the directory cannot be ensured,
|
|
100
|
+
in which case the pointer is simply not consulted/updated.
|
|
101
|
+
"""
|
|
102
|
+
if not allowed_dir:
|
|
103
|
+
return None
|
|
104
|
+
store = os.path.join(os.path.abspath(allowed_dir), SKILLS_DIRNAME)
|
|
105
|
+
try:
|
|
106
|
+
os.makedirs(store, exist_ok=True)
|
|
107
|
+
return store
|
|
108
|
+
except OSError as e:
|
|
109
|
+
ui.console.note(f"Could not use {store} for the latest-session pointer: {e}", "warn")
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def save_last_session_pointer(allowed_dir, session_dir):
|
|
114
|
+
"""Record ``session_dir`` as the latest session in ``allowed_dir/.mini``.
|
|
115
|
+
|
|
116
|
+
Writes a tiny JSON file (the absolute path plus a timestamp) so a later
|
|
117
|
+
--load-last-session run can resume the most recent session without scanning
|
|
118
|
+
the cache dir. Best-effort: never raises, returns the written path or None.
|
|
119
|
+
"""
|
|
120
|
+
if not session_dir:
|
|
121
|
+
return None
|
|
122
|
+
store = _last_session_dir_store(allowed_dir)
|
|
123
|
+
if store is None:
|
|
124
|
+
return None
|
|
125
|
+
payload = {
|
|
126
|
+
"session_dir": os.path.abspath(session_dir),
|
|
127
|
+
"recorded_at": time.time(),
|
|
128
|
+
}
|
|
129
|
+
path = os.path.join(store, LAST_SESSION_FILENAME)
|
|
130
|
+
try:
|
|
131
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
132
|
+
json.dump(payload, f, indent=2)
|
|
133
|
+
return path
|
|
134
|
+
except OSError as e:
|
|
135
|
+
ui.console.note(f"Could not record latest-session pointer: {e}", "warn")
|
|
136
|
+
return None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def load_last_session_pointer(allowed_dir):
|
|
140
|
+
"""Read the latest-session pointer recorded under ``allowed_dir/.mini``.
|
|
141
|
+
|
|
142
|
+
Returns the recorded session directory (a path into the cache folder), or
|
|
143
|
+
None if no pointer exists or it cannot be read. Best-effort: a missing or
|
|
144
|
+
malformed pointer simply yields None so the caller falls back to the
|
|
145
|
+
timestamp heuristic.
|
|
146
|
+
"""
|
|
147
|
+
if not allowed_dir:
|
|
148
|
+
return None
|
|
149
|
+
store = os.path.join(os.path.abspath(allowed_dir), SKILLS_DIRNAME)
|
|
150
|
+
path = os.path.join(store, LAST_SESSION_FILENAME)
|
|
151
|
+
if not os.path.isfile(path):
|
|
152
|
+
return None
|
|
153
|
+
try:
|
|
154
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
155
|
+
payload = json.load(f)
|
|
156
|
+
except Exception:
|
|
157
|
+
return None
|
|
158
|
+
if not isinstance(payload, dict):
|
|
159
|
+
return None
|
|
160
|
+
session_dir = payload.get("session_dir")
|
|
161
|
+
if not session_dir or not os.path.isdir(session_dir):
|
|
162
|
+
return None
|
|
163
|
+
return session_dir
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def find_last_session_dir(cache_dir=None, allowed_dir=None, use_pointer=True):
|
|
167
|
+
"""Return the path to the most recently *active* session dir.
|
|
168
|
+
|
|
169
|
+
When ``use_pointer`` and an ``allowed_dir`` are given, the .mini pointer
|
|
170
|
+
recorded by a previous run (see save_last_session_pointer) is consulted
|
|
171
|
+
*first* -- it is an explicit record of the session the user last worked in.
|
|
172
|
+
Only if that yields nothing do we fall back to the timestamp heuristic over
|
|
173
|
+
``cache_dir`` (find_last_session_dir_by_mtime). Session folders with no
|
|
174
|
+
files are ignored. Never raises: a missing/unreadable cache dir simply
|
|
175
|
+
yields None.
|
|
176
|
+
"""
|
|
177
|
+
if use_pointer and allowed_dir:
|
|
178
|
+
pointed = load_last_session_pointer(allowed_dir)
|
|
179
|
+
if pointed:
|
|
180
|
+
return pointed
|
|
181
|
+
return find_last_session_dir_by_mtime(cache_dir)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def find_last_session_dir_by_mtime(cache_dir):
|
|
185
|
+
"""Timestamp heuristic for the most recently *active* session dir.
|
|
81
186
|
|
|
82
187
|
"Most recent" is decided by the newest modification time of any file inside
|
|
83
188
|
each session folder (a session rewrites its ``history_{n}.json`` every step),
|
mini_code_cli/ui.py
CHANGED
|
@@ -99,6 +99,12 @@ class Console:
|
|
|
99
99
|
# "… thinking" spinner state (see start_thinking / stop_thinking).
|
|
100
100
|
self._thinking_thread = None
|
|
101
101
|
self._thinking_stop = None
|
|
102
|
+
# "… calling tool" spinner state (see start_tool_call / stop_tool_call).
|
|
103
|
+
# Mirrors the thinking spinner: a daemon thread that rewrites a single
|
|
104
|
+
# live line in place (via the transient region) so the tool-execution
|
|
105
|
+
# phase looks alive without scrolling the transcript.
|
|
106
|
+
self._tool_call_thread = None
|
|
107
|
+
self._tool_call_stop = None
|
|
102
108
|
# Live "transient" region (non-verbose mode) renders the tool-call area
|
|
103
109
|
# and the usage line without scrolling the screen. We keep a single
|
|
104
110
|
# invariant: whenever ``_transient_lines`` is > 0 a block of that many
|
|
@@ -365,6 +371,16 @@ class Console:
|
|
|
365
371
|
"""
|
|
366
372
|
self.emit(self.style("-" * self.width, fg="gray", dim=True))
|
|
367
373
|
|
|
374
|
+
def short_divider(self, char="-", width=8, fg="gray"):
|
|
375
|
+
"""Render a short horizontal delimiter line (not the full width).
|
|
376
|
+
|
|
377
|
+
Used to set off the user's own "You:" turn from the preceding assistant
|
|
378
|
+
reply with a light, unobtrusive rule rather than a full-width divider.
|
|
379
|
+
Defaults to a plain ASCII dash so it renders consistently regardless of
|
|
380
|
+
the console's unicode setting.
|
|
381
|
+
"""
|
|
382
|
+
self.emit(self.style(char * width, fg=fg, dim=True))
|
|
383
|
+
|
|
368
384
|
def clear_screen(self):
|
|
369
385
|
"""Clear the terminal and move the cursor to the top-left.
|
|
370
386
|
|
|
@@ -432,6 +448,55 @@ class Console:
|
|
|
432
448
|
if thread is not None and thread.is_alive():
|
|
433
449
|
thread.join(timeout=1.0)
|
|
434
450
|
|
|
451
|
+
def start_tool_call(self, message="Calling tool"):
|
|
452
|
+
"""Start a background "… calling tool" spinner (non-scrolling).
|
|
453
|
+
|
|
454
|
+
Shown while the agent executes a tool call, mirroring
|
|
455
|
+
:meth:`start_thinking` which covers the LLM request. Runs a short
|
|
456
|
+
animation in a daemon thread that rewrites a single live line in place
|
|
457
|
+
via the transient region, so the wait looks alive without scrolling the
|
|
458
|
+
transcript. On a real colour/utf-8 TTY it animates in place; on piped /
|
|
459
|
+
ASCII output it simply prints one static line and starts no thread.
|
|
460
|
+
Stop it with :meth:`stop_tool_call` before drawing the next turn.
|
|
461
|
+
"""
|
|
462
|
+
self.stop_tool_call() # be defensive: never leak more than one spinner
|
|
463
|
+
if not self._can_clear_cursor():
|
|
464
|
+
self.emit(self.style(f"\u2026 {message}", fg="magenta", dim=True))
|
|
465
|
+
return
|
|
466
|
+
self._tool_call_stop = threading.Event()
|
|
467
|
+
self._tool_call_thread = threading.Thread(
|
|
468
|
+
target=self._spin_tool_call, args=(message,), daemon=True)
|
|
469
|
+
self._tool_call_thread.start()
|
|
470
|
+
|
|
471
|
+
def _spin_tool_call(self, message):
|
|
472
|
+
"""Animation loop for the tool-call spinner (mirror of :meth:`_spin`)."""
|
|
473
|
+
stop = self._tool_call_stop
|
|
474
|
+
i = 0
|
|
475
|
+
while not stop.is_set():
|
|
476
|
+
dots = "\u2026 " * min((i % 3) + 1, 3)
|
|
477
|
+
i += 1
|
|
478
|
+
self.emit_transient(self.style(f"{dots}{message}", fg="magenta", dim=True))
|
|
479
|
+
stop.wait(0.3)
|
|
480
|
+
# Replace the spinner line with a blank so the following real output
|
|
481
|
+
# lands cleanly; the blank stays tracked as the transient block and is
|
|
482
|
+
# cleared by the next :meth:`emit` (e.g. the next assistant turn).
|
|
483
|
+
self.emit_transient(None)
|
|
484
|
+
|
|
485
|
+
def stop_tool_call(self):
|
|
486
|
+
"""Stop the "… calling tool" spinner started by :meth:`start_tool_call`.
|
|
487
|
+
|
|
488
|
+
Signals the animation thread to finish its current frame and exit, then
|
|
489
|
+
joins it, so no writes race with the main thread's subsequent output.
|
|
490
|
+
Safe to call when no spinner is running (a no-op)."""
|
|
491
|
+
stop = self._tool_call_stop
|
|
492
|
+
thread = self._tool_call_thread
|
|
493
|
+
self._tool_call_stop = None
|
|
494
|
+
self._tool_call_thread = None
|
|
495
|
+
if stop is not None:
|
|
496
|
+
stop.set()
|
|
497
|
+
if thread is not None and thread.is_alive():
|
|
498
|
+
thread.join(timeout=1.0)
|
|
499
|
+
|
|
435
500
|
def tool_group(self, name, params=None, result=None, status="ran"):
|
|
436
501
|
"""Render one attempted tool call: its name, parameters and result."""
|
|
437
502
|
self.emit(self.style(f"{self.glyph('gear')} {name}", fg="magenta", bold=True))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mini-code-cli
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.2
|
|
4
4
|
Summary: Mini Coding Agent. Allows you to easily experiment with a small coding agent.
|
|
5
5
|
Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
|
|
6
6
|
License: GNU AFFERO GENERAL PUBLIC LICENSE
|
|
@@ -720,7 +720,7 @@ mini-code --enable-shell --auto-mode --agent-md "./skill.md" --allowed-dir "./"
|
|
|
720
720
|
- allowed-dir: this specifies which dirs the agent can read and write from, that does not stop the shell comands! (though dangerous shell commands are still blocked by the classifier above).
|
|
721
721
|
- ask-permission: asks user permission before every tool call. For shell commands, commands in the ALLOW tier skip the prompt, DENY is blocked, and ASK always prompts.
|
|
722
722
|
- system-prompt: you can override the default prompt by using a string. (Note: Agent.md is always appended after the system prompt.)
|
|
723
|
-
- load-last-session: instead of starting a fresh conversation, automatically continue your most recently active session
|
|
723
|
+
- load-last-session: instead of starting a fresh conversation, automatically continue your most recently active session from the default cache dir. It resumes under a brand-new session id, so the original session is never overwritten. An explicit `--restore-session` (a specific path) takes precedence if both are given. The "most recent" session is resolved in two steps: at the end of every interactive session, its path is recorded in a tiny `.mini/last_session.json` pointer inside the `--allowed-dir`, and `--load-last-session` consults that pointer first; only if there is no pointer (e.g. a fresh allowed dir, or the `--skills` feature disabled) does it fall back to the timestamp heuristic (the session whose history was modified most recently). The `.mini` pointer is the same directory the `--skills` feature uses, but it is a single dedicated file, so the two never clash.
|
|
724
724
|
- plan: `--plan` enables **plan mode**. The agent now keeps two tracking files in the session directory: `CURRENT_GOAL.md` (the user's latest instruction) and `CURRENT_PLAN.md` (its own step-by-step plan, which it must update as it works and mark finished once the goal is done). The system message gets an extra instruction telling the agent to do this. Plan mode also changes how history compaction works: after the history is compacted to a summary, the current `CURRENT_PLAN.md` and the user's last instruction (`CURRENT_GOAL.md`) are re-injected into the messages so the agent keeps working against the same goal. When the session ends, the active goal is marked finished in `CURRENT_PLAN.md`.
|
|
725
725
|
|
|
726
726
|
### Available tools
|
|
@@ -871,4 +871,19 @@ vllm serve ornith-ai/Ornith-1.5-397B-FP8 \
|
|
|
871
871
|
--trust-remote-code
|
|
872
872
|
```
|
|
873
873
|
|
|
874
|
+
## Citation
|
|
875
|
+
|
|
876
|
+
If you use `mini-code-cli` in your research or writing, please cite it using the BibTeX entry below:
|
|
877
|
+
|
|
878
|
+
```bibtex
|
|
879
|
+
@misc{mini-code-cli,
|
|
880
|
+
author = {Rozanov, Nikolai},
|
|
881
|
+
title = {mini-code-cli: A minimal, dependency-light coding agent},
|
|
882
|
+
year = {2026},
|
|
883
|
+
version = {0.1.0.0},
|
|
884
|
+
howpublished = {\url{https://github.com/ai-nikolai/mini-code-cli}},
|
|
885
|
+
note = {A no-bloat, dependency-free agent CLI for experimenting.}
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
874
889
|
## (C) Nikolai Rozanov, 2026 - Present
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
mini_code_cli/__init__.py,sha256=iNgHmKSHOuH99obGjnUMoS2JP4Gw4AOrIfu68EYaZQM,77
|
|
2
|
-
mini_code_cli/agent_loop.py,sha256=
|
|
2
|
+
mini_code_cli/agent_loop.py,sha256=mU0IW3zekmgnnT7kXcjKL0I4F0dkBMpCpSBRo6JSkL0,59433
|
|
3
3
|
mini_code_cli/config.py,sha256=AvzDZAKynQbuj5lcTtL-DcUwhyLa99QmNrLK6fYpdOA,814
|
|
4
4
|
mini_code_cli/permissions.py,sha256=uQpQlUtQyVICpjUqdh9QQ_mgl51hZrGLHJzQR9vG124,2017
|
|
5
|
-
mini_code_cli/persistence.py,sha256=
|
|
5
|
+
mini_code_cli/persistence.py,sha256=BxUL4x8rha8ic_1vkWJu1cAFTuYyhK85ZdFVD9aFRYQ,14055
|
|
6
6
|
mini_code_cli/plan.py,sha256=M5WZQuiN8F_K7zyWIrU_5R9L8dcN9k8iYouXzfL_KOs,6985
|
|
7
7
|
mini_code_cli/prompts.py,sha256=X05Krwf9X46WinbHzHj8WYq4RTnO56KZpMiRYS2CGLw,5452
|
|
8
8
|
mini_code_cli/runner.py,sha256=oYUAzbQOrkakuR2uT1OnIrruh_XJYWEDzKpM09T4Zp0,1927
|
|
@@ -10,14 +10,14 @@ mini_code_cli/sandbox.py,sha256=t7xS_AIpE8fbo5kj9zbUFbfEaAnq6iUE7plQcn6-uAk,2128
|
|
|
10
10
|
mini_code_cli/shell_safety_utils.py,sha256=5E126vGnG7iIuTZe_R5JrxB_zxZpMKGe_pilz_Z2r4w,5551
|
|
11
11
|
mini_code_cli/skills.py,sha256=baaz0RkPQ-OgWWwH5sHsKBmJdR430yT4HK3VLX0_88Y,13704
|
|
12
12
|
mini_code_cli/tools.py,sha256=bW1zYyIj6OFp5TIcg5DCwheN-4xI1LQmDOQaHIfRV7M,10998
|
|
13
|
-
mini_code_cli/ui.py,sha256=
|
|
13
|
+
mini_code_cli/ui.py,sha256=064mcoxrKSaC0eWTWqeM6urPy5MD7hE5Tr-z2jjnqO4,32613
|
|
14
14
|
mini_code_cli/utils.py,sha256=ffUAIisO8_h7VvlwAOLhdoNSAIdssGdtQHXm-i8bcLE,4620
|
|
15
15
|
mini_code_cli/subagents/__init__.py,sha256=3fgcwY0pNq5iW29OxLPtSA3H5IKXhl2LUhwbBjLUDuU,1884
|
|
16
16
|
mini_code_cli/subagents/base.py,sha256=bhZEENoTVjebfqaEKS7uPWeU0SwQWFN3BD40EtziPbg,12660
|
|
17
17
|
mini_code_cli/subagents/builtin.py,sha256=ENzuooXIylzfZZ8uYkZmdxgCrCQ4pdbfrOONkBdGSPg,7598
|
|
18
|
-
mini_code_cli-0.1.0.
|
|
19
|
-
mini_code_cli-0.1.0.
|
|
20
|
-
mini_code_cli-0.1.0.
|
|
21
|
-
mini_code_cli-0.1.0.
|
|
22
|
-
mini_code_cli-0.1.0.
|
|
23
|
-
mini_code_cli-0.1.0.
|
|
18
|
+
mini_code_cli-0.1.0.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
19
|
+
mini_code_cli-0.1.0.2.dist-info/METADATA,sha256=WRYRunHU0Swx0qpojq_JRynzzgjN8unAYLQ6E1Ppcf0,53278
|
|
20
|
+
mini_code_cli-0.1.0.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
21
|
+
mini_code_cli-0.1.0.2.dist-info/entry_points.txt,sha256=MwDp5UbXpI7wodSmOSYL8_nKutnBiyp96ZDDtaX7x14,60
|
|
22
|
+
mini_code_cli-0.1.0.2.dist-info/top_level.txt,sha256=3Hb6PpNkJYXvzDKXuuFJRvzCdsTRh0TuRWz-ScJ6qV0,14
|
|
23
|
+
mini_code_cli-0.1.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|