nanoPyCodeAgent 0.3.0__tar.gz → 0.5.0__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.
Files changed (55) hide show
  1. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/.agents/skills/release/SKILL.md +8 -2
  2. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/.agents/skills/release/scripts/verify-release.sh +6 -1
  3. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/.gitignore +2 -0
  4. nanopycodeagent-0.5.0/.ignore +1 -0
  5. nanopycodeagent-0.5.0/.zed/debug.json +20 -0
  6. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/PKG-INFO +1 -1
  7. nanopycodeagent-0.5.0/docs/changelogs/0.4.x.md +34 -0
  8. nanopycodeagent-0.5.0/docs/changelogs/0.5.x.md +35 -0
  9. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/README.md +5 -0
  10. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/en/0.2.x.md +1 -1
  11. nanopycodeagent-0.5.0/docs/dev_notes/en/0.4.x.md +68 -0
  12. nanopycodeagent-0.5.0/docs/dev_notes/en/0.5.x.md +57 -0
  13. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/zh-CN/0.2.x.md +1 -1
  14. nanopycodeagent-0.5.0/docs/dev_notes/zh-CN/0.4.x.md +49 -0
  15. nanopycodeagent-0.5.0/docs/dev_notes/zh-CN/0.5.x.md +56 -0
  16. nanopycodeagent-0.5.0/docs/research/README.md +14 -0
  17. nanopycodeagent-0.5.0/docs/research/agent_tools.md +154 -0
  18. nanopycodeagent-0.5.0/docs/research/read_tool.md +337 -0
  19. nanopycodeagent-0.5.0/src/nanopycodeagent/agent.py +148 -0
  20. nanopycodeagent-0.5.0/src/nanopycodeagent/bash_tool.py +77 -0
  21. nanopycodeagent-0.5.0/src/nanopycodeagent/read_tool.py +176 -0
  22. nanopycodeagent-0.5.0/src/nanopycodeagent/settings.py +35 -0
  23. nanopycodeagent-0.5.0/src/nanopycodeagent/terminal.py +29 -0
  24. nanopycodeagent-0.5.0/tests/conftest.py +31 -0
  25. nanopycodeagent-0.5.0/tests/helpers.py +114 -0
  26. nanopycodeagent-0.5.0/tests/test_agent.py +235 -0
  27. nanopycodeagent-0.5.0/tests/test_bash_tool.py +62 -0
  28. nanopycodeagent-0.5.0/tests/test_read_tool.py +254 -0
  29. nanopycodeagent-0.5.0/tests/test_settings.py +102 -0
  30. nanopycodeagent-0.5.0/tests/test_terminal.py +39 -0
  31. nanopycodeagent-0.3.0/src/nanopycodeagent/agent.py +0 -181
  32. nanopycodeagent-0.3.0/tests/test_agent.py +0 -512
  33. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/.agents/skills/land-pr/SKILL.md +0 -0
  34. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/.github/workflows/release.yml +0 -0
  35. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/.python-version +0 -0
  36. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/AGENTS.md +0 -0
  37. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/CLAUDE.md +0 -0
  38. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/LICENSE +0 -0
  39. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/README.md +0 -0
  40. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/README.zh-CN.md +0 -0
  41. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/RELEASING.md +0 -0
  42. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/changelogs/0.1.x.md +0 -0
  43. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/changelogs/0.2.x.md +0 -0
  44. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/changelogs/0.3.x.md +0 -0
  45. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/changelogs/README.md +0 -0
  46. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/en/0.1.x.md +0 -0
  47. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/en/0.3.x.md +0 -0
  48. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/zh-CN/0.1.x.md +0 -0
  49. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/dev_notes/zh-CN/0.3.x.md +0 -0
  50. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/superpowers/plans/2026-06-21-release-skills.md +0 -0
  51. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/superpowers/specs/2026-06-21-release-skill-design.md +0 -0
  52. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/docs/superpowers/specs/2026-06-29-config-file-support-design.md +0 -0
  53. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/pyproject.toml +0 -0
  54. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/src/nanopycodeagent/__init__.py +0 -0
  55. {nanopycodeagent-0.3.0 → nanopycodeagent-0.5.0}/uv.lock +0 -0
@@ -50,12 +50,18 @@ Read the entries under `## [Unreleased]` and propose the next semver `X.Y.Z`:
50
50
  **0.x caveat:** on `0.y.z` a minor may still contain breaking changes; treat the
51
51
  proposal as a suggestion. The maintainer makes the final call at the gate (step 5).
52
52
 
53
- ### 4. Cut the changelog
53
+ ### 4. Cut the changelog and date the dev notes
54
54
  In `<series>`:
55
55
  - rename `## [Unreleased]` to `## [X.Y.Z] - <today YYYY-MM-DD>`, keeping its entries;
56
56
  - add a fresh empty `## [Unreleased]` section above it.
57
57
 
58
- Touch no other file — dev notes and README were synced per-PR by `land-pr`.
58
+ In `docs/dev_notes/zh-CN/<series>.md` and `docs/dev_notes/en/<series>.md`,
59
+ replace the `YYYY.MM.DD` placeholder in the `## X.Y.Z - YYYY.MM.DD` heading
60
+ with today's date (dot-separated). Skip if the dev notes have no section for
61
+ this version.
62
+
63
+ Touch no other file — dev notes content and README were synced per-PR by
64
+ `land-pr`.
59
65
 
60
66
  ### 5. ⛔ Confirmation gate (mandatory)
61
67
  Present to the maintainer: the proposed **version** `X.Y.Z` and the **changelog
@@ -60,9 +60,14 @@ grep -q '\.tar\.gz$' <<<"$assets" || fail "Release ${tag} missing an sdist (.tar
60
60
  printf 'OK: GitHub Release %s has wheel + sdist\n' "$tag"
