zettcode 0.1.0__tar.gz → 0.1.2__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 (146) hide show
  1. zettcode-0.1.2/.gitignore +21 -0
  2. zettcode-0.1.2/PKG-INFO +136 -0
  3. zettcode-0.1.2/README.md +107 -0
  4. zettcode-0.1.2/README.zh-CN.md +98 -0
  5. zettcode-0.1.2/pyproject.toml +101 -0
  6. zettcode-0.1.2/src/zettcode/__init__.py +47 -0
  7. zettcode-0.1.2/src/zettcode/_compat.py +35 -0
  8. zettcode-0.1.2/src/zettcode/app/__init__.py +63 -0
  9. zettcode-0.1.2/src/zettcode/app/agent/__init__.py +45 -0
  10. zettcode-0.1.2/src/zettcode/app/agent/agent.py +580 -0
  11. zettcode-0.1.2/src/zettcode/app/agent/approval.py +49 -0
  12. zettcode-0.1.2/src/zettcode/app/agent/blocks.py +381 -0
  13. zettcode-0.1.2/src/zettcode/app/agent/capabilities.py +44 -0
  14. zettcode-0.1.2/src/zettcode/app/agent/compaction.py +33 -0
  15. zettcode-0.1.2/src/zettcode/app/agent/context.py +293 -0
  16. zettcode-0.1.2/src/zettcode/app/agent/entries.py +189 -0
  17. zettcode-0.1.2/src/zettcode/app/agent/export.py +413 -0
  18. zettcode-0.1.2/src/zettcode/app/agent/mcp.py +29 -0
  19. zettcode-0.1.2/src/zettcode/app/agent/mentions.py +126 -0
  20. zettcode-0.1.2/src/zettcode/app/agent/projection.py +184 -0
  21. zettcode-0.1.2/src/zettcode/app/agent/rendering.py +381 -0
  22. zettcode-0.1.2/src/zettcode/app/agent/replay.py +79 -0
  23. zettcode-0.1.2/src/zettcode/app/agent/rows.py +215 -0
  24. zettcode-0.1.2/src/zettcode/app/agent/runtime.py +374 -0
  25. zettcode-0.1.2/src/zettcode/app/agent/storage/__init__.py +28 -0
  26. zettcode-0.1.2/src/zettcode/app/agent/storage/metadata.py +135 -0
  27. zettcode-0.1.2/src/zettcode/app/agent/storage/records.py +266 -0
  28. zettcode-0.1.2/src/zettcode/app/agent/storage/store.py +588 -0
  29. zettcode-0.1.2/src/zettcode/app/agent/subagents.py +123 -0
  30. zettcode-0.1.2/src/zettcode/app/agent/title.py +72 -0
  31. zettcode-0.1.2/src/zettcode/app/agent/trace/script.js +18 -0
  32. zettcode-0.1.2/src/zettcode/app/agent/trace/style.css +136 -0
  33. zettcode-0.1.2/src/zettcode/app/agent/trace/template.html +31 -0
  34. zettcode-0.1.2/src/zettcode/app/agent/transcript.py +520 -0
  35. zettcode-0.1.2/src/zettcode/app/agent/usage.py +211 -0
  36. zettcode-0.1.2/src/zettcode/app/commands.py +132 -0
  37. zettcode-0.1.2/src/zettcode/app/registry.py +61 -0
  38. zettcode-0.1.2/src/zettcode/app/ui/__init__.py +63 -0
  39. zettcode-0.1.2/src/zettcode/app/ui/app.py +398 -0
  40. zettcode-0.1.2/src/zettcode/app/ui/clipboard.py +314 -0
  41. zettcode-0.1.2/src/zettcode/app/ui/commands.py +194 -0
  42. zettcode-0.1.2/src/zettcode/app/ui/demo.py +160 -0
  43. zettcode-0.1.2/src/zettcode/app/ui/keys.py +181 -0
  44. zettcode-0.1.2/src/zettcode/app/ui/notices.py +98 -0
  45. zettcode-0.1.2/src/zettcode/app/ui/rows.py +113 -0
  46. zettcode-0.1.2/src/zettcode/app/ui/sessions.py +89 -0
  47. zettcode-0.1.2/src/zettcode/app/ui/settings.py +79 -0
  48. zettcode-0.1.2/src/zettcode/app/ui/shell.py +101 -0
  49. zettcode-0.1.2/src/zettcode/app/ui/widgets/__init__.py +84 -0
  50. zettcode-0.1.2/src/zettcode/app/ui/widgets/approval_page.py +242 -0
  51. zettcode-0.1.2/src/zettcode/app/ui/widgets/completer.py +63 -0
  52. zettcode-0.1.2/src/zettcode/app/ui/widgets/composer.py +299 -0
  53. zettcode-0.1.2/src/zettcode/app/ui/widgets/context_page.py +70 -0
  54. zettcode-0.1.2/src/zettcode/app/ui/widgets/model_page.py +55 -0
  55. zettcode-0.1.2/src/zettcode/app/ui/widgets/panel.py +24 -0
  56. zettcode-0.1.2/src/zettcode/app/ui/widgets/root.py +48 -0
  57. zettcode-0.1.2/src/zettcode/app/ui/widgets/sessions_page.py +84 -0
  58. zettcode-0.1.2/src/zettcode/app/ui/widgets/steering.py +119 -0
  59. zettcode-0.1.2/src/zettcode/app/ui/widgets/transcript.py +228 -0
  60. zettcode-0.1.2/src/zettcode/app/ui/widgets/welcome.py +19 -0
  61. zettcode-0.1.2/src/zettcode/cli.py +162 -0
  62. zettcode-0.1.2/src/zettcode/config.py +407 -0
  63. zettcode-0.1.2/src/zettcode/plugins/__init__.py +98 -0
  64. zettcode-0.1.2/src/zettcode/plugins/builtins.py +68 -0
  65. zettcode-0.1.2/src/zettcode/plugins/container.py +294 -0
  66. zettcode-0.1.2/src/zettcode/plugins/extension.py +180 -0
  67. zettcode-0.1.2/src/zettcode/plugins/loader.py +191 -0
  68. zettcode-0.1.2/src/zettcode/plugins/mixins.py +214 -0
  69. zettcode-0.1.2/src/zettcode/plugins/plugin.py +99 -0
  70. zettcode-0.1.2/src/zettcode/plugins/state.py +196 -0
  71. zettcode-0.1.2/src/zettcode/tui/__init__.py +392 -0
  72. zettcode-0.1.2/src/zettcode/tui/capabilities.py +101 -0
  73. zettcode-0.1.2/src/zettcode/tui/console.py +229 -0
  74. zettcode-0.1.2/src/zettcode/tui/core/__init__.py +63 -0
  75. zettcode-0.1.2/src/zettcode/tui/core/app.py +520 -0
  76. zettcode-0.1.2/src/zettcode/tui/core/events.py +134 -0
  77. zettcode-0.1.2/src/zettcode/tui/core/focus.py +85 -0
  78. zettcode-0.1.2/src/zettcode/tui/core/geometry.py +212 -0
  79. zettcode-0.1.2/src/zettcode/tui/core/host.py +66 -0
  80. zettcode-0.1.2/src/zettcode/tui/core/keymap.py +218 -0
  81. zettcode-0.1.2/src/zettcode/tui/core/scheduler.py +96 -0
  82. zettcode-0.1.2/src/zettcode/tui/core/screen.py +80 -0
  83. zettcode-0.1.2/src/zettcode/tui/core/theme.py +172 -0
  84. zettcode-0.1.2/src/zettcode/tui/core/widget.py +187 -0
  85. zettcode-0.1.2/src/zettcode/tui/gallery.py +588 -0
  86. zettcode-0.1.2/src/zettcode/tui/glyphs.py +73 -0
  87. zettcode-0.1.2/src/zettcode/tui/input/__init__.py +18 -0
  88. zettcode-0.1.2/src/zettcode/tui/input/bridge.py +43 -0
  89. zettcode-0.1.2/src/zettcode/tui/input/decoder.py +165 -0
  90. zettcode-0.1.2/src/zettcode/tui/input/events.py +55 -0
  91. zettcode-0.1.2/src/zettcode/tui/input/keys.py +60 -0
  92. zettcode-0.1.2/src/zettcode/tui/input/reader.py +147 -0
  93. zettcode-0.1.2/src/zettcode/tui/layout/__init__.py +24 -0
  94. zettcode-0.1.2/src/zettcode/tui/layout/box.py +153 -0
  95. zettcode-0.1.2/src/zettcode/tui/layout/overlay.py +164 -0
  96. zettcode-0.1.2/src/zettcode/tui/layout/scroll.py +357 -0
  97. zettcode-0.1.2/src/zettcode/tui/layout/solver.py +95 -0
  98. zettcode-0.1.2/src/zettcode/tui/layout/spacing.py +136 -0
  99. zettcode-0.1.2/src/zettcode/tui/render/__init__.py +63 -0
  100. zettcode-0.1.2/src/zettcode/tui/render/canvas.py +153 -0
  101. zettcode-0.1.2/src/zettcode/tui/render/code.py +456 -0
  102. zettcode-0.1.2/src/zettcode/tui/render/color.py +177 -0
  103. zettcode-0.1.2/src/zettcode/tui/render/renderer.py +113 -0
  104. zettcode-0.1.2/src/zettcode/tui/render/rich_text.py +66 -0
  105. zettcode-0.1.2/src/zettcode/tui/render/style.py +102 -0
  106. zettcode-0.1.2/src/zettcode/tui/render/sweep.py +87 -0
  107. zettcode-0.1.2/src/zettcode/tui/render/text.py +217 -0
  108. zettcode-0.1.2/src/zettcode/tui/runner.py +223 -0
  109. zettcode-0.1.2/src/zettcode/tui/terminal.py +216 -0
  110. zettcode-0.1.2/src/zettcode/tui/testing/__init__.py +6 -0
  111. zettcode-0.1.2/src/zettcode/tui/testing/harness.py +220 -0
  112. zettcode-0.1.2/src/zettcode/tui/testing/snapshot.py +64 -0
  113. zettcode-0.1.2/src/zettcode/tui/theme_file.py +98 -0
  114. zettcode-0.1.2/src/zettcode/tui/widgets/__init__.py +118 -0
  115. zettcode-0.1.2/src/zettcode/tui/widgets/collapsible.py +115 -0
  116. zettcode-0.1.2/src/zettcode/tui/widgets/completion.py +205 -0
  117. zettcode-0.1.2/src/zettcode/tui/widgets/dialog.py +165 -0
  118. zettcode-0.1.2/src/zettcode/tui/widgets/diff.py +236 -0
  119. zettcode-0.1.2/src/zettcode/tui/widgets/list.py +251 -0
  120. zettcode-0.1.2/src/zettcode/tui/widgets/list_page.py +100 -0
  121. zettcode-0.1.2/src/zettcode/tui/widgets/markdown.py +589 -0
  122. zettcode-0.1.2/src/zettcode/tui/widgets/progress.py +166 -0
  123. zettcode-0.1.2/src/zettcode/tui/widgets/rich_text.py +84 -0
  124. zettcode-0.1.2/src/zettcode/tui/widgets/status.py +86 -0
  125. zettcode-0.1.2/src/zettcode/tui/widgets/table.py +101 -0
  126. zettcode-0.1.2/src/zettcode/tui/widgets/tasks.py +129 -0
  127. zettcode-0.1.2/src/zettcode/tui/widgets/text.py +108 -0
  128. zettcode-0.1.2/src/zettcode/tui/widgets/textarea.py +627 -0
  129. zettcode-0.1.2/src/zettcode/tui/widgets/toast.py +136 -0
  130. zettcode-0.1.0/.gitignore +0 -12
  131. zettcode-0.1.0/PKG-INFO +0 -109
  132. zettcode-0.1.0/README.md +0 -89
  133. zettcode-0.1.0/pyproject.toml +0 -66
  134. zettcode-0.1.0/src/zettcode/__init__.py +0 -6
  135. zettcode-0.1.0/src/zettcode/cli.py +0 -78
  136. zettcode-0.1.0/src/zettcode/config.py +0 -72
  137. zettcode-0.1.0/src/zettcode/runtime.py +0 -121
  138. zettcode-0.1.0/src/zettcode/tui.py +0 -924
  139. zettcode-0.1.0/src/zettcode/tui_framework/__init__.py +0 -29
  140. zettcode-0.1.0/src/zettcode/tui_framework/application.py +0 -91
  141. zettcode-0.1.0/src/zettcode/tui_framework/components.py +0 -821
  142. zettcode-0.1.0/src/zettcode/tui_framework/events.py +0 -37
  143. zettcode-0.1.0/src/zettcode/tui_framework/input.py +0 -163
  144. zettcode-0.1.0/src/zettcode/tui_framework/screen.py +0 -214
  145. zettcode-0.1.0/src/zettcode/tui_framework/terminal.py +0 -118
  146. {zettcode-0.1.0 → zettcode-0.1.2}/LICENSE +0 -0
