bitacora 1.0.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.
- bitacora-1.0.0/.gitignore +11 -0
- bitacora-1.0.0/CHANGELOG.md +17 -0
- bitacora-1.0.0/LICENSE +21 -0
- bitacora-1.0.0/PKG-INFO +166 -0
- bitacora-1.0.0/README.es.md +134 -0
- bitacora-1.0.0/README.md +134 -0
- bitacora-1.0.0/bitacora/__init__.py +3 -0
- bitacora-1.0.0/bitacora/__main__.py +3 -0
- bitacora-1.0.0/bitacora/cli.py +78 -0
- bitacora-1.0.0/bitacora/collector.py +95 -0
- bitacora-1.0.0/bitacora/demo.py +323 -0
- bitacora-1.0.0/bitacora/i18n.py +256 -0
- bitacora-1.0.0/bitacora/model.py +269 -0
- bitacora-1.0.0/bitacora/procs.py +121 -0
- bitacora-1.0.0/bitacora/providers/__init__.py +13 -0
- bitacora-1.0.0/bitacora/providers/base.py +44 -0
- bitacora-1.0.0/bitacora/providers/claude.py +368 -0
- bitacora-1.0.0/bitacora/providers/codex.py +344 -0
- bitacora-1.0.0/bitacora/store.py +110 -0
- bitacora-1.0.0/bitacora/ui/__init__.py +0 -0
- bitacora-1.0.0/bitacora/ui/app.py +839 -0
- bitacora-1.0.0/bitacora/ui/masthead.py +324 -0
- bitacora-1.0.0/bitacora/ui/render.py +235 -0
- bitacora-1.0.0/bitacora/ui/screens.py +39 -0
- bitacora-1.0.0/bitacora/ui/setup.py +202 -0
- bitacora-1.0.0/bitacora/util.py +100 -0
- bitacora-1.0.0/bitacora/views.py +59 -0
- bitacora-1.0.0/pyproject.toml +59 -0
- bitacora-1.0.0/tests/conftest.py +25 -0
- bitacora-1.0.0/tests/test_app.py +212 -0
- bitacora-1.0.0/tests/test_claude.py +169 -0
- bitacora-1.0.0/tests/test_codex.py +145 -0
- bitacora-1.0.0/tests/test_collector.py +62 -0
- bitacora-1.0.0/tests/test_store_i18n.py +61 -0
- bitacora-1.0.0/tests/test_util.py +66 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 (2026-10-07)
|
|
4
|
+
|
|
5
|
+
First public release.
|
|
6
|
+
|
|
7
|
+
- Live dashboard for coding agents working in the background, with providers for Codex and Claude Code.
|
|
8
|
+
- Every session with its status, an activity feed (commands, edits, messages, thinking time) and tabs for messages, files, log, result and details.
|
|
9
|
+
- Matches live processes to their sessions by id, or by working directory and start time, including forked Claude Code sessions.
|
|
10
|
+
- Finds a run's log from the parent shell's redirect and remembers result and log paths after the run ends.
|
|
11
|
+
- Codex quota, tokens and context per session.
|
|
12
|
+
- Views (background runs, everything, interactive, live only) and custom views in `config.toml`.
|
|
13
|
+
- Home and setup screen with live counts per view, language, history window, notifications and theme.
|
|
14
|
+
- Notifications when a run finishes, stops or asks a question.
|
|
15
|
+
- Kill with confirmation, open result or log, copy the resume command.
|
|
16
|
+
- English and Spanish interface.
|
|
17
|
+
- `--demo` mode with generated sessions, also used by the tests and the README screenshots.
|
bitacora-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Baltazar Andersson
|
|
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.
|
bitacora-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: bitacora
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Live dashboard for coding agents (Codex, Claude Code) working in the background.
|
|
5
|
+
Project-URL: Repository, https://github.com/baltazarandersson/bitacora
|
|
6
|
+
Project-URL: Issues, https://github.com/baltazarandersson/bitacora/issues
|
|
7
|
+
Project-URL: Changelog, https://github.com/baltazarandersson/bitacora/blob/main/CHANGELOG.md
|
|
8
|
+
Author: Baltazar Andersson
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: agents,claude-code,codex,monitor,textual,tui
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Software Development
|
|
23
|
+
Classifier: Topic :: System :: Monitoring
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: psutil>=5.9
|
|
26
|
+
Requires-Dist: textual>=1.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Bitácora
|
|
34
|
+
|
|
35
|
+
**English** · [Español](README.es.md)
|
|
36
|
+
|
|
37
|
+
A live dashboard for coding agents working in the background. It supports Codex and Claude Code today.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
## Why
|
|
42
|
+
|
|
43
|
+
When you hand work to agents in the background (a `codex exec` in another terminal, a `claude -p` from a script, an orchestrator that launches five runs at once), each one writes its progress to a file you never open. You find out how it went when it ends, or when you remember to look.
|
|
44
|
+
|
|
45
|
+
Bitácora reads those files while they grow. It pairs every agent process with its session and shows what each run is doing, which files it touched, how much context it has left and whether it is waiting for you. It is not a launcher and not a history search tool. It is the panel you keep open while your agents work.
|
|
46
|
+
|
|
47
|
+
## What it shows
|
|
48
|
+
|
|
49
|
+
- Every session with its status: running, waiting for an answer, your turn, done, aborted or cut off.
|
|
50
|
+
- An activity feed per session: commands with their exit codes, file edits with lines added and removed, messages, and how long the agent thought before each step.
|
|
51
|
+
- Tabs for messages, touched files, the run's log, its result file and the full session details.
|
|
52
|
+
- Tokens, context used and, for Codex, how much of your 5-hour and weekly quota is gone.
|
|
53
|
+
- A notification when a run finishes, stops or asks you something.
|
|
54
|
+
- Actions on the selected run: kill it (it asks first), open its result or log, copy the command that resumes it.
|
|
55
|
+
|
|
56
|
+
## Install
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
uvx bitacora
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or install it for good with `pipx install bitacora` or `uv tool install bitacora`. You need Python 3.11 or newer.
|
|
63
|
+
|
|
64
|
+
On Windows you can also download `bitacora.exe` from the [latest release](https://github.com/baltazarandersson/bitacora/releases/latest) and run it without Python. It is not code-signed, so SmartScreen will warn you the first time.
|
|
65
|
+
|
|
66
|
+
To try it without any agent installed:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uvx bitacora --demo
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Use
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bitacora # live sessions plus the last 24 hours
|
|
76
|
+
bitacora --view background # start in a view, skipping the setup screen
|
|
77
|
+
bitacora --live # live sessions only
|
|
78
|
+
bitacora --hours 72 # a longer history window
|
|
79
|
+
bitacora --lang es # Spanish interface
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The first time you open it, Bitácora shows its home screen. Each view there comes with the number of sessions it would show from your own machine, so you can tell at a glance what you are choosing. You can come back to that screen anytime with `s` to change the view, language, history window, notifications or theme. On short terminals the header folds into a single line by itself; `h` brings it back.
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
| Key | Action |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `↑` `↓` | Move through sessions |
|
|
89
|
+
| `Enter` / `Esc` | Go into the detail pane / back to the list |
|
|
90
|
+
| `1`–`6` | Activity, Messages, Files, Log, Result, Info |
|
|
91
|
+
| `PgUp` `PgDn`, `Shift+↑↓` | Scroll the detail pane from anywhere |
|
|
92
|
+
| `v` | Full detail: command output, diffs, complete prompts |
|
|
93
|
+
| `a` | Live sessions only |
|
|
94
|
+
| `f` | Pause or resume auto-scroll |
|
|
95
|
+
| `s` | Home: views and preferences |
|
|
96
|
+
| `h` | Fold or unfold the header (or click the `▲ bitácora` strip) |
|
|
97
|
+
| `Ctrl+P` | Command palette: switch view or language, fold the header, toggle filters |
|
|
98
|
+
| `k` | Kill the selected run and its children (asks first) |
|
|
99
|
+
| `o` / `l` | Open the result file / the log |
|
|
100
|
+
| `c` | Copy the command that resumes the session |
|
|
101
|
+
| `t` | Next theme |
|
|
102
|
+
| `q` | Quit |
|
|
103
|
+
|
|
104
|
+
### Views
|
|
105
|
+
|
|
106
|
+
| View | Shows |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| Background runs | `codex exec`, `claude -p` and SDK runs |
|
|
109
|
+
| Everything | Every session from every agent |
|
|
110
|
+
| Interactive | Desktop apps, IDEs and terminal chats |
|
|
111
|
+
| Live only | Whatever is running right now |
|
|
112
|
+
|
|
113
|
+
You can add your own in `config.toml`. `bitacora --paths` prints where that file lives.
|
|
114
|
+
|
|
115
|
+
```toml
|
|
116
|
+
lang = "en" # en, es, or leave it out to follow the system
|
|
117
|
+
hours = 24 # history window
|
|
118
|
+
notify = true # toast and bell when a run finishes, stops or asks
|
|
119
|
+
|
|
120
|
+
[views.codex-bg]
|
|
121
|
+
name = "Codex in the background"
|
|
122
|
+
providers = ["codex"] # codex, claude
|
|
123
|
+
kind = "background" # background, interactive or all
|
|
124
|
+
live_only = false
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
What you save in the setup screen takes precedence over `config.toml`, and command line flags take precedence over both.
|
|
128
|
+
|
|
129
|
+
## How it works
|
|
130
|
+
|
|
131
|
+
Bitácora only reads files the agents already write.
|
|
132
|
+
|
|
133
|
+
- **Codex**: rollouts in `~/.codex/sessions` (or `$CODEX_HOME`), thread names from `session_index.jsonl`, and the `codex exec` / `codex` processes with their flags (`-m`, `-c`, `-C`, `-s`, `-o`, `resume <id>`).
|
|
134
|
+
- **Claude Code**: transcripts in `~/.claude/projects` (or `$CLAUDE_CONFIG_DIR`) and the `claude` processes with `--resume`, `--session-id`, `--model`, `-p`.
|
|
135
|
+
|
|
136
|
+
A live process is matched to its session by id when the command line has one, and otherwise by working directory and start time. The log comes from a `> run.log` redirect in the parent shell's command line. Bitácora remembers each session's label, result and log paths in its own state file, so a finished run still shows them.
|
|
137
|
+
|
|
138
|
+
## Privacy
|
|
139
|
+
|
|
140
|
+
Everything stays on your machine. Bitácora makes no network requests and never writes to the agents' files. The only thing it can do to an agent is kill it, and only after you confirm. Its state file holds session labels and file paths; `bitacora --paths` shows where.
|
|
141
|
+
|
|
142
|
+
## Limits
|
|
143
|
+
|
|
144
|
+
- Neither format is documented, and both change between versions. This release was tested with Codex CLI 0.161 and Claude Code 2.1.
|
|
145
|
+
- Windows is where it gets the most use. Linux and macOS pass the test suite in CI, but have seen less real use.
|
|
146
|
+
- Reasoning is stored encrypted or not at all, so the activity feed shows how long the agent thought rather than what it thought (Codex includes summaries if `model_reasoning_summary` is on).
|
|
147
|
+
- Claude Code does not record its context window, so for Claude sessions Bitácora shows context in tokens instead of a percentage.
|
|
148
|
+
- Subagent sessions are not grouped under their parent yet.
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
git clone https://github.com/baltazarandersson/bitacora
|
|
154
|
+
cd bitacora
|
|
155
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]" # .venv\Scripts\pip on Windows
|
|
156
|
+
pytest
|
|
157
|
+
ruff check .
|
|
158
|
+
python -m bitacora --demo
|
|
159
|
+
python scripts/screenshot.py en # regenerates docs/*.svg from the demo
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Adding an agent means writing a provider in `bitacora/providers/`: where its sessions live, how its processes look and how to turn its records into events. The demo generator in `bitacora/demo.py` doubles as the test fixture.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT. Bitácora is an independent project, not affiliated with OpenAI or Anthropic.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Bitácora
|
|
2
|
+
|
|
3
|
+
[English](README.md) · **Español**
|
|
4
|
+
|
|
5
|
+
Un panel en vivo para los agentes de código que trabajan en segundo plano. Hoy funciona con Codex y Claude Code.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Por qué
|
|
10
|
+
|
|
11
|
+
Cuando le delegas trabajo a agentes en segundo plano (un `codex exec` en otra terminal, un `claude -p` desde un script, un orquestador que lanza cinco corridas a la vez), cada uno va dejando su progreso en un archivo que nunca abres. Te enteras de cómo fue cuando termina, o cuando te acuerdas de mirar.
|
|
12
|
+
|
|
13
|
+
Bitácora lee esos archivos mientras crecen. Empareja cada proceso con su sesión y muestra qué está haciendo cada corrida, qué archivos tocó, cuánto contexto le queda y si está esperando algo de ti. No lanza agentes ni busca en tu historial: es el panel que dejas abierto mientras tus agentes trabajan.
|
|
14
|
+
|
|
15
|
+
## Qué muestra
|
|
16
|
+
|
|
17
|
+
- Cada sesión con su estado: corriendo, esperando respuesta, te toca, terminada, abortada o cortada.
|
|
18
|
+
- La actividad de cada sesión: comandos con su código de salida, archivos editados con líneas agregadas y quitadas, mensajes, y cuánto pensó el agente antes de cada paso.
|
|
19
|
+
- Pestañas para los mensajes, los archivos tocados, el log de la corrida, su archivo de resultado y todos los datos de la sesión.
|
|
20
|
+
- Tokens, contexto usado y, en Codex, cuánto llevas gastado de la cuota de 5 horas y de la semanal.
|
|
21
|
+
- Un aviso cuando una corrida termina, se corta o te pregunta algo.
|
|
22
|
+
- Acciones sobre la corrida seleccionada: matarla (te pide confirmación), abrir su resultado o su log, copiar el comando que la retoma.
|
|
23
|
+
|
|
24
|
+
## Instalación
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uvx bitacora
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Si prefieres dejarlo instalado: `pipx install bitacora` o `uv tool install bitacora`. Necesitas Python 3.11 o más nuevo.
|
|
31
|
+
|
|
32
|
+
En Windows también puedes descargar `bitacora.exe` desde la [última release](https://github.com/baltazarandersson/bitacora/releases/latest) y usarlo sin Python. No está firmado, así que SmartScreen te va a avisar la primera vez.
|
|
33
|
+
|
|
34
|
+
Para probarlo sin tener ningún agente instalado:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uvx bitacora --demo
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Uso
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bitacora # sesiones vivas y las últimas 24 horas
|
|
44
|
+
bitacora --view background # arranca en una vista y saltea la pantalla de inicio
|
|
45
|
+
bitacora --live # solo sesiones vivas
|
|
46
|
+
bitacora --hours 72 # una ventana de historial más larga
|
|
47
|
+
bitacora --lang en # interfaz en inglés
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
La primera vez, Bitácora abre su pantalla de inicio. Cada vista aparece con la cantidad de sesiones que mostraría con los datos de tu propia máquina, así sabes qué estás eligiendo antes de elegirlo. Puedes volver a esa pantalla cuando quieras con `s` para cambiar la vista, el idioma, la ventana de historial, los avisos o el tema. En terminales bajas la cabecera se pliega sola en una línea; `h` la despliega.
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
| Tecla | Acción |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `↑` `↓` | Moverse entre sesiones |
|
|
57
|
+
| `Enter` / `Esc` | Entrar al panel de detalle / volver a la lista |
|
|
58
|
+
| `1`–`6` | Actividad, Mensajes, Archivos, Log, Resultado, Info |
|
|
59
|
+
| `PgUp` `PgDn`, `Shift+↑↓` | Desplazar el detalle desde cualquier lado |
|
|
60
|
+
| `v` | Detalle completo: salida de comandos, diffs, prompts enteros |
|
|
61
|
+
| `a` | Solo sesiones vivas |
|
|
62
|
+
| `f` | Pausar o reanudar el auto-scroll |
|
|
63
|
+
| `s` | Inicio: vistas y preferencias |
|
|
64
|
+
| `h` | Plegar o desplegar la cabecera (o un clic en la franja `▲ bitácora`) |
|
|
65
|
+
| `Ctrl+P` | Paleta de comandos: cambiar de vista o idioma, plegar la cabecera, alternar filtros |
|
|
66
|
+
| `k` | Matar la corrida seleccionada y sus hijos (pide confirmación) |
|
|
67
|
+
| `o` / `l` | Abrir el archivo de resultado / el log |
|
|
68
|
+
| `c` | Copiar el comando que retoma la sesión |
|
|
69
|
+
| `t` | Siguiente tema |
|
|
70
|
+
| `q` | Salir |
|
|
71
|
+
|
|
72
|
+
### Vistas
|
|
73
|
+
|
|
74
|
+
| Vista | Muestra |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| En segundo plano | `codex exec`, `claude -p` y corridas del SDK |
|
|
77
|
+
| Todo | Todas las sesiones de todos los agentes |
|
|
78
|
+
| Interactivas | Apps de escritorio, IDEs y chats en terminal |
|
|
79
|
+
| Solo vivas | Lo que está corriendo ahora |
|
|
80
|
+
|
|
81
|
+
Puedes sumar las tuyas en `config.toml`. `bitacora --paths` te dice dónde está ese archivo.
|
|
82
|
+
|
|
83
|
+
```toml
|
|
84
|
+
lang = "es" # en, es, o no lo pongas para seguir al sistema
|
|
85
|
+
hours = 24 # ventana de historial
|
|
86
|
+
notify = true # aviso y campana cuando una corrida termina, se corta o pregunta
|
|
87
|
+
|
|
88
|
+
[views.codex-bg]
|
|
89
|
+
name = "Codex en segundo plano"
|
|
90
|
+
providers = ["codex"] # codex, claude
|
|
91
|
+
kind = "background" # background, interactive o all
|
|
92
|
+
live_only = false
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Lo que guardas en la pantalla de configuración tiene prioridad sobre `config.toml`, y los flags de la línea de comandos tienen prioridad sobre los dos.
|
|
96
|
+
|
|
97
|
+
## Cómo funciona
|
|
98
|
+
|
|
99
|
+
Bitácora solo lee archivos que los agentes ya escriben.
|
|
100
|
+
|
|
101
|
+
- **Codex**: los rollouts en `~/.codex/sessions` (o `$CODEX_HOME`), los nombres de los threads en `session_index.jsonl`, y los procesos `codex exec` / `codex` con sus flags (`-m`, `-c`, `-C`, `-s`, `-o`, `resume <id>`).
|
|
102
|
+
- **Claude Code**: las transcripciones en `~/.claude/projects` (o `$CLAUDE_CONFIG_DIR`) y los procesos `claude` con `--resume`, `--session-id`, `--model`, `-p`.
|
|
103
|
+
|
|
104
|
+
Cada proceso vivo se empareja con su sesión por id cuando la línea de comandos lo trae, y si no, por carpeta de trabajo y hora de inicio. El log sale de una redirección `> run.log` en la línea de comandos del shell padre. Bitácora guarda el nombre, el resultado y el log de cada sesión en su propio archivo de estado, así una corrida terminada los sigue mostrando.
|
|
105
|
+
|
|
106
|
+
## Privacidad
|
|
107
|
+
|
|
108
|
+
Todo queda en tu máquina. Bitácora no hace pedidos a la red y nunca escribe en los archivos de los agentes. Lo único que le puede hacer a un agente es matarlo, y solo después de que confirmes. Su archivo de estado guarda nombres de sesiones y rutas de archivos; `bitacora --paths` te muestra dónde.
|
|
109
|
+
|
|
110
|
+
## Límites
|
|
111
|
+
|
|
112
|
+
- Ninguno de los dos formatos está documentado, y los dos cambian entre versiones. Esta versión se probó con Codex CLI 0.161 y Claude Code 2.1.
|
|
113
|
+
- Donde más se usa es en Windows. En Linux y macOS pasa los tests en CI, pero tiene menos uso real.
|
|
114
|
+
- El razonamiento se guarda cifrado o no se guarda, así que la actividad muestra cuánto pensó el agente y no qué pensó (Codex incluye resúmenes si `model_reasoning_summary` está activo).
|
|
115
|
+
- Claude Code no registra su ventana de contexto, así que en sus sesiones Bitácora muestra el contexto en tokens y no en porcentaje.
|
|
116
|
+
- Las sesiones de subagentes todavía no se agrupan bajo su sesión madre.
|
|
117
|
+
|
|
118
|
+
## Desarrollo
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git clone https://github.com/baltazarandersson/bitacora
|
|
122
|
+
cd bitacora
|
|
123
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]" # .venv\Scripts\pip en Windows
|
|
124
|
+
pytest
|
|
125
|
+
ruff check .
|
|
126
|
+
python -m bitacora --demo
|
|
127
|
+
python scripts/screenshot.py es # regenera docs/*.svg a partir de la demo
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Sumar un agente es escribir un provider en `bitacora/providers/`: dónde guarda sus sesiones, cómo se ven sus procesos y cómo convertir sus registros en eventos. El generador de la demo en `bitacora/demo.py` también sirve como fixture de los tests.
|
|
131
|
+
|
|
132
|
+
## Licencia
|
|
133
|
+
|
|
134
|
+
MIT. Bitácora es un proyecto independiente, sin relación con OpenAI ni Anthropic.
|
bitacora-1.0.0/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Bitácora
|
|
2
|
+
|
|
3
|
+
**English** · [Español](README.es.md)
|
|
4
|
+
|
|
5
|
+
A live dashboard for coding agents working in the background. It supports Codex and Claude Code today.
|
|
6
|
+
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
When you hand work to agents in the background (a `codex exec` in another terminal, a `claude -p` from a script, an orchestrator that launches five runs at once), each one writes its progress to a file you never open. You find out how it went when it ends, or when you remember to look.
|
|
12
|
+
|
|
13
|
+
Bitácora reads those files while they grow. It pairs every agent process with its session and shows what each run is doing, which files it touched, how much context it has left and whether it is waiting for you. It is not a launcher and not a history search tool. It is the panel you keep open while your agents work.
|
|
14
|
+
|
|
15
|
+
## What it shows
|
|
16
|
+
|
|
17
|
+
- Every session with its status: running, waiting for an answer, your turn, done, aborted or cut off.
|
|
18
|
+
- An activity feed per session: commands with their exit codes, file edits with lines added and removed, messages, and how long the agent thought before each step.
|
|
19
|
+
- Tabs for messages, touched files, the run's log, its result file and the full session details.
|
|
20
|
+
- Tokens, context used and, for Codex, how much of your 5-hour and weekly quota is gone.
|
|
21
|
+
- A notification when a run finishes, stops or asks you something.
|
|
22
|
+
- Actions on the selected run: kill it (it asks first), open its result or log, copy the command that resumes it.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
uvx bitacora
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or install it for good with `pipx install bitacora` or `uv tool install bitacora`. You need Python 3.11 or newer.
|
|
31
|
+
|
|
32
|
+
On Windows you can also download `bitacora.exe` from the [latest release](https://github.com/baltazarandersson/bitacora/releases/latest) and run it without Python. It is not code-signed, so SmartScreen will warn you the first time.
|
|
33
|
+
|
|
34
|
+
To try it without any agent installed:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uvx bitacora --demo
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Use
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
bitacora # live sessions plus the last 24 hours
|
|
44
|
+
bitacora --view background # start in a view, skipping the setup screen
|
|
45
|
+
bitacora --live # live sessions only
|
|
46
|
+
bitacora --hours 72 # a longer history window
|
|
47
|
+
bitacora --lang es # Spanish interface
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The first time you open it, Bitácora shows its home screen. Each view there comes with the number of sessions it would show from your own machine, so you can tell at a glance what you are choosing. You can come back to that screen anytime with `s` to change the view, language, history window, notifications or theme. On short terminals the header folds into a single line by itself; `h` brings it back.
|
|
51
|
+
|
|
52
|
+

|
|
53
|
+
|
|
54
|
+
| Key | Action |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `↑` `↓` | Move through sessions |
|
|
57
|
+
| `Enter` / `Esc` | Go into the detail pane / back to the list |
|
|
58
|
+
| `1`–`6` | Activity, Messages, Files, Log, Result, Info |
|
|
59
|
+
| `PgUp` `PgDn`, `Shift+↑↓` | Scroll the detail pane from anywhere |
|
|
60
|
+
| `v` | Full detail: command output, diffs, complete prompts |
|
|
61
|
+
| `a` | Live sessions only |
|
|
62
|
+
| `f` | Pause or resume auto-scroll |
|
|
63
|
+
| `s` | Home: views and preferences |
|
|
64
|
+
| `h` | Fold or unfold the header (or click the `▲ bitácora` strip) |
|
|
65
|
+
| `Ctrl+P` | Command palette: switch view or language, fold the header, toggle filters |
|
|
66
|
+
| `k` | Kill the selected run and its children (asks first) |
|
|
67
|
+
| `o` / `l` | Open the result file / the log |
|
|
68
|
+
| `c` | Copy the command that resumes the session |
|
|
69
|
+
| `t` | Next theme |
|
|
70
|
+
| `q` | Quit |
|
|
71
|
+
|
|
72
|
+
### Views
|
|
73
|
+
|
|
74
|
+
| View | Shows |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| Background runs | `codex exec`, `claude -p` and SDK runs |
|
|
77
|
+
| Everything | Every session from every agent |
|
|
78
|
+
| Interactive | Desktop apps, IDEs and terminal chats |
|
|
79
|
+
| Live only | Whatever is running right now |
|
|
80
|
+
|
|
81
|
+
You can add your own in `config.toml`. `bitacora --paths` prints where that file lives.
|
|
82
|
+
|
|
83
|
+
```toml
|
|
84
|
+
lang = "en" # en, es, or leave it out to follow the system
|
|
85
|
+
hours = 24 # history window
|
|
86
|
+
notify = true # toast and bell when a run finishes, stops or asks
|
|
87
|
+
|
|
88
|
+
[views.codex-bg]
|
|
89
|
+
name = "Codex in the background"
|
|
90
|
+
providers = ["codex"] # codex, claude
|
|
91
|
+
kind = "background" # background, interactive or all
|
|
92
|
+
live_only = false
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
What you save in the setup screen takes precedence over `config.toml`, and command line flags take precedence over both.
|
|
96
|
+
|
|
97
|
+
## How it works
|
|
98
|
+
|
|
99
|
+
Bitácora only reads files the agents already write.
|
|
100
|
+
|
|
101
|
+
- **Codex**: rollouts in `~/.codex/sessions` (or `$CODEX_HOME`), thread names from `session_index.jsonl`, and the `codex exec` / `codex` processes with their flags (`-m`, `-c`, `-C`, `-s`, `-o`, `resume <id>`).
|
|
102
|
+
- **Claude Code**: transcripts in `~/.claude/projects` (or `$CLAUDE_CONFIG_DIR`) and the `claude` processes with `--resume`, `--session-id`, `--model`, `-p`.
|
|
103
|
+
|
|
104
|
+
A live process is matched to its session by id when the command line has one, and otherwise by working directory and start time. The log comes from a `> run.log` redirect in the parent shell's command line. Bitácora remembers each session's label, result and log paths in its own state file, so a finished run still shows them.
|
|
105
|
+
|
|
106
|
+
## Privacy
|
|
107
|
+
|
|
108
|
+
Everything stays on your machine. Bitácora makes no network requests and never writes to the agents' files. The only thing it can do to an agent is kill it, and only after you confirm. Its state file holds session labels and file paths; `bitacora --paths` shows where.
|
|
109
|
+
|
|
110
|
+
## Limits
|
|
111
|
+
|
|
112
|
+
- Neither format is documented, and both change between versions. This release was tested with Codex CLI 0.161 and Claude Code 2.1.
|
|
113
|
+
- Windows is where it gets the most use. Linux and macOS pass the test suite in CI, but have seen less real use.
|
|
114
|
+
- Reasoning is stored encrypted or not at all, so the activity feed shows how long the agent thought rather than what it thought (Codex includes summaries if `model_reasoning_summary` is on).
|
|
115
|
+
- Claude Code does not record its context window, so for Claude sessions Bitácora shows context in tokens instead of a percentage.
|
|
116
|
+
- Subagent sessions are not grouped under their parent yet.
|
|
117
|
+
|
|
118
|
+
## Development
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
git clone https://github.com/baltazarandersson/bitacora
|
|
122
|
+
cd bitacora
|
|
123
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]" # .venv\Scripts\pip on Windows
|
|
124
|
+
pytest
|
|
125
|
+
ruff check .
|
|
126
|
+
python -m bitacora --demo
|
|
127
|
+
python scripts/screenshot.py en # regenerates docs/*.svg from the demo
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Adding an agent means writing a provider in `bitacora/providers/`: where its sessions live, how its processes look and how to turn its records into events. The demo generator in `bitacora/demo.py` doubles as the test fixture.
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT. Bitácora is an independent project, not affiliated with OpenAI or Anthropic.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Command line entry point."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from bitacora import __version__, i18n
|
|
12
|
+
from bitacora.store import State, config_path, load_config, state_path
|
|
13
|
+
from bitacora.views import BUILTIN
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
17
|
+
ap = argparse.ArgumentParser(
|
|
18
|
+
prog="bitacora",
|
|
19
|
+
description="Live dashboard for coding agents (Codex, Claude Code) working in the background.")
|
|
20
|
+
ap.add_argument("--view", help="start in this view: " + ", ".join(v.id for v in BUILTIN)
|
|
21
|
+
+ " or one from config.toml (skips the first-run picker)")
|
|
22
|
+
ap.add_argument("--live", action="store_true", help="show live sessions only")
|
|
23
|
+
ap.add_argument("--hours", type=float, help="history window in hours (default 24)")
|
|
24
|
+
ap.add_argument("--lang", choices=i18n.LANGS, help="interface language (default: system)")
|
|
25
|
+
ap.add_argument("--demo", action="store_true", help="run on generated sample sessions")
|
|
26
|
+
ap.add_argument("--paths", action="store_true", help="print where config and state live, and exit")
|
|
27
|
+
ap.add_argument("--version", action="version", version=f"bitacora {__version__}")
|
|
28
|
+
return ap
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main(argv: list[str] | None = None) -> None:
|
|
32
|
+
args = build_parser().parse_args(argv)
|
|
33
|
+
if args.paths:
|
|
34
|
+
print(json.dumps({"config": str(config_path()), "state": str(state_path())}, indent=1))
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
animator = None
|
|
38
|
+
if args.demo:
|
|
39
|
+
from bitacora import demo
|
|
40
|
+
root = Path(tempfile.mkdtemp(prefix="bitacora-demo-"))
|
|
41
|
+
data = demo.generate(root)
|
|
42
|
+
os.environ["CODEX_HOME"] = str(data["codex_home"])
|
|
43
|
+
os.environ["CLAUDE_CONFIG_DIR"] = str(data["claude_home"])
|
|
44
|
+
os.environ["BITACORA_HOME"] = str(root / "home")
|
|
45
|
+
seed = State()
|
|
46
|
+
for key, entry in data["state"].items():
|
|
47
|
+
seed.remember(key, entry)
|
|
48
|
+
seed.save()
|
|
49
|
+
animator = demo.Animator(data["live"])
|
|
50
|
+
|
|
51
|
+
from bitacora.providers import all_providers
|
|
52
|
+
from bitacora.ui.app import create_app
|
|
53
|
+
|
|
54
|
+
if animator:
|
|
55
|
+
animator.start()
|
|
56
|
+
view, collector = args.view, None
|
|
57
|
+
try:
|
|
58
|
+
while True:
|
|
59
|
+
# Precedence: command line, then what you saved in setup, then config.toml.
|
|
60
|
+
config, state = load_config(), State()
|
|
61
|
+
lang = args.lang or state.get("lang") or config.lang or "auto"
|
|
62
|
+
config.hours = args.hours or state.get("hours") or config.hours
|
|
63
|
+
config.notify = state.get("notify", config.notify)
|
|
64
|
+
i18n.set_lang(None if lang == "auto" else lang)
|
|
65
|
+
app = create_app(all_providers(), config, state, view=view, live_only=args.live,
|
|
66
|
+
scan_procs=not args.demo, lang=args.lang or state.get("lang") or "auto",
|
|
67
|
+
collector=collector)
|
|
68
|
+
if app.run() != "relaunch":
|
|
69
|
+
break
|
|
70
|
+
collector = app.collector # keep what was already read; the new app only relabels
|
|
71
|
+
view = None # a relaunch after setup starts in the view just saved
|
|
72
|
+
finally:
|
|
73
|
+
if animator:
|
|
74
|
+
animator.stop.set()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if __name__ == "__main__":
|
|
78
|
+
sys.exit(main())
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Joins live processes with their session files, plus recent history, across providers."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import time
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from bitacora import procs
|
|
8
|
+
from bitacora.model import ProcInfo, Run, Session
|
|
9
|
+
from bitacora.providers import Provider
|
|
10
|
+
from bitacora.store import State
|
|
11
|
+
from bitacora.util import label_from_out, label_from_prompt
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Collector:
|
|
15
|
+
def __init__(self, providers: list[Provider], state: State, hours: float, scan_procs: bool = True):
|
|
16
|
+
self.providers = providers
|
|
17
|
+
self.state = state
|
|
18
|
+
self.hours = hours
|
|
19
|
+
self.scan_procs = scan_procs
|
|
20
|
+
self.sessions: dict[Path, Session] = {}
|
|
21
|
+
self.pid_map: dict[tuple[str, int], Path] = {}
|
|
22
|
+
self.log_seen: dict[tuple[str, int], str | None] = {}
|
|
23
|
+
self.quotas: dict[str, dict] = {}
|
|
24
|
+
|
|
25
|
+
def _session(self, prov: Provider, path: Path) -> Session:
|
|
26
|
+
s = self.sessions.get(path)
|
|
27
|
+
if s is None:
|
|
28
|
+
s = self.sessions[path] = prov.session_cls(path)
|
|
29
|
+
s.update()
|
|
30
|
+
title = prov.title(s)
|
|
31
|
+
if title:
|
|
32
|
+
s.title = title
|
|
33
|
+
return s
|
|
34
|
+
|
|
35
|
+
def _live_run(self, prov: Provider, pi: ProcInfo, claimed: set[Path]) -> Run:
|
|
36
|
+
key = (prov.name, pi.pid)
|
|
37
|
+
path = self.pid_map.get(key)
|
|
38
|
+
if path is None or not path.exists():
|
|
39
|
+
path = prov.match(pi, claimed)
|
|
40
|
+
s = None
|
|
41
|
+
if path:
|
|
42
|
+
self.pid_map[key] = path
|
|
43
|
+
claimed.add(path)
|
|
44
|
+
s = self._session(prov, path)
|
|
45
|
+
sid = s.session_id if s else None
|
|
46
|
+
skey = f"{prov.name}:{sid}" if sid else ""
|
|
47
|
+
cached = self.state.session(skey) if skey else {}
|
|
48
|
+
out = pi.out or cached.get("out")
|
|
49
|
+
if pi.log is None:
|
|
50
|
+
# The redirect is fixed at launch and walking parent processes is slow: look once per process.
|
|
51
|
+
if key not in self.log_seen:
|
|
52
|
+
self.log_seen[key] = cached.get("log") or procs.find_log(pi.pid, out, prov.marker)
|
|
53
|
+
pi.log = self.log_seen[key]
|
|
54
|
+
label = (label_from_out(out) or (s and s.title) or cached.get("label")
|
|
55
|
+
or (s and label_from_prompt(s.first_prompt)) or f"pid {pi.pid}")
|
|
56
|
+
if skey:
|
|
57
|
+
self.state.remember(skey, {"label": label, "out": out, "log": pi.log, "cmdline": pi.cmdline})
|
|
58
|
+
return Run(skey or f"{prov.name}:pid{pi.pid}", prov.name, s, pi, label, out, pi.log)
|
|
59
|
+
|
|
60
|
+
def collect(self) -> list[Run]:
|
|
61
|
+
snap = procs.snapshot() if self.scan_procs else []
|
|
62
|
+
runs: dict[str, Run] = {}
|
|
63
|
+
since = time.time() - self.hours * 3600
|
|
64
|
+
seen: set[Path] = set()
|
|
65
|
+
alive: set[tuple[str, int]] = set()
|
|
66
|
+
for prov in self.providers:
|
|
67
|
+
if not prov.available():
|
|
68
|
+
continue
|
|
69
|
+
claimed: set[Path] = set()
|
|
70
|
+
for pi in sorted(prov.procs(snap), key=lambda x: x.create):
|
|
71
|
+
alive.add((prov.name, pi.pid))
|
|
72
|
+
run = self._live_run(prov, pi, claimed)
|
|
73
|
+
runs[run.key] = run
|
|
74
|
+
seen |= claimed
|
|
75
|
+
for f in prov.history(since):
|
|
76
|
+
if f in claimed:
|
|
77
|
+
continue
|
|
78
|
+
s = self._session(prov, f)
|
|
79
|
+
seen.add(f)
|
|
80
|
+
key = f"{prov.name}:{s.session_id or f}"
|
|
81
|
+
if key in runs:
|
|
82
|
+
continue
|
|
83
|
+
cached = self.state.session(key)
|
|
84
|
+
label = (label_from_out(cached.get("out")) or s.title or cached.get("label")
|
|
85
|
+
or label_from_prompt(s.first_prompt) or f.stem[-12:])
|
|
86
|
+
runs[key] = Run(key, prov.name, s, None, label, cached.get("out"), cached.get("log"))
|
|
87
|
+
mine = [s for p, s in self.sessions.items() if s.provider == prov.name and p in seen]
|
|
88
|
+
quota = prov.quota(mine)
|
|
89
|
+
if quota:
|
|
90
|
+
self.quotas[prov.name] = quota
|
|
91
|
+
self.pid_map = {k: v for k, v in self.pid_map.items() if k in alive}
|
|
92
|
+
self.log_seen = {k: v for k, v in self.log_seen.items() if k in alive}
|
|
93
|
+
self.sessions = {p: s for p, s in self.sessions.items() if p in seen}
|
|
94
|
+
self.state.save()
|
|
95
|
+
return sorted(runs.values(), key=lambda r: (not r.live, -(r.start or 0)))
|