61
61
 
62
62
  # 3. uvx smoke test (official index, with retries) ------------------------
63
+ # --default-index (not --index): --index only *adds* an index, and uv's
64
+ # first-index strategy would stop at a locally configured mirror that has the
65
+ # package but not the new version yet. --refresh: uv's cached simple-index
66
+ # metadata can be stale right after a publish. </dev/null: the entry point
67
+ # reads stdin; EOF makes it exit immediately instead of hanging interactively.
63
68
  log "Smoke-testing uvx ${PACKAGE}@${version}"
64
69
  deadline=$(( $(date +%s) + POLL_TIMEOUT ))
65
- until uvx --index "$PYPI_INDEX" --from "${PACKAGE}@${version}" "$PACKAGE" >/dev/null 2>&1; do
70
+ until uvx --refresh --default-index "$PYPI_INDEX" --from "${PACKAGE}@${version}" "$PACKAGE" >/dev/null 2>&1 </dev/null; do
66
71
  (( $(date +%s) >= deadline )) && fail "uvx smoke test failed for ${PACKAGE}@${version} after ${POLL_TIMEOUT}s"
67
72
  printf 'uvx not ready (index propagation?); retrying in %ss...\n' "$POLL_INTERVAL"; sleep "$POLL_INTERVAL"
68
73
  done
@@ -216,3 +216,5 @@ __marimo__/
216
216
 
217
217
  # Streamlit
218
218
  .streamlit/secrets.toml
219
+
220
+ references/
@@ -0,0 +1 @@
1
+ !references/
@@ -0,0 +1,20 @@
1
+ [
2
+ {
3
+ "label": "nanoPyCodeAgent: CLI",
4
+ "adapter": "Debugpy",
5
+ "request": "launch",
6
+ "program": "$ZED_WORKTREE_ROOT/.venv/bin/nanoPyCodeAgent",
7
+ "cwd": "$ZED_WORKTREE_ROOT",
8
+ "console": "integratedTerminal",
9
+ "justMyCode": true
10
+ },
11
+ {
12
+ "label": "nanoPyCodeAgent: all tests",
13
+ "adapter": "Debugpy",
14
+ "request": "launch",
15
+ "module": "pytest",
16
+ "args": ["-q"],
17
+ "cwd": "$ZED_WORKTREE_ROOT",
18
+ "justMyCode": true
19
+ }
20
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nanoPyCodeAgent
3
- Version: 0.3.0
3
+ Version: 0.5.0
4
4
  Summary: A nano code agent built from scratch in pure Python.
5
5
  Project-URL: Homepage, https://github.com/minixalpha/nanoPyCodeAgent
6
6
  Project-URL: Repository, https://github.com/minixalpha/nanoPyCodeAgent
@@ -0,0 +1,34 @@
1
+ # Changelog — 0.4.x
2
+
3
+ All notable changes in the **0.4.x** release series are documented here.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.4.0] - 2026-07-14
8
+
9
+ ### Added
10
+ - A `bash` tool with a tool-use loop: the model can now run shell commands and
11
+ read their output. Each command and its result are echoed to the terminal as
12
+ they happen; long output is truncated and hung commands are killed after a
13
+ timeout.
14
+ - Tool commands and output are shaded with a background color so they read
15
+ apart from the agent's own text (skipped when stdout is not a terminal or
16
+ `NO_COLOR` is set).
17
+
18
+ ### Changed
19
+ - The agent loop now handles only the happy path: anything unexpected — a
20
+ network error, a Ctrl-C mid-turn, a malformed config file — crashes the
21
+ session, and restarting it is the recovery. The previous hardened variant is
22
+ preserved at the `hardened-agent-loop` tag.
23
+
24
+ <!--
25
+ When cutting a release, copy the relevant items from [Unreleased] into a new
26
+ version section above it, e.g.:
27
+
28
+ ## [0.4.0] - YYYY-MM-DD
29
+
30
+ ### Added
31
+ ### Changed
32
+ ### Fixed
33
+ ### Removed
34
+ -->
@@ -0,0 +1,35 @@
1
+ # Changelog — 0.5.x
2
+
3
+ All notable changes in the **0.5.x** release series are documented here.
4
+
5
+ ## [Unreleased]
6
+
7
+ ## [0.5.0] - 2026-07-29
8
+
9
+ ### Added
10
+ - A dedicated `read` tool for viewing files: it returns the content with
11
+ 1-based line numbers, `cat -n` style, and takes `offset`/`limit` to read a
12
+ window of a large file. Output is capped at 2000 lines and 50000 characters
13
+ per call, truncated only at line boundaries; a truncated result names the
14
+ range returned, the file's total line count, and the offset to continue
15
+ from. Files over 10 MB, binary files, and non-regular files (directories,
16
+ FIFOs, device files) are rejected, and every error message points at the
17
+ next action to take.
18
+
19
+ ### Changed
20
+ - The startup banner now prints the installed package version, so it is clear
21
+ which version is running.
22
+ - The system prompt now tells the model to prefer the `read` tool for viewing
23
+ files and to use `bash` for running commands and searching with `grep`.
24
+
25
+ <!--
26
+ When cutting a release, copy the relevant items from [Unreleased] into a new
27
+ version section above it, e.g.:
28
+
29
+ ## [0.5.0] - YYYY-MM-DD
30
+
31
+ ### Added
32
+ ### Changed
33
+ ### Fixed
34
+ ### Removed
35
+ -->
@@ -11,3 +11,8 @@ Notes are bilingual, split by language:
11
11
  These are free-form notes — insights, takeaways, and design decisions gathered
