mini-code-cli 0.1.1.2__py3-none-any.whl → 0.1.1.4__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/__init__.py +29 -10
- mini_code_cli/__main__.py +12 -0
- mini_code_cli/agent_files.py +90 -0
- mini_code_cli/agent_loop.py +546 -76
- mini_code_cli/compaction.py +245 -0
- mini_code_cli/config.py +35 -19
- mini_code_cli/permissions.py +15 -9
- mini_code_cli/planning.py +29 -0
- mini_code_cli/prompts.py +144 -6
- mini_code_cli/runner.py +21 -7
- mini_code_cli/skills.py +4 -1
- mini_code_cli/subagents/base.py +64 -8
- mini_code_cli/subagents/builtin.py +10 -9
- mini_code_cli/tokens.py +66 -0
- mini_code_cli/tools.py +92 -30
- mini_code_cli/ui.py +156 -16
- mini_code_cli/utils.py +146 -68
- mini_code_cli-0.1.1.4.dist-info/METADATA +440 -0
- mini_code_cli-0.1.1.4.dist-info/RECORD +28 -0
- mini_code_cli-0.1.1.2.dist-info/METADATA +0 -1028
- mini_code_cli-0.1.1.2.dist-info/RECORD +0 -24
- {mini_code_cli-0.1.1.2.dist-info → mini_code_cli-0.1.1.4.dist-info}/WHEEL +0 -0
- {mini_code_cli-0.1.1.2.dist-info → mini_code_cli-0.1.1.4.dist-info}/entry_points.txt +0 -0
- {mini_code_cli-0.1.1.2.dist-info → mini_code_cli-0.1.1.4.dist-info}/licenses/LICENSE +0 -0
- {mini_code_cli-0.1.1.2.dist-info → mini_code_cli-0.1.1.4.dist-info}/top_level.txt +0 -0
mini_code_cli/__init__.py
CHANGED
|
@@ -11,22 +11,41 @@ to ``"unknown"`` so importing the package can never fail over a version lookup.
|
|
|
11
11
|
__all__ = ["__version__"]
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def _version_from_file(path):
|
|
15
|
+
"""Return the ``[project] version`` in ``path``.
|
|
16
|
+
|
|
17
|
+
``tomllib`` only exists from Python 3.11, and the scan below is not a
|
|
18
|
+
nicety for 3.10: without it this function used to skip the file entirely on
|
|
19
|
+
that interpreter and report the *installed* metadata instead, so a checkout
|
|
20
|
+
with a stale editable install showed the wrong version in the banner (found
|
|
21
|
+
on the 3.10 CI leg). Both paths read the same key, and the section tracking
|
|
22
|
+
matters: a ``version`` key in some other table must not win.
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
import tomllib # Python >= 3.11
|
|
26
|
+
except ImportError: # pragma: no cover - exercised on the 3.10 CI leg
|
|
27
|
+
tomllib = None
|
|
28
|
+
if tomllib is not None:
|
|
29
|
+
with open(path, "rb") as f:
|
|
30
|
+
return str(tomllib.load(f)["project"]["version"])
|
|
31
|
+
section = None
|
|
32
|
+
with open(path, encoding="utf-8") as f:
|
|
33
|
+
for line in f:
|
|
34
|
+
stripped = line.strip()
|
|
35
|
+
if stripped.startswith("["):
|
|
36
|
+
section = stripped
|
|
37
|
+
elif section == "[project]" and stripped.startswith("version"):
|
|
38
|
+
return stripped.split("=", 1)[1].strip().strip('"')
|
|
39
|
+
raise KeyError(f"no version in the [project] table of {path}")
|
|
40
|
+
|
|
41
|
+
|
|
14
42
|
def _read_version():
|
|
15
43
|
"""Best-effort package version: pyproject.toml, then installed metadata."""
|
|
16
44
|
try:
|
|
17
45
|
from pathlib import Path
|
|
18
46
|
|
|
19
47
|
pyproject = Path(__file__).resolve().parent.parent / "pyproject.toml"
|
|
20
|
-
|
|
21
|
-
import tomllib # Python >= 3.11
|
|
22
|
-
except ImportError: # pragma: no cover - older interpreters
|
|
23
|
-
tomllib = None
|
|
24
|
-
if tomllib is not None:
|
|
25
|
-
try:
|
|
26
|
-
with open(pyproject, "rb") as f:
|
|
27
|
-
return str(tomllib.load(f)["project"]["version"])
|
|
28
|
-
except Exception:
|
|
29
|
-
pass
|
|
48
|
+
return _version_from_file(pyproject)
|
|
30
49
|
except Exception:
|
|
31
50
|
pass
|
|
32
51
|
try:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Allow ``python -m mini_code_cli`` to start the agent.
|
|
2
|
+
|
|
3
|
+
The console script ``mini-code`` (see ``[project.scripts]``) is the normal entry
|
|
4
|
+
point; this module exists so the package also runs straight from a checkout or
|
|
5
|
+
an installed wheel without the script on ``PATH`` -- which is what a reader
|
|
6
|
+
tries first after ``pip install -e .``.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .agent_loop import main
|
|
10
|
+
|
|
11
|
+
if __name__ == "__main__":
|
|
12
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Agent-owned files kept in ``--allowed-dir/.mini``.
|
|
2
|
+
|
|
3
|
+
Two opt-in features put a file the *agent* may read and write there, rather
|
|
4
|
+
than in the session directory the loop owns:
|
|
5
|
+
|
|
6
|
+
* the **scratchpad** (``--scratchpad``): a free-form note file for thoughts,
|
|
7
|
+
plans and learnings, plus as many additional scratch files as the agent
|
|
8
|
+
wants to create;
|
|
9
|
+
* the **open plan** (``--plan-strategy open-plan``): a plan document the
|
|
10
|
+
agent maintains itself with the normal file tools.
|
|
11
|
+
|
|
12
|
+
Both live under the same ``.mini`` directory as the skills folder and the
|
|
13
|
+
latest-session pointer, so they are inside the ``--allowed-dir`` sandbox and
|
|
14
|
+
the normal ``read_file`` / ``write_file`` / ``edit`` tools can reach them. The
|
|
15
|
+
directory name is read from :mod:`mini_code_cli.persistence` at call time, so
|
|
16
|
+
the test suite can redirect it globally (exactly like the session pointer).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
from . import persistence
|
|
22
|
+
|
|
23
|
+
SCRATCHPAD_FILENAME = "SCRATCHPAD.md"
|
|
24
|
+
OPEN_PLAN_FILENAME = "PLAN.md"
|
|
25
|
+
|
|
26
|
+
_SCRATCHPAD_HEADER = """# Scratchpad
|
|
27
|
+
|
|
28
|
+
This file belongs to the agent. It may read and write it freely and may create
|
|
29
|
+
additional scratch files in the same directory. Use it for thoughts, plans,
|
|
30
|
+
learnings and notes that should survive a compaction.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
_OPEN_PLAN_HEADER = """# Agent Plan
|
|
34
|
+
|
|
35
|
+
This plan document belongs to the agent. Read it before continuing, keep it up
|
|
36
|
+
to date as work progresses, and mark steps done or outstanding.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def mini_dir(allowed_dir):
|
|
41
|
+
"""The ``.mini`` directory inside ``allowed_dir`` (not created)."""
|
|
42
|
+
return os.path.join(os.path.abspath(allowed_dir), persistence.SESSION_DIRNAME)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def scratchpad_path(allowed_dir):
|
|
46
|
+
"""Path to the main scratch-pad file."""
|
|
47
|
+
return os.path.join(mini_dir(allowed_dir), SCRATCHPAD_FILENAME)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def open_plan_path(allowed_dir):
|
|
51
|
+
"""Path to the agent-authored plan document."""
|
|
52
|
+
return os.path.join(mini_dir(allowed_dir), OPEN_PLAN_FILENAME)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def ensure_scratchpad(allowed_dir):
|
|
56
|
+
"""Create the main scratch-pad file (and its directory) if missing."""
|
|
57
|
+
return _ensure_file(scratchpad_path(allowed_dir), _SCRATCHPAD_HEADER)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def ensure_open_plan(allowed_dir):
|
|
61
|
+
"""Create the agent plan document (and its directory) if missing."""
|
|
62
|
+
return _ensure_file(open_plan_path(allowed_dir), _OPEN_PLAN_HEADER)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_text(path, limit=None):
|
|
66
|
+
"""Read ``path`` as text, returning ``""`` when it cannot be read.
|
|
67
|
+
|
|
68
|
+
``limit`` caps the returned characters (the open-plan re-injection after a
|
|
69
|
+
compaction should not be able to re-fill the context with the document).
|
|
70
|
+
"""
|
|
71
|
+
try:
|
|
72
|
+
with open(path, "r", encoding="utf-8", errors="replace") as handle:
|
|
73
|
+
text = handle.read()
|
|
74
|
+
except OSError:
|
|
75
|
+
return ""
|
|
76
|
+
if limit is not None and len(text) > limit:
|
|
77
|
+
return text[:limit] + "\n[... truncated ...]\n"
|
|
78
|
+
return text
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _ensure_file(path, header):
|
|
82
|
+
"""Best-effort create-with-header; never raises, always returns ``path``."""
|
|
83
|
+
try:
|
|
84
|
+
if not os.path.isfile(path):
|
|
85
|
+
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
86
|
+
with open(path, "w", encoding="utf-8") as handle:
|
|
87
|
+
handle.write(header)
|
|
88
|
+
except OSError:
|
|
89
|
+
pass
|
|
90
|
+
return path
|