plyngent 0.1.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 (145) hide show
  1. plyngent-0.1.0/LICENSE +21 -0
  2. plyngent-0.1.0/PKG-INFO +268 -0
  3. plyngent-0.1.0/README.md +248 -0
  4. plyngent-0.1.0/pyproject.toml +182 -0
  5. plyngent-0.1.0/src/plyngent/__init__.py +6 -0
  6. plyngent-0.1.0/src/plyngent/__main__.py +4 -0
  7. plyngent-0.1.0/src/plyngent/agent/__init__.py +28 -0
  8. plyngent-0.1.0/src/plyngent/agent/budget.py +200 -0
  9. plyngent-0.1.0/src/plyngent/agent/chat.py +337 -0
  10. plyngent-0.1.0/src/plyngent/agent/client.py +35 -0
  11. plyngent-0.1.0/src/plyngent/agent/compact.py +180 -0
  12. plyngent-0.1.0/src/plyngent/agent/events.py +66 -0
  13. plyngent-0.1.0/src/plyngent/agent/loop.py +299 -0
  14. plyngent-0.1.0/src/plyngent/agent/responses_bridge.py +312 -0
  15. plyngent-0.1.0/src/plyngent/agent/responses_client.py +126 -0
  16. plyngent-0.1.0/src/plyngent/agent/tools.py +243 -0
  17. plyngent-0.1.0/src/plyngent/agent/usage.py +154 -0
  18. plyngent-0.1.0/src/plyngent/cli/__init__.py +1 -0
  19. plyngent-0.1.0/src/plyngent/cli/__main__.py +4 -0
  20. plyngent-0.1.0/src/plyngent/cli/app.py +488 -0
  21. plyngent-0.1.0/src/plyngent/cli/display.py +215 -0
  22. plyngent-0.1.0/src/plyngent/cli/editor.py +175 -0
  23. plyngent-0.1.0/src/plyngent/cli/exit_codes.py +8 -0
  24. plyngent-0.1.0/src/plyngent/cli/export.py +122 -0
  25. plyngent-0.1.0/src/plyngent/cli/input_text.py +125 -0
  26. plyngent-0.1.0/src/plyngent/cli/interrupt.py +73 -0
  27. plyngent-0.1.0/src/plyngent/cli/limits.py +168 -0
  28. plyngent-0.1.0/src/plyngent/cli/models_source.py +62 -0
  29. plyngent-0.1.0/src/plyngent/cli/provider_recovery.py +113 -0
  30. plyngent-0.1.0/src/plyngent/cli/readline_setup.py +102 -0
  31. plyngent-0.1.0/src/plyngent/cli/repl.py +59 -0
  32. plyngent-0.1.0/src/plyngent/cli/retry.py +215 -0
  33. plyngent-0.1.0/src/plyngent/cli/selection.py +101 -0
  34. plyngent-0.1.0/src/plyngent/cli/slash.py +876 -0
  35. plyngent-0.1.0/src/plyngent/cli/state.py +448 -0
  36. plyngent-0.1.0/src/plyngent/config/__init__.py +47 -0
  37. plyngent-0.1.0/src/plyngent/config/models.py +110 -0
  38. plyngent-0.1.0/src/plyngent/config/path.py +19 -0
  39. plyngent-0.1.0/src/plyngent/config/store.py +273 -0
  40. plyngent-0.1.0/src/plyngent/lmproto/__init__.py +0 -0
  41. plyngent-0.1.0/src/plyngent/lmproto/deepseek/__init__.py +8 -0
  42. plyngent-0.1.0/src/plyngent/lmproto/deepseek/openai_compat/client.py +56 -0
  43. plyngent-0.1.0/src/plyngent/lmproto/deepseek/openai_compat/model.py +57 -0
  44. plyngent-0.1.0/src/plyngent/lmproto/openai/__init__.py +12 -0
  45. plyngent-0.1.0/src/plyngent/lmproto/openai/client.py +131 -0
  46. plyngent-0.1.0/src/plyngent/lmproto/openai/model.py +325 -0
  47. plyngent-0.1.0/src/plyngent/lmproto/openai_compatible/__init__.py +43 -0
  48. plyngent-0.1.0/src/plyngent/lmproto/openai_compatible/client.py +249 -0
  49. plyngent-0.1.0/src/plyngent/lmproto/openai_compatible/config.py +7 -0
  50. plyngent-0.1.0/src/plyngent/lmproto/openai_compatible/model.py +276 -0
  51. plyngent-0.1.0/src/plyngent/memory/__init__.py +9 -0
  52. plyngent-0.1.0/src/plyngent/memory/database/__init__.py +10 -0
  53. plyngent-0.1.0/src/plyngent/memory/database/engine.py +35 -0
  54. plyngent-0.1.0/src/plyngent/memory/database/schema.py +57 -0
  55. plyngent-0.1.0/src/plyngent/memory/database/store.py +310 -0
  56. plyngent-0.1.0/src/plyngent/prompting.py +403 -0
  57. plyngent-0.1.0/src/plyngent/runtime/__init__.py +3 -0
  58. plyngent-0.1.0/src/plyngent/runtime/client_factory.py +83 -0
  59. plyngent-0.1.0/src/plyngent/tools/__init__.py +44 -0
  60. plyngent-0.1.0/src/plyngent/tools/chat/__init__.py +5 -0
  61. plyngent-0.1.0/src/plyngent/tools/chat/ask.py +18 -0
  62. plyngent-0.1.0/src/plyngent/tools/chat/choose.py +78 -0
  63. plyngent-0.1.0/src/plyngent/tools/chat/form.py +74 -0
  64. plyngent-0.1.0/src/plyngent/tools/danger.py +46 -0
  65. plyngent-0.1.0/src/plyngent/tools/file/__init__.py +25 -0
  66. plyngent-0.1.0/src/plyngent/tools/file/edit_lineno.py +97 -0
  67. plyngent-0.1.0/src/plyngent/tools/file/edit_replace.py +20 -0
  68. plyngent-0.1.0/src/plyngent/tools/file/fs_ops.py +152 -0
  69. plyngent-0.1.0/src/plyngent/tools/file/glob_paths.py +99 -0
  70. plyngent-0.1.0/src/plyngent/tools/file/grep_files.py +171 -0
  71. plyngent-0.1.0/src/plyngent/tools/file/listdir.py +17 -0
  72. plyngent-0.1.0/src/plyngent/tools/file/read.py +24 -0
  73. plyngent-0.1.0/src/plyngent/tools/file/tree.py +149 -0
  74. plyngent-0.1.0/src/plyngent/tools/file/write.py +13 -0
  75. plyngent-0.1.0/src/plyngent/tools/process/__init__.py +7 -0
  76. plyngent-0.1.0/src/plyngent/tools/process/close_pty.py +17 -0
  77. plyngent-0.1.0/src/plyngent/tools/process/open_pty.py +32 -0
  78. plyngent-0.1.0/src/plyngent/tools/process/pty_backend.py +287 -0
  79. plyngent-0.1.0/src/plyngent/tools/process/pty_session.py +415 -0
  80. plyngent-0.1.0/src/plyngent/tools/process/read_pty.py +45 -0
  81. plyngent-0.1.0/src/plyngent/tools/process/run_command.py +172 -0
  82. plyngent-0.1.0/src/plyngent/tools/process/write_pty.py +27 -0
  83. plyngent-0.1.0/src/plyngent/tools/vcs/__init__.py +11 -0
  84. plyngent-0.1.0/src/plyngent/tools/vcs/backend.py +29 -0
  85. plyngent-0.1.0/src/plyngent/tools/vcs/detect.py +50 -0
  86. plyngent-0.1.0/src/plyngent/tools/vcs/git_backend.py +120 -0
  87. plyngent-0.1.0/src/plyngent/tools/vcs/tools.py +83 -0
  88. plyngent-0.1.0/src/plyngent/tools/workspace.py +114 -0
  89. plyngent-0.1.0/src/plyngent/typedef.py +7 -0
  90. plyngent-0.1.0/src/plyngent/utils/__init__.py +1 -0
  91. plyngent-0.1.0/src/plyngent/utils/components.py +48 -0
  92. plyngent-0.1.0/tests/__init__.py +0 -0
  93. plyngent-0.1.0/tests/test_agent/test_budget.py +22 -0
  94. plyngent-0.1.0/tests/test_agent/test_compact.py +262 -0
  95. plyngent-0.1.0/tests/test_agent/test_compact_summarize.py +150 -0
  96. plyngent-0.1.0/tests/test_agent/test_confirm.py +59 -0
  97. plyngent-0.1.0/tests/test_agent/test_loop.py +827 -0
  98. plyngent-0.1.0/tests/test_agent/test_parallel_tools.py +158 -0
  99. plyngent-0.1.0/tests/test_agent/test_responses_bridge.py +174 -0
  100. plyngent-0.1.0/tests/test_agent/test_stream_merge.py +49 -0
  101. plyngent-0.1.0/tests/test_agent/test_tools.py +90 -0
  102. plyngent-0.1.0/tests/test_agent/test_usage.py +109 -0
  103. plyngent-0.1.0/tests/test_cli/test_app.py +33 -0
  104. plyngent-0.1.0/tests/test_cli/test_compact_cmd.py +133 -0
  105. plyngent-0.1.0/tests/test_cli/test_display.py +107 -0
  106. plyngent-0.1.0/tests/test_cli/test_editor.py +161 -0
  107. plyngent-0.1.0/tests/test_cli/test_export.py +49 -0
  108. plyngent-0.1.0/tests/test_cli/test_input_text.py +61 -0
  109. plyngent-0.1.0/tests/test_cli/test_interrupt.py +50 -0
  110. plyngent-0.1.0/tests/test_cli/test_limits.py +32 -0
  111. plyngent-0.1.0/tests/test_cli/test_models_source.py +52 -0
  112. plyngent-0.1.0/tests/test_cli/test_oneshot.py +159 -0
  113. plyngent-0.1.0/tests/test_cli/test_provider_recovery.py +75 -0
  114. plyngent-0.1.0/tests/test_cli/test_readline_setup.py +183 -0
  115. plyngent-0.1.0/tests/test_cli/test_repl_commands.py +392 -0
  116. plyngent-0.1.0/tests/test_cli/test_retry.py +213 -0
  117. plyngent-0.1.0/tests/test_cli/test_selection.py +88 -0
  118. plyngent-0.1.0/tests/test_cli/test_workspace_resume.py +183 -0
  119. plyngent-0.1.0/tests/test_config/.gitignore +1 -0
  120. plyngent-0.1.0/tests/test_config/plyngent-bad.toml +15 -0
  121. plyngent-0.1.0/tests/test_config/plyngent-empty.toml +0 -0
  122. plyngent-0.1.0/tests/test_config/plyngent-invalid.toml +3 -0
  123. plyngent-0.1.0/tests/test_config/plyngent-valid.toml +32 -0
  124. plyngent-0.1.0/tests/test_config/test_agent_section.py +43 -0
  125. plyngent-0.1.0/tests/test_config/test_config.py +198 -0
  126. plyngent-0.1.0/tests/test_lmproto/test_models.py +58 -0
  127. plyngent-0.1.0/tests/test_lmproto/test_responses.py +155 -0
  128. plyngent-0.1.0/tests/test_lmproto/test_sse_parse.py +116 -0
  129. plyngent-0.1.0/tests/test_memory/test_store.py +205 -0
  130. plyngent-0.1.0/tests/test_prompting.py +144 -0
  131. plyngent-0.1.0/tests/test_runtime/test_client_factory.py +80 -0
  132. plyngent-0.1.0/tests/test_tools/__init__.py +0 -0
  133. plyngent-0.1.0/tests/test_tools/conftest.py +19 -0
  134. plyngent-0.1.0/tests/test_tools/helpers.py +20 -0
  135. plyngent-0.1.0/tests/test_tools/test_chat_tools.py +66 -0
  136. plyngent-0.1.0/tests/test_tools/test_danger.py +28 -0
  137. plyngent-0.1.0/tests/test_tools/test_edit_lineno.py +37 -0
  138. plyngent-0.1.0/tests/test_tools/test_file.py +40 -0
  139. plyngent-0.1.0/tests/test_tools/test_fs_ops.py +99 -0
  140. plyngent-0.1.0/tests/test_tools/test_glob_paths.py +42 -0
  141. plyngent-0.1.0/tests/test_tools/test_grep_files.py +47 -0
  142. plyngent-0.1.0/tests/test_tools/test_process.py +295 -0
  143. plyngent-0.1.0/tests/test_tools/test_tree.py +85 -0
  144. plyngent-0.1.0/tests/test_tools/test_vcs.py +110 -0
  145. plyngent-0.1.0/tests/test_tools/test_workspace.py +55 -0
