nanocode-cli 0.9.4__tar.gz → 0.9.6__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.9.4/nanocode_cli.egg-info → nanocode_cli-0.9.6}/PKG-INFO +1 -1
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/nanocode.py +53 -20
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6/nanocode_cli.egg-info}/PKG-INFO +1 -1
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/pyproject.toml +1 -1
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/LICENSE +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/MANIFEST.in +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/README.md +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/README.zh-CN.md +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/nanocode_cli.egg-info/SOURCES.txt +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/nanocode_cli.egg-info/dependency_links.txt +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/nanocode_cli.egg-info/entry_points.txt +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/nanocode_cli.egg-info/requires.txt +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/nanocode_cli.egg-info/top_level.txt +0 -0
- {nanocode_cli-0.9.4 → nanocode_cli-0.9.6}/setup.cfg +0 -0
|
@@ -76,7 +76,7 @@ except ImportError: # pragma: no cover - optional highlighting dependency
|
|
|
76
76
|
pygments = None
|
|
77
77
|
Token = None # keep the name defined so class-body/token lookups don't NameError
|
|
78
78
|
|
|
79
|
-
__version__ = "0.9.
|
|
79
|
+
__version__ = "0.9.6"
|
|
80
80
|
|
|
81
81
|
Json = dict[str, Any]
|
|
82
82
|
|
|
@@ -3210,8 +3210,10 @@ class BashTool(Tool):
|
|
|
3210
3210
|
threading.Thread(target=drain_pipe, args=(stderr_pipe,), daemon=True).start()
|
|
3211
3211
|
partial_stdout = "".join(stdout_parts)
|
|
3212
3212
|
partial_stderr = "".join(stderr_parts)
|
|
3213
|
-
note =
|
|
3214
|
-
|
|
3213
|
+
note = (
|
|
3214
|
+
f'backgrounded after {self.session.settings.bash_wait_timeout}s; still running as {job_id}. Use Job(action="wait"|"status"|"kill", job="{job_id}").'
|
|
3215
|
+
)
|
|
3216
|
+
partial_stderr = partial_stderr + ("\n" if partial_stderr else "") + note
|
|
3215
3217
|
return self.process_result("BashToolResult", -1, partial_stdout, partial_stderr)
|
|
3216
3218
|
|
|
3217
3219
|
def drain_selector(self, selector: selectors.BaseSelector, stdout_parts: list[str], stderr_parts: list[str]) -> None:
|
|
@@ -6286,7 +6288,7 @@ REVIEW MODE:
|
|
|
6286
6288
|
FINAL:
|
|
6287
6289
|
- Be concise: lead with the result, often 1-3 lines, no preamble/recap/filler.
|
|
6288
6290
|
- Note changed files and checks run (or not run).
|
|
6289
|
-
- Use GitHub-flavored Markdown: flat lists (`1. 2. 3.`), backticks for code/paths, info strings on code blocks, clickable file links `[app.py](/abs/path/app.py:12)` without backticks or file://, vscode://, https://.
|
|
6291
|
+
- Use GitHub-flavored Markdown: flat lists (`1. 2. 3.`), backticks for code/paths, info strings on code blocks, clickable file links `[app.py](/abs/path/app.py:12)` without backticks or file://, vscode://, https://. Write http(s) URLs bare (terminal auto-links them); `[text](url)` prints as `text (url)` here.
|
|
6290
6292
|
- No emoji/em dash unless asked; no "X rather than Y" framing; no trailing "If you want".
|
|
6291
6293
|
- The user doesn't see raw outputs; summarize when asked. If you couldn't do something, say so.
|
|
6292
6294
|
- LANGUAGE (strict): write in the user's current natural language, detected per turn. Keep code, identifiers, paths, shell commands, and tool/API names verbatim — translate only prose.
|
|
@@ -6609,6 +6611,20 @@ class UiPrinter:
|
|
|
6609
6611
|
# as a solid band. We track the SGR bg state per token and only strip whitespace rendered with
|
|
6610
6612
|
# bg off.
|
|
6611
6613
|
SGR_RE: ClassVar[re.Pattern[str]] = re.compile(r"\x1b\[([0-9;]*)m")
|
|
6614
|
+
# OSC / APC / DCS / SOS / PM sequences are terminal control strings that prompt_toolkit's ANSI
|
|
6615
|
+
# parser doesn't recognize. When they slip through Rich's output (OSC 8 hyperlinks were the
|
|
6616
|
+
# historical culprit, iTerm image escapes / Kitty graphics / shell-integration marks are
|
|
6617
|
+
# potential future ones), pt eats the ESC framing but leaks the payload as visible garbage
|
|
6618
|
+
# (e.g. `8;id=…;https://…;;` for OSC 8). Strip these up front so pt only ever sees CSI escapes.
|
|
6619
|
+
# The trade is that any legitimate uses of these (clickable hyperlinks, inline images) never
|
|
6620
|
+
# reach the terminal — but they weren't working through pt anyway; better clean than garbled.
|
|
6621
|
+
NON_CSI_ESCAPE_RE: ClassVar[re.Pattern[str]] = re.compile(
|
|
6622
|
+
r"\x1b[\]_PX^][^\x07\x1b]*(?:\x07|\x1b\\)"
|
|
6623
|
+
)
|
|
6624
|
+
|
|
6625
|
+
@classmethod
|
|
6626
|
+
def strip_unknown_escapes(cls, text: str) -> str:
|
|
6627
|
+
return cls.NON_CSI_ESCAPE_RE.sub("", text)
|
|
6612
6628
|
|
|
6613
6629
|
@classmethod
|
|
6614
6630
|
def strip_trailing_pad(cls, text: str) -> str:
|
|
@@ -6662,7 +6678,7 @@ class UiPrinter:
|
|
|
6662
6678
|
console = Console(force_terminal=True, width=shutil.get_terminal_size().columns)
|
|
6663
6679
|
with console.capture() as capture:
|
|
6664
6680
|
self.render_message(console, text, role, rule, indent)
|
|
6665
|
-
print_formatted_text(ANSI(self.strip_trailing_pad(capture.get())), end="", flush=True)
|
|
6681
|
+
print_formatted_text(ANSI(self.strip_unknown_escapes(self.strip_trailing_pad(capture.get()))), end="", flush=True)
|
|
6666
6682
|
|
|
6667
6683
|
@staticmethod
|
|
6668
6684
|
def indent_message(text: str, role: str = "", indent: int = 0) -> str:
|
|
@@ -6678,13 +6694,13 @@ class UiPrinter:
|
|
|
6678
6694
|
console.print("")
|
|
6679
6695
|
console.print(Padding(RichText(UiPrinter.USER_LOG_PREFIX + text, style=self.user_log_style()), (0, 0, 0, len(margin))))
|
|
6680
6696
|
elif role == "assistant":
|
|
6681
|
-
content = RichText(text, style="red") if error else Markdown(text)
|
|
6697
|
+
content = RichText(text, style="red") if error else Markdown(text, hyperlinks=False)
|
|
6682
6698
|
console.print(Padding(content, (0, 0, 0, len(margin))))
|
|
6683
6699
|
else:
|
|
6684
6700
|
if role:
|
|
6685
6701
|
label = RichText(role + ":", style=self.MESSAGE_ROLE_STYLES.get(role, "bright_black"))
|
|
6686
6702
|
console.print(Padding(label, (0, 0, 0, len(margin))))
|
|
6687
|
-
content = RichText(text, style="red") if error else Markdown(text)
|
|
6703
|
+
content = RichText(text, style="red") if error else Markdown(text, hyperlinks=False)
|
|
6688
6704
|
console.print(Padding(content, (0, 0, 0, len(margin))))
|
|
6689
6705
|
|
|
6690
6706
|
def emit_markdown(self, text: str) -> None:
|
|
@@ -6696,8 +6712,8 @@ class UiPrinter:
|
|
|
6696
6712
|
return
|
|
6697
6713
|
console = Console(force_terminal=True, width=shutil.get_terminal_size().columns)
|
|
6698
6714
|
with console.capture() as capture:
|
|
6699
|
-
console.print(Markdown(text))
|
|
6700
|
-
print_formatted_text(ANSI(self.strip_trailing_pad(capture.get())), end="", flush=True)
|
|
6715
|
+
console.print(Markdown(text, hyperlinks=False))
|
|
6716
|
+
print_formatted_text(ANSI(self.strip_unknown_escapes(self.strip_trailing_pad(capture.get()))), end="", flush=True)
|
|
6701
6717
|
|
|
6702
6718
|
@staticmethod
|
|
6703
6719
|
def tab_segments(titles: tuple[str, ...], active: int) -> list[tuple[str, str]]:
|
|
@@ -6904,18 +6920,35 @@ class UiPrinter:
|
|
|
6904
6920
|
DIFF_GUTTER_WIDTH: ClassVar[int] = 12
|
|
6905
6921
|
|
|
6906
6922
|
def diff_segments(self, text: str, row_width: int | None = None) -> list[tuple[str, str]]:
|
|
6923
|
+
return self._diff_segments(text, row_width=row_width, live=False)
|
|
6924
|
+
|
|
6925
|
+
def diff_segments_live(self, text: str, row_width: int | None = None) -> list[tuple[str, str]]:
|
|
6926
|
+
"""Same as diff_segments, but pads the bg band to the current pane width. Only for live
|
|
6927
|
+
full-screen renderers that repaint on resize (the `/diff` viewer). Scrollback callers must
|
|
6928
|
+
NOT use this — baked-in wide padding wraps on a later pane shrink and drops the bg color on
|
|
6929
|
+
the wrapped continuation, which looks broken."""
|
|
6930
|
+
return self._diff_segments(text, row_width=row_width, live=True)
|
|
6931
|
+
|
|
6932
|
+
def _diff_segments(self, text: str, *, row_width: int | None, live: bool) -> list[tuple[str, str]]:
|
|
6907
6933
|
segments: list[tuple[str, str]] = []
|
|
6908
6934
|
old_line: int | None = None
|
|
6909
6935
|
new_line: int | None = None
|
|
6910
6936
|
lines = text.splitlines()
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
# trees, tree edges) so the padding doesn't overflow and wrap onto a phantom second row.
|
|
6937
|
+
natural_changed_width = max(
|
|
6938
|
+
(get_cwidth(line) for line in lines if line.startswith(("+", "-")) and not line.startswith(("+++", "---"))),
|
|
6939
|
+
default=1,
|
|
6940
|
+
)
|
|
6916
6941
|
if row_width is None:
|
|
6917
6942
|
row_width = shutil.get_terminal_size((120, 20)).columns - 3
|
|
6918
|
-
|
|
6943
|
+
available_changed_width = max(1, row_width - self.DIFF_GUTTER_WIDTH)
|
|
6944
|
+
# Scrollback path: cap the bg band at the widest actual changed line, and drop bg entirely
|
|
6945
|
+
# if that would already exceed the pane — a later resize can't wrap what wasn't padded. Live
|
|
6946
|
+
# path (the /diff viewer): fill edge-to-edge with the current pane width; the viewer repaints
|
|
6947
|
+
# on resize, so wide padding stays fresh.
|
|
6948
|
+
if live:
|
|
6949
|
+
changed_width: int | None = available_changed_width
|
|
6950
|
+
else:
|
|
6951
|
+
changed_width = natural_changed_width if natural_changed_width <= available_changed_width else None
|
|
6919
6952
|
|
|
6920
6953
|
# Determine the target file path from the diff header. The `+++` line
|
|
6921
6954
|
# names the resulting file; for created files `---` is /dev/null.
|
|
@@ -6973,7 +7006,7 @@ class UiPrinter:
|
|
|
6973
7006
|
for style, piece in content_hl:
|
|
6974
7007
|
segments.append((styled(style), piece))
|
|
6975
7008
|
width = get_cwidth(prefix) + sum(get_cwidth(piece) for _style, piece in content_hl)
|
|
6976
|
-
padding = " " * max(0, changed_width - width) if background else ""
|
|
7009
|
+
padding = " " * max(0, changed_width - width) if background and changed_width is not None else ""
|
|
6977
7010
|
segments.append((background if padding else "", padding + suffix))
|
|
6978
7011
|
|
|
6979
7012
|
for index, line in enumerate(lines):
|
|
@@ -7578,6 +7611,8 @@ class CommandLoop:
|
|
|
7578
7611
|
"Scaffold a fresh config with `nanocode --init-config`.",
|
|
7579
7612
|
"Launch with `--yolo` to skip confirmations; `/debug` shows recent diagnostics.",
|
|
7580
7613
|
'Filter MCP servers at launch with `--mcp "name*,!exclude"`.',
|
|
7614
|
+
# Rendering behavior
|
|
7615
|
+
"Markdown links render as `text (url)` — clickable hyperlinks are disabled so the URL stays visible; most terminals auto-link the bare URL.",
|
|
7581
7616
|
)
|
|
7582
7617
|
|
|
7583
7618
|
def startup_tip(self) -> str:
|
|
@@ -7789,9 +7824,7 @@ Tools:
|
|
|
7789
7824
|
"class:divider.glow4",
|
|
7790
7825
|
)
|
|
7791
7826
|
|
|
7792
|
-
def sweep_divider_fragments(
|
|
7793
|
-
self, label: str, width: int | None = None, prefix: list[tuple[str, str]] | None = None
|
|
7794
|
-
) -> list[tuple[str, str]]:
|
|
7827
|
+
def sweep_divider_fragments(self, label: str, width: int | None = None, prefix: list[tuple[str, str]] | None = None) -> list[tuple[str, str]]:
|
|
7795
7828
|
prefix = prefix or []
|
|
7796
7829
|
prefix_len = sum(len(text) for _style, text in prefix)
|
|
7797
7830
|
cols = shutil.get_terminal_size((80, 20)).columns
|
|
@@ -8863,7 +8896,7 @@ Tools:
|
|
|
8863
8896
|
status, path, diff = sections[state.file]
|
|
8864
8897
|
parts.append(("", "\n"))
|
|
8865
8898
|
parts.append(("ansicyan", f" {status.title()} · {path}\n"))
|
|
8866
|
-
lines = self.ui.segment_lines(self.ui.
|
|
8899
|
+
lines = self.ui.segment_lines(self.ui.diff_segments_live(diff))
|
|
8867
8900
|
visible = state.view.visible(lines, viewport())
|
|
8868
8901
|
for line in visible:
|
|
8869
8902
|
parts.extend(line)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|