zugzbot 1.0.6 → 1.0.7
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.
- package/.opencode/plugins/sdd-bridge.ts +83 -1
- package/.utils/docs_opencode/acp.md +165 -0
- package/.utils/docs_opencode/acp.pdf +0 -0
- package/.utils/docs_opencode/agents.md +803 -0
- package/.utils/docs_opencode/agents.pdf +0 -0
- package/.utils/docs_opencode/commands.md +354 -0
- package/.utils/docs_opencode/commands.pdf +0 -0
- package/.utils/docs_opencode/custom-tools.md +209 -0
- package/.utils/docs_opencode/custom-tools.pdf +0 -0
- package/.utils/docs_opencode/ecosystem.md +81 -0
- package/.utils/docs_opencode/ecosystem.pdf +0 -0
- package/.utils/docs_opencode/formatters.md +142 -0
- package/.utils/docs_opencode/formatters.pdf +0 -0
- package/.utils/docs_opencode/keybinds.md +205 -0
- package/.utils/docs_opencode/keybinds.pdf +0 -0
- package/.utils/docs_opencode/lsp.md +202 -0
- package/.utils/docs_opencode/lsp.pdf +0 -0
- package/.utils/docs_opencode/mcp-servers.md +565 -0
- package/.utils/docs_opencode/mcp-servers.pdf +0 -0
- package/.utils/docs_opencode/models.md +234 -0
- package/.utils/docs_opencode/models.pdf +0 -0
- package/.utils/docs_opencode/permissions.md +248 -0
- package/.utils/docs_opencode/permissions.pdf +0 -0
- package/.utils/docs_opencode/plugins.md +409 -0
- package/.utils/docs_opencode/plugins.pdf +0 -0
- package/.utils/docs_opencode/rules.md +189 -0
- package/.utils/docs_opencode/rules.pdf +0 -0
- package/.utils/docs_opencode/sdk.md +522 -0
- package/.utils/docs_opencode/sdk.pdf +0 -0
- package/.utils/docs_opencode/server.md +324 -0
- package/.utils/docs_opencode/server.pdf +0 -0
- package/.utils/docs_opencode/skills.md +235 -0
- package/.utils/docs_opencode/skills.pdf +0 -0
- package/.utils/docs_opencode/themes.md +378 -0
- package/.utils/docs_opencode/themes.pdf +0 -0
- package/.utils/docs_opencode/tools.md +364 -0
- package/.utils/docs_opencode/tools.pdf +0 -0
- package/.utils/export_opencode_session.py +242 -0
- package/.utils/toggle_model.py +441 -0
- package/README.md +39 -0
- package/bin/init.js +25 -6
- package/models.json +8 -0
- package/opencode.json +4 -5
- package/package.json +3 -1
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# Herramientas
|
|
2
|
+
|
|
3
|
+
Administre las herramientas que puede usar un LLM.
|
|
4
|
+
|
|
5
|
+
Las herramientas permiten que LLM realice acciones en su código base. OpenCode viene con un conjunto de herramientas integradas, pero puede ampliarlo con [herramientas personalizadas](https://opencode.ai/docs/custom-tools) o [servidores MCP](https://opencode.ai/docs/mcp-servers).
|
|
6
|
+
|
|
7
|
+
De forma predeterminada, todas las herramientas están **habilitadas** y no necesitan permiso para ejecutarse. Puede controlar el comportamiento de la herramienta a través de [permisos](https://opencode.ai/docs/permissions).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## [Configuración](#configuración)
|
|
12
|
+
|
|
13
|
+
Utilice el campo `permission` para controlar el comportamiento de la herramienta. Puede permitir, denegar o exigir aprobación para cada herramienta.
|
|
14
|
+
|
|
15
|
+
**File**: opencode.json
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"$schema": "https://opencode.ai/config.json",
|
|
20
|
+
"permission": {
|
|
21
|
+
"edit": "deny",
|
|
22
|
+
"bash": "ask",
|
|
23
|
+
"webfetch": "allow"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
También puedes utilizar comodines para controlar varias herramientas a la vez. Por ejemplo, para solicitar aprobación para todas las herramientas de un servidor MCP:
|
|
29
|
+
|
|
30
|
+
**File**: opencode.json
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"$schema": "https://opencode.ai/config.json",
|
|
35
|
+
"permission": {
|
|
36
|
+
"mymcp_*": "ask"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
[Más información](https://opencode.ai/docs/permissions) sobre la configuración de permisos.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## [Integradas](#integradas)
|
|
46
|
+
|
|
47
|
+
Aquí están todas las herramientas integradas disponibles en OpenCode.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
### [bash](#bash)
|
|
52
|
+
|
|
53
|
+
Ejecute comandos de shell en el entorno de su proyecto.
|
|
54
|
+
|
|
55
|
+
**File**: opencode.json
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"$schema": "https://opencode.ai/config.json",
|
|
60
|
+
"permission": {
|
|
61
|
+
"bash": "allow"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Esta herramienta permite que LLM ejecute comandos de terminal como `npm install`, `git status` o cualquier otro comando de shell.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
### [edit](#edit)
|
|
71
|
+
|
|
72
|
+
Modifique archivos existentes utilizando reemplazos de cadenas exactas.
|
|
73
|
+
|
|
74
|
+
**File**: opencode.json
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"$schema": "https://opencode.ai/config.json",
|
|
79
|
+
"permission": {
|
|
80
|
+
"edit": "allow"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Esta herramienta realiza ediciones precisas de archivos reemplazando coincidencias de texto exactas. Es la forma principal en que LLM modifica el código.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
### [write](#write)
|
|
90
|
+
|
|
91
|
+
Cree nuevos archivos o sobrescriba los existentes.
|
|
92
|
+
|
|
93
|
+
**File**: opencode.json
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"$schema": "https://opencode.ai/config.json",
|
|
98
|
+
"permission": {
|
|
99
|
+
"edit": "allow"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Utilice esto para permitir que LLM cree nuevos archivos. Sobrescribirá los archivos existentes si ya existen.
|
|
105
|
+
|
|
106
|
+
> [!NOTE]
|
|
107
|
+
> La herramienta `write` está controlada por el permiso `edit`, que cubre todas las modificaciones de archivos (`edit`, `write`, `patch`).
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### [read](#read)
|
|
112
|
+
|
|
113
|
+
Lea el contenido del archivo desde su base de código.
|
|
114
|
+
|
|
115
|
+
**File**: opencode.json
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"$schema": "https://opencode.ai/config.json",
|
|
120
|
+
"permission": {
|
|
121
|
+
"read": "allow"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Esta herramienta lee archivos y devuelve su contenido. Admite la lectura de rangos de líneas específicos para archivos grandes.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### [grep](#grep)
|
|
131
|
+
|
|
132
|
+
Busque contenidos de archivos utilizando expresiones regulares.
|
|
133
|
+
|
|
134
|
+
**File**: opencode.json
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"$schema": "https://opencode.ai/config.json",
|
|
139
|
+
"permission": {
|
|
140
|
+
"grep": "allow"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Búsqueda rápida de contenido en su base de código. Admite sintaxis de expresiones regulares completa y filtrado de patrones de archivos.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### [glob](#glob)
|
|
150
|
+
|
|
151
|
+
Encuentre archivos por coincidencia de patrones.
|
|
152
|
+
|
|
153
|
+
**File**: opencode.json
|
|
154
|
+
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"$schema": "https://opencode.ai/config.json",
|
|
158
|
+
"permission": {
|
|
159
|
+
"glob": "allow"
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Busque archivos usando patrones globales como `**/*.js` o `src/**/*.ts`. Devuelve rutas de archivos coincidentes ordenadas por hora de modificación.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### [lsp (experimental)](#lsp-experimental)
|
|
169
|
+
|
|
170
|
+
Interactúe con sus servidores LSP configurados para obtener funciones de inteligencia de código como definiciones, referencias, información de desplazamiento y jerarquía de llamadas.
|
|
171
|
+
|
|
172
|
+
> [!NOTE]
|
|
173
|
+
> Esta herramienta solo está disponible cuando `OPENCODE_EXPERIMENTAL_LSP_TOOL=true` (o `OPENCODE_EXPERIMENTAL=true`).
|
|
174
|
+
|
|
175
|
+
**File**: opencode.json
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"$schema": "https://opencode.ai/config.json",
|
|
180
|
+
"permission": {
|
|
181
|
+
"lsp": "allow"
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Las operaciones admitidas incluyen `goToDefinition`, `findReferences`, `hover`, `documentSymbol`, `workspaceSymbol`, `goToImplementation`, `prepareCallHierarchy`, `incomingCalls` y `outgoingCalls`.
|
|
187
|
+
|
|
188
|
+
Para configurar qué servidores LSP están disponibles para su proyecto, consulte [Servidores LSP](https://opencode.ai/docs/lsp).
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### [patch](#patch)
|
|
193
|
+
|
|
194
|
+
Aplicar parches a los archivos.
|
|
195
|
+
|
|
196
|
+
**File**: opencode.json
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"$schema": "https://opencode.ai/config.json",
|
|
201
|
+
"permission": {
|
|
202
|
+
"edit": "allow"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Esta herramienta aplica archivos de parche a su código base. Útil para aplicar diferencias y parches de diversas fuentes.
|
|
208
|
+
|
|
209
|
+
> [!NOTE]
|
|
210
|
+
> La herramienta `patch` está controlada por el permiso `edit`, que cubre todas las modificaciones de archivos (`edit`, `write`, `patch`).
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### [skill](#skill)
|
|
215
|
+
|
|
216
|
+
Cargue una [habilidad](https://opencode.ai/docs/skills) (un archivo `SKILL.md`) y devuelva su contenido en la conversación.
|
|
217
|
+
|
|
218
|
+
**File**: opencode.json
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"$schema": "https://opencode.ai/config.json",
|
|
223
|
+
"permission": {
|
|
224
|
+
"skill": "allow"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### [todowrite](#todowrite)
|
|
232
|
+
|
|
233
|
+
Administre listas de tareas pendientes durante las sesiones de codificación.
|
|
234
|
+
|
|
235
|
+
**File**: opencode.json
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
{
|
|
239
|
+
"$schema": "https://opencode.ai/config.json",
|
|
240
|
+
"permission": {
|
|
241
|
+
"todowrite": "allow"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Crea y actualiza listas de tareas para realizar un seguimiento del progreso durante operaciones complejas. El LLM usa esto para organizar tareas de varios pasos.
|
|
247
|
+
|
|
248
|
+
> [!NOTE]
|
|
249
|
+
> Esta herramienta está deshabilitada para los subagentes de forma predeterminada, pero puede habilitarla manualmente. [Más información](https://opencode.ai/docs/agents/#permissions)
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
### [webfetch](#webfetch)
|
|
254
|
+
|
|
255
|
+
Obtener contenido web.
|
|
256
|
+
|
|
257
|
+
**File**: opencode.json
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"$schema": "https://opencode.ai/config.json",
|
|
262
|
+
"permission": {
|
|
263
|
+
"webfetch": "allow"
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Permite que LLM busque y lea páginas web. Útil para buscar documentación o investigar recursos en línea.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
### [websearch](#websearch)
|
|
273
|
+
|
|
274
|
+
Busque información en la web.
|
|
275
|
+
|
|
276
|
+
> [!NOTE]
|
|
277
|
+
> Esta herramienta solo está disponible cuando se utiliza el proveedor OpenCode o cuando la variable de entorno `OPENCODE_ENABLE_EXA` está configurada en cualquier valor verdadero (por ejemplo, `true` o `1`).
|
|
278
|
+
> Para habilitar al iniciar OpenCode:
|
|
279
|
+
> **File**: Ventana de terminal
|
|
280
|
+
> ```bash
|
|
281
|
+
> OPENCODE_ENABLE_EXA=1 opencode
|
|
282
|
+
> ```
|
|
283
|
+
|
|
284
|
+
**File**: opencode.json
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"$schema": "https://opencode.ai/config.json",
|
|
289
|
+
"permission": {
|
|
290
|
+
"websearch": "allow"
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Realiza búsquedas web utilizando Exa AI para encontrar información relevante en línea. Útil para investigar temas, encontrar eventos actuales o recopilar información más allá del límite de datos de entrenamiento.
|
|
296
|
+
|
|
297
|
+
No se requiere ninguna clave API: la herramienta se conecta directamente al servicio MCP alojado de Exa AI sin autenticación.
|
|
298
|
+
|
|
299
|
+
> [!TIP]
|
|
300
|
+
> Utilice `websearch` cuando necesite encontrar información (descubrimiento) y `webfetch` cuando necesite recuperar contenido de una URL específica (recuperación).
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### [question](#question)
|
|
305
|
+
|
|
306
|
+
Haga preguntas al usuario durante la ejecución.
|
|
307
|
+
|
|
308
|
+
**File**: opencode.json
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"$schema": "https://opencode.ai/config.json",
|
|
313
|
+
"permission": {
|
|
314
|
+
"question": "allow"
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Esta herramienta permite que LLM haga preguntas al usuario durante una tarea. Es útil para:
|
|
320
|
+
|
|
321
|
+
- Recopilar preferencias o requisitos del usuario.
|
|
322
|
+
- Aclarar instrucciones ambiguas
|
|
323
|
+
- Tomar decisiones sobre las opciones de implementación.
|
|
324
|
+
- Ofrecer opciones sobre qué dirección tomar.
|
|
325
|
+
|
|
326
|
+
Cada pregunta incluye un encabezado, el texto de la pregunta y una lista de opciones. Los usuarios pueden seleccionar entre las opciones proporcionadas o escribir una respuesta personalizada. Cuando hay varias preguntas, los usuarios pueden navegar entre ellas antes de enviar todas las respuestas.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## [Herramientas personalizadas](#herramientas-personalizadas)
|
|
331
|
+
|
|
332
|
+
Las herramientas personalizadas le permiten definir sus propias funciones a las que LLM puede llamar. Estos están definidos en su archivo de configuración y pueden ejecutar código arbitrario.
|
|
333
|
+
|
|
334
|
+
[Más información](https://opencode.ai/docs/custom-tools) sobre la creación de herramientas personalizadas.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## [Servidores MCP](#servidores-mcp)
|
|
339
|
+
|
|
340
|
+
Los servidores MCP (Model Context Protocol) le permiten integrar herramientas y servicios externos. Esto incluye acceso a bases de datos, integraciones API y servicios de terceros.
|
|
341
|
+
|
|
342
|
+
[Más información](https://opencode.ai/docs/mcp-servers) sobre la configuración de servidores MCP.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## [Internos](#internos)
|
|
347
|
+
|
|
348
|
+
Internamente, herramientas como `grep` y `glob` usan [ripgrep](https://github.com/BurntSushi/ripgrep) bajo el capó. De forma predeterminada, ripgrep respeta los patrones `.gitignore`, lo que significa que los archivos y directorios enumerados en su `.gitignore` se excluirán de las búsquedas y listados.
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
### [Ignorar patrones](#ignorar-patrones)
|
|
353
|
+
|
|
354
|
+
Para incluir archivos que normalmente se ignorarían, cree un archivo `.ignore` en la raíz de su proyecto. Este archivo puede permitir explícitamente ciertas rutas.
|
|
355
|
+
|
|
356
|
+
**File**: .ignore
|
|
357
|
+
|
|
358
|
+
```text
|
|
359
|
+
!node_modules/
|
|
360
|
+
!dist/
|
|
361
|
+
!build/
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Por ejemplo, este archivo `.ignore` permite que ripgrep busque dentro de los directorios `node_modules/`, `dist/` y `build/` incluso si figuran en `.gitignore`.
|
|
Binary file
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import sqlite3
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
DB_PATH = "/Users/wavesbyte/.local/share/opencode/opencode.db"
|
|
9
|
+
|
|
10
|
+
# Escribe aquí el ID de la sesión que deseas exportar (ejemplo: "ses_1234...")
|
|
11
|
+
# Si se deja vacío, el script requerirá el ID como argumento al ejecutarlo
|
|
12
|
+
TARGET_SESSION_ID = "ses_12f8"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def format_timestamp(ts):
|
|
16
|
+
if not ts:
|
|
17
|
+
return "N/A"
|
|
18
|
+
try:
|
|
19
|
+
# DB timestamp is in milliseconds
|
|
20
|
+
dt = datetime.fromtimestamp(ts / 1000.0)
|
|
21
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
22
|
+
except Exception:
|
|
23
|
+
return str(ts)
|
|
24
|
+
|
|
25
|
+
def sanitize_filename(name):
|
|
26
|
+
# Keep only alphanumeric, spaces, hyphens, and underscores
|
|
27
|
+
return "".join(c for c in name if c.isalnum() or c in (" ", "-", "_")).rstrip().replace(" ", "_")
|
|
28
|
+
|
|
29
|
+
def get_session_tree(cursor, session_id):
|
|
30
|
+
"""Recursively fetch child sessions and build a flat list with structure info."""
|
|
31
|
+
cursor.execute("SELECT * FROM session WHERE id = ?", (session_id,))
|
|
32
|
+
main = cursor.fetchone()
|
|
33
|
+
if not main:
|
|
34
|
+
return []
|
|
35
|
+
|
|
36
|
+
sessions = []
|
|
37
|
+
|
|
38
|
+
def traverse(sid, depth=0):
|
|
39
|
+
cursor.execute("SELECT * FROM session WHERE id = ?", (sid,))
|
|
40
|
+
s = cursor.fetchone()
|
|
41
|
+
if not s:
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
s_dict = dict(s)
|
|
45
|
+
s_dict['depth'] = depth
|
|
46
|
+
sessions.append(s_dict)
|
|
47
|
+
|
|
48
|
+
# Find children
|
|
49
|
+
cursor.execute("SELECT id FROM session WHERE parent_id = ? ORDER BY time_created ASC", (sid,))
|
|
50
|
+
children = cursor.fetchall()
|
|
51
|
+
for child in children:
|
|
52
|
+
traverse(child['id'], depth + 1)
|
|
53
|
+
|
|
54
|
+
traverse(session_id)
|
|
55
|
+
return sessions
|
|
56
|
+
|
|
57
|
+
def export_session_messages(cursor, session, output_dir, file_index):
|
|
58
|
+
sid = session['id']
|
|
59
|
+
title = session['title'] or f"Subagent_{sid[:8]}"
|
|
60
|
+
agent = session['agent'] or "unknown"
|
|
61
|
+
|
|
62
|
+
filename = f"{file_index:02d}_{sanitize_filename(title)}.md"
|
|
63
|
+
filepath = os.path.join(output_dir, filename)
|
|
64
|
+
|
|
65
|
+
# Fetch messages and their parts
|
|
66
|
+
cursor.execute("SELECT * FROM message WHERE session_id = ? ORDER BY time_created ASC", (sid,))
|
|
67
|
+
messages = cursor.fetchall()
|
|
68
|
+
|
|
69
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
70
|
+
f.write(f"# {title}\n\n")
|
|
71
|
+
f.write(f"* **ID de Sesión**: `{sid}`\n")
|
|
72
|
+
if session['parent_id']:
|
|
73
|
+
f.write(f"* **ID Padre**: `{session['parent_id']}`\n")
|
|
74
|
+
f.write(f"* **Agente**: `{agent}`\n")
|
|
75
|
+
f.write(f"* **Modelo**: `{session['model']}`\n")
|
|
76
|
+
f.write(f"* **Creado**: {format_timestamp(session['time_created'])}\n")
|
|
77
|
+
f.write(f"* **Costo estimado**: ${session['cost'] or 0.0:.6f}\n")
|
|
78
|
+
f.write(f"* **Tokens**: Entrada: {session['tokens_input'] or 0} | Salida: {session['tokens_output'] or 0} | Razonamiento: {session['tokens_reasoning'] or 0}\n\n")
|
|
79
|
+
f.write("---\n\n")
|
|
80
|
+
|
|
81
|
+
for msg in messages:
|
|
82
|
+
msg_id = msg['id']
|
|
83
|
+
msg_data = {}
|
|
84
|
+
try:
|
|
85
|
+
msg_data = json.loads(msg['data'])
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
role = msg_data.get('role', 'System')
|
|
90
|
+
role_emoji = "👤 Usuario" if role == "user" else "🤖 Asistente"
|
|
91
|
+
f.write(f"## {role_emoji} ({format_timestamp(msg['time_created'])})\n\n")
|
|
92
|
+
|
|
93
|
+
# Fetch parts for this message
|
|
94
|
+
cursor.execute("SELECT * FROM part WHERE message_id = ? ORDER BY time_created ASC", (msg_id,))
|
|
95
|
+
parts = cursor.fetchall()
|
|
96
|
+
|
|
97
|
+
for part in parts:
|
|
98
|
+
part_data = {}
|
|
99
|
+
try:
|
|
100
|
+
part_data = json.loads(part['data'])
|
|
101
|
+
except Exception:
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
p_type = part_data.get('type')
|
|
105
|
+
|
|
106
|
+
if p_type == 'text':
|
|
107
|
+
f.write(part_data.get('text', '') + "\n\n")
|
|
108
|
+
|
|
109
|
+
elif p_type == 'reasoning':
|
|
110
|
+
f.write("<details>\n<summary>💭 Pensamiento (Reasoning)</summary>\n\n")
|
|
111
|
+
f.write(part_data.get('text', '') + "\n")
|
|
112
|
+
f.write("\n</details>\n\n")
|
|
113
|
+
|
|
114
|
+
elif p_type == 'tool':
|
|
115
|
+
tool_name = part_data.get('tool', 'unknown')
|
|
116
|
+
state = part_data.get('state', {})
|
|
117
|
+
status = state.get('status', 'unknown')
|
|
118
|
+
|
|
119
|
+
# Tool Call Input
|
|
120
|
+
f.write(f"⚙️ **Llamada a Herramienta**: `{tool_name}` (Estado: *{status}*)\n\n")
|
|
121
|
+
tool_input = state.get('input', {})
|
|
122
|
+
f.write("```json\n")
|
|
123
|
+
f.write(json.dumps(tool_input, indent=2) + "\n")
|
|
124
|
+
f.write("```\n\n")
|
|
125
|
+
|
|
126
|
+
# Tool Call Output
|
|
127
|
+
tool_output = state.get('output', '')
|
|
128
|
+
f.write("📥 **Resultado de la Herramienta**:\n")
|
|
129
|
+
try:
|
|
130
|
+
parsed_out = json.loads(tool_output)
|
|
131
|
+
f.write("```json\n" + json.dumps(parsed_out, indent=2) + "\n```\n\n")
|
|
132
|
+
except Exception:
|
|
133
|
+
if "\n" in tool_output:
|
|
134
|
+
f.write("```\n" + tool_output + "\n```\n\n")
|
|
135
|
+
else:
|
|
136
|
+
f.write(f"`{tool_output}`\n\n")
|
|
137
|
+
|
|
138
|
+
print(f" Exportado: {filename}")
|
|
139
|
+
return filename
|
|
140
|
+
|
|
141
|
+
def list_sessions(cursor):
|
|
142
|
+
cursor.execute("""
|
|
143
|
+
SELECT id, title, agent, time_created
|
|
144
|
+
FROM session
|
|
145
|
+
WHERE parent_id IS NULL OR parent_id = ''
|
|
146
|
+
ORDER BY time_created DESC
|
|
147
|
+
LIMIT 15
|
|
148
|
+
""")
|
|
149
|
+
sessions = cursor.fetchall()
|
|
150
|
+
print("Últimas sesiones de OpenCode disponibles:")
|
|
151
|
+
print("-" * 85)
|
|
152
|
+
for s in sessions:
|
|
153
|
+
title = s['title'] or "(Sin título)"
|
|
154
|
+
created = format_timestamp(s['time_created'])
|
|
155
|
+
print(f"ID: {s['id']} | {created} | {s['agent']} - {title[:35]}")
|
|
156
|
+
print("-" * 85)
|
|
157
|
+
|
|
158
|
+
def main():
|
|
159
|
+
if not os.path.exists(DB_PATH):
|
|
160
|
+
print(f"Error: No se encontró la base de datos de OpenCode en {DB_PATH}")
|
|
161
|
+
sys.exit(1)
|
|
162
|
+
|
|
163
|
+
conn = sqlite3.connect(DB_PATH)
|
|
164
|
+
conn.row_factory = sqlite3.Row
|
|
165
|
+
cursor = conn.cursor()
|
|
166
|
+
|
|
167
|
+
target_session_id = TARGET_SESSION_ID
|
|
168
|
+
|
|
169
|
+
# If not configured in the script, look for command line arguments
|
|
170
|
+
if not target_session_id:
|
|
171
|
+
if len(sys.argv) < 2:
|
|
172
|
+
list_sessions(cursor)
|
|
173
|
+
print("\nUso: python3 export_opencode_session.py <session_id>")
|
|
174
|
+
print("O configura la variable 'TARGET_SESSION_ID' al inicio de este script.")
|
|
175
|
+
conn.close()
|
|
176
|
+
sys.exit(0)
|
|
177
|
+
target_session_id = sys.argv[1]
|
|
178
|
+
|
|
179
|
+
# Resolver el ID si es parcial/prefijo (ej. "ses_1619")
|
|
180
|
+
# Prioriza sesiones raíz (sin padre) para no agarrar un subagente por error
|
|
181
|
+
cursor.execute("""
|
|
182
|
+
SELECT id FROM session
|
|
183
|
+
WHERE id = ? OR id LIKE ?
|
|
184
|
+
ORDER BY (parent_id IS NULL OR parent_id = '') DESC, time_created DESC
|
|
185
|
+
LIMIT 1
|
|
186
|
+
""", (target_session_id, f"{target_session_id}%"))
|
|
187
|
+
row = cursor.fetchone()
|
|
188
|
+
if row:
|
|
189
|
+
target_session_id = row['id']
|
|
190
|
+
|
|
191
|
+
# 1. Fetch entire tree
|
|
192
|
+
session_tree = get_session_tree(cursor, target_session_id)
|
|
193
|
+
|
|
194
|
+
if not session_tree:
|
|
195
|
+
print(f"Error: No se encontró la sesión con ID '{target_session_id}'")
|
|
196
|
+
conn.close()
|
|
197
|
+
sys.exit(1)
|
|
198
|
+
|
|
199
|
+
main_session = session_tree[0]
|
|
200
|
+
safe_title = sanitize_filename(main_session['title'] or "session")
|
|
201
|
+
export_dir_name = f"export_{target_session_id[:8]}_{safe_title}"
|
|
202
|
+
output_dir = os.path.abspath(export_dir_name)
|
|
203
|
+
|
|
204
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
205
|
+
print(f"\nExportando sesión principal y subagentes a: {output_dir}\n")
|
|
206
|
+
|
|
207
|
+
# 2. Export each session
|
|
208
|
+
exported_files = []
|
|
209
|
+
for idx, s in enumerate(session_tree):
|
|
210
|
+
fname = export_session_messages(cursor, s, output_dir, idx)
|
|
211
|
+
exported_files.append((s, fname))
|
|
212
|
+
|
|
213
|
+
# 3. Create README index file
|
|
214
|
+
readme_path = os.path.join(output_dir, "README.md")
|
|
215
|
+
with open(readme_path, "w", encoding="utf-8") as f:
|
|
216
|
+
f.write(f"# Índice de Sesión de OpenCode\n\n")
|
|
217
|
+
f.write(f"Este directorio contiene los registros completos y ordenados de la sesión y todos sus subagentes.\n\n")
|
|
218
|
+
|
|
219
|
+
f.write("## Datos de la Sesión Principal\n")
|
|
220
|
+
f.write(f"* **ID**: `{main_session['id']}`\n")
|
|
221
|
+
f.write(f"* **Título**: {main_session['title']}\n")
|
|
222
|
+
f.write(f"* **Agente Principal**: `{main_session['agent']}`\n")
|
|
223
|
+
f.write(f"* **Creado**: {format_timestamp(main_session['time_created'])}\n")
|
|
224
|
+
f.write(f"* **Última Actualización**: {format_timestamp(main_session['time_updated'])}\n\n")
|
|
225
|
+
|
|
226
|
+
f.write("## 🌳 Árbol de Ejecución de Subagentes\n")
|
|
227
|
+
f.write("Haz clic en los enlaces para abrir el historial detallado de cada subagente:\n\n")
|
|
228
|
+
|
|
229
|
+
for s, fname in exported_files:
|
|
230
|
+
indent = " " * s['depth']
|
|
231
|
+
bullet = "•" if s['depth'] > 0 else "📂"
|
|
232
|
+
agent_label = f"({s['agent']})" if s['agent'] else ""
|
|
233
|
+
title_text = s['title'] or f"Subagente {s['id'][:8]}"
|
|
234
|
+
f.write(f"{indent}{bullet} [{title_text}]({fname}) {agent_label}\n")
|
|
235
|
+
|
|
236
|
+
f.write("\n\n---\n*Exportación generada automáticamente el: {}*\n".format(datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
|
|
237
|
+
|
|
238
|
+
print(f"\n¡Listo! Se ha creado un archivo README.md indexado en:\n{readme_path}\n")
|
|
239
|
+
conn.close()
|
|
240
|
+
|
|
241
|
+
if __name__ == "__main__":
|
|
242
|
+
main()
|