pico-cli 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.
- {pico_cli-0.1.0 → pico_cli-0.1.2}/PKG-INFO +50 -17
- {pico_cli-0.1.0 → pico_cli-0.1.2}/README.md +48 -15
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/README.md +1 -1
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/pyproject.toml +2 -2
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/README.md +1 -1
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/pyproject.toml +4 -4
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/README.md +1 -1
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/pyproject.toml +5 -5
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/cli.py +2 -2
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/README.md +2 -2
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/pyproject.toml +3 -3
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/app.py +17 -3
- {pico_cli-0.1.0 → pico_cli-0.1.2}/pyproject.toml +5 -5
- {pico_cli-0.1.0 → pico_cli-0.1.2}/src/pico/__init__.py +1 -1
- pico_cli-0.1.2/tests/test_first_run_setup.py +70 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/uv.lock +34 -34
- pico_cli-0.1.0/IMPLEMENTATION_SUMMARY.md +0 -80
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.gitignore +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/01-unified-ai-call.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/02-tracer-bullet-headless-text-response.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/03-tools.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/04-session-fork.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/05-compaction.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/06-extension-binding.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/issues/07-config-file.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/headless-agent/spec.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/pico-tui/spec.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/trace-view/01-assistant-duration.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/trace-view/02-row-assembly.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/.scratch/trace-view/03-overlay.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/AGENTS.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/CONTEXT.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/LICENSE +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/adr/0001-monorepo-package-split.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/adr/0002-tree-based-session.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/adr/0003-hardcoded-core.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/adr/0004-native-provider-adapters.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/adr/0005-task-tool-subagents.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/adr/0006-trace-view-durations.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/agents/domain.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/agents/issue-tracker.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/docs/agents/triage-labels.md +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/__init__.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/openrouter.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/provider.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/__init__.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/_compat.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/anthropic.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/deepseek.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/gemini.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/ollama.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/openai.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/providers/spec.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/py.typed +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_ai/src/pico_ai/types.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/__init__.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/fsm.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/py.typed +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/session.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/subagents.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/todos.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/tools.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_core/src/pico_core/trace.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/__init__.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/__main__.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/config.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/extensions.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/providers.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/py.typed +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/session.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_sdk/src/pico_sdk/skills.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/__init__.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/commands.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/history_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/modal.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/model_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/provider_form.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/provider_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/py.typed +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/render.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/skill_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/status_bar.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/todo_panel.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/packages/pico_tui/src/pico_tui/trace_view.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/conftest.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_agent_session.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_ai_call.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_cli.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_config.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_extensions.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_first_token_timeout.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_fsm.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_history_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_model_persistence.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_model_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_picker_search.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_pointer_shape.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_providers.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_result_display.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_session.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_skill_picker.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_status_bar.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_stream_order.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_subagents.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_todos.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_tools.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_trace.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_trace_view.py +0 -0
- {pico_cli-0.1.0 → pico_cli-0.1.2}/tests/test_tui.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pico-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: A Python CLI coding agent — autonomous, tool-using, and session-persistent.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Arya-Ojha/PicoCLI_Learn
|
|
6
6
|
Project-URL: Repository, https://github.com/Arya-Ojha/PicoCLI_Learn
|
|
@@ -20,7 +20,7 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
|
20
20
|
Classifier: Topic :: Utilities
|
|
21
21
|
Requires-Python: >=3.12
|
|
22
22
|
Requires-Dist: pico-cli-sdk
|
|
23
|
-
Requires-Dist: pico-tui
|
|
23
|
+
Requires-Dist: pico-cli-tui
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
26
|
# pico
|
|
@@ -37,8 +37,8 @@ pico_ai ─► pico_core ─► pico_sdk ─► pico_tui
|
|
|
37
37
|
|
|
38
38
|
## Features
|
|
39
39
|
|
|
40
|
-
- **Headless CLI** — `
|
|
41
|
-
- **Interactive TUI** — `
|
|
40
|
+
- **Headless CLI** — `picocli-chat run "do a task"` completes a coding task end-to-end with a single prompt.
|
|
41
|
+
- **Interactive TUI** — `picocli` is a full terminal UI (Textual + Rich) for back-and-forth sessions.
|
|
42
42
|
- **Status bar** — the bottom bar always shows `provider | model`, a `thinking` indicator while streaming, and a color-coded context-window bar (`green < 70%`, `yellow < 90%`, `red ≥ 90%`) with the live token estimate.
|
|
43
43
|
- **Nine hardcoded core tools** — `read`, `write`, `edit`, `grep`, `fetch`, `websearch`, `bash`, `todo`, and `task` (see ADR-0003, ADR-0005).
|
|
44
44
|
- **Todo tracking** — the agent tracks multi-step work with a `todo` tool (add / update / list / clear); the TUI shows the in-memory list in a read-only side panel that appears once the first todo exists. A run only ends once every todo is completed — stopping early nudges the agent back in. When the run ends clean, the list is cleared for the next run (a run stopped by the stuck-model guard keeps its open todos).
|
|
@@ -80,10 +80,43 @@ Dependencies flow one way — `pico_ai` ← `pico_core` ← `pico_sdk` ← `pico
|
|
|
80
80
|
| DeepSeek | `DEEPSEEK_API_KEY` | Chat + reasoner (reasoning streams as thinking blocks) |
|
|
81
81
|
| Ollama | — | Local server (`OLLAMA_HOST`, default `http://localhost:11434`) |
|
|
82
82
|
|
|
83
|
-
In the TUI, `/provider` opens a picker with ✓/✗ setup status, then a setup form for that provider's API key, base URL, model, and extras. Only changed values are stored (in `settings.json` — prefer env vars on shared machines); blanks fall back to env/defaults. Effective precedence: field default < environment < stored value. Switching providers resets the model to that provider's stored/default model. Headless: `
|
|
83
|
+
In the TUI, `/provider` opens a picker with ✓/✗ setup status, then a setup form for that provider's API key, base URL, model, and extras. Only changed values are stored (in `settings.json` — prefer env vars on shared machines); blanks fall back to env/defaults. Effective precedence: field default < environment < stored value. Switching providers resets the model to that provider's stored/default model. Headless: `picocli-chat run --provider ollama "..."`.
|
|
84
84
|
|
|
85
85
|
## Installation
|
|
86
86
|
|
|
87
|
+
Requires Python **3.12+**.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# recommended: isolated install — `picocli` + `picocli-chat` work in any
|
|
91
|
+
# directory, like `npm i -g`
|
|
92
|
+
pipx install pico-cli
|
|
93
|
+
|
|
94
|
+
# ... or via uv (same result)
|
|
95
|
+
uv tool install pico-cli
|
|
96
|
+
|
|
97
|
+
# plain pip also works (see the Windows PATH note below)
|
|
98
|
+
pip install pico-cli
|
|
99
|
+
|
|
100
|
+
# try without installing (npx-style)
|
|
101
|
+
uvx pico-cli --help
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then open a terminal **in any directory** and run `picocli`. On first launch (no API key yet) the provider setup opens automatically — pick a provider, paste the key, done. Keys can also come from environment variables (see Configuration below).
|
|
105
|
+
|
|
106
|
+
### Windows: `pip install` and PATH
|
|
107
|
+
|
|
108
|
+
`pip --user` installs (the default when site-packages isn't writable) drop the commands into `~/.local`-style user Scripts, which is often **not** on PATH — that's when `picocli` is "not recognized". Prefer `pipx`/`uv tool` above, or add the dir once (then open a fresh terminal):
|
|
109
|
+
|
|
110
|
+
```powershell
|
|
111
|
+
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
112
|
+
$newDir = "$env:APPDATA\Python\Python314\Scripts" # adjust version to yours
|
|
113
|
+
if ($userPath -split ";" -notcontains $newDir) {
|
|
114
|
+
[Environment]::SetEnvironmentVariable("Path", "$userPath;$newDir", "User")
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### From source (contributors)
|
|
119
|
+
|
|
87
120
|
```bash
|
|
88
121
|
uv sync
|
|
89
122
|
```
|
|
@@ -128,22 +161,22 @@ Create `~/.pico/settings.json` to override defaults:
|
|
|
128
161
|
|
|
129
162
|
```bash
|
|
130
163
|
# complete a task in one shot
|
|
131
|
-
uv run
|
|
164
|
+
uv run picocli-chat run "explain what this repo does"
|
|
132
165
|
|
|
133
166
|
# let the agent run shell commands (bash is on by default)
|
|
134
|
-
uv run
|
|
167
|
+
uv run picocli-chat run "run the tests and fix failures"
|
|
135
168
|
|
|
136
169
|
# work in another directory, pick a model
|
|
137
|
-
uv run
|
|
170
|
+
uv run picocli-chat run "summarize this code" --cwd D:\some\repo --model openai/gpt-4o-mini
|
|
138
171
|
|
|
139
172
|
# resume a previous session by id
|
|
140
|
-
uv run
|
|
173
|
+
uv run picocli-chat run "continue" --session <session-id>
|
|
141
174
|
|
|
142
175
|
# compact a session headlessly (with optional steering text)
|
|
143
|
-
uv run
|
|
176
|
+
uv run picocli-chat run "/compact focus on the auth refactor"
|
|
144
177
|
```
|
|
145
178
|
|
|
146
|
-
Flags for `
|
|
179
|
+
Flags for `picocli-chat run`:
|
|
147
180
|
|
|
148
181
|
| Flag | Purpose |
|
|
149
182
|
|---|---|
|
|
@@ -159,10 +192,10 @@ Flags for `picoCLI run`:
|
|
|
159
192
|
### Interactive TUI
|
|
160
193
|
|
|
161
194
|
```bash
|
|
162
|
-
uv run
|
|
195
|
+
uv run picocli
|
|
163
196
|
```
|
|
164
197
|
|
|
165
|
-
`
|
|
198
|
+
`picocli` shares the same flags. Inside the prompt you can type a message or use:
|
|
166
199
|
|
|
167
200
|
| Slash command | Key | Action |
|
|
168
201
|
|---|---|---|
|
|
@@ -202,7 +235,7 @@ Permission gating via `allowed_tools` in `settings.json` (`null` = all tools, `[
|
|
|
202
235
|
|
|
203
236
|
## Where sessions live
|
|
204
237
|
|
|
205
|
-
Sessions are persisted as JSONL under `~/.pico/sessions/<id>.jsonl` by default (configurable via `session_dir`). Both `
|
|
238
|
+
Sessions are persisted as JSONL under `~/.pico/sessions/<id>.jsonl` by default (configurable via `session_dir`). Both `picocli-chat run` and `picocli` accept `--session <id>` to resume; `/history`, `/fork`, and `/undo` rewind within the tree without deleting nodes.
|
|
206
239
|
|
|
207
240
|
## Development
|
|
208
241
|
|
|
@@ -215,10 +248,10 @@ uv run mypy packages/pico_ai/src packages/pico_core/src packages/pico_sdk/src pa
|
|
|
215
248
|
|
|
216
249
|
# build all wheels into dist/ (root `pico-cli` is a meta-package: deps + entry points only)
|
|
217
250
|
uv build --package pico-cli --out-dir dist
|
|
218
|
-
uv build --package pico-ai --out-dir dist
|
|
219
|
-
uv build --package pico-core --out-dir dist
|
|
251
|
+
uv build --package pico-cli-ai --out-dir dist
|
|
252
|
+
uv build --package pico-cli-core --out-dir dist
|
|
220
253
|
uv build --package pico-cli-sdk --out-dir dist
|
|
221
|
-
uv build --package pico-tui --out-dir dist
|
|
254
|
+
uv build --package pico-cli-tui --out-dir dist
|
|
222
255
|
```
|
|
223
256
|
|
|
224
257
|
The test suite is network-free: it drives the whole agent loop through a scripted fake provider (`FakeProvider`) and a temporary filesystem, exercising `pico_ai`, `pico_core`, `pico_sdk`, and `pico_tui`.
|
|
@@ -12,8 +12,8 @@ pico_ai ─► pico_core ─► pico_sdk ─► pico_tui
|
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
|
-
- **Headless CLI** — `
|
|
16
|
-
- **Interactive TUI** — `
|
|
15
|
+
- **Headless CLI** — `picocli-chat run "do a task"` completes a coding task end-to-end with a single prompt.
|
|
16
|
+
- **Interactive TUI** — `picocli` is a full terminal UI (Textual + Rich) for back-and-forth sessions.
|
|
17
17
|
- **Status bar** — the bottom bar always shows `provider | model`, a `thinking` indicator while streaming, and a color-coded context-window bar (`green < 70%`, `yellow < 90%`, `red ≥ 90%`) with the live token estimate.
|
|
18
18
|
- **Nine hardcoded core tools** — `read`, `write`, `edit`, `grep`, `fetch`, `websearch`, `bash`, `todo`, and `task` (see ADR-0003, ADR-0005).
|
|
19
19
|
- **Todo tracking** — the agent tracks multi-step work with a `todo` tool (add / update / list / clear); the TUI shows the in-memory list in a read-only side panel that appears once the first todo exists. A run only ends once every todo is completed — stopping early nudges the agent back in. When the run ends clean, the list is cleared for the next run (a run stopped by the stuck-model guard keeps its open todos).
|
|
@@ -55,10 +55,43 @@ Dependencies flow one way — `pico_ai` ← `pico_core` ← `pico_sdk` ← `pico
|
|
|
55
55
|
| DeepSeek | `DEEPSEEK_API_KEY` | Chat + reasoner (reasoning streams as thinking blocks) |
|
|
56
56
|
| Ollama | — | Local server (`OLLAMA_HOST`, default `http://localhost:11434`) |
|
|
57
57
|
|
|
58
|
-
In the TUI, `/provider` opens a picker with ✓/✗ setup status, then a setup form for that provider's API key, base URL, model, and extras. Only changed values are stored (in `settings.json` — prefer env vars on shared machines); blanks fall back to env/defaults. Effective precedence: field default < environment < stored value. Switching providers resets the model to that provider's stored/default model. Headless: `
|
|
58
|
+
In the TUI, `/provider` opens a picker with ✓/✗ setup status, then a setup form for that provider's API key, base URL, model, and extras. Only changed values are stored (in `settings.json` — prefer env vars on shared machines); blanks fall back to env/defaults. Effective precedence: field default < environment < stored value. Switching providers resets the model to that provider's stored/default model. Headless: `picocli-chat run --provider ollama "..."`.
|
|
59
59
|
|
|
60
60
|
## Installation
|
|
61
61
|
|
|
62
|
+
Requires Python **3.12+**.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# recommended: isolated install — `picocli` + `picocli-chat` work in any
|
|
66
|
+
# directory, like `npm i -g`
|
|
67
|
+
pipx install pico-cli
|
|
68
|
+
|
|
69
|
+
# ... or via uv (same result)
|
|
70
|
+
uv tool install pico-cli
|
|
71
|
+
|
|
72
|
+
# plain pip also works (see the Windows PATH note below)
|
|
73
|
+
pip install pico-cli
|
|
74
|
+
|
|
75
|
+
# try without installing (npx-style)
|
|
76
|
+
uvx pico-cli --help
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Then open a terminal **in any directory** and run `picocli`. On first launch (no API key yet) the provider setup opens automatically — pick a provider, paste the key, done. Keys can also come from environment variables (see Configuration below).
|
|
80
|
+
|
|
81
|
+
### Windows: `pip install` and PATH
|
|
82
|
+
|
|
83
|
+
`pip --user` installs (the default when site-packages isn't writable) drop the commands into `~/.local`-style user Scripts, which is often **not** on PATH — that's when `picocli` is "not recognized". Prefer `pipx`/`uv tool` above, or add the dir once (then open a fresh terminal):
|
|
84
|
+
|
|
85
|
+
```powershell
|
|
86
|
+
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
|
|
87
|
+
$newDir = "$env:APPDATA\Python\Python314\Scripts" # adjust version to yours
|
|
88
|
+
if ($userPath -split ";" -notcontains $newDir) {
|
|
89
|
+
[Environment]::SetEnvironmentVariable("Path", "$userPath;$newDir", "User")
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### From source (contributors)
|
|
94
|
+
|
|
62
95
|
```bash
|
|
63
96
|
uv sync
|
|
64
97
|
```
|
|
@@ -103,22 +136,22 @@ Create `~/.pico/settings.json` to override defaults:
|
|
|
103
136
|
|
|
104
137
|
```bash
|
|
105
138
|
# complete a task in one shot
|
|
106
|
-
uv run
|
|
139
|
+
uv run picocli-chat run "explain what this repo does"
|
|
107
140
|
|
|
108
141
|
# let the agent run shell commands (bash is on by default)
|
|
109
|
-
uv run
|
|
142
|
+
uv run picocli-chat run "run the tests and fix failures"
|
|
110
143
|
|
|
111
144
|
# work in another directory, pick a model
|
|
112
|
-
uv run
|
|
145
|
+
uv run picocli-chat run "summarize this code" --cwd D:\some\repo --model openai/gpt-4o-mini
|
|
113
146
|
|
|
114
147
|
# resume a previous session by id
|
|
115
|
-
uv run
|
|
148
|
+
uv run picocli-chat run "continue" --session <session-id>
|
|
116
149
|
|
|
117
150
|
# compact a session headlessly (with optional steering text)
|
|
118
|
-
uv run
|
|
151
|
+
uv run picocli-chat run "/compact focus on the auth refactor"
|
|
119
152
|
```
|
|
120
153
|
|
|
121
|
-
Flags for `
|
|
154
|
+
Flags for `picocli-chat run`:
|
|
122
155
|
|
|
123
156
|
| Flag | Purpose |
|
|
124
157
|
|---|---|
|
|
@@ -134,10 +167,10 @@ Flags for `picoCLI run`:
|
|
|
134
167
|
### Interactive TUI
|
|
135
168
|
|
|
136
169
|
```bash
|
|
137
|
-
uv run
|
|
170
|
+
uv run picocli
|
|
138
171
|
```
|
|
139
172
|
|
|
140
|
-
`
|
|
173
|
+
`picocli` shares the same flags. Inside the prompt you can type a message or use:
|
|
141
174
|
|
|
142
175
|
| Slash command | Key | Action |
|
|
143
176
|
|---|---|---|
|
|
@@ -177,7 +210,7 @@ Permission gating via `allowed_tools` in `settings.json` (`null` = all tools, `[
|
|
|
177
210
|
|
|
178
211
|
## Where sessions live
|
|
179
212
|
|
|
180
|
-
Sessions are persisted as JSONL under `~/.pico/sessions/<id>.jsonl` by default (configurable via `session_dir`). Both `
|
|
213
|
+
Sessions are persisted as JSONL under `~/.pico/sessions/<id>.jsonl` by default (configurable via `session_dir`). Both `picocli-chat run` and `picocli` accept `--session <id>` to resume; `/history`, `/fork`, and `/undo` rewind within the tree without deleting nodes.
|
|
181
214
|
|
|
182
215
|
## Development
|
|
183
216
|
|
|
@@ -190,10 +223,10 @@ uv run mypy packages/pico_ai/src packages/pico_core/src packages/pico_sdk/src pa
|
|
|
190
223
|
|
|
191
224
|
# build all wheels into dist/ (root `pico-cli` is a meta-package: deps + entry points only)
|
|
192
225
|
uv build --package pico-cli --out-dir dist
|
|
193
|
-
uv build --package pico-ai --out-dir dist
|
|
194
|
-
uv build --package pico-core --out-dir dist
|
|
226
|
+
uv build --package pico-cli-ai --out-dir dist
|
|
227
|
+
uv build --package pico-cli-core --out-dir dist
|
|
195
228
|
uv build --package pico-cli-sdk --out-dir dist
|
|
196
|
-
uv build --package pico-tui --out-dir dist
|
|
229
|
+
uv build --package pico-cli-tui --out-dir dist
|
|
197
230
|
```
|
|
198
231
|
|
|
199
232
|
The test suite is network-free: it drives the whole agent loop through a scripted fake provider (`FakeProvider`) and a temporary filesystem, exercising `pico_ai`, `pico_core`, `pico_sdk`, and `pico_tui`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# pico-core
|
|
1
|
+
# pico-cli-core
|
|
2
2
|
|
|
3
3
|
The agent loop (explicit finite-state machine) and the append-only, tree-based session model for `pico-cli`: nine hardcoded core tools (`read`, `write`, `edit`, `grep`, `fetch`, `websearch`, `bash`, `todo`, `task`), todo tracking, sub-agents, and compaction.
|
|
4
4
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
|
-
name = "pico-core"
|
|
3
|
-
version = "0.1.
|
|
2
|
+
name = "pico-cli-core"
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Agent loop and session tree."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -15,7 +15,7 @@ classifiers = [
|
|
|
15
15
|
"Programming Language :: Python :: 3.12",
|
|
16
16
|
]
|
|
17
17
|
requires-python = ">=3.12"
|
|
18
|
-
dependencies = ["pico-ai", "pydantic>=2", "httpx>=0.27"]
|
|
18
|
+
dependencies = ["pico-cli-ai", "pydantic>=2", "httpx>=0.27"]
|
|
19
19
|
|
|
20
20
|
[project.urls]
|
|
21
21
|
Homepage = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
@@ -30,4 +30,4 @@ build-backend = "hatchling.build"
|
|
|
30
30
|
packages = ["src/pico_core"]
|
|
31
31
|
|
|
32
32
|
[tool.uv.sources]
|
|
33
|
-
pico-ai = { workspace = true }
|
|
33
|
+
pico-cli-ai = { workspace = true }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# pico-cli-sdk
|
|
2
2
|
|
|
3
|
-
Headless library API for `pico-cli`: `AgentSession`, curated observe-only hooks, `SKILL.md` skills, provider setup, and the `
|
|
3
|
+
Headless library API for `pico-cli`: `AgentSession`, curated observe-only hooks, `SKILL.md` skills, provider setup, and the `picocli-chat` command-line interface.
|
|
4
4
|
|
|
5
5
|
See the [pico-cli README](https://github.com/Arya-Ojha/PicoCLI_Learn#readme) for the full picture.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pico-cli-sdk"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Headless library API and extension/plugin binding."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -16,14 +16,14 @@ classifiers = [
|
|
|
16
16
|
"Programming Language :: Python :: 3.12",
|
|
17
17
|
]
|
|
18
18
|
requires-python = ">=3.12"
|
|
19
|
-
dependencies = ["pico-core", "pico-ai", "pydantic>=2"]
|
|
19
|
+
dependencies = ["pico-cli-core", "pico-cli-ai", "pydantic>=2"]
|
|
20
20
|
|
|
21
21
|
[project.urls]
|
|
22
22
|
Homepage = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
23
23
|
Repository = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
24
24
|
Issues = "https://github.com/Arya-Ojha/PicoCLI_Learn/issues"
|
|
25
25
|
|
|
26
|
-
# Note: the `
|
|
26
|
+
# Note: the `picocli-chat` entry point lives on the `pico-cli` meta-package only,
|
|
27
27
|
# so installing the stack never ships duplicate console scripts.
|
|
28
28
|
[build-system]
|
|
29
29
|
requires = ["hatchling"]
|
|
@@ -33,5 +33,5 @@ build-backend = "hatchling.build"
|
|
|
33
33
|
packages = ["src/pico_sdk"]
|
|
34
34
|
|
|
35
35
|
[tool.uv.sources]
|
|
36
|
-
pico-core = { workspace = true }
|
|
37
|
-
pico-ai = { workspace = true }
|
|
36
|
+
pico-cli-core = { workspace = true }
|
|
37
|
+
pico-cli-ai = { workspace = true }
|
|
@@ -53,7 +53,7 @@ def apply_cli_overrides(args: argparse.Namespace, settings: Settings) -> bool:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
def build_parser() -> argparse.ArgumentParser:
|
|
56
|
-
parser = argparse.ArgumentParser(prog="
|
|
56
|
+
parser = argparse.ArgumentParser(prog="picocli-chat", description="A headless coding agent.")
|
|
57
57
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
58
58
|
|
|
59
59
|
run = sub.add_parser("run", help="Run the agent on a prompt.")
|
|
@@ -97,7 +97,7 @@ async def run_command(args: argparse.Namespace) -> int:
|
|
|
97
97
|
sys.stderr.write(
|
|
98
98
|
f"error: provider '{settings.provider}' is missing required "
|
|
99
99
|
f"config: {', '.join(missing)}.\n"
|
|
100
|
-
|
|
100
|
+
f"Run `picocli` and use /provider to configure it, or set "
|
|
101
101
|
f"the corresponding environment variable.\n"
|
|
102
102
|
)
|
|
103
103
|
return 1
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# pico-tui
|
|
1
|
+
# pico-cli-tui
|
|
2
2
|
|
|
3
|
-
Interactive terminal UI for `pico-cli` (Textual + Rich): chat log, todo side panel, trace view, filterable pickers, provider setup forms, and the `
|
|
3
|
+
Interactive terminal UI for `pico-cli` (Textual + Rich): chat log, todo side panel, trace view, filterable pickers, provider setup forms, and the `picocli` entry point.
|
|
4
4
|
|
|
5
5
|
See the [pico-cli README](https://github.com/Arya-Ojha/PicoCLI_Learn#readme) for the full picture.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
|
-
name = "pico-tui"
|
|
3
|
-
version = "0.1.
|
|
2
|
+
name = "pico-cli-tui"
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "Terminal user interface view."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -27,7 +27,7 @@ Homepage = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
|
27
27
|
Repository = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
28
28
|
Issues = "https://github.com/Arya-Ojha/PicoCLI_Learn/issues"
|
|
29
29
|
|
|
30
|
-
# Note: the `
|
|
30
|
+
# Note: the `picocli` entry point lives on the `pico-cli`
|
|
31
31
|
# meta-package only, so installing the stack never ships duplicate
|
|
32
32
|
# console scripts.
|
|
33
33
|
[build-system]
|
|
@@ -357,10 +357,20 @@ class PicoApp(App[None]):
|
|
|
357
357
|
("f1", "show_help", "Help"),
|
|
358
358
|
]
|
|
359
359
|
|
|
360
|
-
def __init__(
|
|
360
|
+
def __init__(
|
|
361
|
+
self,
|
|
362
|
+
mgr: _SessionManager,
|
|
363
|
+
*,
|
|
364
|
+
startup_notice: str = "",
|
|
365
|
+
auto_setup: bool = False,
|
|
366
|
+
) -> None:
|
|
361
367
|
super().__init__()
|
|
362
368
|
self._mgr = mgr
|
|
363
369
|
self._startup_notice = startup_notice
|
|
370
|
+
# First-run onboarding: when True and the active provider is
|
|
371
|
+
# missing required config, the provider picker opens on launch
|
|
372
|
+
# so keys/URLs are entered in-UI instead of discovered via /help.
|
|
373
|
+
self._auto_setup = auto_setup
|
|
364
374
|
self._streaming = False
|
|
365
375
|
# Everything written to the chat log, in order. Thinking blocks are
|
|
366
376
|
# stored as ThinkingSegment so they can collapse/expand on click.
|
|
@@ -399,6 +409,10 @@ class PicoApp(App[None]):
|
|
|
399
409
|
self._write_chat(
|
|
400
410
|
Panel(self._startup_notice, title="setup", border_style="yellow")
|
|
401
411
|
)
|
|
412
|
+
if self._auto_setup:
|
|
413
|
+
settings = self._mgr.session.settings
|
|
414
|
+
if missing_required(settings.provider, settings):
|
|
415
|
+
self.call_after_refresh(self._show_provider_picker)
|
|
402
416
|
|
|
403
417
|
def _refresh_todo_panel(self) -> None:
|
|
404
418
|
"""Re-render the todo side panel (auto-hides while empty)."""
|
|
@@ -878,7 +892,7 @@ class PicoApp(App[None]):
|
|
|
878
892
|
|
|
879
893
|
def main(argv: list[str] | None = None) -> int:
|
|
880
894
|
parser = argparse.ArgumentParser(
|
|
881
|
-
prog="
|
|
895
|
+
prog="picocli", description="Interactive pico session (Textual TUI)."
|
|
882
896
|
)
|
|
883
897
|
parser.add_argument(
|
|
884
898
|
"--no-bash",
|
|
@@ -955,7 +969,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
955
969
|
f"Provider {provider_label} is missing required config: "
|
|
956
970
|
f"{', '.join(missing)}. Use /provider to configure it."
|
|
957
971
|
)
|
|
958
|
-
app = PicoApp(mgr, startup_notice=startup_notice)
|
|
972
|
+
app = PicoApp(mgr, startup_notice=startup_notice, auto_setup=True)
|
|
959
973
|
app.run()
|
|
960
974
|
return 0
|
|
961
975
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pico-cli"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "A Python CLI coding agent — autonomous, tool-using, and session-persistent."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -18,7 +18,7 @@ classifiers = [
|
|
|
18
18
|
"Topic :: Utilities",
|
|
19
19
|
]
|
|
20
20
|
requires-python = ">=3.12"
|
|
21
|
-
dependencies = ["pico-cli-sdk", "pico-tui"]
|
|
21
|
+
dependencies = ["pico-cli-sdk", "pico-cli-tui"]
|
|
22
22
|
|
|
23
23
|
[project.urls]
|
|
24
24
|
Homepage = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
@@ -26,8 +26,8 @@ Repository = "https://github.com/Arya-Ojha/PicoCLI_Learn"
|
|
|
26
26
|
Issues = "https://github.com/Arya-Ojha/PicoCLI_Learn/issues"
|
|
27
27
|
|
|
28
28
|
[project.scripts]
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
picocli-chat = "pico_sdk.cli:main"
|
|
30
|
+
picocli = "pico_tui.app:main"
|
|
31
31
|
|
|
32
32
|
# The root `pico` dist is a meta-package: the implementation lives in the
|
|
33
33
|
# workspace members, so the wheel ships the stub above plus metadata
|
|
@@ -44,7 +44,7 @@ members = ["packages/*"]
|
|
|
44
44
|
|
|
45
45
|
[tool.uv.sources]
|
|
46
46
|
pico-cli-sdk = { workspace = true }
|
|
47
|
-
pico-tui = { workspace = true }
|
|
47
|
+
pico-cli-tui = { workspace = true }
|
|
48
48
|
|
|
49
49
|
[dependency-groups]
|
|
50
50
|
dev = [
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
``pico_ai``, ``pico_core``, ``pico_sdk``, and ``pico_tui`` (under
|
|
4
4
|
``packages/``) provide the implementation; this dist only carries the
|
|
5
|
-
dependency pins and the ``
|
|
5
|
+
dependency pins and the ``picocli`` / ``picocli-chat`` entry points.
|
|
6
6
|
"""
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""First-run onboarding: missing provider config auto-opens setup."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from pico_sdk.config import Settings
|
|
6
|
+
from pico_sdk.providers import create_provider, missing_required
|
|
7
|
+
from pico_tui.app import PicoApp, _SessionManager
|
|
8
|
+
from pico_tui.provider_picker import ProviderPickerScreen
|
|
9
|
+
|
|
10
|
+
from conftest import FakeProvider, make_session
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def fresh_settings(monkeypatch):
|
|
15
|
+
"""Settings as on a brand-new machine: no keys anywhere."""
|
|
16
|
+
for var in (
|
|
17
|
+
"OPENROUTER_API_KEY",
|
|
18
|
+
"OPENAI_API_KEY",
|
|
19
|
+
"ANTHROPIC_API_KEY",
|
|
20
|
+
"GOOGLE_API_KEY",
|
|
21
|
+
"DEEPSEEK_API_KEY",
|
|
22
|
+
):
|
|
23
|
+
monkeypatch.delenv(var, raising=False)
|
|
24
|
+
settings = Settings()
|
|
25
|
+
assert missing_required(settings.provider, settings), "expected missing config"
|
|
26
|
+
return settings
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _app(tmp_path, settings, auto_setup=False):
|
|
30
|
+
session = make_session(
|
|
31
|
+
FakeProvider([]), tmp_path, settings=settings, load_skills=False
|
|
32
|
+
)
|
|
33
|
+
return PicoApp(_SessionManager(session), auto_setup=auto_setup)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def test_missing_config_auto_opens_provider_picker(tmp_path, fresh_settings):
|
|
37
|
+
app = _app(tmp_path, fresh_settings, auto_setup=True)
|
|
38
|
+
async with app.run_test() as pilot:
|
|
39
|
+
await pilot.pause()
|
|
40
|
+
await pilot.pause()
|
|
41
|
+
assert isinstance(app.screen, ProviderPickerScreen)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def test_auto_setup_off_by_default(tmp_path, fresh_settings):
|
|
45
|
+
app = _app(tmp_path, fresh_settings)
|
|
46
|
+
async with app.run_test() as pilot:
|
|
47
|
+
await pilot.pause()
|
|
48
|
+
assert not isinstance(app.screen, ProviderPickerScreen)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
async def test_configured_never_auto_opens(tmp_path, monkeypatch):
|
|
52
|
+
monkeypatch.setenv("OPENROUTER_API_KEY", "test-key")
|
|
53
|
+
settings = Settings()
|
|
54
|
+
assert not missing_required(settings.provider, settings)
|
|
55
|
+
session = make_session(
|
|
56
|
+
create_provider(settings), tmp_path, settings=settings, load_skills=False
|
|
57
|
+
)
|
|
58
|
+
app = PicoApp(_SessionManager(session), auto_setup=True)
|
|
59
|
+
async with app.run_test() as pilot:
|
|
60
|
+
await pilot.pause()
|
|
61
|
+
await pilot.pause()
|
|
62
|
+
assert not isinstance(app.screen, ProviderPickerScreen)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_main_requests_auto_setup():
|
|
66
|
+
import inspect
|
|
67
|
+
|
|
68
|
+
from pico_tui.app import main
|
|
69
|
+
|
|
70
|
+
assert "auto_setup=True" in inspect.getsource(main)
|
|
@@ -8,11 +8,11 @@ resolution-markers = [
|
|
|
8
8
|
|
|
9
9
|
[manifest]
|
|
10
10
|
members = [
|
|
11
|
-
"pico-ai",
|
|
12
11
|
"pico-cli",
|
|
12
|
+
"pico-cli-ai",
|
|
13
|
+
"pico-cli-core",
|
|
13
14
|
"pico-cli-sdk",
|
|
14
|
-
"pico-
|
|
15
|
-
"pico-tui",
|
|
15
|
+
"pico-cli-tui",
|
|
16
16
|
]
|
|
17
17
|
|
|
18
18
|
[[package]]
|
|
@@ -406,28 +406,13 @@ wheels = [
|
|
|
406
406
|
{ url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
|
|
407
407
|
]
|
|
408
408
|
|
|
409
|
-
[[package]]
|
|
410
|
-
name = "pico-ai"
|
|
411
|
-
version = "0.1.0"
|
|
412
|
-
source = { editable = "packages/pico_ai" }
|
|
413
|
-
dependencies = [
|
|
414
|
-
{ name = "httpx" },
|
|
415
|
-
{ name = "pydantic" },
|
|
416
|
-
]
|
|
417
|
-
|
|
418
|
-
[package.metadata]
|
|
419
|
-
requires-dist = [
|
|
420
|
-
{ name = "httpx", specifier = ">=0.27" },
|
|
421
|
-
{ name = "pydantic", specifier = ">=2" },
|
|
422
|
-
]
|
|
423
|
-
|
|
424
409
|
[[package]]
|
|
425
410
|
name = "pico-cli"
|
|
426
|
-
version = "0.1.
|
|
411
|
+
version = "0.1.2"
|
|
427
412
|
source = { editable = "." }
|
|
428
413
|
dependencies = [
|
|
429
414
|
{ name = "pico-cli-sdk" },
|
|
430
|
-
{ name = "pico-tui" },
|
|
415
|
+
{ name = "pico-cli-tui" },
|
|
431
416
|
]
|
|
432
417
|
|
|
433
418
|
[package.dev-dependencies]
|
|
@@ -440,7 +425,7 @@ dev = [
|
|
|
440
425
|
[package.metadata]
|
|
441
426
|
requires-dist = [
|
|
442
427
|
{ name = "pico-cli-sdk", editable = "packages/pico_sdk" },
|
|
443
|
-
{ name = "pico-tui", editable = "packages/pico_tui" },
|
|
428
|
+
{ name = "pico-cli-tui", editable = "packages/pico_tui" },
|
|
444
429
|
]
|
|
445
430
|
|
|
446
431
|
[package.metadata.requires-dev]
|
|
@@ -451,42 +436,57 @@ dev = [
|
|
|
451
436
|
]
|
|
452
437
|
|
|
453
438
|
[[package]]
|
|
454
|
-
name = "pico-cli-
|
|
455
|
-
version = "0.1.
|
|
456
|
-
source = { editable = "packages/
|
|
439
|
+
name = "pico-cli-ai"
|
|
440
|
+
version = "0.1.2"
|
|
441
|
+
source = { editable = "packages/pico_ai" }
|
|
457
442
|
dependencies = [
|
|
458
|
-
{ name = "
|
|
459
|
-
{ name = "pico-core" },
|
|
443
|
+
{ name = "httpx" },
|
|
460
444
|
{ name = "pydantic" },
|
|
461
445
|
]
|
|
462
446
|
|
|
463
447
|
[package.metadata]
|
|
464
448
|
requires-dist = [
|
|
465
|
-
{ name = "
|
|
466
|
-
{ name = "pico-core", editable = "packages/pico_core" },
|
|
449
|
+
{ name = "httpx", specifier = ">=0.27" },
|
|
467
450
|
{ name = "pydantic", specifier = ">=2" },
|
|
468
451
|
]
|
|
469
452
|
|
|
470
453
|
[[package]]
|
|
471
|
-
name = "pico-core"
|
|
472
|
-
version = "0.1.
|
|
454
|
+
name = "pico-cli-core"
|
|
455
|
+
version = "0.1.2"
|
|
473
456
|
source = { editable = "packages/pico_core" }
|
|
474
457
|
dependencies = [
|
|
475
458
|
{ name = "httpx" },
|
|
476
|
-
{ name = "pico-ai" },
|
|
459
|
+
{ name = "pico-cli-ai" },
|
|
477
460
|
{ name = "pydantic" },
|
|
478
461
|
]
|
|
479
462
|
|
|
480
463
|
[package.metadata]
|
|
481
464
|
requires-dist = [
|
|
482
465
|
{ name = "httpx", specifier = ">=0.27" },
|
|
483
|
-
{ name = "pico-ai", editable = "packages/pico_ai" },
|
|
466
|
+
{ name = "pico-cli-ai", editable = "packages/pico_ai" },
|
|
484
467
|
{ name = "pydantic", specifier = ">=2" },
|
|
485
468
|
]
|
|
486
469
|
|
|
487
470
|
[[package]]
|
|
488
|
-
name = "pico-
|
|
489
|
-
version = "0.1.
|
|
471
|
+
name = "pico-cli-sdk"
|
|
472
|
+
version = "0.1.2"
|
|
473
|
+
source = { editable = "packages/pico_sdk" }
|
|
474
|
+
dependencies = [
|
|
475
|
+
{ name = "pico-cli-ai" },
|
|
476
|
+
{ name = "pico-cli-core" },
|
|
477
|
+
{ name = "pydantic" },
|
|
478
|
+
]
|
|
479
|
+
|
|
480
|
+
[package.metadata]
|
|
481
|
+
requires-dist = [
|
|
482
|
+
{ name = "pico-cli-ai", editable = "packages/pico_ai" },
|
|
483
|
+
{ name = "pico-cli-core", editable = "packages/pico_core" },
|
|
484
|
+
{ name = "pydantic", specifier = ">=2" },
|
|
485
|
+
]
|
|
486
|
+
|
|
487
|
+
[[package]]
|
|
488
|
+
name = "pico-cli-tui"
|
|
489
|
+
version = "0.1.2"
|
|
490
490
|
source = { editable = "packages/pico_tui" }
|
|
491
491
|
dependencies = [
|
|
492
492
|
{ name = "pico-cli-sdk" },
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
# Context Status Bar Implementation Summary
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
Added a context status bar widget to the PicoCLI TUI that displays:
|
|
5
|
-
- Provider name (e.g., "OpenRouter")
|
|
6
|
-
- Model name (e.g., "nvidia/nemotron-3.5-lightning:free")
|
|
7
|
-
- Thinking indicator (shown when the model is reasoning)
|
|
8
|
-
- Context window progress bar (fills as context fills, color-coded by usage level)
|
|
9
|
-
- Current token count (formatted with commas, e.g., "38,932")
|
|
10
|
-
|
|
11
|
-
## Files Modified
|
|
12
|
-
|
|
13
|
-
### 1. `packages/pico_core/src/pico_core/fsm.py`
|
|
14
|
-
- Added public `estimate_tokens()` method to `AgentLoop` class
|
|
15
|
-
- This exposes the private `_estimate_tokens()` functionality
|
|
16
|
-
|
|
17
|
-
### 2. `packages/pico_sdk/src/pico_sdk/session.py`
|
|
18
|
-
- Added `provider_name` property to `AgentSession` class
|
|
19
|
-
- Extracts provider name from the provider class name
|
|
20
|
-
- Returns "OpenRouter" for OpenRouterProvider
|
|
21
|
-
- Added `context_window` property to `AgentSession` class
|
|
22
|
-
- Returns the context window size from the agent loop
|
|
23
|
-
- Added `estimate_tokens()` method to `AgentSession` class
|
|
24
|
-
- Returns current estimated token count from the agent loop
|
|
25
|
-
|
|
26
|
-
### 3. `packages/pico_tui/src/pico_tui/status_bar.py` (NEW FILE)
|
|
27
|
-
- Created `ContextStatusBar` widget extending Textual's `Static`
|
|
28
|
-
- Features:
|
|
29
|
-
- Displays provider | model information
|
|
30
|
-
- Shows "thinking" indicator in yellow italic when active
|
|
31
|
-
- Renders a 20-character progress bar for context window usage
|
|
32
|
-
- Color-codes the progress bar:
|
|
33
|
-
- Green: 0-50% usage (░ character)
|
|
34
|
-
- Yellow: 50-70% usage (▒ character)
|
|
35
|
-
- Orange: 70-90% usage (▓ character)
|
|
36
|
-
- Red: 90-100% usage (█ character)
|
|
37
|
-
- Shows token count with comma formatting in cyan
|
|
38
|
-
- Methods:
|
|
39
|
-
- `on_mount()`: Renders initial display after widget mount
|
|
40
|
-
- `update_info()`: Updates all status information
|
|
41
|
-
- `set_thinking()`: Updates thinking state
|
|
42
|
-
- `_update_display()`: Renders the status bar with Rich Text
|
|
43
|
-
|
|
44
|
-
### 4. `packages/pico_tui/src/pico_tui/app.py`
|
|
45
|
-
- Imported `ContextStatusBar` widget
|
|
46
|
-
- Added CSS styling for `#status-bar`:
|
|
47
|
-
- Docked to bottom
|
|
48
|
-
- Height: 1 line
|
|
49
|
-
- Background: $surface color
|
|
50
|
-
- Padding: 0 1
|
|
51
|
-
- Added `ContextStatusBar` to `compose()` method after Input widget
|
|
52
|
-
- Added `_update_status_bar()` method to update the status bar with current session info
|
|
53
|
-
- Updated `on_mount()` to call `_update_status_bar()` on initialization
|
|
54
|
-
- Updated `_run_prompt()` to call `_update_status_bar()` when streaming starts
|
|
55
|
-
- Updated `_stream_worker()` to call `_update_status_bar()` when streaming completes
|
|
56
|
-
|
|
57
|
-
### 5. `tests/test_status_bar.py` (NEW FILE)
|
|
58
|
-
- Added 3 tests for the ContextStatusBar widget:
|
|
59
|
-
- `test_status_bar_initialization`: Verifies default values
|
|
60
|
-
- `test_status_bar_stores_info`: Verifies info storage
|
|
61
|
-
- `test_status_bar_set_thinking`: Verifies thinking state updates
|
|
62
|
-
|
|
63
|
-
## Test Results
|
|
64
|
-
All 92 tests pass successfully, including:
|
|
65
|
-
- 19 existing TUI tests
|
|
66
|
-
- 3 new status bar tests
|
|
67
|
-
- All other existing tests remain passing
|
|
68
|
-
|
|
69
|
-
## Usage
|
|
70
|
-
The status bar automatically appears below the input bar in the TUI and updates:
|
|
71
|
-
- On application startup (shows initial provider, model, and 0 tokens)
|
|
72
|
-
- When a prompt is submitted (shows "thinking" indicator)
|
|
73
|
-
- When streaming completes (updates token count and hides "thinking")
|
|
74
|
-
- When the model changes via `/model` command
|
|
75
|
-
|
|
76
|
-
The status bar provides real-time visibility into:
|
|
77
|
-
- Which AI provider and model is being used
|
|
78
|
-
- Whether the model is currently thinking/reasoning
|
|
79
|
-
- How much of the context window is being used
|
|
80
|
-
- The exact token count in the current context
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|