plyngent-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NCBM
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,268 @@
1
+ Metadata-Version: 2.1
2
+ Name: plyngent
3
+ Version: 0.1.0
4
+ Summary: LLM chat and agent toolkit
5
+ Author-Email: HivertMoZara <worldmozara@163.com>
6
+ License: MIT
7
+ Requires-Python: <4,>=3.14
8
+ Requires-Dist: msgspec>=0.21.1
9
+ Requires-Dist: niquests>=3.20
10
+ Requires-Dist: typing-extensions>=4.15.0
11
+ Requires-Dist: platformdirs>=4.10.0
12
+ Requires-Dist: tomlkit>=0.15.0
13
+ Requires-Dist: sqlalchemy>=2.0.51
14
+ Requires-Dist: aiosqlite>=0.22.1
15
+ Requires-Dist: click>=8.4.2
16
+ Requires-Dist: awaitlet>=0.0.1
17
+ Requires-Dist: pywinpty>=2.0.0; sys_platform == "win32"
18
+ Requires-Dist: rich>=13
19
+ Description-Content-Type: text/markdown
20
+
21
+ # plyngent
22
+
23
+ Single-user LLM chat and agent toolkit for the terminal.
24
+
25
+ Python **3.14+**. OpenAI-compatible APIs (including DeepSeek OpenAI-compat), OpenAI Responses with optional hosted tools, SQLite session memory, workspace-scoped file/process/VCS tools, and a readline REPL with slash commands.
26
+
27
+ Requires **Python 3.14+** on your `PATH` (or via [uv](https://docs.astral.sh/uv/) / [pipx](https://pipx.pypa.io/)).
28
+
29
+ ## Install
30
+
31
+ ### Quick try (`uvx`)
32
+
33
+ No permanent install — runs the published package in a temporary environment:
34
+
35
+ ```bash
36
+ uvx plyngent --help
37
+ uvx plyngent chat
38
+ ```
39
+
40
+ ### User tool install
41
+
42
+ Keep `plyngent` on your PATH as a managed tool:
43
+
44
+ ```bash
45
+ # uv (recommended)
46
+ uv tool install plyngent
47
+ plyngent --help
48
+
49
+ # pipx
50
+ pipx install plyngent
51
+ plyngent --help
52
+ ```
53
+
54
+ Upgrade later:
55
+
56
+ ```bash
57
+ uv tool upgrade plyngent
58
+ # or: pipx upgrade plyngent
59
+ ```
60
+
61
+ ### pip (venv or user)
62
+
63
+ ```bash
64
+ python3.14 -m venv .venv
65
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
66
+ pip install -U pip
67
+ pip install plyngent
68
+ plyngent --help
69
+ ```
70
+
71
+ Or user install (if you accept that layout):
72
+
73
+ ```bash
74
+ pip install --user plyngent
75
+ ```
76
+
77
+ ### From a git clone (development)
78
+
79
+ ```bash
80
+ pdm install # first time
81
+ pdm sync # after pull
82
+ pdm run plyngent --help
83
+ ```
84
+
85
+ Dev checks:
86
+
87
+ ```bash
88
+ pdm run basedpyright .
89
+ pdm run ruff check .
90
+ pdm run ruff format .
91
+ pdm run pytest
92
+ ```
93
+
94
+ ## Basic usage
95
+
96
+ ```bash
97
+ # 1) Create / open config
98
+ plyngent config path
99
+ plyngent config edit # needs $EDITOR
100
+
101
+ # Minimal provider (OpenAI platform — Responses API; preset defaults to openai):
102
+ # [providers.oai]
103
+ # access_key_or_token = "sk-..."
104
+ # # models default: gpt-5.4, gpt-5.4-mini, gpt-5.4-nano
105
+ # # provider_tools default: web_search (use provider_tools = [] to disable)
106
+
107
+ # 2) Chat
108
+ plyngent chat
109
+ plyngent chat --provider oai --model gpt-5.4-mini
110
+ plyngent chat -p "Summarize this repo" --provider oai --model gpt-5.4-mini --no-stream
111
+
112
+ # 3) List providers from config
113
+ plyngent providers
114
+ ```
115
+
116
+ In the REPL: type normally, use `/help` for slash commands, `"""` … `"""` for multiline, `/markdown` for Rich rendering, `/quit` to leave.
117
+
118
+ ## Configure
119
+
120
+ Default config path (platformdirs):
121
+
122
+ ```bash
123
+ plyngent config path
124
+ plyngent config edit # opens $EDITOR (e.g. codium --wait)
125
+ ```
126
+
127
+ Copy the example and fill in a real token:
128
+
129
+ ```bash
130
+ cp doc/plyngent.example.toml "$(plyngent config path)"
131
+ # then edit providers
132
+ ```
133
+
134
+ Minimal shape:
135
+
136
+ ```toml
137
+ [providers.local]
138
+ preset = "openai-compatible"
139
+ url = "https://api.openai.com/v1"
140
+ access_key_or_token = "sk-..."
141
+
142
+ [providers.local.models]
143
+ "gpt-4o-mini" = { text = true }
144
+
145
+ [agent]
146
+ system_prompt = "You are a careful coding assistant."
147
+ confirm_destructive = true
148
+ max_context_tokens = 200000
149
+ ```
150
+
151
+ Supported provider presets today: `openai` (default if `preset` is omitted; default models `gpt-5.4` / `gpt-5.4-mini` / `gpt-5.4-nano` when `models` is omitted), `openai-compatible`, `deepseek` (OpenAI convention; default models `deepseek-v4-flash` and `deepseek-v4-pro` if `models` is omitted). Anthropic presets are modeled in config but not wired in the runtime client yet.
152
+
153
+ If `[database]` is omitted (or SQLite `url` is empty/`":memory:"`), chat uses a durable file under the user data dir (e.g. `~/.local/share/plyngent/chat.db` on Linux).
154
+
155
+ ## Chat
156
+
157
+ ### Interactive REPL
158
+
159
+ ```bash
160
+ plyngent chat
161
+ plyngent chat --provider local --model gpt-4o-mini
162
+ plyngent chat --workspace /path/to/project --new
163
+ plyngent chat --session 3
164
+ ```
165
+
166
+ | Flag | Meaning |
167
+ |------|---------|
168
+ | `--provider` / `--model` | Select from config (required when multiple and non-interactive) |
169
+ | `--workspace` | Tool root (default: cwd); sessions bind to this path |
170
+ | `--new` / `--session ID` | Fresh session vs resume by id |
171
+ | `--tools` / `--no-tools` | Default tools on |
172
+ | `--max-rounds` | Tool-loop rounds per turn (default 32) |
173
+ | `--stream` / `--no-stream` | Streaming deltas (default on) |
174
+ | `--quiet` | Less status on stderr |
175
+ | `--yes` | Allow destructive tools without confirm (also for one-shot) |
176
+ | `--log-level` | On the root CLI: `DEBUG`, `INFO`, `WARNING`, … |
177
+
178
+ Sessions resume the **most recently updated** session for the current workspace unless you pass `--new` or `--session`. Each session remembers the last **provider** and **model** (restored on resume so you are not re-prompted).
179
+
180
+ ### One-shot (scripts / CI)
181
+
182
+ ```bash
183
+ plyngent chat -p "Summarize README.md" --provider local --model gpt-4o-mini --no-stream
184
+ echo "hello" | plyngent chat --provider local --model gpt-4o-mini
185
+ ```
186
+
187
+ Exit codes (one-shot):
188
+
189
+ | Code | Meaning |
190
+ |------|---------|
191
+ | 0 | Success |
192
+ | 1 | Config / usage error |
193
+ | 2 | Cancelled |
194
+ | 3 | Turn failed (API / incomplete) |
195
+
196
+ ### Input ergonomics
197
+
198
+ - **Multiline**: start a message with `"""`, end a later line with `"""`.
199
+ - **`/edit`**: compose a turn in `$EDITOR` (empty buffer cancels).
200
+ - **Tab**: completes slash commands and some arguments (provider, model, on/off, export, `/help` targets).
201
+
202
+ ### Slash commands
203
+
204
+ Type `/help` in the REPL for the live list. Common ones:
205
+
206
+ | Command | Purpose |
207
+ |---------|---------|
208
+ | `/status` | Provider, session, context/usage estimates |
209
+ | `/history [n]` | Recent messages |
210
+ | `/sessions` | Sessions for this workspace |
211
+ | `/new` `/resume` `/rename` `/delete` | Session lifecycle (`/delete` confirms) |
212
+ | `/export [md\|json] [path]` | Transcript from DB (no secrets) |
213
+ | `/compact` | Soft-compact + model summary into a **new** session |
214
+ | `/stream` `/verbose` `/markdown` `/tools` `/rounds` | Toggles and limits |
215
+
216
+ | `/retry` | Re-run incomplete last user turn (after error/cancel) |
217
+ | `/provider` `/model` | Switch without restarting |
218
+ | `/models` | List config + remote `GET /models` (`--refresh` bypasses cache) |
219
+ | `/config` | Edit `plyngent.toml` in `$EDITOR` and reload |
220
+ | `/quit` | Leave the REPL |
221
+
222
+ User messages are saved immediately. On API error or Ctrl+C, partial assistant/tool output is discarded but the user message stays so `/retry` works after resume. Interactive auto-retry uses 10s / 20s / 30s delays.
223
+
224
+ ## Workspace model
225
+
226
+ - **Workspace** = root for file/process/VCS tools (default cwd).
227
+ - **Session** = SQLite chat bound to a workspace path.
228
+ - Resuming a session from another directory prompts: keep session workspace, rebind to current, or abort.
229
+
230
+ ## Tools (when enabled)
231
+
232
+ Default registry: file ops, `run_command` / PTY (POSIX openpty; Windows ConPTY via pywinpty), read-only VCS (git), and human prompts (`ask_user` / `choose_user` / `form_user`).
233
+
234
+ Safety defaults:
235
+
236
+ - Paths stay under the workspace; optional `path_denylist` substrings.
237
+ - Command basename denylist (e.g. dangerous shells/utilities).
238
+ - Destructive tools (delete/move/overwrite) can require confirm (`confirm_destructive`; default deny in non-TTY).
239
+ - PTY sessions: caps, idle TTL, output budget; master FD is non-inheritable; sessions closed on chat exit.
240
+
241
+ ## Usage / context (CLI)
242
+
243
+ - **Context size** prefers API `prompt_tokens` from the last model call; otherwise a char-based estimate (~4 chars/token).
244
+ - **Turn/session usage** sum billed completion usage across tool rounds (history is re-sent each round).
245
+ - Soft compact can calibrate from reported `prompt_tokens`. See `/status`.
246
+
247
+ ## Other commands
248
+
249
+ ```bash
250
+ plyngent providers # list configured providers
251
+ plyngent config path|edit
252
+ plyngent --log-level INFO chat ...
253
+ ```
254
+
255
+ ## Architecture (short)
256
+
257
+ See [doc/architecture.md](doc/architecture.md) and [CLAUDE.md](CLAUDE.md) for developers.
258
+
259
+ - **`lmproto/`** — OpenAI-compatible (+ DeepSeek) msgspec models and async SSE clients
260
+ - **`agent/`** — tool loop, streaming, usage, compact
261
+ - **`memory/`** — async SQLAlchemy sessions/messages
262
+ - **`tools/`** — workspace tools
263
+ - **`cli/`** — Click entry + slash registry (`awaitlet` bridges sync Click to async work)
264
+ - Multi-tenant / web (`router/`, real `web/`) are **not** in scope for the single-user CLI (Phase H).
265
+
266
+ ## License
267
+
268
+ MIT
@@ -0,0 +1,248 @@
1
+ # plyngent
2
+
3
+ Single-user LLM chat and agent toolkit for the terminal.
4
+
5
+ Python **3.14+**. OpenAI-compatible APIs (including DeepSeek OpenAI-compat), OpenAI Responses with optional hosted tools, SQLite session memory, workspace-scoped file/process/VCS tools, and a readline REPL with slash commands.
6
+
7
+ Requires **Python 3.14+** on your `PATH` (or via [uv](https://docs.astral.sh/uv/) / [pipx](https://pipx.pypa.io/)).
8
+
9
+ ## Install
10
+
11
+ ### Quick try (`uvx`)
12
+
13
+ No permanent install — runs the published package in a temporary environment:
14
+
15
+ ```bash
16
+ uvx plyngent --help
17
+ uvx plyngent chat
18
+ ```
19
+
20
+ ### User tool install
21
+
22
+ Keep `plyngent` on your PATH as a managed tool:
23
+
24
+ ```bash
25
+ # uv (recommended)
26
+ uv tool install plyngent
27
+ plyngent --help
28
+
29
+ # pipx
30
+ pipx install plyngent
31
+ plyngent --help
32
+ ```
33
+
34
+ Upgrade later:
35
+
36
+ ```bash
37
+ uv tool upgrade plyngent
38
+ # or: pipx upgrade plyngent
39
+ ```
40
+
41
+ ### pip (venv or user)
42
+
43
+ ```bash
44
+ python3.14 -m venv .venv
45
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
46
+ pip install -U pip
47
+ pip install plyngent
48
+ plyngent --help
49
+ ```
50
+
51
+ Or user install (if you accept that layout):
52
+
53
+ ```bash
54
+ pip install --user plyngent
55
+ ```
56
+
57
+ ### From a git clone (development)
58
+
59
+ ```bash
60
+ pdm install # first time
61
+ pdm sync # after pull
62
+ pdm run plyngent --help
63
+ ```
64
+
65
+ Dev checks:
66
+
67
+ ```bash
68
+ pdm run basedpyright .
69
+ pdm run ruff check .
70
+ pdm run ruff format .
71
+ pdm run pytest
72
+ ```
73
+
74
+ ## Basic usage
75
+
76
+ ```bash
77
+ # 1) Create / open config
78
+ plyngent config path
79
+ plyngent config edit # needs $EDITOR
80
+
81
+ # Minimal provider (OpenAI platform — Responses API; preset defaults to openai):
82
+ # [providers.oai]
83
+ # access_key_or_token = "sk-..."
84
+ # # models default: gpt-5.4, gpt-5.4-mini, gpt-5.4-nano
85
+ # # provider_tools default: web_search (use provider_tools = [] to disable)
86
+
87
+ # 2) Chat
88
+ plyngent chat
89
+ plyngent chat --provider oai --model gpt-5.4-mini
90
+ plyngent chat -p "Summarize this repo" --provider oai --model gpt-5.4-mini --no-stream
91
+
92
+ # 3) List providers from config
93
+ plyngent providers
94
+ ```
95
+
96
+ In the REPL: type normally, use `/help` for slash commands, `"""` … `"""` for multiline, `/markdown` for Rich rendering, `/quit` to leave.
97
+
98
+ ## Configure
99
+
100
+ Default config path (platformdirs):
101
+
102
+ ```bash
103
+ plyngent config path
104
+ plyngent config edit # opens $EDITOR (e.g. codium --wait)
105
+ ```
106
+
107
+ Copy the example and fill in a real token:
108
+
109
+ ```bash
110
+ cp doc/plyngent.example.toml "$(plyngent config path)"
111
+ # then edit providers
112
+ ```
113
+
114
+ Minimal shape:
115
+
116
+ ```toml
117
+ [providers.local]
118
+ preset = "openai-compatible"
119
+ url = "https://api.openai.com/v1"
120
+ access_key_or_token = "sk-..."
121
+
122
+ [providers.local.models]
123
+ "gpt-4o-mini" = { text = true }
124
+
125
+ [agent]
126
+ system_prompt = "You are a careful coding assistant."
127
+ confirm_destructive = true
128
+ max_context_tokens = 200000
129
+ ```
130
+
131
+ Supported provider presets today: `openai` (default if `preset` is omitted; default models `gpt-5.4` / `gpt-5.4-mini` / `gpt-5.4-nano` when `models` is omitted), `openai-compatible`, `deepseek` (OpenAI convention; default models `deepseek-v4-flash` and `deepseek-v4-pro` if `models` is omitted). Anthropic presets are modeled in config but not wired in the runtime client yet.
132
+
133
+ If `[database]` is omitted (or SQLite `url` is empty/`":memory:"`), chat uses a durable file under the user data dir (e.g. `~/.local/share/plyngent/chat.db` on Linux).
134
+
135
+ ## Chat
136
+
137
+ ### Interactive REPL
138
+
139
+ ```bash
140
+ plyngent chat
141
+ plyngent chat --provider local --model gpt-4o-mini
142
+ plyngent chat --workspace /path/to/project --new
143
+ plyngent chat --session 3
144
+ ```
145
+
146
+ | Flag | Meaning |
147
+ |------|---------|
148
+ | `--provider` / `--model` | Select from config (required when multiple and non-interactive) |
149
+ | `--workspace` | Tool root (default: cwd); sessions bind to this path |
150
+ | `--new` / `--session ID` | Fresh session vs resume by id |
151
+ | `--tools` / `--no-tools` | Default tools on |
152
+ | `--max-rounds` | Tool-loop rounds per turn (default 32) |
153
+ | `--stream` / `--no-stream` | Streaming deltas (default on) |
154
+ | `--quiet` | Less status on stderr |
155
+ | `--yes` | Allow destructive tools without confirm (also for one-shot) |
156
+ | `--log-level` | On the root CLI: `DEBUG`, `INFO`, `WARNING`, … |
157
+
158
+ Sessions resume the **most recently updated** session for the current workspace unless you pass `--new` or `--session`. Each session remembers the last **provider** and **model** (restored on resume so you are not re-prompted).
159
+
160
+ ### One-shot (scripts / CI)
161
+
162
+ ```bash
163
+ plyngent chat -p "Summarize README.md" --provider local --model gpt-4o-mini --no-stream
164
+ echo "hello" | plyngent chat --provider local --model gpt-4o-mini
165
+ ```
166
+
167
+ Exit codes (one-shot):
168
+
169
+ | Code | Meaning |
170
+ |------|---------|
171
+ | 0 | Success |
172
+ | 1 | Config / usage error |
173
+ | 2 | Cancelled |
174
+ | 3 | Turn failed (API / incomplete) |
175
+
176
+ ### Input ergonomics
177
+
178
+ - **Multiline**: start a message with `"""`, end a later line with `"""`.
179
+ - **`/edit`**: compose a turn in `$EDITOR` (empty buffer cancels).
180
+ - **Tab**: completes slash commands and some arguments (provider, model, on/off, export, `/help` targets).
181
+
182
+ ### Slash commands
183
+
184
+ Type `/help` in the REPL for the live list. Common ones:
185
+
186
+ | Command | Purpose |
187
+ |---------|---------|
188
+ | `/status` | Provider, session, context/usage estimates |
189
+ | `/history [n]` | Recent messages |
190
+ | `/sessions` | Sessions for this workspace |
191
+ | `/new` `/resume` `/rename` `/delete` | Session lifecycle (`/delete` confirms) |
192
+ | `/export [md\|json] [path]` | Transcript from DB (no secrets) |
193
+ | `/compact` | Soft-compact + model summary into a **new** session |
194
+ | `/stream` `/verbose` `/markdown` `/tools` `/rounds` | Toggles and limits |
195
+
196
+ | `/retry` | Re-run incomplete last user turn (after error/cancel) |
197
+ | `/provider` `/model` | Switch without restarting |
198
+ | `/models` | List config + remote `GET /models` (`--refresh` bypasses cache) |
199
+ | `/config` | Edit `plyngent.toml` in `$EDITOR` and reload |
200
+ | `/quit` | Leave the REPL |
201
+
202
+ User messages are saved immediately. On API error or Ctrl+C, partial assistant/tool output is discarded but the user message stays so `/retry` works after resume. Interactive auto-retry uses 10s / 20s / 30s delays.
203
+
204
+ ## Workspace model
205
+
206
+ - **Workspace** = root for file/process/VCS tools (default cwd).
207
+ - **Session** = SQLite chat bound to a workspace path.
208
+ - Resuming a session from another directory prompts: keep session workspace, rebind to current, or abort.
209
+
210
+ ## Tools (when enabled)
211
+
212
+ Default registry: file ops, `run_command` / PTY (POSIX openpty; Windows ConPTY via pywinpty), read-only VCS (git), and human prompts (`ask_user` / `choose_user` / `form_user`).
213
+
214
+ Safety defaults:
215
+
216
+ - Paths stay under the workspace; optional `path_denylist` substrings.
217
+ - Command basename denylist (e.g. dangerous shells/utilities).
218
+ - Destructive tools (delete/move/overwrite) can require confirm (`confirm_destructive`; default deny in non-TTY).
219
+ - PTY sessions: caps, idle TTL, output budget; master FD is non-inheritable; sessions closed on chat exit.
220
+
221
+ ## Usage / context (CLI)
222
+
223
+ - **Context size** prefers API `prompt_tokens` from the last model call; otherwise a char-based estimate (~4 chars/token).
224
+ - **Turn/session usage** sum billed completion usage across tool rounds (history is re-sent each round).
225
+ - Soft compact can calibrate from reported `prompt_tokens`. See `/status`.
226
+
227
+ ## Other commands
228
+
229
+ ```bash
230
+ plyngent providers # list configured providers
231
+ plyngent config path|edit
232
+ plyngent --log-level INFO chat ...
233
+ ```
234
+
235
+ ## Architecture (short)
236
+
237
+ See [doc/architecture.md](doc/architecture.md) and [CLAUDE.md](CLAUDE.md) for developers.
238
+
239
+ - **`lmproto/`** — OpenAI-compatible (+ DeepSeek) msgspec models and async SSE clients
240
+ - **`agent/`** — tool loop, streaming, usage, compact
241
+ - **`memory/`** — async SQLAlchemy sessions/messages
242
+ - **`tools/`** — workspace tools
243
+ - **`cli/`** — Click entry + slash registry (`awaitlet` bridges sync Click to async work)
244
+ - Multi-tenant / web (`router/`, real `web/`) are **not** in scope for the single-user CLI (Phase H).
245
+
246
+ ## License
247
+
248
+ MIT