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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nanocode-cli
3
- Version: 0.2.8
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
  | ![Screenshot 1](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot1.png) | ![Screenshot 2](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot2.png) |
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
+ | ![Screenshot 1](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot1.png) | ![Screenshot 2](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot2.png) |
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.8"
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
- new_content, _ = self._apply_unified_diff(original, self._normalized_unified_diff())
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
- new_content, hunks = self._apply_unified_diff(original, self._normalized_unified_diff())
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
- index = ApplyPatchTool._find_hunk_position(lines, expected, target)
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 5 tool actions unless the user explicitly asked for broad parallel work.
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
- tokens = "last:" + self._format_count(session.last_total_tokens) + " session:" + self._format_count(session.session_total_tokens)
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.8
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
  | ![Screenshot 1](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot1.png) | ![Screenshot 2](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot2.png) |
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:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nanocode-cli"
7
- version = "0.2.8"
7
+ version = "0.2.9"
8
8
  description = "A lightweight terminal-based AI coding assistant"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -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
- | ![Screenshot 1](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot1.png) | ![Screenshot 2](https://raw.githubusercontent.com/hit9/nanocode/master/snapshots/nanocode-snapshot2.png) |
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