nanocode-cli 0.8.0__tar.gz → 0.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {nanocode_cli-0.8.0/nanocode_cli.egg-info → nanocode_cli-0.8.2}/PKG-INFO +2 -1
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/README.md +1 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/README.zh-CN.md +1 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/nanocode.py +96 -12
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2/nanocode_cli.egg-info}/PKG-INFO +2 -1
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/pyproject.toml +1 -1
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/LICENSE +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/MANIFEST.in +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/nanocode_cli.egg-info/SOURCES.txt +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/nanocode_cli.egg-info/dependency_links.txt +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/nanocode_cli.egg-info/entry_points.txt +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/nanocode_cli.egg-info/requires.txt +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/nanocode_cli.egg-info/top_level.txt +0 -0
- {nanocode_cli-0.8.0 → nanocode_cli-0.8.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nanocode-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: A small terminal coding agent written in Python
|
|
5
5
|
Author-email: hit9 <hit9@icloud.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -104,6 +104,7 @@ Useful arguments:
|
|
|
104
104
|
- `--mcp <selector>`: choose which configured MCP servers to enable.
|
|
105
105
|
- `--debug`: write model I/O debug traces.
|
|
106
106
|
- `-v`, `--version`: show the version.
|
|
107
|
+
- `update` / `upgrade`: update nanocode to the latest release, using the right installer (uv tool, pipx, or pip) for how it was installed.
|
|
107
108
|
|
|
108
109
|
During a running turn, type into the `+>` prompt to add follow-up input for the next model request.
|
|
109
110
|
|
|
@@ -67,6 +67,7 @@ Useful arguments:
|
|
|
67
67
|
- `--mcp <selector>`: choose which configured MCP servers to enable.
|
|
68
68
|
- `--debug`: write model I/O debug traces.
|
|
69
69
|
- `-v`, `--version`: show the version.
|
|
70
|
+
- `update` / `upgrade`: update nanocode to the latest release, using the right installer (uv tool, pipx, or pip) for how it was installed.
|
|
70
71
|
|
|
71
72
|
During a running turn, type into the `+>` prompt to add follow-up input for the next model request.
|
|
72
73
|
|
|
@@ -63,7 +63,7 @@ from rich.console import Console
|
|
|
63
63
|
from rich.markdown import Markdown
|
|
64
64
|
from rich.rule import Rule
|
|
65
65
|
|
|
66
|
-
__version__ = "0.8.
|
|
66
|
+
__version__ = "0.8.2"
|
|
67
67
|
|
|
68
68
|
Json = dict[str, Any]
|
|
69
69
|
HTTP_USER_AGENT = "nanocode/" + __version__
|
|
@@ -7268,17 +7268,25 @@ Tools:
|
|
|
7268
7268
|
while self.queue_input_active.is_set() and time.monotonic() < deadline:
|
|
7269
7269
|
time.sleep(0.01)
|
|
7270
7270
|
|
|
7271
|
-
def
|
|
7272
|
-
"""
|
|
7273
|
-
texts = []
|
|
7274
|
-
|
|
7275
|
-
texts.extend(text for text in self.session.pending_user_inputs if text.strip())
|
|
7276
|
-
self.session.pending_user_inputs.clear()
|
|
7277
|
-
if self.queue_input_text.strip():
|
|
7278
|
-
texts.append(self.queue_input_text)
|
|
7279
|
-
self.queue_input_text = ""
|
|
7271
|
+
def take_entered_input(self) -> str:
|
|
7272
|
+
"""Enter-committed queue input (pending_user_inputs), joined and cleared."""
|
|
7273
|
+
texts = [text for text in self.session.pending_user_inputs if text.strip()]
|
|
7274
|
+
self.session.pending_user_inputs.clear()
|
|
7280
7275
|
return "\n".join(texts)
|
|
7281
7276
|
|
|
7277
|
+
def take_typed_input(self) -> str:
|
|
7278
|
+
"""Un-entered text left in the +> box when the agent stopped, cleared."""
|
|
7279
|
+
typed = self.queue_input_text if self.queue_input_text.strip() else ""
|
|
7280
|
+
self.queue_input_text = ""
|
|
7281
|
+
return typed
|
|
7282
|
+
|
|
7283
|
+
def drain_queued_input(self) -> str:
|
|
7284
|
+
"""Entered input first, then still-typed text (used for the headless combined path)."""
|
|
7285
|
+
return "\n".join(text for text in (self.take_entered_input(), self.take_typed_input()) if text)
|
|
7286
|
+
|
|
7287
|
+
def echo_input_line(self, text: str) -> None:
|
|
7288
|
+
print_formatted_text(FormattedText([("class:prompt", "nano> "), ("", text)]), style=self.style())
|
|
7289
|
+
|
|
7282
7290
|
def run(self) -> int:
|
|
7283
7291
|
self.emit(f"nanocode {__version__}. /help for commands.")
|
|
7284
7292
|
if tip := self.startup_tip():
|
|
@@ -7291,8 +7299,19 @@ Tools:
|
|
|
7291
7299
|
UpdateChecker(self.session).start()
|
|
7292
7300
|
while True:
|
|
7293
7301
|
try:
|
|
7294
|
-
|
|
7295
|
-
|
|
7302
|
+
entered = self.take_entered_input()
|
|
7303
|
+
typed = self.take_typed_input()
|
|
7304
|
+
if entered and self.interactive_input:
|
|
7305
|
+
# Input you already pressed Enter on in the +> queue auto-submits as the next turn —
|
|
7306
|
+
# no second Enter. Any half-typed text goes back to the box for the following prompt.
|
|
7307
|
+
if typed:
|
|
7308
|
+
self.queue_input_text = typed
|
|
7309
|
+
self.echo_input_line(entered)
|
|
7310
|
+
user_input = entered
|
|
7311
|
+
else:
|
|
7312
|
+
# Headless (returns initial_text directly), or nothing entered: pre-fill the still-typed
|
|
7313
|
+
# text into the prompt for review/edit.
|
|
7314
|
+
user_input = self.read_input(initial_text="\n".join(text for text in (entered, typed) if text))
|
|
7296
7315
|
except EOFError:
|
|
7297
7316
|
self.emit("")
|
|
7298
7317
|
self.save_and_emit_resume()
|
|
@@ -8480,6 +8499,68 @@ Tools:
|
|
|
8480
8499
|
return "Set " + key
|
|
8481
8500
|
|
|
8482
8501
|
|
|
8502
|
+
class Updater:
|
|
8503
|
+
"""Upgrade nanocode in place, choosing the command that matches how it was installed."""
|
|
8504
|
+
|
|
8505
|
+
PACKAGE = "nanocode-cli"
|
|
8506
|
+
|
|
8507
|
+
def run(self) -> int:
|
|
8508
|
+
try:
|
|
8509
|
+
latest = self.fetch_latest()
|
|
8510
|
+
except Exception as error:
|
|
8511
|
+
print("Error: failed to check latest version: " + Text.clean(str(error)), file=sys.stderr)
|
|
8512
|
+
return 1
|
|
8513
|
+
if not UpdateStatus(latest=latest).newer_than(__version__):
|
|
8514
|
+
print(f"nanocode {__version__} is already up to date (latest: {latest}).")
|
|
8515
|
+
return 0
|
|
8516
|
+
method, command = self.detect()
|
|
8517
|
+
if command is None:
|
|
8518
|
+
print(f"nanocode {__version__} -> {latest} available, but this is an {method} install.", file=sys.stderr)
|
|
8519
|
+
print("Update it the same way you installed it (e.g. git pull, or reinstall).", file=sys.stderr)
|
|
8520
|
+
return 1
|
|
8521
|
+
print(f"Updating nanocode {__version__} -> {latest} ({method}): {' '.join(command)}")
|
|
8522
|
+
try:
|
|
8523
|
+
result = subprocess.run(command)
|
|
8524
|
+
except Exception as error:
|
|
8525
|
+
print("Error: upgrade command failed: " + Text.clean(str(error)), file=sys.stderr)
|
|
8526
|
+
return 1
|
|
8527
|
+
if result.returncode != 0:
|
|
8528
|
+
print("Error: upgrade command exited with status " + str(result.returncode), file=sys.stderr)
|
|
8529
|
+
return result.returncode
|
|
8530
|
+
print(f"Updated nanocode to {latest}.")
|
|
8531
|
+
return 0
|
|
8532
|
+
|
|
8533
|
+
def fetch_latest(self) -> str:
|
|
8534
|
+
request = Request(UpdateChecker.PYPI_URL, headers={"Accept": "application/json", "User-Agent": HTTP_USER_AGENT})
|
|
8535
|
+
with urlopen(request, timeout=UpdateChecker.TIMEOUT) as response:
|
|
8536
|
+
data = json.loads(response.read().decode("utf-8", "replace"))
|
|
8537
|
+
version = data.get("info", {}).get("version") if isinstance(data, dict) else ""
|
|
8538
|
+
if not isinstance(version, str) or not UpdateStatus.version_tuple(version):
|
|
8539
|
+
raise NanocodeError("invalid PyPI version response")
|
|
8540
|
+
return version
|
|
8541
|
+
|
|
8542
|
+
def detect(self) -> tuple[str, list[str] | None]:
|
|
8543
|
+
"""Return (method label, upgrade command). command is None when we cannot self-update."""
|
|
8544
|
+
if self.is_editable():
|
|
8545
|
+
return "editable", None
|
|
8546
|
+
location = os.path.realpath(os.path.dirname(__file__))
|
|
8547
|
+
if os.sep + "uv" + os.sep + "tools" + os.sep in location + os.sep and shutil.which("uv"):
|
|
8548
|
+
return "uv tool", ["uv", "tool", "upgrade", self.PACKAGE]
|
|
8549
|
+
if os.sep + "pipx" + os.sep in location + os.sep and shutil.which("pipx"):
|
|
8550
|
+
return "pipx", ["pipx", "upgrade", self.PACKAGE]
|
|
8551
|
+
return "pip", [sys.executable, "-m", "pip", "install", "--upgrade", self.PACKAGE]
|
|
8552
|
+
|
|
8553
|
+
@staticmethod
|
|
8554
|
+
def is_editable() -> bool:
|
|
8555
|
+
try:
|
|
8556
|
+
import importlib.metadata as metadata
|
|
8557
|
+
|
|
8558
|
+
raw = metadata.distribution(Updater.PACKAGE).read_text("direct_url.json")
|
|
8559
|
+
return bool(raw) and json.loads(raw).get("dir_info", {}).get("editable", False)
|
|
8560
|
+
except Exception:
|
|
8561
|
+
return False
|
|
8562
|
+
|
|
8563
|
+
|
|
8483
8564
|
def main(argv: list[str] | None = None) -> int:
|
|
8484
8565
|
parser = argparse.ArgumentParser(prog="nanocode")
|
|
8485
8566
|
parser.add_argument("--config", default=None, help="Path to config TOML")
|
|
@@ -8489,10 +8570,13 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
8489
8570
|
parser.add_argument("--mcp", default="", help='Filter MCP servers, e.g. "orion*,!orionEval", "all", or "none"')
|
|
8490
8571
|
parser.add_argument("--resume", default="", nargs="?", const="latest", help='Resume a session by UID, or "latest"/"last" for most recent')
|
|
8491
8572
|
parser.add_argument("-v", "--version", action="store_true", help="Show version")
|
|
8573
|
+
parser.add_argument("command", nargs="?", choices=["update", "upgrade"], help="Update nanocode to the latest version")
|
|
8492
8574
|
args = parser.parse_args(argv)
|
|
8493
8575
|
if args.version:
|
|
8494
8576
|
print(__version__)
|
|
8495
8577
|
return 0
|
|
8578
|
+
if args.command in ("update", "upgrade"):
|
|
8579
|
+
return Updater().run()
|
|
8496
8580
|
try:
|
|
8497
8581
|
if args.init_config:
|
|
8498
8582
|
path, created = ConfigFile.init(args.config)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nanocode-cli
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.2
|
|
4
4
|
Summary: A small terminal coding agent written in Python
|
|
5
5
|
Author-email: hit9 <hit9@icloud.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -104,6 +104,7 @@ Useful arguments:
|
|
|
104
104
|
- `--mcp <selector>`: choose which configured MCP servers to enable.
|
|
105
105
|
- `--debug`: write model I/O debug traces.
|
|
106
106
|
- `-v`, `--version`: show the version.
|
|
107
|
+
- `update` / `upgrade`: update nanocode to the latest release, using the right installer (uv tool, pipx, or pip) for how it was installed.
|
|
107
108
|
|
|
108
109
|
During a running turn, type into the `+>` prompt to add follow-up input for the next model request.
|
|
109
110
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|