12
12
  while developing each series, with no fixed structure. User-facing changes live
13
13
  in [`../changelogs/`](../changelogs/).
14
+
15
+ The date in a `## X.Y.Z - YYYY.MM.DD` section heading is the **release date**
16
+ of that version (the same date as the git tag and the changelog entry). While
17
+ the version is unreleased, keep the literal `YYYY.MM.DD` placeholder — it is
18
+ filled in by the release process.
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Generated from the Chinese source [`../zh-CN/0.2.x.md`](../zh-CN/0.2.x.md). Do not edit by hand.
4
4
 
5
- ## 0.2.0 - 2026.06.29
5
+ ## 0.2.0 - 2026.07.09
6
6
 
7
7
  This release implements a minimally functional chatbot that holds a multi-turn conversation with the LLM through the [Messages](https://platform.claude.com/docs/en/api/python/messages/create) API.
8
8
 
@@ -0,0 +1,68 @@
1
+ # Development Notes — 0.4.x
2
+
3
+ > Generated from the Chinese source [`../zh-CN/0.4.x.md`](../zh-CN/0.4.x.md). Do not edit by hand.
4
+
5
+ ## 0.4.0 - 2026.07.14
6
+
7
+ ### The bash tool
8
+
9
+ This version adds tool use, starting with a single bash tool. The bash tool can do almost anything — powerful yet simple — and it makes a good base to gradually split other tools out of, which will make it easy to compare having more tools against having just the one bash tool.
10
+
11
+ Also, with no test environment for the other systems, Linux is the only platform that can be fully tested. Mac's bash is probably fairly similar to Linux's?
12
+
13
+ ### Background color for tool output
14
+
15
+ I tried giving tool output a background color so it stands apart from the agent's text output, similar to the pi code agent's implementation.
16
+
17
+ Along the way I learned how changing the terminal background color actually works:
18
+
19
+ ```
20
+ 1. Terminal colors are "in-band control": styling is mixed into the data stream
21
+
22
+ Between a program and the terminal there is only one byte stream — no separate
23
+ styling channel. To change how things look, you insert control sequences
24
+ starting with ESC (\x1b) into the stream. The \x1b[<params>m family is called
25
+ SGR (Select Graphic Rendition); it changes the terminal's current "brush"
26
+ state — every character printed afterwards is written into its screen cell
27
+ with the current brush attributes, until the brush changes again. The byte
28
+ stream for each output line is really:
29
+
30
+ \x1b[48;5;236m [bash]$ ls ... \x1b[K \x1b[0m \n
31
+ └─ set bg color ─┘ └── text ──┘ └erase to EOL┘ └reset┘
32
+
33
+ 2. 48;5;236: dark gray from the 256-color palette
34
+
35
+ 48;5;N means "background color = entry N of the 256-color palette". The
36
+ palette has three segments: 0–15 basic colors, 16–231 the color cube, 232–255
37
+ the grayscale ramp. Entry 236 is a dark gray (about #303030). Picking the
38
+ grayscale ramp over the basic background colors 41–47 is deliberate: basic
39
+ colors get remapped by the user's terminal theme and can be harsh on the eyes,
40
+ while grays stay "just a bit lighter than the base color" under any theme —
41
+ exactly right for visual layering.
42
+
43
+ 3. \x1b[K makes the color block fill the whole line
44
+
45
+ Background color only applies to cells where characters were actually
46
+ printed; the blank space at the end of a line keeps the default background,
47
+ so the block hugs the text with a ragged right edge. \x1b[K (Erase in Line)
48
+ erases from the cursor to the end of the line, and mainstream terminals
49
+ implement BCE (Background Color Erase): erased cells are filled with the
50
+ current background color. So emitting \x1b[K before resetting the brush
51
+ amounts to "painting the rest of this line dark gray" — which is why the
52
+ blocks in the screenshot are clean rectangles.
53
+ ```
54
+
55
+ ### From production hardening back to the core
56
+
57
+ The implementation had come to feel somewhat complex: the edge-case handling was crowding out the core. Those edge cases matter in a production-grade application, but for now, as long as normal use is unaffected, they hardly seem necessary.
58
+
59
+ With that in mind, I stripped out the production hardening wholesale and kept only the core flow. The design principle moves from "repair history and survive on error" to "crash on the unexpected, restart to recover":
60
+
61
+ - **Agent loop**: the per-moment Ctrl-C history repair, the API-error rollback (`ran_tools` / `turn_start`), patching `tool_result`s for truncated replies, and the per-turn request budget (`MAX_REQUESTS_PER_TURN`) are all deleted; the loop handles only the happy path.
62
+ - **bash tool**: switched to `subprocess.run`; temp-file capture, process-group kill, capped per-stream reads, and NUL protection are all removed. Known trades: a background child (`some_server &`) blocks until the timeout; the timeout kills bash itself, with no guarantee of killing the whole process tree; and text mode translates `\r` in output to `\n`.
63
+ - **terminal**: only this version's new tool-output background shading remains; control-character stripping and the stdout encoding fallback are removed.
64
+ - **settings**: happy path only — a missing file is normal, a corrupt file simply raises.
65
+
66
+ What stays is what genuinely belongs to agent design itself: the tool_use / tool_result pairing protocol, output truncation (protecting the context window), the command timeout, and `is_error` meaning only tool failure (the exit code goes into the result text, so the model can tell a negative answer from a failed execution).
67
+
68
+ The source drops from 544 lines to 262, with agent.py going from 250 to 116 — the main loop now fits on one screen. How each edge case was discovered is recorded in the git history; the hardened version before the trim is preserved under the `hardened-agent-loop` tag.
@@ -0,0 +1,57 @@
1
+ # Development Notes — 0.5.x
2
+
3
+ > Generated from the Chinese source [`../zh-CN/0.5.x.md`](../zh-CN/0.5.x.md). Do not edit by hand.
4
+
5
+ ## 0.5.0 - 2026.07.29
6
+
7
+ ### Printing the version
8
+
9
+ When the tool starts up without printing its version, I sometimes cannot tell which version I am actually running. So let's build that first.
10
+
11
+ ### Built-in tools in common code agents
12
+
13
+ For a survey of the built-in tools of mainstream code agents, see [Built-in Tools in Common Code Agents](../../research/agent_tools.md) (Chinese).
14
+
15
+ ### Designing the built-in tools
16
+
17
+ As the survey shows, beyond the core file/command tools that something like pi offers, the other agents have grown a large number of tools around subagent orchestration, task management, scheduling, user interaction, and so on. For our nano code agent, though, let's start expanding from the most basic category of all: file operations.
18
+
19
+ #### The read tool
20
+
21
+ We also surveyed the [read tool](../../research/read_tool.md) (Chinese): apart from codex, every code agent has a dedicated read tool. pi and opencode are model-agnostic, and you can see it in their read tools — their limits are line counts and file sizes, with no token limit. The other, model-specific code agents all have token limits; presumably because they only support their own vendor's models, where tokens are easier to count?
22
+
23
+ Functionally, read and bash both seem able to satisfy the need to read a file. Even accounting for files that may exceed a size limit, bash can still read them fine. Take codex: in actual testing, whatever the size of the file, it always starts with a `sed -n '1,260p' foo.md`, and then, if it finds there is more, it may follow up with `wc -l foo.md && sed -n '1,260p' foo.md` — reading chunk by chunk, which looks a lot like the `limit` of a dedicated read tool. But I'm not sure whether, for any given model, reading files is better done with a dedicated tool or with `bash`.
24
+
25
+ Another advantage of the read tool is that it makes it easy to constrain the agent to read-only operations, whereas bash is less directly controllable at the tool layer — which means control has to happen somewhere else, in a sandbox for instance. While testing the programmatic APIs of claude code, pi, and codex, I found that claude code and pi only need to be configured to use the read tool to be restricted to read-only operations, while codex needs a sandbox (`codex --sandbox read_only`).
26
+
27
+ OpenAI's 2024 paper [SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering](https://arxiv.org/abs/2405.15793) argues that designing a dedicated tool set works better than using bash alone. But that is 2024 work, and models are certainly more capable now, so I don't know whether plain bash is in fact better (codex reads files with bash itself). We can't verify any of this for now, though — we probably need our set of basic tools to be a bit more complete before it can be tested. For the moment we can start by implementing basic tool capabilities similar to pi's.
28
+
29
+ Here is what read implements today:
30
+
31
+ **Interface**
32
+
33
+ - **Parameters**: `path` (required, absolute or relative, `~` is expanded), `offset` (which line to start from, 1-based, default 1), `limit` (how many lines to return at most; omit it to read up to the line cap, and larger values are clamped to it). The line cap is interpolated into the schema description straight from the constant, because `limit` is the parameter the model can actively control — let it pick the right window size in one shot, instead of passing 5000, being silently cut to 2000, and only realizing after the fact from the truncation hint. The character cap, by contrast, is not mentioned in `limit`'s description: the model has no corresponding parameter to tune, so knowing the number would not change how it calls the tool; when it is hit, the runtime truncation hint and error messages explain it more effectively.
34
+ - **Output format**: `cat -n` style, with a 1-based line number in front of every line (width 6 plus a tab), so it looks just like `cat -n` in bash and the model can cite line numbers directly.
35
+
36
+ **Caps and truncation**
37
+
38
+ - **Two output caps**: at most 2000 lines and 50000 characters per call, truncating at whichever is hit first, and **only at line boundaries** — a line is never cut in half. This way a single file cannot flood the context in one go.
39
+ - **Continuation hint**: when the window stops short of the end of the file, the output ends with `[showing lines a-b of N; continue with offset=x]`, telling the model both the total line count and where to resume next time, so it doesn't need another `wc -l`.
40
+
41
+ **Handling file content**
42
+
43
+ - **Encoding**: decoded as UTF-8, with invalid bytes falling back to the replacement character (`errors="replace"`), matching the bash tool's tolerance.
44
+ - **Splitting lines**: on `\n` alone, not with `splitlines()` — that one also breaks on `\x0c`, `\x0b`, `\x85` and U+2028, which puts the line numbers out of step with `sed`, `grep -n` and the editor; and the over-long-line error hands over exactly a `sed -n 'Np'`, which would then slice the wrong line. A trailing `\r` is dropped, so CRLF files read like LF ones.
45
+ - **Binary rejection**: a `\x00` in the first 8KB marks the file as binary and it is rejected, to avoid stuffing garbage into the context.
46
+
47
+ **Error handling**
48
+
49
+ - **One return convention**: every error path returns `is_error=True`, covering `offset < 1`, `limit < 1`, a missing file, a path that is a directory or any other non-regular file, other `OSError`s, binary files, an `offset` past the total line count, and a single line over the character cap. An empty file returns `(empty file)`.
50
+ - **Only regular files are opened**: a FIFO blocks until something writes to it, and a device file like `/dev/zero` never ends — and read has no timeout to escape that the way the bash tool does, so the type check has to come before the read.
51
+ - **Error messages name the next action**: the message says not just "what went wrong" but "what to do next". For example, when `offset` is past the end of the file, it reports how many lines the file actually has; when a line is longer than the character cap, it hands over a bash command for the model to slice it itself (`sed -n 'Np' -- file | cut -c 1-2000`). That command is meant to be run, so the path in it is expanded first (quoting would stop the shell from expanding `~` itself), then escaped with `shlex.quote` and placed behind `--` — a space, a `$(...)` or a leading `-` in a filename should never get to reshape the command.
52
+
53
+ **Division of labor with bash, and implementation trade-offs**
54
+
55
+ - **The tool description steers the division of labor**: it tells the model to prefer read over bash's `cat`/`sed` for viewing files, while searching (`grep`) and slicing over-long lines still go through bash.
56
+ - **The whole file is read into memory**: good enough for source and config files, and the output caps already bound what actually reaches the context, so there is no streaming read. The cost is that the line and character caps only get their turn once the whole file has been loaded, so there is one more gate in front of the read: a 10 MB byte cap, over which the file is turned away and the model is pointed at bash to slice it, so a multi-gigabyte log cannot blow up the process. That number is far more generous than the output caps because a small window may well sit deep inside a large file — it only has to stop files that should never be loaded at all.
57
+ - **Call display**: the agent prints `[read] <path>` when executing, keeping the same style as the bash tool's call display.
@@ -2,7 +2,7 @@
2
2
 
3
3
  > 本文件为**手写中文源文件**(source of truth);英文版 [`../en/0.2.x.md`](../en/0.2.x.md) 由其生成。
4
4
 
5
- ## 0.2.0 - 2026.06.29
5
+ ## 0.2.0 - 2026.07.09
6
6
 
7
7
  这个版本实现了一个最小功能的 chatbot,使用 [Message](https://platform.claude.com/docs/en/api/python/messages/create) 接口,完成与 LLM 的多轮对话。
8
8
 
@@ -0,0 +1,49 @@
1
+ # 开发笔记 — 0.4.x
2
+
3
+ > 本文件为**手写中文源文件**(source of truth);英文版 [`../en/0.4.x.md`](../en/0.4.x.md) 由其生成。
4
+
5
+ ## 0.4.0 - 2026.07.14
6
+
7
+ ### bash 工具
8
+
9
+ 此版本增加 tool use 功能,首先增加一个 bash 工具,bash 工具几乎可以做任何事,功能强大又简单,可以以此为基础,再逐步拆分出其他工具,方便对比增加其他工具后与单一 bash 工具的区别。
10
+
11
+ 另外,由于其他系统无测试环境,能完全测试的只有 Linux。Mac 与 Linux 的 bash 可能比较类似?
12
+
13
+ ### 工具输出背景色
14
+
15
+ 尝试给工具输出增加背景颜色,这样方便与 agent 的文本输出有区分,类似 pi code agent 的实现。
16
+
17
+ 另外,学习了终端改背景色的原理:
18
+
19
+ ```
20
+ 1. 终端颜色是"带内控制":样式混在数据流里
21
+
22
+ 程序和终端之间只有一条字节流,没有独立的样式通道。想改变显示效果,就往流里插入以 ESC(\x1b)开头的控制序列。\x1b[<参数>m 这一族叫 SGR(Select Graphic Rendition),它修改终端当前的"画笔"状态——之后打印的每个字符都带着当前画笔属性写进屏幕单元格,直到画笔再次被改。每行输出的字节流实际是:
23
+
24
+ \x1b[48;5;236m [bash]$ ls ... \x1b[K \x1b[0m \n
25
+ └─ 设背景色 ─┘ └── 文字 ──┘ └擦到行尾┘ └重置┘
26
+
27
+ 2. 48;5;236:256 色调色板里的深灰
28
+
29
+ 48;5;N 表示"背景色 = 256 色调色板第 N 号"。调色板分三段:0–15 基本色、16–231 色立方、232–255 灰阶梯。236 号是深灰(约 #303030)。选灰阶而不选 41–47 基本背景色,是因为基本色会被用户终端主题重映射、容易刺眼;灰阶在各种主题下都只是"比底色浅一点",做层次区分正合适。
30
+
31
+ 3. \x1b[K 让色块铺满整行
32
+
33
+ 背景色只作用于实际打印了字符的单元格,行尾空白仍是默认背景,色块会紧贴文字、右缘参差。\x1b[K(Erase in Line)从光标擦到行尾,而主流终端实现了 BCE(Background Color Erase):擦除时用当前背景色填充。所以在重置画笔前发一个 \x1b[K,等于"用深灰刷满本行剩余部分"——这就是截图里色块是整齐矩形的原因。
34
+ ```
35
+
36
+ ### 从生产级加固回到核心
37
+
38
+ 我觉得目前的实现,似乎有些复杂,边界条件的处理反而让核心内容不够明显。这些边界条件在生产级应用时有用,但目前如果不影响正常使用,似乎并无必要。
39
+
40
+ 基于这个想法,把生产级加固整体剥离,只保留核心流程。设计原则从「出错时修复历史并存活」改为「意外即崩溃,重启即恢复」:
41
+
42
+ - **agent 循环**:Ctrl-C 分时刻的历史修复、API 错误回滚(`ran_tools` / `turn_start`)、截断回复补 `tool_result`、每轮请求预算(`MAX_REQUESTS_PER_TURN`)全部删除,循环只处理快乐路径。
43
+ - **bash 工具**:改用 `subprocess.run`,临时文件捕获、进程组击杀、分流限量读、NUL 防护全部移除。已知取舍:后台子进程(`some_server &`)会阻塞到超时;超时只杀 bash 本体,不保证杀掉整棵进程树;文本模式会把输出中的 `\r` 转成 `\n`。
44
+ - **terminal**:只保留本版新增的工具输出背景色,控制字符剥离和 stdout 编码兜底移除。
45
+ - **settings**:只保留快乐路径 —— 文件缺失属正常,文件损坏直接抛异常。
46
+
47
+ 保留下来的是真正属于 agent 设计本身的部分:tool_use / tool_result 的配对协议、输出截断(保护上下文窗口)、命令超时、`is_error` 只表示工具失败(退出码写进正文,让模型区分「否定答案」和「执行失败」)。
48
+
49
+ 源码从 544 行降到 262 行,其中 agent.py 从 250 行降到 116 行,主循环可以一屏读完。这些边界条件的发现过程记录在 git 历史里;精简前的加固版打了 tag `hardened-agent-loop` 留档。
@@ -0,0 +1,56 @@
1
+ # 开发笔记 — 0.5.x
2
+
3
+ > 本文件为**手写中文源文件**(source of truth);英文版 [`../en/0.5.x.md`](../en/0.5.x.md) 由其生成。
4
+
5
+ ## 0.5.0 - 2026.07.29
6
+
7
+ ### 打印版本号
8
+ 工具启动时,如果不打印版本号,有时候自己分辨不出来是哪个版本。先开发一下这个功能。
9
+
10
+ ### 常见 code agent 内置工具
11
+ 主流 Code Agent 的内置工具调研见 [《常见 Code Agent 内置工具》](../../research/agent_tools.md)。
12
+
13
+ ### 内置工具设计
14
+
15
+ 可以看出,除了 pi 那样的核心文件/命令工具外,其他 agent 还围绕子代理编排、任务管理、定时调度、用户交互等场景扩展了大量工具。不过对我们的 nano code agent 来说,先从文件操作这一类最基础的工具开始扩展。
16
+
17
+ #### read 工具
18
+
19
+ 我们对 [read工具](../../research/read_tool.md) 也做了调研,除了 codex 外,其他 code agent 都有专门的 read 工具。pi 和 opencode 是模型无关的,可以看出他们的 read 工具,limit 除了行数,还有文件大小,并没有 token 限制。而其他专用的 code agent,都有 token 限制,大概是只支持自家模型,token 更方便计算?
20
+
21
+ 功能上讲,似乎 read 和 bash 都能实现读文件需求。即使考虑文件大小可能超出限制,bash 也能正常读取。例如 codex,实际测试中发现 codex 读文件的操作,不管读什么大小文件,他都会先来个 `sed -n '1,260p' foo.md`,之后如果发现还有内容,可能会 `wc -l foo.md && sed -n '1,260p' foo.md`,相当于一块一块读,和专用 read 工具的 limit 似乎差不多。但不太确定各个模型,在读文件的时候,是用专用工具好,还是用 `bash` 更好。
22
+
23
+ 另外, read 工具的好处是可以方便地控制 agent 进行只读操作,而 bash 可能在工具层控制起来不那么直接,这样可能就需要在其他层面做控制,例如,沙箱。我在测试 claude code, pi, codex 的程序化方式调用时就发现,claude code 和 pi 只需要设定只使用 read 工具就可以限制只读操作,但 codex 要用沙箱才行(`codex --sandbox read_only`)。
24
+
25
+ 在 OpenAI 2024 年的论文 [SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering
26
+ ](https://arxiv.org/abs/2405.15793) 中提到了设计专有工具集比只用 bash 效果要好,但这是 2024 年的工作,现在模型的能力肯定更强了,不知道单纯用 bash 是不是更好(codex 自己读文件就用 bash)。不过,这些我们目前似乎无法验证,可能需要我们的基础工具集完善一点后,才好测试。目前可以先实现类似 pi 的基础工具能力。
27
+
28
+ 对于 read,目前实现的功能有:
29
+
30
+ **接口**
31
+
32
+ - **参数**:`path`(必填,绝对或相对路径,支持 `~` 展开)、`offset`(从第几行开始,1 起,默认 1)、`limit`(最多返回多少行,不传就读到行数上限为止,超过上限会被 clamp)。行数上限这个数字直接从常量插值进 schema 描述里,因为 `limit` 是模型能主动控制的参数——让它一次就选对窗口大小,而不是传了个 5000 被静默截到 2000、只能靠事后的截断提示反应过来。字符上限则没写进 `limit` 的描述:模型没有对应的参数可调,知道了也改变不了调用方式,撞上时由运行时的截断提示和错误消息说明更有效。
33
+ - **输出格式**:`cat -n` 风格,每行前面带 1 起的行号(宽度 6 + 制表符),和 bash 里 `cat -n` 的观感一致,方便模型直接引用行号。
34
+
35
+ **上限与截断**
36
+
37
+ - **两道输出上限**:一次调用最多 2000 行、50000 字符,哪个先到按哪个截断,并且**只在行边界截断**,不会把一行切成两半。这样单个文件不会一次性淹没上下文。
38
+ - **续读提示**:窗口没读到文件末尾时,结尾追加 `[showing lines a-b of N; continue with offset=x]`,直接告诉模型总行数和下一次该从哪行接着读,不用再去 `wc -l`。
39
+
40
+ **文件内容处理**
41
+
42
+ - **编码**:按 UTF-8 解码,非法字节用替换字符兜底(`errors="replace"`),和 bash 工具的容忍度保持一致。
43
+ - **行的切分**:只按 `\n` 切,不用 `splitlines()`——后者还会在 `\x0c`、`\x0b`、`\x85`、U+2028 处断行,行号就和 `sed`、`grep -n`、编辑器对不上了;而超长行的错误消息里恰好给了一条 `sed -n 'Np'`,对不上就会切到错误的行。行尾的 `\r` 会去掉,CRLF 文件读起来和 LF 一样。
44
+ - **二进制拒绝**:前 8KB 里出现 `\x00` 就判定为二进制文件并拒绝,避免把乱码塞进上下文。
45
+
46
+ **错误处理**
47
+
48
+ - **统一返回**:所有错误路径都返回 `is_error=True`,覆盖 `offset < 1`、`limit < 1`、文件不存在、路径是目录或其他非普通文件、其他 `OSError`、二进制文件、`offset` 超出文件总行数、单行超过字符上限。空文件返回 `(empty file)`。
49
+ - **只打开普通文件**:FIFO 会一直阻塞到有人写入,`/dev/zero` 这类设备文件则永远读不完,而 read 不像 bash 工具那样有超时可以脱身,所以类型检查必须放在读之前。
50
+ - **错误信息带下一步动作**:消息里不只说"哪里错了",还说"接下来该怎么办"。比如 `offset` 超过文件总行数时会报出文件实际有多少行;某一行长度超过字符上限时,会直接给出一条 bash 命令让模型自己切片(`sed -n 'Np' -- file | cut -c 1-2000`)。这条命令是要交给模型去执行的,所以里面的路径先展开 `~`(引号会挡住 shell 自己的 `~` 展开),再 `shlex.quote` 转义,并放在 `--` 之后——文件名里的空格、`$(...)` 或开头的 `-` 都不该有机会改变这条命令的形状。
51
+
52
+ **与 bash 的分工、实现取舍**
53
+
54
+ - **工具描述里做了分工引导**:告诉模型查看文件优先用 read 而不是 bash 里的 `cat`/`sed`,但搜索(`grep`)和切超长行仍然走 bash。
55
+ - **一次性读入内存**:对源码、配置这类文件足够用,而且输出上限已经限定了真正进入上下文的量,所以没有做流式读取。代价是行数和字符上限都要等整个文件读完才轮得到它们生效,所以读之前还有一道 10 MB 的字节闸门:超过就直接拒绝并让模型改用 bash 切片,免得一个几 GB 的日志把进程撑爆。这个数字比输出上限宽松得多,因为一个小窗口完全可能落在大文件的深处,它只需要挡住"整个文件根本不该被载入"的情况。
56
+ - **调用展示**:agent 侧执行时打印 `[read] <path>`,和 bash 工具的调用展示保持同一种风格。
@@ -0,0 +1,14 @@
1
+ # Research
2
+
3
+ Notes from surveying how other projects solve a problem, written before
4
+ building the equivalent here. Unlike changelogs and dev notes, these are not
5
+ tied to a release series — each file stands on its own, and the decisions they
6
+ feed into are recorded in [`../dev_notes/`](../dev_notes/).
7
+
8
+ These notes are **written and maintained in Chinese**; each file says so at the
9
+ top.
10
+
11
+ - [`agent_tools.md`](agent_tools.md) — the built-in tools of Pi, Claude Code,
12
+ Codex, OpenCode and Grok Build.
13
+ - [`read_tool.md`](read_tool.md) — how five agent projects read files, and what
14
+ a dedicated read tool buys over plain bash.
@@ -0,0 +1,154 @@
1
+ # 常见 Code Agent 内置工具
2
+
3
+ > 本文明确指定使用中文编写和维护。
4
+
5
+ 目前 nanoPyCodeAgent 只有 Bash 工具,但主流 Code Agent 通常还会提供其他内置工具。本文调研 Pi、Claude Code、Codex、OpenCode 和 Grok Build 的工具设计。
6
+
7
+ ## Pi
8
+
9
+ Pi 的核心工具比较少。[Pi Quick Start](https://pi.dev/docs/latest/quickstart#first-session)介绍了四个核心工具:
10
+
11
+ - `read`:读文件
12
+ - `write`:创建或覆盖文件
13
+ - `edit`:编辑文件
14
+ - `bash`:运行 Shell 命令
15
+
16
+ 除此之外,还有三个可选扩展工具:`grep`、`find`、`ls`。
17
+
18
+ ## Claude Code
19
+
20
+ Claude Code 提供的工具更多。[Claude Code 工具参考](https://code.claude.com/docs/en/tools-reference)介绍了以下主要工具。
21
+
22
+ ### 文件操作
23
+
24
+ - `Read`:读取文件内容,支持文本、图片、PDF、Jupyter Notebook
25
+ - `Write`:创建或整体覆盖文件
26
+ - `Edit`:对文件做精确字符串替换式的局部修改
27
+ - `NotebookEdit`:按 cell 修改 Jupyter Notebook
28
+
29
+ ### 搜索与代码智能
30
+
31
+ - `Glob`:按文件名模式查找文件
32
+ - `Grep`:按内容模式搜索文件,基于 ripgrep
33
+ - `LSP`:通过语言服务器提供代码智能,例如跳转定义、查找引用、报告类型错误
34
+
35
+ ### 命令执行
36
+
37
+ - `Bash`:在环境中执行 Shell 命令
38
+ - `PowerShell`:原生执行 PowerShell 命令,主要面向 Windows
39
+ - `Monitor`:在后台运行命令并逐行回传输出,用于监控日志、轮询状态、订阅 WebSocket 事件
40
+
41
+ ### Web 访问
42
+
43
+ - `WebFetch`:抓取 URL 内容,转换成 Markdown 后用小模型按 prompt 提取信息
44
+ - `WebSearch`:执行网络搜索,返回结果标题和链接
45
+
46
+ ### 子代理与编排
47
+
48
+ - `Agent`:派生一个拥有独立上下文窗口的子代理去完成任务
49
+ - `SendMessage`:给 Agent Team 队友发消息,或按 ID 恢复某个子代理
50
+ - `Workflow`:运行动态工作流脚本,在后台编排多个子代理并返回汇总结果
51
+ - `Skill`:在主对话中执行一个 Skill,即可复用的提示词工作流
52
+
53
+ ### 任务管理
54
+
55
+ - `TaskCreate`、`TaskGet`、`TaskList`、`TaskUpdate`:创建、查询、列出、更新任务列表
56
+ - `TaskOutput`:获取后台任务输出;已不推荐,改用 `Read` 读取输出文件
57
+ - `TaskStop`:按 ID 停止后台任务
58
+ - `TodoWrite`:管理会话任务清单;默认已禁用,被 `Task*` 系列取代
59
+
60
+ ### 计划与工作区
61
+
62
+ - `EnterPlanMode`、`ExitPlanMode`:进入计划模式设计方案,提交计划供批准并退出
63
+ - `EnterWorktree`、`ExitWorktree`:创建并切入隔离的 Git worktree,退出后返回原目录
64
+
65
+ ### 定时与调度
66
+
67
+ - `CronCreate`、`CronDelete`、`CronList`:在当前会话内创建、取消、列出定时任务
68
+ - `ScheduleWakeup`:为自适应节奏的 `/loop` 安排下一次迭代时间
69
+ - `RemoteTrigger`:创建和管理 claude.ai 上的 Routines,即云端定时代理
70
+
71
+ ### 用户交互与输出
72
+
73
+ - `AskUserQuestion`:向用户提多选题以澄清需求
74
+ - `Artifact`:把 HTML 或 Markdown 文件发布为 claude.ai 上的 Artifact 页面
75
+ - `PushNotification`:发送桌面或手机推送通知
76
+ - `SendUserFile`:把会话中的报告、截图等文件直接发送到用户设备
77
+ - `ReportFindings`:以结构化列表上报 Code Review 发现的问题
78
+ - `ShareOnboardingGuide`:上传 `ONBOARDING.md` 并生成团队分享链接
79
+
80
+ ### MCP 相关
81
+
82
+ - `ListMcpResourcesTool`、`ReadMcpResourceTool`:列出、读取 MCP Server 暴露的资源
83
+ - `ToolSearch`:按需搜索并加载延迟加载的工具,需要开启 Tool Search
84
+ - `WaitForMcpServers`:等待仍在后台连接中的 MCP Server 就绪
85
+
86
+ ## Codex
87
+
88
+ Codex 与 Pi 和 Claude Code 不太一样,没有单独的 `Read`、`Glob`、`Grep`;读取和搜索由 `exec_command` 调用 `rg`、`sed` 等命令完成。除[官方工具文档](https://developers.openai.com/api/docs/guides/tools)外,本文也参考了开源的 [Codex](https://github.com/openai/codex) 源码。
89
+
90
+ 标准本地 Coding Turn 常见的工具有:
91
+
92
+ - `exec_command`:执行 Shell 命令
93
+ - `write_stdin`:向长时间运行的命令继续输入或轮询
94
+ - `apply_patch`:用结构化 Patch 修改文件
95
+ - `update_plan`:更新任务计划
96
+ - `view_image`:查看本地图片
97
+ - `web_search`,或新式命名空间下的 `web.run`:搜索网页
98
+
99
+ 条件性工具还包括:
100
+
101
+ - 交互:`request_user_input`
102
+ - MCP 资源:`list_mcp_resources`、`list_mcp_resource_templates`、`read_mcp_resource`
103
+ - 权限:`request_permissions`
104
+ - 多代理:`spawn_agent`、`send_message`、`followup_task`、`wait_agent`、`interrupt_agent`、`list_agents`
105
+ - 搜索工具发现:`tool_search`
106
+ - 图片生成:`image_gen.imagegen`
107
+ - MCP、Apps、插件注入的动态工具
108
+
109
+ ## OpenCode
110
+
111
+ [OpenCode](https://github.com/anomalyco/opencode) v1.18.3 的内置工具有:
112
+
113
+ ### 文件与执行
114
+
115
+ - `bash`:执行 Shell 命令
116
+ - `read`:读取文本、图片或列出目录内容
117
+ - `write`:创建或覆盖文件
118
+ - `edit`:精确字符串替换
119
+ - `apply_patch`:用 Patch 批量新增、修改、删除文件
120
+ - `glob`:按文件名模式查找文件
121
+ - `grep`:用正则搜索文件内容
122
+
123
+ ### Web 访问
124
+
125
+ - `webfetch`:获取指定 URL 内容
126
+ - `websearch`:搜索互联网
127
+
128
+ ### 用户交互
129
+
130
+ - `question`:在执行过程中向用户提问
131
+
132
+ ### 技能与任务管理
133
+
134
+ - `skill`:加载 `SKILL.md`
135
+ - `todowrite`:创建和更新任务列表
136
+
137
+ ## Grok Build
138
+
139
+ 刚开源的 [Grok Build](https://github.com/xai-org/grok-build) 内置工具有:
140
+
141
+ - 文件与执行:`run_terminal_command`、`read_file`、`search_replace`、`write`、`list_dir`、`grep`、`lsp`
142
+ - 后台任务与子代理:`spawn_subagent`、`get_command_or_subagent_output`、`wait_commands_or_subagents`、`kill_command_or_subagent`、`monitor`
143
+ - 任务管理:`todo_write`、`update_goal`
144
+ - 定时任务:`scheduler_create`、`scheduler_delete`、`scheduler_list`
145
+ - Plan 模式:`enter_plan_mode`、`exit_plan_mode`、`ask_user_question`
146
+ - Web:`web_search`、`web_fetch`
147
+ - MCP 元工具:`search_tool`、`use_tool`
148
+ - Memory:`memory_search`、`memory_get`
149
+ - 媒体:`image_gen`、`image_to_video`、`reference_to_video`
150
+
151
+ 运行时还可能动态加入:
152
+
153
+ - `image_edit`
154
+ - 服务端 `x_search`