wizolt 0.37.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.
- wizolt-0.37.0/LICENSE +28 -0
- wizolt-0.37.0/MANIFEST.in +4 -0
- wizolt-0.37.0/PKG-INFO +121 -0
- wizolt-0.37.0/README.md +76 -0
- wizolt-0.37.0/pyproject.toml +97 -0
- wizolt-0.37.0/setup.cfg +4 -0
- wizolt-0.37.0/wizolt/__init__.py +5 -0
- wizolt-0.37.0/wizolt/__main__.py +163 -0
- wizolt-0.37.0/wizolt/base.py +603 -0
- wizolt-0.37.0/wizolt/builtin_skills/__init__.py +1 -0
- wizolt-0.37.0/wizolt/builtin_skills/wizolt-help/SKILL.md +264 -0
- wizolt-0.37.0/wizolt/cli/__init__.py +32 -0
- wizolt-0.37.0/wizolt/cli/commands.py +1007 -0
- wizolt-0.37.0/wizolt/cli/hints.py +102 -0
- wizolt-0.37.0/wizolt/cli/loop.py +1046 -0
- wizolt-0.37.0/wizolt/cli/modals.py +901 -0
- wizolt-0.37.0/wizolt/cli/runtime.py +268 -0
- wizolt-0.37.0/wizolt/cli/update.py +94 -0
- wizolt-0.37.0/wizolt/cli/view.py +577 -0
- wizolt-0.37.0/wizolt/cli/worker.py +307 -0
- wizolt-0.37.0/wizolt/compaction.py +442 -0
- wizolt-0.37.0/wizolt/config.py +674 -0
- wizolt-0.37.0/wizolt/context.py +544 -0
- wizolt-0.37.0/wizolt/engine.py +689 -0
- wizolt-0.37.0/wizolt/image.py +503 -0
- wizolt-0.37.0/wizolt/mcp/__init__.py +8 -0
- wizolt-0.37.0/wizolt/mcp/config.py +74 -0
- wizolt-0.37.0/wizolt/mcp/manager.py +1081 -0
- wizolt-0.37.0/wizolt/mcp/rendering.py +378 -0
- wizolt-0.37.0/wizolt/mcp/tokens.py +115 -0
- wizolt-0.37.0/wizolt/mentions.py +577 -0
- wizolt-0.37.0/wizolt/model/__init__.py +10 -0
- wizolt-0.37.0/wizolt/model/anthropic.py +420 -0
- wizolt-0.37.0/wizolt/model/chat.py +229 -0
- wizolt-0.37.0/wizolt/model/client.py +703 -0
- wizolt-0.37.0/wizolt/model/history.py +14 -0
- wizolt-0.37.0/wizolt/model/protocol.py +375 -0
- wizolt-0.37.0/wizolt/model/resilience.py +252 -0
- wizolt-0.37.0/wizolt/model/responses.py +352 -0
- wizolt-0.37.0/wizolt/prompts.py +234 -0
- wizolt-0.37.0/wizolt/providers/__init__.py +1 -0
- wizolt-0.37.0/wizolt/providers/catalog.json +1999 -0
- wizolt-0.37.0/wizolt/providers/catalog.py +728 -0
- wizolt-0.37.0/wizolt/providers/compat.py +691 -0
- wizolt-0.37.0/wizolt/providers/schema.py +334 -0
- wizolt-0.37.0/wizolt/providers/sync.py +316 -0
- wizolt-0.37.0/wizolt/render.py +1579 -0
- wizolt-0.37.0/wizolt/runner.py +578 -0
- wizolt-0.37.0/wizolt/session/__init__.py +630 -0
- wizolt-0.37.0/wizolt/session/codec.py +483 -0
- wizolt-0.37.0/wizolt/session/diffs.py +225 -0
- wizolt-0.37.0/wizolt/session/images.py +73 -0
- wizolt-0.37.0/wizolt/session/jobs.py +146 -0
- wizolt-0.37.0/wizolt/session/queue.py +55 -0
- wizolt-0.37.0/wizolt/session/store.py +619 -0
- wizolt-0.37.0/wizolt/skill.py +123 -0
- wizolt-0.37.0/wizolt/tools/__init__.py +73 -0
- wizolt-0.37.0/wizolt/tools/ask.py +95 -0
- wizolt-0.37.0/wizolt/tools/base.py +252 -0
- wizolt-0.37.0/wizolt/tools/delegate.py +507 -0
- wizolt-0.37.0/wizolt/tools/editplan.py +188 -0
- wizolt-0.37.0/wizolt/tools/files.py +821 -0
- wizolt-0.37.0/wizolt/tools/mcp.py +103 -0
- wizolt-0.37.0/wizolt/tools/memory.py +451 -0
- wizolt-0.37.0/wizolt/tools/search.py +558 -0
- wizolt-0.37.0/wizolt/tools/shell.py +624 -0
- wizolt-0.37.0/wizolt/tools/skill.py +34 -0
- wizolt-0.37.0/wizolt/tools/toolblocks.py +378 -0
- wizolt-0.37.0/wizolt/tools/tooloutput.py +253 -0
- wizolt-0.37.0/wizolt/tools/toolscript.py +458 -0
- wizolt-0.37.0/wizolt/tui/__init__.py +30 -0
- wizolt-0.37.0/wizolt/tui/app.py +1391 -0
- wizolt-0.37.0/wizolt/tui/views.py +601 -0
- wizolt-0.37.0/wizolt/vision.py +57 -0
- wizolt-0.37.0/wizolt.egg-info/PKG-INFO +121 -0
- wizolt-0.37.0/wizolt.egg-info/SOURCES.txt +78 -0
- wizolt-0.37.0/wizolt.egg-info/dependency_links.txt +1 -0
- wizolt-0.37.0/wizolt.egg-info/entry_points.txt +2 -0
- wizolt-0.37.0/wizolt.egg-info/requires.txt +17 -0
- wizolt-0.37.0/wizolt.egg-info/top_level.txt +1 -0
wizolt-0.37.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, hit9
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
|
16
|
+
may be used to endorse or promote products derived from this software
|
|
17
|
+
without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT INCLUDING NEGLIGENCE OR OTHERWISE ARISING IN ANY WAY OUT OF THE USE OF
|
|
28
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
wizolt-0.37.0/PKG-INFO
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wizolt
|
|
3
|
+
Version: 0.37.0
|
|
4
|
+
Summary: A terminal coding agent written in Python
|
|
5
|
+
Author-email: hit9 <hit9@icloud.com>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/hit9/wizolt
|
|
8
|
+
Project-URL: Repository, https://github.com/hit9/wizolt
|
|
9
|
+
Project-URL: Issues, https://github.com/hit9/wizolt/issues
|
|
10
|
+
Project-URL: Documentation, https://wizolt.readthedocs.io
|
|
11
|
+
Keywords: ai,coding-agent,cli,terminal
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
23
|
+
Classifier: Topic :: Software Development
|
|
24
|
+
Classifier: Topic :: Terminals
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: anthropic>=1.0.0
|
|
29
|
+
Requires-Dist: code-symbol-index>=0.5.1
|
|
30
|
+
Requires-Dist: json-repair
|
|
31
|
+
Requires-Dist: fastmcp-slim[client]<4,>=3
|
|
32
|
+
Requires-Dist: openai>=3.0.0
|
|
33
|
+
Requires-Dist: pathspec>=1.0
|
|
34
|
+
Requires-Dist: pillow>=11.0
|
|
35
|
+
Requires-Dist: prompt-toolkit>=3.0.53
|
|
36
|
+
Requires-Dist: rich>=13.0
|
|
37
|
+
Requires-Dist: socksio>=1.0.0
|
|
38
|
+
Requires-Dist: websockets>=15.0.1
|
|
39
|
+
Provides-Extra: dev
|
|
40
|
+
Requires-Dist: pyright>=1.1.411; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-xdist>=3.8; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff==0.16.0; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
<h1 align="center">wizolt</h1>
|
|
47
|
+
|
|
48
|
+
<p align="center">
|
|
49
|
+
<img src="https://raw.githubusercontent.com/hit9/wizolt/master/snapshots/wizolt1.gif" alt="wizolt editing code and running tools" width="600">
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
<p align="center">
|
|
53
|
+
A terminal coding agent I use, maintain, and customize, shipped as a self-contained Python package.
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
## Safety
|
|
57
|
+
|
|
58
|
+
**Use at your own risk.** wizolt can edit files and run shell commands in the environment where it starts. It does not provide sandbox isolation; use a container or VM when needed.
|
|
59
|
+
|
|
60
|
+
## What it is
|
|
61
|
+
|
|
62
|
+
wizolt does not introduce a new kind of coding agent. It combines familiar features — reading and editing files, running commands, follow-ups, sessions, diffs, MCP, and skills — into a tool I use personally.
|
|
63
|
+
|
|
64
|
+
It works on real repositories, including its own: I use wizolt to build and maintain wizolt. Everything ships in one self-contained Python package, so I can change the behavior directly whenever I want the workflow to work differently.
|
|
65
|
+
|
|
66
|
+
Wizolt is the former minacode, which began as the single-file nanocode. The implementation outgrew both earlier names; the project history remains continuous.
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<img src="https://raw.githubusercontent.com/hit9/wizolt/master/snapshots/wizolt2.gif" alt="wizolt resuming a saved session" width="600">
|
|
70
|
+
</p>
|
|
71
|
+
<p align="center"><sub>Resuming a saved session with its conversation and tool history.</sub></p>
|
|
72
|
+
|
|
73
|
+
## Highlights
|
|
74
|
+
|
|
75
|
+
- **Worker delegation:** hand a bounded task to a second in-process session on its own provider with `/worker`; the `Delegate` tool keeps worker context across delegations until reset.
|
|
76
|
+
- **Forced reply language:** `/language` or `[runtime] language` pins the reply language for the session.
|
|
77
|
+
- **Smarter retries:** exponential backoff with jitter and provider `Retry-After`, shown as a live `retrying` phase with a countdown.
|
|
78
|
+
- **Prompt-cache aware:** stable request prefixes let supported providers reuse work and can reach 90–99% cache hit rates; `/status` shows the reported result.
|
|
79
|
+
- **Code navigation:** jump to definitions, callers, and implementations with a searchable code index.
|
|
80
|
+
- **Live follow-ups:** type while the agent works; `Enter` queues a message for the next model step, while `Ctrl-C` discards a draft or interrupts the task once the input is empty.
|
|
81
|
+
- **Anchored edits:** structured edits use `line:hash` anchors and reject stale file content.
|
|
82
|
+
- **Resumable sessions:** conversation, tool calls, diffs, and working memory survive `-c` or `--resume`.
|
|
83
|
+
- **Built-in diff viewer:** `/diff` shows the latest round and the net session result.
|
|
84
|
+
- **MCP and skills:** connect Model Context Protocol servers and load Markdown instruction packs on demand.
|
|
85
|
+
- **Provider-side web search:** opt in to a provider's own search tool (OpenAI, Qwen, Anthropic, Z.AI) and see each search and its sources in the transcript.
|
|
86
|
+
- **Provider compatibility:** OpenAI-compatible APIs and Anthropic.
|
|
87
|
+
|
|
88
|
+
## Install
|
|
89
|
+
|
|
90
|
+
Requires macOS or Linux, Python 3.11+, and [uv](https://docs.astral.sh/uv/).
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
uv tool install wizolt
|
|
94
|
+
wizolt --init-config
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Add your provider to `~/.wizolt/config.toml`:
|
|
98
|
+
|
|
99
|
+
```toml
|
|
100
|
+
[provider]
|
|
101
|
+
active = "default"
|
|
102
|
+
|
|
103
|
+
[provider.default]
|
|
104
|
+
url = "https://api.deepseek.com"
|
|
105
|
+
key = "sk-..."
|
|
106
|
+
model = "deepseek-v4-flash"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then run:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
wizolt
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Upgrade with `uv tool upgrade wizolt`.
|
|
116
|
+
|
|
117
|
+
## Links
|
|
118
|
+
|
|
119
|
+
- [Documentation](https://wizolt.readthedocs.io/en/latest/) — full usage guide and reference.
|
|
120
|
+
- [Blog post](https://hit9.dev/post/nanocode) — why and how it was built.
|
|
121
|
+
- [code-symbol-index](https://github.com/hit9/code-symbol-index) — the code index library wizolt uses.
|
wizolt-0.37.0/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<h1 align="center">wizolt</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/hit9/wizolt/master/snapshots/wizolt1.gif" alt="wizolt editing code and running tools" width="600">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
A terminal coding agent I use, maintain, and customize, shipped as a self-contained Python package.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
## Safety
|
|
12
|
+
|
|
13
|
+
**Use at your own risk.** wizolt can edit files and run shell commands in the environment where it starts. It does not provide sandbox isolation; use a container or VM when needed.
|
|
14
|
+
|
|
15
|
+
## What it is
|
|
16
|
+
|
|
17
|
+
wizolt does not introduce a new kind of coding agent. It combines familiar features — reading and editing files, running commands, follow-ups, sessions, diffs, MCP, and skills — into a tool I use personally.
|
|
18
|
+
|
|
19
|
+
It works on real repositories, including its own: I use wizolt to build and maintain wizolt. Everything ships in one self-contained Python package, so I can change the behavior directly whenever I want the workflow to work differently.
|
|
20
|
+
|
|
21
|
+
Wizolt is the former minacode, which began as the single-file nanocode. The implementation outgrew both earlier names; the project history remains continuous.
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
<img src="https://raw.githubusercontent.com/hit9/wizolt/master/snapshots/wizolt2.gif" alt="wizolt resuming a saved session" width="600">
|
|
25
|
+
</p>
|
|
26
|
+
<p align="center"><sub>Resuming a saved session with its conversation and tool history.</sub></p>
|
|
27
|
+
|
|
28
|
+
## Highlights
|
|
29
|
+
|
|
30
|
+
- **Worker delegation:** hand a bounded task to a second in-process session on its own provider with `/worker`; the `Delegate` tool keeps worker context across delegations until reset.
|
|
31
|
+
- **Forced reply language:** `/language` or `[runtime] language` pins the reply language for the session.
|
|
32
|
+
- **Smarter retries:** exponential backoff with jitter and provider `Retry-After`, shown as a live `retrying` phase with a countdown.
|
|
33
|
+
- **Prompt-cache aware:** stable request prefixes let supported providers reuse work and can reach 90–99% cache hit rates; `/status` shows the reported result.
|
|
34
|
+
- **Code navigation:** jump to definitions, callers, and implementations with a searchable code index.
|
|
35
|
+
- **Live follow-ups:** type while the agent works; `Enter` queues a message for the next model step, while `Ctrl-C` discards a draft or interrupts the task once the input is empty.
|
|
36
|
+
- **Anchored edits:** structured edits use `line:hash` anchors and reject stale file content.
|
|
37
|
+
- **Resumable sessions:** conversation, tool calls, diffs, and working memory survive `-c` or `--resume`.
|
|
38
|
+
- **Built-in diff viewer:** `/diff` shows the latest round and the net session result.
|
|
39
|
+
- **MCP and skills:** connect Model Context Protocol servers and load Markdown instruction packs on demand.
|
|
40
|
+
- **Provider-side web search:** opt in to a provider's own search tool (OpenAI, Qwen, Anthropic, Z.AI) and see each search and its sources in the transcript.
|
|
41
|
+
- **Provider compatibility:** OpenAI-compatible APIs and Anthropic.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
Requires macOS or Linux, Python 3.11+, and [uv](https://docs.astral.sh/uv/).
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
uv tool install wizolt
|
|
49
|
+
wizolt --init-config
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Add your provider to `~/.wizolt/config.toml`:
|
|
53
|
+
|
|
54
|
+
```toml
|
|
55
|
+
[provider]
|
|
56
|
+
active = "default"
|
|
57
|
+
|
|
58
|
+
[provider.default]
|
|
59
|
+
url = "https://api.deepseek.com"
|
|
60
|
+
key = "sk-..."
|
|
61
|
+
model = "deepseek-v4-flash"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then run:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
wizolt
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Upgrade with `uv tool upgrade wizolt`.
|
|
71
|
+
|
|
72
|
+
## Links
|
|
73
|
+
|
|
74
|
+
- [Documentation](https://wizolt.readthedocs.io/en/latest/) — full usage guide and reference.
|
|
75
|
+
- [Blog post](https://hit9.dev/post/nanocode) — why and how it was built.
|
|
76
|
+
- [code-symbol-index](https://github.com/hit9/code-symbol-index) — the code index library wizolt uses.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wizolt"
|
|
7
|
+
version = "0.37.0"
|
|
8
|
+
description = "A terminal coding agent written in Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "BSD-3-Clause"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "hit9", email = "hit9@icloud.com" },
|
|
15
|
+
]
|
|
16
|
+
keywords = ["ai", "coding-agent", "cli", "terminal"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Operating System :: POSIX :: Linux",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Programming Language :: Python :: 3.15",
|
|
29
|
+
"Topic :: Software Development",
|
|
30
|
+
"Topic :: Terminals",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
dependencies = [
|
|
34
|
+
# anthropic 1.x and openai 3.x both moved their HTTP client to httpx2, which stops installing
|
|
35
|
+
# certifi and verifies TLS against the OS trust store instead (SSL_CERT_FILE/SSL_CERT_DIR
|
|
36
|
+
# override it). The floors pin that migration rather than leaving it to the resolver: an older
|
|
37
|
+
# pair would land on plain httpx, a combination retryable_error's transport-error matching is
|
|
38
|
+
# tested against but the rest of the tree no longer is.
|
|
39
|
+
"anthropic>=1.0.0",
|
|
40
|
+
"code-symbol-index>=0.5.1",
|
|
41
|
+
"json-repair",
|
|
42
|
+
"fastmcp-slim[client]>=3,<4",
|
|
43
|
+
"openai>=3.0.0",
|
|
44
|
+
"pathspec>=1.0",
|
|
45
|
+
"pillow>=11.0",
|
|
46
|
+
# 3.0.53 fixed Application.invalidate() so it is safe to call from any thread; older versions
|
|
47
|
+
# crash with "no running event loop" when the agent thread asks for a redraw.
|
|
48
|
+
"prompt-toolkit>=3.0.53",
|
|
49
|
+
"rich>=13.0",
|
|
50
|
+
"socksio>=1.0.0",
|
|
51
|
+
"websockets>=15.0.1",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[project.urls]
|
|
55
|
+
Homepage = "https://github.com/hit9/wizolt"
|
|
56
|
+
Repository = "https://github.com/hit9/wizolt"
|
|
57
|
+
Issues = "https://github.com/hit9/wizolt/issues"
|
|
58
|
+
Documentation = "https://wizolt.readthedocs.io"
|
|
59
|
+
|
|
60
|
+
[project.scripts]
|
|
61
|
+
wizolt = "wizolt.__main__:main"
|
|
62
|
+
|
|
63
|
+
[project.optional-dependencies]
|
|
64
|
+
dev = [
|
|
65
|
+
"pyright>=1.1.411",
|
|
66
|
+
"pytest>=8.0",
|
|
67
|
+
"pytest-xdist>=3.8",
|
|
68
|
+
"ruff==0.16.0",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.setuptools]
|
|
72
|
+
packages = ["wizolt", "wizolt.builtin_skills", "wizolt.cli", "wizolt.model", "wizolt.mcp", "wizolt.providers", "wizolt.session", "wizolt.tools", "wizolt.tui"]
|
|
73
|
+
|
|
74
|
+
[tool.setuptools.package-data]
|
|
75
|
+
wizolt = ["builtin_skills/*/SKILL.md", "providers/catalog.json"]
|
|
76
|
+
|
|
77
|
+
[tool.pytest.ini_options]
|
|
78
|
+
testpaths = ["tests"]
|
|
79
|
+
addopts = ["-n", "auto"]
|
|
80
|
+
|
|
81
|
+
[tool.ruff]
|
|
82
|
+
line-length = 160
|
|
83
|
+
target-version = "py311"
|
|
84
|
+
|
|
85
|
+
[tool.ruff.lint]
|
|
86
|
+
extend-select = ["F403"]
|
|
87
|
+
per-file-ignores = { "wizolt/__init__.py" = ["F401"] }
|
|
88
|
+
|
|
89
|
+
[tool.pyright]
|
|
90
|
+
include = ["wizolt"]
|
|
91
|
+
# `uv build` leaves a full copy of the package under build/lib. An editor that analyzes any file in
|
|
92
|
+
# the workspace type-checks that copy as a second definition of every class, so an assignment there
|
|
93
|
+
# reports a mismatch between two same-named types (`ToolRunner* is not assignable to ToolRunner`).
|
|
94
|
+
# `include` alone does not stop it, because the file is analyzed when opened.
|
|
95
|
+
exclude = ["build", "dist", "**/__pycache__", "**/.venv"]
|
|
96
|
+
pythonVersion = "3.11"
|
|
97
|
+
typeCheckingMode = "basic"
|
wizolt-0.37.0/setup.cfg
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""wizolt entry point: command-line argument parsing and dispatch.
|
|
2
|
+
|
|
3
|
+
Invoked through the ``wizolt`` console script or ``python -m wizolt``.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import argparse
|
|
9
|
+
import contextlib
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
import sys
|
|
13
|
+
import threading
|
|
14
|
+
|
|
15
|
+
from wizolt.base import ConfigError, UpdateStatus, WizoltError, __version__, configure_logging
|
|
16
|
+
from wizolt.cli import CommandLoop
|
|
17
|
+
from wizolt.cli.update import UpdateChecker
|
|
18
|
+
from wizolt.config import (
|
|
19
|
+
Config,
|
|
20
|
+
ConfigFile,
|
|
21
|
+
RuntimeSettings,
|
|
22
|
+
)
|
|
23
|
+
from wizolt.engine import Agent
|
|
24
|
+
from wizolt.providers.schema import CatalogError
|
|
25
|
+
from wizolt.providers.sync import CatalogRuntime
|
|
26
|
+
from wizolt.render import Theme
|
|
27
|
+
from wizolt.session import Session
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run_update() -> int:
|
|
31
|
+
"""Check PyPI for a newer wizolt and upgrade it via the detected package manager."""
|
|
32
|
+
print(f"wizolt {__version__}")
|
|
33
|
+
try:
|
|
34
|
+
latest = UpdateChecker.fetch_latest()
|
|
35
|
+
except Exception as error: # noqa: BLE001 - update failures from any network/backend layer are reported uniformly.
|
|
36
|
+
print(f"Error: could not check the latest version: {error}", file=sys.stderr)
|
|
37
|
+
return 1
|
|
38
|
+
if not UpdateStatus(latest=latest).newer_than(__version__):
|
|
39
|
+
print(f"already up to date ({__version__})")
|
|
40
|
+
return 0
|
|
41
|
+
command = UpdateChecker.upgrade_command()
|
|
42
|
+
print(f"updating {__version__} -> {latest}: {' '.join(command)}")
|
|
43
|
+
try:
|
|
44
|
+
return subprocess.call(command)
|
|
45
|
+
except OSError as error:
|
|
46
|
+
print(f"Error: could not run the upgrade command: {error}", file=sys.stderr)
|
|
47
|
+
return 1
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def warm_provider_sdks() -> None:
|
|
51
|
+
"""Import the provider SDKs off the main thread so the prompt accepts input immediately.
|
|
52
|
+
|
|
53
|
+
ModelClient imports them lazily because they cost ~0.8s, which was the whole of the delay
|
|
54
|
+
before a fresh prompt echoed keystrokes. Loading them here in the background keeps the prompt
|
|
55
|
+
instant without moving that cost onto the first request: the user's first message takes far
|
|
56
|
+
longer to type than the import takes to finish.
|
|
57
|
+
|
|
58
|
+
Racing this thread against the request path is safe, and deliberately so:
|
|
59
|
+
|
|
60
|
+
- CPython locks imports per module (`importlib._bootstrap._ModuleLock`), so a request-path
|
|
61
|
+
`from openai import OpenAI` that lands mid-warm-up blocks on that module's lock and then
|
|
62
|
+
reads the finished module from `sys.modules`. It cannot observe a half-initialized module,
|
|
63
|
+
and both threads therefore bind the same class object.
|
|
64
|
+
- Per-module locks can deadlock only on an import cycle entered from two threads at once.
|
|
65
|
+
`anthropic` and `openai` do not import each other, and their shared dependencies form a DAG,
|
|
66
|
+
so the lock-wait graph has no cycle. `_DeadlockError` detection is the backstop if that ever
|
|
67
|
+
stops being true.
|
|
68
|
+
- The thread is a daemon because warming must never delay exit. CPython freezes daemon threads
|
|
69
|
+
at finalization rather than letting them run against a torn-down import system, so quitting
|
|
70
|
+
mid-import is silent.
|
|
71
|
+
|
|
72
|
+
Verified by stress test: a barrier-synchronized four-way race and repeated immediate-exit runs
|
|
73
|
+
produce no deadlock, no exception, and no stderr noise.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
def load() -> None:
|
|
77
|
+
# Warming is only an optimization, and an uncaught failure here would print a thread
|
|
78
|
+
# traceback over the live prompt. Any real problem resurfaces on the request path, which
|
|
79
|
+
# imports the same modules and reports the failure to the user.
|
|
80
|
+
with contextlib.suppress(Exception):
|
|
81
|
+
import anthropic # noqa: F401 - imported for its side effect of populating sys.modules
|
|
82
|
+
import openai # noqa: F401
|
|
83
|
+
|
|
84
|
+
threading.Thread(target=load, name="sdk-warmup", daemon=True).start()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def main(argv: list[str] | None = None) -> int:
|
|
88
|
+
configure_logging()
|
|
89
|
+
if sys.platform == "win32":
|
|
90
|
+
print("Error: wizolt does not support native Windows; use WSL instead.", file=sys.stderr)
|
|
91
|
+
return 1
|
|
92
|
+
|
|
93
|
+
parser = argparse.ArgumentParser(prog="wizolt", epilog="Documentation: https://wizolt.readthedocs.io")
|
|
94
|
+
parser.add_argument("--config", default=None, help="Path to config TOML")
|
|
95
|
+
parser.add_argument("--init-config", action="store_true", help="Create a default config file")
|
|
96
|
+
parser.add_argument("--yolo", action="store_true", help="Skip confirmations for mutating tools")
|
|
97
|
+
parser.add_argument(
|
|
98
|
+
"--theme", choices=["auto", "light", "dark"], default="", help="Color theme (defaults to runtime.theme, then auto-detect via COLORFGBG)"
|
|
99
|
+
)
|
|
100
|
+
resume = parser.add_mutually_exclusive_group()
|
|
101
|
+
resume.add_argument(
|
|
102
|
+
"--resume",
|
|
103
|
+
default="",
|
|
104
|
+
nargs="?",
|
|
105
|
+
const="latest",
|
|
106
|
+
help='Resume a session by UID, uid prefix, or name, or "latest"/"last" for this project\'s most recent',
|
|
107
|
+
)
|
|
108
|
+
resume.add_argument("-c", "--last", "--latest", dest="continue_project", action="store_true", help="Resume the latest session in the current project")
|
|
109
|
+
parser.add_argument("-v", "--version", action="store_true", help="Show version")
|
|
110
|
+
parser.add_argument(
|
|
111
|
+
"command", nargs="?", choices=["update", "upgrade"], default=None, help="Maintenance command: update/upgrade wizolt to the latest version"
|
|
112
|
+
)
|
|
113
|
+
args = parser.parse_args(argv)
|
|
114
|
+
if args.version:
|
|
115
|
+
print(__version__)
|
|
116
|
+
return 0
|
|
117
|
+
if args.command in {"update", "upgrade"}:
|
|
118
|
+
return run_update()
|
|
119
|
+
try:
|
|
120
|
+
if args.init_config:
|
|
121
|
+
path, created = ConfigFile.init(args.config)
|
|
122
|
+
print(("Created" if created else "Exists") + " config: " + path)
|
|
123
|
+
return 0
|
|
124
|
+
# Switching sessions ends one run and starts the next rather than re-pointing a live
|
|
125
|
+
# object graph at another Session: everything below is built around one, and this is the
|
|
126
|
+
# only moment nothing is running. Teardown stays in the `finally` that already does it.
|
|
127
|
+
resume = args.resume or ("latest" if args.continue_project else "")
|
|
128
|
+
while True:
|
|
129
|
+
if resume:
|
|
130
|
+
data = ConfigFile.load(args.config)
|
|
131
|
+
catalog = CatalogRuntime(Config.data_dir_from(data))
|
|
132
|
+
config = Config.from_dict(data, policy=catalog.policy)
|
|
133
|
+
session = Session.load_snapshot(
|
|
134
|
+
resume,
|
|
135
|
+
config=config,
|
|
136
|
+
settings=RuntimeSettings.from_dict(data, yolo=args.yolo, theme=args.theme),
|
|
137
|
+
cwd=os.getcwd(),
|
|
138
|
+
catalog=catalog,
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
session = Session.from_config_file(path=args.config, yolo=args.yolo, theme=args.theme)
|
|
142
|
+
Theme.set_mode(Theme.resolve(session.settings.theme))
|
|
143
|
+
warm_provider_sdks()
|
|
144
|
+
command_loop = CommandLoop(Agent(session))
|
|
145
|
+
try:
|
|
146
|
+
code = command_loop.run()
|
|
147
|
+
finally:
|
|
148
|
+
command_loop.close_background_output()
|
|
149
|
+
if session.mcp is not None:
|
|
150
|
+
session.mcp.close()
|
|
151
|
+
resume = command_loop.resume_request
|
|
152
|
+
if not resume:
|
|
153
|
+
return code
|
|
154
|
+
except ConfigError as error:
|
|
155
|
+
print("ConfigError: " + str(error), file=sys.stderr)
|
|
156
|
+
return 2
|
|
157
|
+
except (WizoltError, CatalogError) as error:
|
|
158
|
+
print("Error: " + str(error), file=sys.stderr)
|
|
159
|
+
return 1
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
raise SystemExit(main())
|