nanocode-cli 0.5.9__tar.gz → 0.5.11__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.5.9/nanocode_cli.egg-info → nanocode_cli-0.5.11}/PKG-INFO +15 -3
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/README.md +13 -1
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/README.zh-CN.md +11 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/nanocode.py +67 -17
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11/nanocode_cli.egg-info}/PKG-INFO +15 -3
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/nanocode_cli.egg-info/requires.txt +1 -1
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/pyproject.toml +2 -2
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/LICENSE +0 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/MANIFEST.in +0 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/nanocode_cli.egg-info/SOURCES.txt +0 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/nanocode_cli.egg-info/dependency_links.txt +0 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/nanocode_cli.egg-info/entry_points.txt +0 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/nanocode_cli.egg-info/top_level.txt +0 -0
- {nanocode_cli-0.5.9 → nanocode_cli-0.5.11}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nanocode-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.11
|
|
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
|
|
@@ -22,7 +22,7 @@ Requires-Python: >=3.11
|
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: anthropic>=0.64.0
|
|
25
|
-
Requires-Dist: code-symbol-index>=0.
|
|
25
|
+
Requires-Dist: code-symbol-index>=0.3.0
|
|
26
26
|
Requires-Dist: json-repair
|
|
27
27
|
Requires-Dist: openai>=2.37.0
|
|
28
28
|
Requires-Dist: prompt-toolkit>=3.0
|
|
@@ -39,6 +39,8 @@ A small terminal coding agent written in Python.
|
|
|
39
39
|
|
|
40
40
|
[简体中文](README.zh-CN.md)
|
|
41
41
|
|
|
42
|
+
[Chinese blog](https://hit9.dev/post/nanocode)
|
|
43
|
+
|
|
42
44
|
nanocode is pre-1.0 software. Commands, configuration, and tool behavior may change before a stable release.
|
|
43
45
|
|
|
44
46
|

|
|
@@ -48,7 +50,7 @@ nanocode is pre-1.0 software. Commands, configuration, and tool behavior may cha
|
|
|
48
50
|
- **Live turn control**: Add follow-up input while the agent is still working, without losing the current tool flow.
|
|
49
51
|
- **File-state brain**: Reads and edits build a current, line-numbered view of the files that matter now.
|
|
50
52
|
- **Stale-edit protection**: `line:hash` anchors reject edits when the target code has drifted.
|
|
51
|
-
- **Project-aware navigation**: Use the symbol index to jump through outlines, references, and changed files quickly.
|
|
53
|
+
- **Project-aware navigation**: Use the symbol index to jump through outlines, references, implementors, call chains, and changed files quickly.
|
|
52
54
|
- **Recoverable context**: Tool output stays bounded in the prompt, while raw `tr.N` results remain recallable.
|
|
53
55
|
- **Cache-aware context**: Stable sections stay early and noisy working state stays late to improve prompt-cache reuse.
|
|
54
56
|
- **Focused working memory**: `Note` separates goal, plan, and known facts from noisy execution logs.
|
|
@@ -138,6 +140,16 @@ Main fields:
|
|
|
138
140
|
|
|
139
141
|
`api = "auto"` chooses between Chat Completions and Anthropic Messages using provider/model profiles. `prompt_cache_key = "auto"` derives a stable key from provider, model, workspace, and tool schema names.
|
|
140
142
|
|
|
143
|
+
|
|
144
|
+
## Tested Providers
|
|
145
|
+
|
|
146
|
+
The following providers have been tested with nanocode:
|
|
147
|
+
|
|
148
|
+
- **deepseek**: DeepSeek API
|
|
149
|
+
- **opencode**: OpenCode API
|
|
150
|
+
- **aliyun**: Alibaba Cloud (Tongyi Qianwen) API via Chat Completions
|
|
151
|
+
- **llama.cpp**: Local inference via llama.cpp server
|
|
152
|
+
|
|
141
153
|
## Context Design
|
|
142
154
|
|
|
143
155
|
Each model request is built manually from explicit messages. Stable context comes first, conversation stays as messages, working memory follows, and the latest file state is appended at the end.
|
|
@@ -4,6 +4,8 @@ A small terminal coding agent written in Python.
|
|
|
4
4
|
|
|
5
5
|
[简体中文](README.zh-CN.md)
|
|
6
6
|
|
|
7
|
+
[Chinese blog](https://hit9.dev/post/nanocode)
|
|
8
|
+
|
|
7
9
|
nanocode is pre-1.0 software. Commands, configuration, and tool behavior may change before a stable release.
|
|
8
10
|
|
|
9
11
|

|
|
@@ -13,7 +15,7 @@ nanocode is pre-1.0 software. Commands, configuration, and tool behavior may cha
|
|
|
13
15
|
- **Live turn control**: Add follow-up input while the agent is still working, without losing the current tool flow.
|
|
14
16
|
- **File-state brain**: Reads and edits build a current, line-numbered view of the files that matter now.
|
|
15
17
|
- **Stale-edit protection**: `line:hash` anchors reject edits when the target code has drifted.
|
|
16
|
-
- **Project-aware navigation**: Use the symbol index to jump through outlines, references, and changed files quickly.
|
|
18
|
+
- **Project-aware navigation**: Use the symbol index to jump through outlines, references, implementors, call chains, and changed files quickly.
|
|
17
19
|
- **Recoverable context**: Tool output stays bounded in the prompt, while raw `tr.N` results remain recallable.
|
|
18
20
|
- **Cache-aware context**: Stable sections stay early and noisy working state stays late to improve prompt-cache reuse.
|
|
19
21
|
- **Focused working memory**: `Note` separates goal, plan, and known facts from noisy execution logs.
|
|
@@ -103,6 +105,16 @@ Main fields:
|
|
|
103
105
|
|
|
104
106
|
`api = "auto"` chooses between Chat Completions and Anthropic Messages using provider/model profiles. `prompt_cache_key = "auto"` derives a stable key from provider, model, workspace, and tool schema names.
|
|
105
107
|
|
|
108
|
+
|
|
109
|
+
## Tested Providers
|
|
110
|
+
|
|
111
|
+
The following providers have been tested with nanocode:
|
|
112
|
+
|
|
113
|
+
- **deepseek**: DeepSeek API
|
|
114
|
+
- **opencode**: OpenCode API
|
|
115
|
+
- **aliyun**: Alibaba Cloud (Tongyi Qianwen) API via Chat Completions
|
|
116
|
+
- **llama.cpp**: Local inference via llama.cpp server
|
|
117
|
+
|
|
106
118
|
## Context Design
|
|
107
119
|
|
|
108
120
|
Each model request is built manually from explicit messages. Stable context comes first, conversation stays as messages, working memory follows, and the latest file state is appended at the end.
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
[English](README.md)
|
|
6
6
|
|
|
7
|
+
[中文博客](https://hit9.dev/post/nanocode)
|
|
8
|
+
|
|
7
9
|
nanocode 仍是 1.0 前的软件。稳定版发布前,命令、配置和工具行为都可能变化。
|
|
8
10
|
|
|
9
11
|

|
|
@@ -103,6 +105,15 @@ nanocode --init-config
|
|
|
103
105
|
|
|
104
106
|
`api = "auto"` 会根据 provider/model profile 在 Chat Completions 和 Anthropic Messages 之间选择。`prompt_cache_key = "auto"` 会根据 provider、model、workspace 和工具 schema 名称生成稳定 key。
|
|
105
107
|
|
|
108
|
+
|
|
109
|
+
## 已测试的 Provider
|
|
110
|
+
|
|
111
|
+
以下 provider 已在 nanocode 中测试通过:
|
|
112
|
+
|
|
113
|
+
- **deepseek**: DeepSeek API
|
|
114
|
+
- **opencode**: OpenCode API
|
|
115
|
+
- **aliyun**: 阿里云通义千问 API(Chat Completions)
|
|
116
|
+
- **llama.cpp**: 通过 llama.cpp 服务端本地推理
|
|
106
117
|
## 上下文设计
|
|
107
118
|
|
|
108
119
|
每次模型请求都由 nanocode 手动构建成明确的 messages。稳定上下文在前,会话作为 messages 保留,工作记忆随后,最新文件状态放在末尾。
|
|
@@ -55,7 +55,7 @@ from prompt_toolkit.widgets import SearchToolbar
|
|
|
55
55
|
from rich.console import Console
|
|
56
56
|
from rich.markdown import Markdown
|
|
57
57
|
|
|
58
|
-
__version__ = "0.5.
|
|
58
|
+
__version__ = "0.5.11"
|
|
59
59
|
|
|
60
60
|
Json = dict[str, Any]
|
|
61
61
|
HTTP_USER_AGENT = "nanocode/" + __version__
|
|
@@ -553,6 +553,7 @@ class Session:
|
|
|
553
553
|
tool_counter: int = 0
|
|
554
554
|
usage: ModelUsage = field(default_factory=ModelUsage)
|
|
555
555
|
update: UpdateStatus = field(default_factory=UpdateStatus)
|
|
556
|
+
_gitignore_cache: dict[str, tuple[int, list[str]]] = field(default_factory=dict)
|
|
556
557
|
|
|
557
558
|
def __post_init__(self) -> None:
|
|
558
559
|
if self.system_info is None:
|
|
@@ -759,12 +760,23 @@ class Tool:
|
|
|
759
760
|
|
|
760
761
|
def gitignore_patterns(self, root: str) -> list[str]:
|
|
761
762
|
patterns = []
|
|
762
|
-
|
|
763
|
+
cache = self.session._gitignore_cache
|
|
764
|
+
paths = [os.path.join(self.session.cwd, ".gitignore")]
|
|
765
|
+
if os.path.isdir(root):
|
|
766
|
+
paths.append(os.path.join(root, ".gitignore"))
|
|
767
|
+
for path in dict.fromkeys(paths):
|
|
763
768
|
try:
|
|
769
|
+
mtime = os.stat(path).st_mtime_ns
|
|
770
|
+
cached = cache.get(path)
|
|
771
|
+
if cached is not None and cached[0] == mtime:
|
|
772
|
+
patterns.extend(cached[1])
|
|
773
|
+
continue
|
|
764
774
|
with open(path, encoding="utf-8") as file:
|
|
765
|
-
|
|
775
|
+
pats = [line.strip() for line in file if line.strip() and not line.lstrip().startswith("#") and not line.startswith("!")]
|
|
776
|
+
cache[path] = (mtime, pats)
|
|
777
|
+
patterns.extend(pats)
|
|
766
778
|
except OSError:
|
|
767
|
-
|
|
779
|
+
cache.pop(path, None)
|
|
768
780
|
return patterns
|
|
769
781
|
|
|
770
782
|
def ignored(self, path: str, patterns: list[str]) -> bool:
|
|
@@ -1435,31 +1447,44 @@ class InspectCodeTool(Tool):
|
|
|
1435
1447
|
NAME = "InspectCode"
|
|
1436
1448
|
MAX_LIMIT: ClassVar[int] = 80
|
|
1437
1449
|
MAX_OUTLINE_LIMIT: ClassVar[int] = 1000
|
|
1438
|
-
|
|
1439
|
-
|
|
1450
|
+
MAX_DEPTH: ClassVar[int] = 5
|
|
1451
|
+
MODES: ClassVar[tuple[str, ...]] = ("find", "inspect", "outline", "refs", "impls", "callers", "callees")
|
|
1452
|
+
SYMBOL_MODES: ClassVar[frozenset[str]] = frozenset({"find", "inspect", "refs", "impls", "callers", "callees"})
|
|
1453
|
+
RESOLVE_MODES: ClassVar[frozenset[str]] = frozenset({"inspect", "refs", "impls", "callers", "callees"})
|
|
1454
|
+
CHAIN_MODES: ClassVar[frozenset[str]] = frozenset({"callers", "callees"})
|
|
1455
|
+
OPTION_KEYS: ClassVar[tuple[str, ...]] = ("limit", "kind", "path", "symbol", "exact_only", "depth", "offset", "all_kinds", "ref_kind", "loose")
|
|
1456
|
+
DESCRIPTION = "Use the code index: find returns symbols; inspect returns anchors/members/references; outline returns a file symbol tree; refs lists classified references; impls lists implementors; callers/callees walk the call chain."
|
|
1457
|
+
SIGNATURE = "InspectCode(mode,target,kind?,path?,symbol?,limit?,exact_only?,depth?,offset?,all_kinds?,ref_kind?,loose?)"
|
|
1440
1458
|
EXAMPLE = (
|
|
1441
1459
|
'Find symbols; kind can be class|function|method|variable|constant|enum|struct|interface|module|type|trait|field|property|impl|namespace|dict_key, comma-ok. Example: {"mode":"find","target":"Tool","kind":"class,function","limit":20}',
|
|
1442
|
-
'Find exact symbol in a path. Example: {"mode":"find","target":"Session","path":"src","exact_only":true,"limit":10}',
|
|
1443
1460
|
'Inspect one symbol; path narrows candidates. Example: {"mode":"inspect","target":"Tool","path":"src/app.py"}',
|
|
1444
1461
|
'Outline one file; symbol narrows subtree. Example: {"mode":"outline","target":"src/app.py","symbol":"App","limit":300}',
|
|
1462
|
+
'List references; default hides import/attribute noise, ref_kind filters to call|read|write|inherit|type|import|attribute|usage (comma-ok), all_kinds shows everything, offset pages. Example: {"mode":"refs","target":"Tool","ref_kind":"call,write","offset":0}',
|
|
1463
|
+
'List implementors of an interface/base. Example: {"mode":"impls","target":"Tool","kind":"class"}',
|
|
1464
|
+
'Walk transitive callers/callees up to depth; callees loose includes ambiguous cross-module matches. Example: {"mode":"callers","target":"handle_job","depth":3}',
|
|
1445
1465
|
)
|
|
1446
1466
|
|
|
1447
1467
|
@classmethod
|
|
1448
1468
|
def params_schema(cls) -> Json:
|
|
1449
1469
|
props = {
|
|
1450
|
-
"mode": {"type": "string", "enum":
|
|
1470
|
+
"mode": {"type": "string", "enum": list(cls.MODES)},
|
|
1451
1471
|
"target": {"type": "string"},
|
|
1452
1472
|
"limit": {"type": "integer", "minimum": 1, "maximum": cls.MAX_OUTLINE_LIMIT},
|
|
1453
1473
|
"kind": {"type": "string"},
|
|
1454
1474
|
"path": {"type": "string"},
|
|
1455
1475
|
"symbol": {"type": "string"},
|
|
1456
1476
|
"exact_only": {"type": "boolean"},
|
|
1477
|
+
"depth": {"type": "integer", "minimum": 1, "maximum": cls.MAX_DEPTH},
|
|
1478
|
+
"offset": {"type": "integer", "minimum": 0},
|
|
1479
|
+
"all_kinds": {"type": "boolean"},
|
|
1480
|
+
"ref_kind": {"type": "string"},
|
|
1481
|
+
"loose": {"type": "boolean"},
|
|
1457
1482
|
}
|
|
1458
1483
|
return {"type": "object", "properties": props, "required": ["mode", "target"], "additionalProperties": False}
|
|
1459
1484
|
|
|
1460
1485
|
@classmethod
|
|
1461
1486
|
def payload_args(cls, payload: Json) -> list[Any]:
|
|
1462
|
-
options = {key: payload[key] for key in
|
|
1487
|
+
options = {key: payload[key] for key in cls.OPTION_KEYS if key in payload}
|
|
1463
1488
|
return [str(payload.get("mode") or ""), str(payload.get("target") or ""), *([options] if options else [])]
|
|
1464
1489
|
|
|
1465
1490
|
def call(self) -> str:
|
|
@@ -1471,22 +1496,37 @@ class InspectCodeTool(Tool):
|
|
|
1471
1496
|
if len(self.args) == 3 and not isinstance(self.args[2], dict):
|
|
1472
1497
|
raise ToolError("InspectCode options must be an object")
|
|
1473
1498
|
options = self.args[2] if len(self.args) == 3 else {}
|
|
1474
|
-
if unexpected := sorted(set(options) -
|
|
1499
|
+
if unexpected := sorted(set(options) - set(self.OPTION_KEYS)):
|
|
1475
1500
|
raise ToolError("InspectCode unexpected option: " + ", ".join(unexpected))
|
|
1476
|
-
if mode not in
|
|
1477
|
-
raise ToolError("InspectCode mode must be
|
|
1501
|
+
if mode not in self.MODES:
|
|
1502
|
+
raise ToolError("InspectCode mode must be one of: " + ", ".join(self.MODES))
|
|
1478
1503
|
if not target:
|
|
1479
1504
|
raise ToolError("InspectCode target is required")
|
|
1480
|
-
if mode in
|
|
1505
|
+
if mode in self.SYMBOL_MODES and re.search(r"\s", target):
|
|
1481
1506
|
raise ToolError("InspectCode symbol target must not contain whitespace")
|
|
1482
|
-
if mode
|
|
1483
|
-
raise ToolError("InspectCode
|
|
1507
|
+
if mode in self.RESOLVE_MODES and (target.endswith(".py") or os.path.exists(self.session.resolve_path(target))):
|
|
1508
|
+
raise ToolError(f"InspectCode {mode} target must be a symbol, not a file")
|
|
1484
1509
|
if mode == "outline" and not os.path.isfile(self.session.resolve_path(target)):
|
|
1485
1510
|
raise ToolError("InspectCode outline target must be an existing file")
|
|
1486
1511
|
limit = options.get("limit")
|
|
1487
1512
|
max_limit = self.MAX_OUTLINE_LIMIT if mode == "outline" else self.MAX_LIMIT
|
|
1488
1513
|
if limit is not None and (isinstance(limit, bool) or not isinstance(limit, int) or limit < 1 or limit > max_limit):
|
|
1489
1514
|
raise ToolError(f"InspectCode {mode} limit must be 1..{max_limit}")
|
|
1515
|
+
depth = options.get("depth")
|
|
1516
|
+
if depth is not None and (isinstance(depth, bool) or not isinstance(depth, int) or depth < 1 or depth > self.MAX_DEPTH):
|
|
1517
|
+
raise ToolError(f"InspectCode depth must be 1..{self.MAX_DEPTH}")
|
|
1518
|
+
offset = options.get("offset")
|
|
1519
|
+
if offset is not None and (isinstance(offset, bool) or not isinstance(offset, int) or offset < 0):
|
|
1520
|
+
raise ToolError("InspectCode offset must be >= 0")
|
|
1521
|
+
ref_kind = options.get("ref_kind")
|
|
1522
|
+
if ref_kind is not None:
|
|
1523
|
+
if not isinstance(ref_kind, str):
|
|
1524
|
+
raise ToolError("InspectCode ref_kind must be a string")
|
|
1525
|
+
if options.get("all_kinds"):
|
|
1526
|
+
raise ToolError("InspectCode ref_kind and all_kinds are mutually exclusive")
|
|
1527
|
+
tokens = [token.strip() for token in ref_kind.split(",") if token.strip()]
|
|
1528
|
+
if unknown := sorted(set(tokens) - csi.REFERENCE_KINDS):
|
|
1529
|
+
raise ToolError("InspectCode unknown ref_kind: " + ", ".join(unknown) + "; valid: " + ", ".join(sorted(csi.REFERENCE_KINDS)))
|
|
1490
1530
|
index = CodeIndex(self.session)
|
|
1491
1531
|
if not index.available():
|
|
1492
1532
|
raise ToolError("code index is not available; run /index")
|
|
@@ -1508,6 +1548,16 @@ class InspectCodeTool(Tool):
|
|
|
1508
1548
|
return csi.search(target, limit=limit or csi.DEFAULT_SEARCH_LIMIT, **common)
|
|
1509
1549
|
if mode == "inspect":
|
|
1510
1550
|
return csi.inspect(target, limit=limit or csi.DEFAULT_PAGE_LIMIT, anchors=True, **common)
|
|
1551
|
+
if mode == "refs":
|
|
1552
|
+
ref_kinds = options.get("ref_kind") or ("all" if options.get("all_kinds") else "behavioral")
|
|
1553
|
+
return csi.refs(target, limit=limit or csi.DEFAULT_MAX_REFERENCES, offset=int(options.get("offset") or 0), ref_kinds=ref_kinds, **common)
|
|
1554
|
+
if mode == "impls":
|
|
1555
|
+
return csi.impls(target, limit=limit or csi.DEFAULT_MAX_IMPLEMENTORS, offset=int(options.get("offset") or 0), **common)
|
|
1556
|
+
if mode in self.CHAIN_MODES:
|
|
1557
|
+
depth = int(options.get("depth") or 3)
|
|
1558
|
+
if mode == "callees":
|
|
1559
|
+
return csi.callees(target, limit=limit or csi.DEFAULT_MAX_CALLEES, depth=depth, loose=bool(options.get("loose")), **common)
|
|
1560
|
+
return csi.callers(target, limit=limit or csi.DEFAULT_MAX_CALLERS, depth=depth, **common)
|
|
1511
1561
|
symbol = options.get("symbol") or None
|
|
1512
1562
|
return csi.outline(
|
|
1513
1563
|
target, root=self.session.cwd, symbol=str(symbol) if symbol else None, max_symbols=limit or csi.DEFAULT_MAX_OUTLINE_SYMBOLS, format="text"
|
|
@@ -1746,7 +1796,7 @@ class EditTool(Tool):
|
|
|
1746
1796
|
|
|
1747
1797
|
class BashTool(Tool):
|
|
1748
1798
|
NAME = "Bash"
|
|
1749
|
-
DESCRIPTION = "Run one bash shell invocation in the workspace; returns exit_code/stdout/stderr and shows live output."
|
|
1799
|
+
DESCRIPTION = "Run one bash shell invocation in the workspace; returns exit_code/stdout/stderr and shows live output. Avoid unbounded output; limit noisy commands with head/tail/sed/rg filters or command-specific limits, and inspect large outputs in chunks."
|
|
1750
1800
|
SIGNATURE = "Bash(command)"
|
|
1751
1801
|
EXAMPLE = (
|
|
1752
1802
|
'Check environment. Example: {"command":"python3 --version"}',
|
|
@@ -1873,7 +1923,7 @@ class BashTool(Tool):
|
|
|
1873
1923
|
|
|
1874
1924
|
class GitTool(Tool):
|
|
1875
1925
|
NAME = "Git"
|
|
1876
|
-
DESCRIPTION = 'Run git with explicit argv
|
|
1926
|
+
DESCRIPTION = 'Run git with explicit argv (default: cwd from Environment; use cwd= for other directories). For add, pass explicit file paths; broad add is rejected.'
|
|
1877
1927
|
SIGNATURE = "Git(argv=[command,...], cwd?)"
|
|
1878
1928
|
EXAMPLE = (
|
|
1879
1929
|
'Status. Example: {"argv":["status","--short"]}',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nanocode-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.11
|
|
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
|
|
@@ -22,7 +22,7 @@ Requires-Python: >=3.11
|
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
License-File: LICENSE
|
|
24
24
|
Requires-Dist: anthropic>=0.64.0
|
|
25
|
-
Requires-Dist: code-symbol-index>=0.
|
|
25
|
+
Requires-Dist: code-symbol-index>=0.3.0
|
|
26
26
|
Requires-Dist: json-repair
|
|
27
27
|
Requires-Dist: openai>=2.37.0
|
|
28
28
|
Requires-Dist: prompt-toolkit>=3.0
|
|
@@ -39,6 +39,8 @@ A small terminal coding agent written in Python.
|
|
|
39
39
|
|
|
40
40
|
[简体中文](README.zh-CN.md)
|
|
41
41
|
|
|
42
|
+
[Chinese blog](https://hit9.dev/post/nanocode)
|
|
43
|
+
|
|
42
44
|
nanocode is pre-1.0 software. Commands, configuration, and tool behavior may change before a stable release.
|
|
43
45
|
|
|
44
46
|

|
|
@@ -48,7 +50,7 @@ nanocode is pre-1.0 software. Commands, configuration, and tool behavior may cha
|
|
|
48
50
|
- **Live turn control**: Add follow-up input while the agent is still working, without losing the current tool flow.
|
|
49
51
|
- **File-state brain**: Reads and edits build a current, line-numbered view of the files that matter now.
|
|
50
52
|
- **Stale-edit protection**: `line:hash` anchors reject edits when the target code has drifted.
|
|
51
|
-
- **Project-aware navigation**: Use the symbol index to jump through outlines, references, and changed files quickly.
|
|
53
|
+
- **Project-aware navigation**: Use the symbol index to jump through outlines, references, implementors, call chains, and changed files quickly.
|
|
52
54
|
- **Recoverable context**: Tool output stays bounded in the prompt, while raw `tr.N` results remain recallable.
|
|
53
55
|
- **Cache-aware context**: Stable sections stay early and noisy working state stays late to improve prompt-cache reuse.
|
|
54
56
|
- **Focused working memory**: `Note` separates goal, plan, and known facts from noisy execution logs.
|
|
@@ -138,6 +140,16 @@ Main fields:
|
|
|
138
140
|
|
|
139
141
|
`api = "auto"` chooses between Chat Completions and Anthropic Messages using provider/model profiles. `prompt_cache_key = "auto"` derives a stable key from provider, model, workspace, and tool schema names.
|
|
140
142
|
|
|
143
|
+
|
|
144
|
+
## Tested Providers
|
|
145
|
+
|
|
146
|
+
The following providers have been tested with nanocode:
|
|
147
|
+
|
|
148
|
+
- **deepseek**: DeepSeek API
|
|
149
|
+
- **opencode**: OpenCode API
|
|
150
|
+
- **aliyun**: Alibaba Cloud (Tongyi Qianwen) API via Chat Completions
|
|
151
|
+
- **llama.cpp**: Local inference via llama.cpp server
|
|
152
|
+
|
|
141
153
|
## Context Design
|
|
142
154
|
|
|
143
155
|
Each model request is built manually from explicit messages. Stable context comes first, conversation stays as messages, working memory follows, and the latest file state is appended at the end.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nanocode-cli"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.11"
|
|
8
8
|
description = "A small terminal coding agent written in Python"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
]
|
|
29
29
|
dependencies = [
|
|
30
30
|
"anthropic>=0.64.0",
|
|
31
|
-
"code-symbol-index>=0.
|
|
31
|
+
"code-symbol-index>=0.3.0",
|
|
32
32
|
"json-repair",
|
|
33
33
|
"openai>=2.37.0",
|
|
34
34
|
"prompt-toolkit>=3.0",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|