@@ -0,0 +1,21 @@
1
+ **/.venv/
2
+ **/__pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .coverage
8
+ **/.pytest_cache/
9
+ **/.ruff_cache/
10
+ .zett-tool-output/
11
+ .env
12
+ .DS_Store
13
+
14
+ # viztracer writes its trace here by default.
15
+ /result.json
16
+
17
+ # The docs site: dependencies, build output, and VitePress' cache.
18
+ node_modules/
19
+ docs/.vitepress/dist/
20
+ docs/.vitepress/cache/
21
+ docs/.vitepress/.temp/
@@ -0,0 +1,136 @@
1
+ Metadata-Version: 2.5
2
+ Name: zettcode
3
+ Version: 0.1.2
4
+ Summary: A focused terminal coding agent powered by zett-agent
5
+ Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
6
+ Project-URL: Issues, https://github.com/Chang-LeHung/zettcode/issues
7
+ Author: Chang-LeHung
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,coding-agent,llm,terminal,tui
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Environment :: Console
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 10
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: exceptiongroup<2,>=1.2; python_version < '3.11'
24
+ Requires-Dist: pyperclip<2,>=1.9
25
+ Requires-Dist: tomli<3,>=2; python_version < '3.11'
26
+ Requires-Dist: wcwidth<1,>=0.2
27
+ Requires-Dist: zett-agent==0.1.10
28
+ Description-Content-Type: text/markdown
29
+
30
+ # ZettCode
31
+
32
+ **English** · [简体中文](README.zh-CN.md)
33
+
34
+ A focused coding agent for your terminal. Point it at a workspace, describe the
35
+ change you want, and watch the work happen — files read, commands approved,
36
+ edits made, tests run — as a conversation you can scroll, select, and copy.
37
+
38
+ The terminal interface is this repository; the model loop is
39
+ [`zett-agent`](https://github.com/Chang-LeHung/zett-agent), published separately.
40
+
41
+ ## Install
42
+
43
+ ZettCode needs Python 3.10 or newer. It is published on PyPI as `zettcode`:
44
+
45
+ ```bash
46
+ uv tool install zettcode
47
+ # or
48
+ pip install zettcode
49
+ ```
50
+
51
+ ## Configure
52
+
53
+ Everything except the workspace lives in `~/.zettcode/config.toml`. At least one
54
+ model, and the first one is active at startup:
55
+
56
+ ```toml
57
+ [[models]]
58
+ model = "gpt-4o"
59
+ token = "sk-..." # or set OPENAI_API_KEY
60
+ base_url = "http://localhost:8787/v1" # any OpenAI-compatible endpoint
61
+ context_window = 200000 # tokens this model can carry
62
+ multimodal = true # accepts images as well as text
63
+ ```
64
+
65
+ Project `AGENTS.md` files, [skills](https://chang-lehung.github.io/zettcode/guide/skills-and-mcp),
66
+ MCP servers, plugins, the palette, and the rest are all in the same file. The
67
+ full key list is in the [configuration reference](https://chang-lehung.github.io/zettcode/guide/config).
68
+
69
+ ## Run
70
+
71
+ ```bash
72
+ cd ~/projects/api
73
+ zettcode # workspace defaults to the cwd
74
+ zettcode -w /path/to/project # work somewhere else
75
+ zettcode --resume <session-id> # reopen a stored session
76
+ zettcode --dry-run # time the startup and exit
77
+ ```
78
+
79
+ Type a task and press **Enter**. `/` opens the command menu, `@` references a
80
+ skill, `Ctrl-C` stops a request, `Ctrl-D` on an empty composer exits — and
81
+ leaving prints the command that brings the session back.
82
+
83
+ ## What it does
84
+
85
+ - **Streams a conversation, not a payload.** Reasoning, answers, and tool calls
86
+ arrive as rows in order; tool output reads like a log — `Read src/app.py`,
87
+ `Ran pytest -q` — never raw JSON.
88
+ - **Asks before it acts.** Every shell command is confirmed first, with
89
+ `a` to allow the rest of the run and `p` to remember one command.
90
+ - **Keeps the context honest.** The status line shows tokens, cache hit rate,
91
+ and how full the window is; `/context` breaks the request down, and compaction
92
+ summarizes before the window overflows.
93
+ - **Remembers the project.** `AGENTS.md` files from the workspace upward become
94
+ project instructions; skills and MCP servers extend what the model can do.
95
+ - **Respects the terminal.** Mouse selection and scrolling, panels for choices,
96
+ image paste, `Ctrl-L`, and a palette that follows your terminal's background —
97
+ or your own `theme.toml`.
98
+
99
+ ## Documentation
100
+
101
+ The user guide is published at
102
+ **<https://chang-lehung.github.io/zettcode/>** (English, with
103
+ [中文](https://chang-lehung.github.io/zettcode/zh/) alongside it):
104
+
105
+ | | |
106
+ | --- | --- |
107
+ | [Getting started](https://chang-lehung.github.io/zettcode/guide/getting-started) | Install, first task, first approvals. |
108
+ | [The interface](https://chang-lehung.github.io/zettcode/guide/interface) | What each row of the screen tells you. |
109
+ | [Keys and mouse](https://chang-lehung.github.io/zettcode/guide/keys) | The full reference. |
110
+ | [Commands](https://chang-lehung.github.io/zettcode/guide/commands) | Every `/command` and `@resource`. |
111
+ | [Sessions](https://chang-lehung.github.io/zettcode/guide/sessions) | Resume, title, export. |
112
+ | [Configuration](https://chang-lehung.github.io/zettcode/guide/config) | One file, every key. |
113
+ | [Models and context](https://chang-lehung.github.io/zettcode/guide/models) | Effort, compaction, cache, images. |
114
+ | [Project instructions](https://chang-lehung.github.io/zettcode/guide/instructions) | Writing a good `AGENTS.md`. |
115
+ | [Skills and MCP](https://chang-lehung.github.io/zettcode/guide/skills-and-mcp) | Teaching it your own tools. |
116
+ | [Plugins](https://chang-lehung.github.io/zettcode/guide/plugins) | Adding commands and rows in Python. |
117
+ | [Problems and questions](https://chang-lehung.github.io/zettcode/guide/faq) | When something does not work. |
118
+
119
+ ## Development
120
+
121
+ ```bash
122
+ uv sync # install the environment
123
+ make check # ruff, mypy, and pytest
124
+ make hooks # run mypy before every commit
125
+ make demo # browse the widgets interactively
126
+ uv run zettcode -w . # run the checkout against a workspace
127
+ ```
128
+
129
+ Notes for working inside the repository — layering, the invariant list, the
130
+ platform seam — are in [`AGENTS.md`](AGENTS.md) and
131
+ [`docs/internal/`](docs/internal/). The docs site itself lives in
132
+ `docs/` and builds with `npm ci --prefix docs && npm run docs:build --prefix docs`.
133
+
134
+ ## License
135
+
136
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,107 @@
1
+ # ZettCode
2
+
3
+ **English** · [简体中文](README.zh-CN.md)
4
+
5
+ A focused coding agent for your terminal. Point it at a workspace, describe the
6
+ change you want, and watch the work happen — files read, commands approved,
7
+ edits made, tests run — as a conversation you can scroll, select, and copy.
8
+
9
+ The terminal interface is this repository; the model loop is
10
+ [`zett-agent`](https://github.com/Chang-LeHung/zett-agent), published separately.
11
+
12
+ ## Install
13
+
14
+ ZettCode needs Python 3.10 or newer. It is published on PyPI as `zettcode`:
15
+
16
+ ```bash
17
+ uv tool install zettcode
18
+ # or
19
+ pip install zettcode
20
+ ```
21
+
22
+ ## Configure
23
+
24
+ Everything except the workspace lives in `~/.zettcode/config.toml`. At least one
25
+ model, and the first one is active at startup:
26
+
27
+ ```toml
28
+ [[models]]
29
+ model = "gpt-4o"
30
+ token = "sk-..." # or set OPENAI_API_KEY
31
+ base_url = "http://localhost:8787/v1" # any OpenAI-compatible endpoint
32
+ context_window = 200000 # tokens this model can carry
33
+ multimodal = true # accepts images as well as text
34
+ ```
35
+
36
+ Project `AGENTS.md` files, [skills](https://chang-lehung.github.io/zettcode/guide/skills-and-mcp),
37
+ MCP servers, plugins, the palette, and the rest are all in the same file. The
38
+ full key list is in the [configuration reference](https://chang-lehung.github.io/zettcode/guide/config).
39
+
40
+ ## Run
41
+
42
+ ```bash
43
+ cd ~/projects/api
44
+ zettcode # workspace defaults to the cwd
45
+ zettcode -w /path/to/project # work somewhere else
46
+ zettcode --resume <session-id> # reopen a stored session
47
+ zettcode --dry-run # time the startup and exit
48
+ ```
49
+
50
+ Type a task and press **Enter**. `/` opens the command menu, `@` references a
51
+ skill, `Ctrl-C` stops a request, `Ctrl-D` on an empty composer exits — and
52
+ leaving prints the command that brings the session back.
53
+
54
+ ## What it does
55
+
56
+ - **Streams a conversation, not a payload.** Reasoning, answers, and tool calls
57
+ arrive as rows in order; tool output reads like a log — `Read src/app.py`,
58
+ `Ran pytest -q` — never raw JSON.
59
+ - **Asks before it acts.** Every shell command is confirmed first, with
60
+ `a` to allow the rest of the run and `p` to remember one command.
61
+ - **Keeps the context honest.** The status line shows tokens, cache hit rate,
62
+ and how full the window is; `/context` breaks the request down, and compaction
63
+ summarizes before the window overflows.
64
+ - **Remembers the project.** `AGENTS.md` files from the workspace upward become
65
+ project instructions; skills and MCP servers extend what the model can do.
66
+ - **Respects the terminal.** Mouse selection and scrolling, panels for choices,
67
+ image paste, `Ctrl-L`, and a palette that follows your terminal's background —
68
+ or your own `theme.toml`.
69
+
70
+ ## Documentation
71
+
72
+ The user guide is published at
73
+ **<https://chang-lehung.github.io/zettcode/>** (English, with
74
+ [中文](https://chang-lehung.github.io/zettcode/zh/) alongside it):
75
+
76
+ | | |
77
+ | --- | --- |
78
+ | [Getting started](https://chang-lehung.github.io/zettcode/guide/getting-started) | Install, first task, first approvals. |
79
+ | [The interface](https://chang-lehung.github.io/zettcode/guide/interface) | What each row of the screen tells you. |
80
+ | [Keys and mouse](https://chang-lehung.github.io/zettcode/guide/keys) | The full reference. |
81
+ | [Commands](https://chang-lehung.github.io/zettcode/guide/commands) | Every `/command` and `@resource`. |
82
+ | [Sessions](https://chang-lehung.github.io/zettcode/guide/sessions) | Resume, title, export. |
83
+ | [Configuration](https://chang-lehung.github.io/zettcode/guide/config) | One file, every key. |
84
+ | [Models and context](https://chang-lehung.github.io/zettcode/guide/models) | Effort, compaction, cache, images. |
85
+ | [Project instructions](https://chang-lehung.github.io/zettcode/guide/instructions) | Writing a good `AGENTS.md`. |
86
+ | [Skills and MCP](https://chang-lehung.github.io/zettcode/guide/skills-and-mcp) | Teaching it your own tools. |
87
+ | [Plugins](https://chang-lehung.github.io/zettcode/guide/plugins) | Adding commands and rows in Python. |
88
+ | [Problems and questions](https://chang-lehung.github.io/zettcode/guide/faq) | When something does not work. |
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ uv sync # install the environment
94
+ make check # ruff, mypy, and pytest
95
+ make hooks # run mypy before every commit
96
+ make demo # browse the widgets interactively
97
+ uv run zettcode -w . # run the checkout against a workspace
98
+ ```
99
+
100
+ Notes for working inside the repository — layering, the invariant list, the
101
+ platform seam — are in [`AGENTS.md`](AGENTS.md) and
102
+ [`docs/internal/`](docs/internal/). The docs site itself lives in
103
+ `docs/` and builds with `npm ci --prefix docs && npm run docs:build --prefix docs`.
104
+
105
+ ## License
106
+
107
+ MIT — see [`LICENSE`](LICENSE).
@@ -0,0 +1,98 @@
1
+ # ZettCode
2
+
3
+ [English](README.md) · **简体中文**
4
+
5
+ 一个专注的终端编码智能体。指向一个工作区,说清楚要改什么,然后看着它把活干出来 —— 读文件、
6
+ 等你批准命令、改代码、跑测试 —— 整个过程是一段可以滚动、选中、复制的对话。
7
+
8
+ 终端界面就是本仓库;模型循环在
9
+ [`zett-agent`](https://github.com/Chang-LeHung/zett-agent),单独发布。
10
+
11
+ ## 安装
12
+
13
+ 需要 Python 3.10 或更新版本,PyPI 包名是 `zettcode`:
14
+
15
+ ```bash
16
+ uv tool install zettcode
17
+ # 或者
18
+ pip install zettcode
19
+ ```
20
+
21
+ ## 配置
22
+
23
+ 除了工作区,其他设置都在 `~/.zettcode/config.toml`。至少要有一个模型,第一张表在启动时生效:
24
+
25
+ ```toml
26
+ [[models]]
27
+ model = "gpt-4o"
28
+ token = "sk-..." # 也可以设置 OPENAI_API_KEY
29
+ base_url = "http://localhost:8787/v1" # 任何 OpenAI 兼容的接入点
30
+ context_window = 200000 # 这个模型能装多少 token
31
+ multimodal = true # 除文本外还接受图片
32
+ ```
33
+
34
+ 项目里的 `AGENTS.md`、[skills](https://chang-lehung.github.io/zettcode/zh/guide/skills-and-mcp)、
35
+ MCP 服务器、插件、配色等等都在同一个文件里,完整的键见
36
+ [配置文件](https://chang-lehung.github.io/zettcode/zh/guide/config)。
37
+
38
+ ## 运行
39
+
40
+ ```bash
41
+ cd ~/projects/api
42
+ zettcode # 工作区默认是当前目录
43
+ zettcode -w /path/to/project # 在别处工作
44
+ zettcode --resume <session-id> # 重新打开一个已保存的会话
45
+ zettcode --dry-run # 只测启动耗时后退出
46
+ ```
47
+
48
+ 输入任务并按 **Enter**。`/` 打开命令菜单,`@` 引用 skill,`Ctrl-C` 停止请求,输入框为空时
49
+ `Ctrl-D` 退出 —— 退出时会打印把会话找回来的命令。
50
+
51
+ ## 它能做什么
52
+
53
+ - **流式地呈现对话,而不是丢一堆数据。** 思考、回答、工具调用按顺序各占一行;工具输出读起来像
54
+ 日志 —— `Read src/app.py`、`Ran pytest -q` —— 永远不会甩出原始 JSON。
55
+ - **动手之前先问你。** 每条 shell 命令都要先确认,`a` 放行本轮剩余命令,`p` 记住某一条。
56
+ - **把上下文讲清楚。** 状态行显示 token、缓存命中率和窗口占用;`/context` 拆开当前请求,
57
+ 压缩会在窗口溢出之前完成。
58
+ - **记住项目。** 从工作区逐级向上的 `AGENTS.md` 会变成项目指令;skills 和 MCP 服务器扩展模型
59
+ 能做的事。
60
+ - **尊重终端。** 鼠标选中与滚动、用面板做选择、粘贴图片、`Ctrl-L`,以及跟随终端背景的配色 ——
61
+ 也可以用你自己的 `theme.toml`。
62
+
63
+ ## 文档
64
+
65
+ 用户指南发布在 **<https://chang-lehung.github.io/zettcode/zh/>**(
66
+ [English](https://chang-lehung.github.io/zettcode/) 与之并列):
67
+
68
+ | | |
69
+ | --- | --- |
70
+ | [快速开始](https://chang-lehung.github.io/zettcode/zh/guide/getting-started) | 安装、第一个任务、第一次审批。 |
71
+ | [界面](https://chang-lehung.github.io/zettcode/zh/guide/interface) | 屏幕上每一行在说什么。 |
72
+ | [快捷键与鼠标](https://chang-lehung.github.io/zettcode/zh/guide/keys) | 完整对照表。 |
73
+ | [命令](https://chang-lehung.github.io/zettcode/zh/guide/commands) | 所有 `/命令` 与 `@资源`。 |
74
+ | [会话](https://chang-lehung.github.io/zettcode/zh/guide/sessions) | 恢复、命名、导出。 |
75
+ | [配置文件](https://chang-lehung.github.io/zettcode/zh/guide/config) | 一个文件,所有键。 |
76
+ | [模型与上下文](https://chang-lehung.github.io/zettcode/zh/guide/models) | 推理档位、压缩、缓存、图片。 |
77
+ | [项目指令](https://chang-lehung.github.io/zettcode/zh/guide/instructions) | 怎么写出好用的 `AGENTS.md`。 |
78
+ | [Skills 与 MCP](https://chang-lehung.github.io/zettcode/zh/guide/skills-and-mcp) | 接入你自己的工具。 |
79
+ | [插件](https://chang-lehung.github.io/zettcode/zh/guide/plugins) | 用 Python 添加命令和界面行。 |
80
+ | [常见问题](https://chang-lehung.github.io/zettcode/zh/guide/faq) | 出问题的时候。 |
81
+
82
+ ## 开发
83
+
84
+ ```bash
85
+ uv sync # 安装环境
86
+ make check # ruff、mypy、pytest
87
+ make hooks # 每次提交前跑 mypy
88
+ make demo # 交互式浏览所有组件
89
+ uv run zettcode -w . # 用源码运行
90
+ ```
91
+
92
+ 仓库内部的约定 —— 分层、不变量清单、平台适配 —— 见 [`AGENTS.md`](AGENTS.md) 和
93
+ [`docs/internal/`](docs/internal/)。文档站在 `docs/`,构建命令是
94
+ `npm ci --prefix docs && npm run docs:build --prefix docs`。
95
+
96
+ ## 许可证
97
+
98
+ MIT,见 [`LICENSE`](LICENSE)。
@@ -0,0 +1,101 @@
1
+ [project]
2
+ name = "zettcode"
3
+ description = "A focused terminal coding agent powered by zett-agent"
4
+ readme = "README.md"
5
+ requires-python = ">=3.10"
6
+ dynamic = ["version"]
7
+ license = "MIT"
8
+ license-files = ["LICENSE"]
9
+ authors = [{name = "Chang-LeHung"}]
10
+ keywords = ["agent", "llm", "tui", "terminal", "coding-agent"]
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Environment :: Console",
14
+ "Operating System :: MacOS",
15
+ "Operating System :: Microsoft :: Windows :: Windows 10",
16
+ "Operating System :: POSIX :: Linux",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Programming Language :: Python :: 3.13",
21
+ "Programming Language :: Python :: 3.14",
22
+ "Typing :: Typed",
23
+ ]
24
+ dependencies = [
25
+ # The stdlib gained both of these in 3.11; older runtimes take the backports.
26
+ "exceptiongroup>=1.2,<2; python_version < '3.11'",
27
+ "pyperclip>=1.9,<2",
28
+ "tomli>=2,<3; python_version < '3.11'",
29
+ "wcwidth>=0.2,<1",
30
+ "zett-agent==0.1.10",
31
+ ]
32
+
33
+ [project.scripts]
34
+ zettcode = "zettcode.cli:main"
35
+
36
+ [project.urls]
37
+ Repository = "https://github.com/Chang-LeHung/zettcode"
38
+ Issues = "https://github.com/Chang-LeHung/zettcode/issues"
39
+
40
+ [build-system]
41
+ requires = ["hatchling"]
42
+ build-backend = "hatchling.build"
43
+
44
+ [tool.hatch.version]
45
+ # Single source of truth: the version is written once, in the package.
46
+ path = "src/zettcode/__init__.py"
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/zettcode"]
50
+
51
+ [tool.hatch.build.targets.sdist]
52
+ include = ["/src", "/README.md", "/README.zh-CN.md", "/LICENSE", "/pyproject.toml"]
53
+
54
+ [dependency-groups]
55
+ dev = [
56
+ # Unlike the conditional runtime deps, these are installed on every
57
+ # interpreter so mypy can check the 3.10 branch of ``_compat`` for real.
58
+ "exceptiongroup>=1.2,<2",
59
+ "mypy>=1.14,<2",
60
+ "pytest>=8,<9",
61
+ "pytest-asyncio>=0.26,<2",
62
+ "pytest-cov>=7.1.0",
63
+ "ruff>=0.11,<1",
64
+ "tomli>=2,<3",
65
+ "viztracer>=1.1.1",
66
+ ]
67
+
68
+ [tool.pytest.ini_options]
69
+ testpaths = ["tests"]
70
+ asyncio_mode = "auto"
71
+
72
+ [tool.mypy]
73
+ python_version = "3.10"
74
+ files = ["src", "perf"]
75
+ # Check the pydantic storage models as models, not as plain classes.
76
+ plugins = ["pydantic.mypy"]
77
+ warn_unused_ignores = true
78
+ warn_redundant_casts = true
79
+ no_implicit_optional = true
80
+
81
+ [[tool.mypy.overrides]]
82
+ # Ships no stubs; the runner only calls the two documented functions.
83
+ module = ["pyperclip"]
84
+ ignore_missing_imports = true
85
+
86
+ [tool.ruff]
87
+ line-length = 120
88
+ target-version = "py310"
89
+ src = ["src", "tests", "perf"]
90
+
91
+ [tool.ruff.lint]
92
+ select = ["E", "F", "I", "UP", "B"]
93
+ ignore = ["E501"]
94
+
95
+ [tool.ruff.lint.isort]
96
+ known-first-party = ["zettcode"]
97
+
98
+ [tool.ruff.format]
99
+ quote-style = "double"
100
+ indent-style = "space"
101
+ line-ending = "lf"
@@ -0,0 +1,47 @@
1
+ """Terminal coding agent built on zett-agent."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ from typing import TYPE_CHECKING
7
+
8
+ #: The one place the version is written. ``pyproject.toml`` reads it from here
9
+ #: and the release workflow checks the tag against it, so a bump is one edit.
10
+ __version__ = "0.1.2"
11
+
12
+ #: Where each public name lives; read by :func:`__getattr__` on first use.
13
+ _EXPORTS = {
14
+ "ModelConfig": ".config",
15
+ "ZettCodeAgent": ".app.agent.agent",
16
+ "ZettCodeConfig": ".config",
17
+ "ZettCodeRuntime": ".app.agent.runtime",
18
+ }
19
+
20
+
21
+ if TYPE_CHECKING: # pragma: no cover - for type checkers, not the runtime
22
+ from .app.agent.agent import ZettCodeAgent
23
+ from .app.agent.runtime import ZettCodeRuntime
24
+ from .config import ModelConfig, ZettCodeConfig
25
+
26
+
27
+ def __getattr__(name: str) -> object:
28
+ """Import the module a public name lives in, the first time it is asked for.
29
+
30
+ This package is a facade over several subpackages, and a caller that wants
31
+ one name should not pay for the rest of them — the TUI alone holds the
32
+ widget library, the Markdown parser, the diff viewer, and the syntax
33
+ scanners. ``__all__`` is still the public surface.
34
+ """
35
+ try:
36
+ module = _EXPORTS[name]
37
+ except KeyError:
38
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
39
+ return getattr(importlib.import_module(module, __name__), name)
40
+
41
+
42
+ def __dir__() -> list[str]:
43
+ """Return the names this package publishes."""
44
+ return sorted(__all__)
45
+
46
+
47
+ __all__ = ["ModelConfig", "ZettCodeAgent", "ZettCodeConfig", "ZettCodeRuntime"]
@@ -0,0 +1,35 @@
1
+ """Small shims for the language versions this project supports.
2
+
3
+ ZettCode runs on Python 3.10 through 3.14, so the few things the standard
4
+ library grew in between live here instead of behind version checks at each use.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+ from enum import Enum
11
+
12
+ if sys.version_info >= (3, 11):
13
+ from builtins import BaseExceptionGroup, ExceptionGroup
14
+
15
+ import tomllib
16
+ else: # pragma: no cover - the branch only the 3.10 runtime takes
17
+ import tomli as tomllib
18
+ from exceptiongroup import BaseExceptionGroup, ExceptionGroup
19
+
20
+
21
+ __all__ = ["BaseExceptionGroup", "ExceptionGroup", "StrEnum", "tomllib"]
22
+
23
+
24
+ class StrEnum(str, Enum):
25
+ """A string-valued enum, for the versions before ``enum.StrEnum``.
26
+
27
+ The standard library's version formats as its value, so ``f"{member}"`` is
28
+ ``"ready"`` rather than ``"Activity.READY"``; the status line and the stored
29
+ metadata rely on that, so every interpreter uses this class instead of the
30
+ standard one and the wording cannot drift with the version.
31
+ """
32
+
33
+ def __str__(self) -> str:
34
+ """Return the member's value, as ``enum.StrEnum`` does."""
35
+ return str(self.value)
@@ -0,0 +1,63 @@
1
+ """ZettCode's application layer, split into its interface and its agent glue.
2
+
3
+ ``ui`` holds what the user sees and drives: the transcript and the application
4
+ shell. ``agent`` holds the adapter that turns agent events into transcript
5
+ entries. This module is the facade over both, so ``from zettcode.app import
6
+ ZettCodeApp`` keeps working for callers that do not care where it lives.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import importlib
12
+ from typing import TYPE_CHECKING
13
+
14
+ #: Where each public name lives; read by :func:`__getattr__` on first use.
15
+ _EXPORTS = {
16
+ "Entry": ".agent",
17
+ "Transcript": ".agent",
18
+ "TranscriptProjector": ".agent",
19
+ "TranscriptSource": ".ui",
20
+ "TranscriptView": ".ui",
21
+ "ZettCodeAgent": ".agent",
22
+ "ZettCodeApp": ".ui",
23
+ "ZettCodeRoot": ".ui",
24
+ "ZettCodeRuntime": ".agent",
25
+ }
26
+
27
+
28
+ if TYPE_CHECKING: # pragma: no cover - for type checkers, not the runtime
29
+ from .agent import Entry, Transcript, TranscriptProjector, ZettCodeAgent, ZettCodeRuntime
30
+ from .ui import TranscriptSource, TranscriptView, ZettCodeApp, ZettCodeRoot
31
+
32
+
33
+ def __getattr__(name: str) -> object:
34
+ """Import the module a public name lives in, the first time it is asked for.
35
+
36
+ This package is a facade over several subpackages, and a caller that wants
37
+ one name should not pay for the rest of them — the TUI alone holds the
38
+ widget library, the Markdown parser, the diff viewer, and the syntax
39
+ scanners. ``__all__`` is still the public surface.
40
+ """
41
+ try:
42
+ module = _EXPORTS[name]
43
+ except KeyError:
44
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
45
+ return getattr(importlib.import_module(module, __name__), name)
46
+
47
+
48
+ def __dir__() -> list[str]:
49
+ """Return the names this package publishes."""
50
+ return sorted(__all__)
51
+
52
+
53
+ __all__ = [
54
+ "Entry",
55
+ "Transcript",
56
+ "TranscriptProjector",
57
+ "TranscriptSource",
58
+ "TranscriptView",
59
+ "ZettCodeApp",
60
+ "ZettCodeAgent",
61
+ "ZettCodeRoot",
62
+ "ZettCodeRuntime",
63
+ ]
@@ -0,0 +1,45 @@
1
+ """Agent-facing glue: the conversation model and the events that fill it."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ from typing import TYPE_CHECKING
7
+
8
+ #: Where each public name lives; read by :func:`__getattr__` on first use.
9
+ _EXPORTS = {
10
+ "Entry": ".transcript",
11
+ "Transcript": ".transcript",
12
+ "TranscriptProjector": ".projection",
13
+ "ZettCodeAgent": ".agent",
14
+ "ZettCodeRuntime": ".runtime",
15
+ }
16
+
17
+
18
+ if TYPE_CHECKING: # pragma: no cover - for type checkers, not the runtime
19
+ from .agent import ZettCodeAgent
20
+ from .projection import TranscriptProjector
21
+ from .runtime import ZettCodeRuntime
22
+ from .transcript import Entry, Transcript
23
+
24
+
25
+ def __getattr__(name: str) -> object:
26
+ """Import the module a public name lives in, the first time it is asked for.
27
+
28
+ This package is a facade over several subpackages, and a caller that wants
29
+ one name should not pay for the rest of them — the TUI alone holds the
30
+ widget library, the Markdown parser, the diff viewer, and the syntax
31
+ scanners. ``__all__`` is still the public surface.
32
+ """
33
+ try:
34
+ module = _EXPORTS[name]
35
+ except KeyError:
36
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from None
37
+ return getattr(importlib.import_module(module, __name__), name)
38
+
39
+
40
+ def __dir__() -> list[str]:
41
+ """Return the names this package publishes."""
42
+ return sorted(__all__)
43
+
44
+
45
+ __all__ = ["Entry", "Transcript", "TranscriptProjector", "ZettCodeAgent", "ZettCodeRuntime"]