nanocode-cli 0.2.8__tar.gz → 0.2.9__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.2.8/nanocode_cli.egg-info → nanocode_cli-0.2.9}/PKG-INFO +36 -24
- nanocode_cli-0.2.9/README.md +93 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/nanocode.py +78 -10
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9/nanocode_cli.egg-info}/PKG-INFO +36 -24
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/pyproject.toml +1 -1
- nanocode_cli-0.2.8/README.md +0 -81
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/LICENSE +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/MANIFEST.in +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/nanocode_cli.egg-info/SOURCES.txt +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/nanocode_cli.egg-info/dependency_links.txt +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/nanocode_cli.egg-info/entry_points.txt +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/nanocode_cli.egg-info/requires.txt +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/nanocode_cli.egg-info/top_level.txt +0 -0
- {nanocode_cli-0.2.8 → nanocode_cli-0.2.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nanocode-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: A lightweight terminal-based AI coding assistant
|
|
5
5
|
Author-email: hit9 <hit9@icloud.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -41,6 +41,41 @@ nanocode is used to help building itself, including features such as `@file` pat
|
|
|
41
41
|
|---|---|
|
|
42
42
|
|  |  |
|
|
43
43
|
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Constrained Output**: Force model replies into auditable action frames.
|
|
47
|
+
- **Verified Edits**: Reject stale range edits before they touch files.
|
|
48
|
+
- **Autonomous Loop**: Chain reading, editing, running, and verification.
|
|
49
|
+
- **Live Telemetry**: Stream tool intent, token use, cost, and status.
|
|
50
|
+
|
|
51
|
+
## Tools
|
|
52
|
+
|
|
53
|
+
- File: `Read`, `LineCount`, `ListDir`, `Search`.
|
|
54
|
+
- Edit: `Edit`, `ReplaceRange`, `BatchReplaceRanges`, `ApplyPatch`.
|
|
55
|
+
- Shell: `Bash`, `Git`.
|
|
56
|
+
- Memory: `Blackboard`.
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
- Info: `/help [question]`, `/status`.
|
|
61
|
+
- Session: `/compact`, `/blackboard [status|clear]`.
|
|
62
|
+
- Config: `/model`, `/compact-at`, `/reason`, `/reason_effort`, `/stream`, `/yolo`.
|
|
63
|
+
- Exit: `/exit`, `/quit`.
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
- Required: `NANOCODE_API_URL`, `NANOCODE_API_KEY`, `NANOCODE_MODEL`.
|
|
68
|
+
- Runtime: `NANOCODE_DIR`, `NANOCODE_TEMPERATURE`, `NANOCODE_STREAM`.
|
|
69
|
+
- Reasoning: `NANOCODE_REASONING`, `NANOCODE_REASONING_EFFORT`.
|
|
70
|
+
- Limits: `NANOCODE_MODEL_TIMEOUT`, `NANOCODE_SHELL_TIMEOUT`, `NANOCODE_COMPACT_AT`, `NANOCODE_MAX_AGENT_STEPS`.
|
|
71
|
+
- Cost: `NANOCODE_PROMPT_PRICE_PER_1M_TOKENS`, `NANOCODE_COMPLETION_PRICE_PER_1M_TOKENS`.
|
|
72
|
+
|
|
73
|
+
## Status
|
|
74
|
+
|
|
75
|
+
- Status bar: model, reasoning, context, tokens/cost, blackboard, elapsed time, and active model-call time.
|
|
76
|
+
- `/status`: model, reasoning, stream, yolo, conversation, tokens/cost, blackboard, goal, and verification.
|
|
77
|
+
|
|
78
|
+
|
|
44
79
|
## Install
|
|
45
80
|
|
|
46
81
|
```sh
|
|
@@ -60,29 +95,6 @@ uv sync --extra dev
|
|
|
60
95
|
uv run nanocode
|
|
61
96
|
```
|
|
62
97
|
|
|
63
|
-
## Environment Variables
|
|
64
|
-
|
|
65
|
-
Required:
|
|
66
|
-
|
|
67
|
-
```sh
|
|
68
|
-
export NANOCODE_API_URL="https://api.example.com/v1"
|
|
69
|
-
export NANOCODE_API_KEY="your-api-key"
|
|
70
|
-
export NANOCODE_MODEL="your-model"
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Optional:
|
|
74
|
-
|
|
75
|
-
```sh
|
|
76
|
-
export NANOCODE_DIR=".nanocode"
|
|
77
|
-
export NANOCODE_TEMPERATURE="0.7"
|
|
78
|
-
export NANOCODE_REASONING="on"
|
|
79
|
-
export NANOCODE_REASONING_EFFORT="medium"
|
|
80
|
-
export NANOCODE_STREAM="on"
|
|
81
|
-
export NANOCODE_MODEL_TIMEOUT="60"
|
|
82
|
-
export NANOCODE_SHELL_TIMEOUT="60"
|
|
83
|
-
export NANOCODE_COMPACT_AT="100"
|
|
84
|
-
```
|
|
85
|
-
|
|
86
98
|
## Usage
|
|
87
99
|
|
|
88
100
|
Start nanocode:
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# nanocode
|
|
2
|
+
|
|
3
|
+
A lightweight terminal-based AI coding assistant.
|
|
4
|
+
|
|
5
|
+
nanocode is used to help building itself, including features such as `@file` path completion.
|
|
6
|
+
|
|
7
|
+
## Screenshots
|
|
8
|
+
|
|
9
|
+
| | |
|
|
10
|
+
|---|---|
|
|
11
|
+
|  |  |
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Constrained Output**: Force model replies into auditable action frames.
|
|
16
|
+
- **Verified Edits**: Reject stale range edits before they touch files.
|
|
17
|
+
- **Autonomous Loop**: Chain reading, editing, running, and verification.
|
|
18
|
+
- **Live Telemetry**: Stream tool intent, token use, cost, and status.
|
|
19
|
+
|
|
20
|
+
## Tools
|
|
21
|
+
|
|
22
|
+
- File: `Read`, `LineCount`, `ListDir`, `Search`.
|
|
23
|
+
- Edit: `Edit`, `ReplaceRange`, `BatchReplaceRanges`, `ApplyPatch`.
|
|
24
|
+
- Shell: `Bash`, `Git`.
|
|
25
|
+
- Memory: `Blackboard`.
|
|
26
|
+
|
|
27
|
+
## Commands
|
|
28
|
+
|
|
29
|
+
- Info: `/help [question]`, `/status`.
|
|
30
|
+
- Session: `/compact`, `/blackboard [status|clear]`.
|
|
31
|
+
- Config: `/model`, `/compact-at`, `/reason`, `/reason_effort`, `/stream`, `/yolo`.
|
|
32
|
+
- Exit: `/exit`, `/quit`.
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
- Required: `NANOCODE_API_URL`, `NANOCODE_API_KEY`, `NANOCODE_MODEL`.
|
|
37
|
+
- Runtime: `NANOCODE_DIR`, `NANOCODE_TEMPERATURE`, `NANOCODE_STREAM`.
|
|
38
|
+
- Reasoning: `NANOCODE_REASONING`, `NANOCODE_REASONING_EFFORT`.
|
|
39
|
+
- Limits: `NANOCODE_MODEL_TIMEOUT`, `NANOCODE_SHELL_TIMEOUT`, `NANOCODE_COMPACT_AT`, `NANOCODE_MAX_AGENT_STEPS`.
|
|
40
|
+
- Cost: `NANOCODE_PROMPT_PRICE_PER_1M_TOKENS`, `NANOCODE_COMPLETION_PRICE_PER_1M_TOKENS`.
|
|
41
|
+
|
|
42
|
+
## Status
|
|
43
|
+
|
|
44
|
+
- Status bar: model, reasoning, context, tokens/cost, blackboard, elapsed time, and active model-call time.
|
|
45
|
+
- `/status`: model, reasoning, stream, yolo, conversation, tokens/cost, blackboard, goal, and verification.
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
## Install
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
uv tool install nanocode-cli
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Upgrade an existing install:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
uv tool upgrade nanocode-cli
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For local development:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
uv sync --extra dev
|
|
64
|
+
uv run nanocode
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
Start nanocode:
|
|
70
|
+
|
|
71
|
+
```sh
|
|
72
|
+
nanocode
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Show available commands:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
/help
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Ask a source-aware question about nanocode itself:
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
/help how does compact work?
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Safety
|
|
88
|
+
|
|
89
|
+
nanocode does not provide sandbox protection. It can run shell commands and edit files in the environment where you start it.
|
|
90
|
+
|
|
91
|
+
If you do not fully trust the model, tools, prompts, or workspace, run nanocode inside your own sandbox, container, VM, or other isolated environment.
|
|
92
|
+
|
|
93
|
+
Use at your own risk.
|
|
@@ -42,7 +42,7 @@ from prompt_toolkit.patch_stdout import patch_stdout
|
|
|
42
42
|
|
|
43
43
|
JsonValue: TypeAlias = Any
|
|
44
44
|
Json: TypeAlias = dict[str, JsonValue]
|
|
45
|
-
__version__ = "0.2.
|
|
45
|
+
__version__ = "0.2.9"
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class Error(Exception): ...
|
|
@@ -459,6 +459,12 @@ class Session:
|
|
|
459
459
|
shell_timeout: int = field(default_factory=lambda: int(os.environ.get("NANOCODE_SHELL_TIMEOUT", "60")))
|
|
460
460
|
compact_at: int = field(default_factory=lambda: int(os.environ.get("NANOCODE_COMPACT_AT", "100")))
|
|
461
461
|
max_agent_steps: int = field(default_factory=lambda: int(os.environ.get("NANOCODE_MAX_AGENT_STEPS", "50")))
|
|
462
|
+
prompt_price_per_1m_tokens: float = field(
|
|
463
|
+
default_factory=lambda: float(os.environ.get("NANOCODE_PROMPT_PRICE_PER_1M_TOKENS", "0"))
|
|
464
|
+
)
|
|
465
|
+
completion_price_per_1m_tokens: float = field(
|
|
466
|
+
default_factory=lambda: float(os.environ.get("NANOCODE_COMPLETION_PRICE_PER_1M_TOKENS", "0"))
|
|
467
|
+
)
|
|
462
468
|
|
|
463
469
|
# ---- runtime variables ----
|
|
464
470
|
yolo: bool = False
|
|
@@ -469,9 +475,11 @@ class Session:
|
|
|
469
475
|
last_prompt_tokens: int = 0
|
|
470
476
|
last_completion_tokens: int = 0
|
|
471
477
|
last_total_tokens: int = 0
|
|
478
|
+
last_cost_usd: float = 0.0
|
|
472
479
|
session_prompt_tokens: int = 0
|
|
473
480
|
session_completion_tokens: int = 0
|
|
474
481
|
session_total_tokens: int = 0
|
|
482
|
+
session_cost_usd: float = 0.0
|
|
475
483
|
current_model_call_started_at: float = 0.0
|
|
476
484
|
|
|
477
485
|
# ---- current and conversation ---
|
|
@@ -1498,7 +1506,8 @@ class ApplyPatchTool(Tool):
|
|
|
1498
1506
|
try:
|
|
1499
1507
|
with open(self.filepath, "r", encoding="utf-8") as f:
|
|
1500
1508
|
original = f.read()
|
|
1501
|
-
|
|
1509
|
+
unified_diff, allow_compatible = self._normalized_unified_diff()
|
|
1510
|
+
new_content, _ = self._apply_unified_diff(original, unified_diff, allow_compatible=allow_compatible)
|
|
1502
1511
|
except (OSError, ToolCallError) as error:
|
|
1503
1512
|
return label + "\n# preview unavailable: " + str(error)
|
|
1504
1513
|
return _make_unified_diff(original, new_content, self.filepath) or label
|
|
@@ -1506,7 +1515,8 @@ class ApplyPatchTool(Tool):
|
|
|
1506
1515
|
def call(self) -> str:
|
|
1507
1516
|
with open(self.filepath, "r", encoding="utf-8") as f:
|
|
1508
1517
|
original = f.read()
|
|
1509
|
-
|
|
1518
|
+
unified_diff, allow_compatible = self._normalized_unified_diff()
|
|
1519
|
+
new_content, hunks = self._apply_unified_diff(original, unified_diff, allow_compatible=allow_compatible)
|
|
1510
1520
|
if new_content == original:
|
|
1511
1521
|
raise ToolCallError("patch produced no changes")
|
|
1512
1522
|
with open(self.filepath, "w", encoding="utf-8") as f:
|
|
@@ -1521,12 +1531,12 @@ class ApplyPatchTool(Tool):
|
|
|
1521
1531
|
]
|
|
1522
1532
|
)
|
|
1523
1533
|
|
|
1524
|
-
def _normalized_unified_diff(self) -> str:
|
|
1534
|
+
def _normalized_unified_diff(self) -> tuple[str, bool]:
|
|
1525
1535
|
lines = self.unified_diff.splitlines(keepends=True)
|
|
1526
1536
|
begin_index = next((index for index, line in enumerate(lines) if line.strip()), -1)
|
|
1527
1537
|
if begin_index < 0 or lines[begin_index].strip() != "*** Begin Patch":
|
|
1528
|
-
return self.unified_diff
|
|
1529
|
-
return self._codex_update_patch_to_unified_diff(lines, begin_index)
|
|
1538
|
+
return self.unified_diff, False
|
|
1539
|
+
return self._codex_update_patch_to_unified_diff(lines, begin_index), True
|
|
1530
1540
|
|
|
1531
1541
|
def _codex_update_patch_to_unified_diff(self, lines: list[str], begin_index: int) -> str:
|
|
1532
1542
|
update_seen = False
|
|
@@ -1574,11 +1584,12 @@ class ApplyPatchTool(Tool):
|
|
|
1574
1584
|
return line
|
|
1575
1585
|
|
|
1576
1586
|
@staticmethod
|
|
1577
|
-
def _apply_unified_diff(content: str, unified_diff: str) -> tuple[str, int]:
|
|
1587
|
+
def _apply_unified_diff(content: str, unified_diff: str, *, allow_compatible: bool = False) -> tuple[str, int]:
|
|
1578
1588
|
lines = content.splitlines(keepends=True)
|
|
1579
1589
|
patch_lines = unified_diff.splitlines(keepends=True)
|
|
1580
1590
|
offset = 0
|
|
1581
1591
|
hunks = 0
|
|
1592
|
+
hunk_number = 0
|
|
1582
1593
|
i = 0
|
|
1583
1594
|
|
|
1584
1595
|
while i < len(patch_lines):
|
|
@@ -1600,6 +1611,7 @@ class ApplyPatchTool(Tool):
|
|
|
1600
1611
|
else:
|
|
1601
1612
|
i += 1
|
|
1602
1613
|
continue
|
|
1614
|
+
hunk_number += 1
|
|
1603
1615
|
|
|
1604
1616
|
i += 1
|
|
1605
1617
|
hunk_lines = []
|
|
@@ -1632,7 +1644,15 @@ class ApplyPatchTool(Tool):
|
|
|
1632
1644
|
raise ToolCallError("invalid hunk line")
|
|
1633
1645
|
|
|
1634
1646
|
target = -1 if fuzzy else max(old_start - 1, 0) + offset
|
|
1635
|
-
|
|
1647
|
+
try:
|
|
1648
|
+
index = ApplyPatchTool._find_hunk_position(lines, expected, target)
|
|
1649
|
+
except ToolCallError as error:
|
|
1650
|
+
if allow_compatible and str(error) == "hunk context did not match":
|
|
1651
|
+
applied_index = ApplyPatchTool._find_already_applied_hunk(lines, replacement)
|
|
1652
|
+
if applied_index is not None:
|
|
1653
|
+
hunks += 1
|
|
1654
|
+
continue
|
|
1655
|
+
raise ToolCallError(ApplyPatchTool._format_hunk_error(hunk_number, str(error), expected, replacement))
|
|
1636
1656
|
lines[index : index + len(expected)] = replacement
|
|
1637
1657
|
offset += len(replacement) - len(expected)
|
|
1638
1658
|
hunks += 1
|
|
@@ -1641,6 +1661,34 @@ class ApplyPatchTool(Tool):
|
|
|
1641
1661
|
raise ToolCallError("patch has no hunks")
|
|
1642
1662
|
return "".join(lines), hunks
|
|
1643
1663
|
|
|
1664
|
+
@staticmethod
|
|
1665
|
+
def _find_already_applied_hunk(lines: list[str], replacement: list[str]) -> int | None:
|
|
1666
|
+
if not replacement:
|
|
1667
|
+
return None
|
|
1668
|
+
matches = []
|
|
1669
|
+
last_start = len(lines) - len(replacement)
|
|
1670
|
+
for position in range(max(0, last_start + 1)):
|
|
1671
|
+
if lines[position : position + len(replacement)] == replacement:
|
|
1672
|
+
matches.append(position)
|
|
1673
|
+
if len(matches) > 1:
|
|
1674
|
+
return None
|
|
1675
|
+
return matches[0] if len(matches) == 1 else None
|
|
1676
|
+
|
|
1677
|
+
@staticmethod
|
|
1678
|
+
def _format_hunk_error(hunk_number: int, reason: str, expected: list[str], replacement: list[str]) -> str:
|
|
1679
|
+
lines = ["hunk " + str(hunk_number) + ": " + reason]
|
|
1680
|
+
if expected:
|
|
1681
|
+
lines.append("expected:")
|
|
1682
|
+
lines.extend(ApplyPatchTool._format_hunk_lines("-", expected))
|
|
1683
|
+
if replacement:
|
|
1684
|
+
lines.append("replacement:")
|
|
1685
|
+
lines.extend(ApplyPatchTool._format_hunk_lines("+", replacement))
|
|
1686
|
+
return "\n".join(lines)
|
|
1687
|
+
|
|
1688
|
+
@staticmethod
|
|
1689
|
+
def _format_hunk_lines(marker: str, lines: list[str]) -> list[str]:
|
|
1690
|
+
return [marker + _shorten(line.rstrip("\n"), 160) for line in lines[:6]]
|
|
1691
|
+
|
|
1644
1692
|
@staticmethod
|
|
1645
1693
|
def _find_hunk_position(lines: list[str], expected: list[str], target: int) -> int:
|
|
1646
1694
|
if not expected:
|
|
@@ -1899,7 +1947,7 @@ Core:
|
|
|
1899
1947
|
|
|
1900
1948
|
Tools:
|
|
1901
1949
|
- MUST use tool actions. Do not use native <tool_call>Tool(args...) syntax.
|
|
1902
|
-
- Keep each tool batch small: never more than
|
|
1950
|
+
- Keep each tool batch small: never more than 10 tool actions unless the user explicitly asked for broad parallel work.
|
|
1903
1951
|
- Use multiple tool calls in one turn only when they are independent and do not require seeing another tool's result first.
|
|
1904
1952
|
- If you need discovery (ListDir/Search/Read/LineCount), do only the necessary discovery batch, then stop and wait for results before editing, patching, testing, or cleanup.
|
|
1905
1953
|
- Do not queue speculative follow-up tools whose arguments depend on unread files, unknown search results, or unverified command output.
|
|
@@ -2404,12 +2452,17 @@ class ModelClient:
|
|
|
2404
2452
|
prompt_tokens = _json_int(usage.get("prompt_tokens"))
|
|
2405
2453
|
completion_tokens = _json_int(usage.get("completion_tokens"))
|
|
2406
2454
|
total_tokens = _json_int(usage.get("total_tokens"))
|
|
2455
|
+
prompt_cost = prompt_tokens * self.session.prompt_price_per_1m_tokens / 1_000_000
|
|
2456
|
+
completion_cost = completion_tokens * self.session.completion_price_per_1m_tokens / 1_000_000
|
|
2457
|
+
total_cost = prompt_cost + completion_cost
|
|
2407
2458
|
self.session.last_prompt_tokens = prompt_tokens
|
|
2408
2459
|
self.session.last_completion_tokens = completion_tokens
|
|
2409
2460
|
self.session.last_total_tokens = total_tokens
|
|
2461
|
+
self.session.last_cost_usd = total_cost
|
|
2410
2462
|
self.session.session_prompt_tokens += prompt_tokens
|
|
2411
2463
|
self.session.session_completion_tokens += completion_tokens
|
|
2412
2464
|
self.session.session_total_tokens += total_tokens
|
|
2465
|
+
self.session.session_cost_usd += total_cost
|
|
2413
2466
|
|
|
2414
2467
|
|
|
2415
2468
|
############################
|
|
@@ -3410,6 +3463,7 @@ class CommandDispatcher:
|
|
|
3410
3463
|
"yolo: " + yolo,
|
|
3411
3464
|
"conversation: " + str(len(session.conversation)) + "/" + str(session.compact_at),
|
|
3412
3465
|
"tokens: last=" + _format_count(session.last_total_tokens) + " session=" + _format_count(session.session_total_tokens),
|
|
3466
|
+
"cost(usd): last=" + _format_cost(session.last_cost_usd) + " session=" + _format_cost(session.session_cost_usd),
|
|
3413
3467
|
"blackboard: " + str(len(session.blackboard)) + " items",
|
|
3414
3468
|
"goal: " + (session.current.goal or "(empty)"),
|
|
3415
3469
|
"verification: " + session.current.verification.status,
|
|
@@ -3515,6 +3569,12 @@ def _format_count(value: int) -> str:
|
|
|
3515
3569
|
return str(value)
|
|
3516
3570
|
|
|
3517
3571
|
|
|
3572
|
+
def _format_cost(value: float) -> str:
|
|
3573
|
+
if value <= 0:
|
|
3574
|
+
return "-"
|
|
3575
|
+
return "$" + f"{value:.6f}"
|
|
3576
|
+
|
|
3577
|
+
|
|
3518
3578
|
############################
|
|
3519
3579
|
# Interactive Loop
|
|
3520
3580
|
############################
|
|
@@ -3610,7 +3670,15 @@ class StatusBar:
|
|
|
3610
3670
|
reasoning = session.reasoning_effort if session.reasoning else "off"
|
|
3611
3671
|
yolo = " | yolo" if session.yolo else ""
|
|
3612
3672
|
context = str(len(session.conversation)) + "/" + str(session.compact_at)
|
|
3613
|
-
|
|
3673
|
+
last_tokens = self._format_count(session.last_total_tokens)
|
|
3674
|
+
last_cost = _format_cost(session.last_cost_usd)
|
|
3675
|
+
if last_cost != "-":
|
|
3676
|
+
last_tokens += "/" + last_cost
|
|
3677
|
+
session_tokens = self._format_count(session.session_total_tokens)
|
|
3678
|
+
session_cost = _format_cost(session.session_cost_usd)
|
|
3679
|
+
if session_cost != "-":
|
|
3680
|
+
session_tokens += "/" + session_cost
|
|
3681
|
+
tokens = "last:" + last_tokens + " session:" + session_tokens
|
|
3614
3682
|
blackboard = "bb:" + str(len(session.blackboard))
|
|
3615
3683
|
parts = [model + " (" + reasoning + ")" + yolo, "ctx:" + context, "tok:" + tokens, blackboard]
|
|
3616
3684
|
if show_elapsed:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nanocode-cli
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: A lightweight terminal-based AI coding assistant
|
|
5
5
|
Author-email: hit9 <hit9@icloud.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -41,6 +41,41 @@ nanocode is used to help building itself, including features such as `@file` pat
|
|
|
41
41
|
|---|---|
|
|
42
42
|
|  |  |
|
|
43
43
|
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Constrained Output**: Force model replies into auditable action frames.
|
|
47
|
+
- **Verified Edits**: Reject stale range edits before they touch files.
|
|
48
|
+
- **Autonomous Loop**: Chain reading, editing, running, and verification.
|
|
49
|
+
- **Live Telemetry**: Stream tool intent, token use, cost, and status.
|
|
50
|
+
|
|
51
|
+
## Tools
|
|
52
|
+
|
|
53
|
+
- File: `Read`, `LineCount`, `ListDir`, `Search`.
|
|
54
|
+
- Edit: `Edit`, `ReplaceRange`, `BatchReplaceRanges`, `ApplyPatch`.
|
|
55
|
+
- Shell: `Bash`, `Git`.
|
|
56
|
+
- Memory: `Blackboard`.
|
|
57
|
+
|
|
58
|
+
## Commands
|
|
59
|
+
|
|
60
|
+
- Info: `/help [question]`, `/status`.
|
|
61
|
+
- Session: `/compact`, `/blackboard [status|clear]`.
|
|
62
|
+
- Config: `/model`, `/compact-at`, `/reason`, `/reason_effort`, `/stream`, `/yolo`.
|
|
63
|
+
- Exit: `/exit`, `/quit`.
|
|
64
|
+
|
|
65
|
+
## Configuration
|
|
66
|
+
|
|
67
|
+
- Required: `NANOCODE_API_URL`, `NANOCODE_API_KEY`, `NANOCODE_MODEL`.
|
|
68
|
+
- Runtime: `NANOCODE_DIR`, `NANOCODE_TEMPERATURE`, `NANOCODE_STREAM`.
|
|
69
|
+
- Reasoning: `NANOCODE_REASONING`, `NANOCODE_REASONING_EFFORT`.
|
|
70
|
+
- Limits: `NANOCODE_MODEL_TIMEOUT`, `NANOCODE_SHELL_TIMEOUT`, `NANOCODE_COMPACT_AT`, `NANOCODE_MAX_AGENT_STEPS`.
|
|
71
|
+
- Cost: `NANOCODE_PROMPT_PRICE_PER_1M_TOKENS`, `NANOCODE_COMPLETION_PRICE_PER_1M_TOKENS`.
|
|
72
|
+
|
|
73
|
+
## Status
|
|
74
|
+
|
|
75
|
+
- Status bar: model, reasoning, context, tokens/cost, blackboard, elapsed time, and active model-call time.
|
|
76
|
+
- `/status`: model, reasoning, stream, yolo, conversation, tokens/cost, blackboard, goal, and verification.
|
|
77
|
+
|
|
78
|
+
|
|
44
79
|
## Install
|
|
45
80
|
|
|
46
81
|
```sh
|
|
@@ -60,29 +95,6 @@ uv sync --extra dev
|
|
|
60
95
|
uv run nanocode
|
|
61
96
|
```
|
|
62
97
|
|
|
63
|
-
## Environment Variables
|
|
64
|
-
|
|
65
|
-
Required:
|
|
66
|
-
|
|
67
|
-
```sh
|
|
68
|
-
export NANOCODE_API_URL="https://api.example.com/v1"
|
|
69
|
-
export NANOCODE_API_KEY="your-api-key"
|
|
70
|
-
export NANOCODE_MODEL="your-model"
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Optional:
|
|
74
|
-
|
|
75
|
-
```sh
|
|
76
|
-
export NANOCODE_DIR=".nanocode"
|
|
77
|
-
export NANOCODE_TEMPERATURE="0.7"
|
|
78
|
-
export NANOCODE_REASONING="on"
|
|
79
|
-
export NANOCODE_REASONING_EFFORT="medium"
|
|
80
|
-
export NANOCODE_STREAM="on"
|
|
81
|
-
export NANOCODE_MODEL_TIMEOUT="60"
|
|
82
|
-
export NANOCODE_SHELL_TIMEOUT="60"
|
|
83
|
-
export NANOCODE_COMPACT_AT="100"
|
|
84
|
-
```
|
|
85
|
-
|
|
86
98
|
## Usage
|
|
87
99
|
|
|
88
100
|
Start nanocode:
|
nanocode_cli-0.2.8/README.md
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# nanocode
|
|
2
|
-
|
|
3
|
-
A lightweight terminal-based AI coding assistant.
|
|
4
|
-
|
|
5
|
-
nanocode is used to help building itself, including features such as `@file` path completion.
|
|
6
|
-
|
|
7
|
-
## Screenshots
|
|
8
|
-
|
|
9
|
-
| | |
|
|
10
|
-
|---|---|
|
|
11
|
-
|  |  |
|
|
12
|
-
|
|
13
|
-
## Install
|
|
14
|
-
|
|
15
|
-
```sh
|
|
16
|
-
uv tool install nanocode-cli
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
Upgrade an existing install:
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
uv tool upgrade nanocode-cli
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
For local development:
|
|
26
|
-
|
|
27
|
-
```sh
|
|
28
|
-
uv sync --extra dev
|
|
29
|
-
uv run nanocode
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Environment Variables
|
|
33
|
-
|
|
34
|
-
Required:
|
|
35
|
-
|
|
36
|
-
```sh
|
|
37
|
-
export NANOCODE_API_URL="https://api.example.com/v1"
|
|
38
|
-
export NANOCODE_API_KEY="your-api-key"
|
|
39
|
-
export NANOCODE_MODEL="your-model"
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Optional:
|
|
43
|
-
|
|
44
|
-
```sh
|
|
45
|
-
export NANOCODE_DIR=".nanocode"
|
|
46
|
-
export NANOCODE_TEMPERATURE="0.7"
|
|
47
|
-
export NANOCODE_REASONING="on"
|
|
48
|
-
export NANOCODE_REASONING_EFFORT="medium"
|
|
49
|
-
export NANOCODE_STREAM="on"
|
|
50
|
-
export NANOCODE_MODEL_TIMEOUT="60"
|
|
51
|
-
export NANOCODE_SHELL_TIMEOUT="60"
|
|
52
|
-
export NANOCODE_COMPACT_AT="100"
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Usage
|
|
56
|
-
|
|
57
|
-
Start nanocode:
|
|
58
|
-
|
|
59
|
-
```sh
|
|
60
|
-
nanocode
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
Show available commands:
|
|
64
|
-
|
|
65
|
-
```text
|
|
66
|
-
/help
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Ask a source-aware question about nanocode itself:
|
|
70
|
-
|
|
71
|
-
```text
|
|
72
|
-
/help how does compact work?
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Safety
|
|
76
|
-
|
|
77
|
-
nanocode does not provide sandbox protection. It can run shell commands and edit files in the environment where you start it.
|
|
78
|
-
|
|
79
|
-
If you do not fully trust the model, tools, prompts, or workspace, run nanocode inside your own sandbox, container, VM, or other isolated environment.
|
|
80
|
-
|
|
81
|
-
Use at your own risk.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|