scrypted-tuya 0.1.2-beta → 2.1.0
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/.clangd +6 -0
- package/CHANGELOG.md +83 -130
- package/LICENSE +21 -0
- package/README.md +118 -51
- package/audio/baby.wav +0 -0
- package/audio/melody.wav +0 -0
- package/audio/plop.wav +0 -0
- package/compile_commands.json +68 -0
- package/conversation.md +18763 -0
- package/db_to_md.py +484 -0
- package/dist/plugin.zip +0 -0
- package/docs/tuya-webrtc-talkback.md +215 -0
- package/icon.png +0 -0
- package/media/tuya-bridge-dashboard.png +0 -0
- package/package.json +5 -5
- package/scripts/audio/talkback-sample.ts +120 -0
- package/chat.json +0 -4852
package/db_to_md.py
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Convert Gemini antigravity conversation .db files to Markdown."""
|
|
3
|
+
|
|
4
|
+
import sqlite3
|
|
5
|
+
import re
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def parse_protobuf(data: bytes) -> dict:
|
|
12
|
+
result = {}
|
|
13
|
+
pos = 0
|
|
14
|
+
while pos < len(data):
|
|
15
|
+
if pos >= len(data):
|
|
16
|
+
break
|
|
17
|
+
byte = data[pos]
|
|
18
|
+
field_tag = byte >> 3
|
|
19
|
+
wire_type = byte & 0x7
|
|
20
|
+
pos += 1
|
|
21
|
+
if wire_type == 0:
|
|
22
|
+
value = 0; shift = 0
|
|
23
|
+
while pos < len(data):
|
|
24
|
+
b = data[pos]
|
|
25
|
+
value |= (b & 0x7f) << shift
|
|
26
|
+
pos += 1
|
|
27
|
+
if (b & 0x80) == 0: break
|
|
28
|
+
shift += 7
|
|
29
|
+
result[field_tag] = value
|
|
30
|
+
elif wire_type == 2:
|
|
31
|
+
if pos >= len(data): break
|
|
32
|
+
length = 0; shift = 0
|
|
33
|
+
while pos < len(data):
|
|
34
|
+
b = data[pos]
|
|
35
|
+
length |= (b & 0x7f) << shift
|
|
36
|
+
pos += 1
|
|
37
|
+
if (b & 0x80) == 0: break
|
|
38
|
+
shift += 7
|
|
39
|
+
value = data[pos:pos + length]
|
|
40
|
+
pos += length
|
|
41
|
+
result[field_tag] = value
|
|
42
|
+
else:
|
|
43
|
+
break
|
|
44
|
+
return result
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def extract_text_from_bytes(data: bytes) -> list:
|
|
48
|
+
if not data: return []
|
|
49
|
+
return [t.decode('utf-8', errors='replace') for t in re.findall(b'[\x20-\x7e]{4,}', data)]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def extract_text_segments(data: bytes) -> list:
|
|
53
|
+
if not data: return []
|
|
54
|
+
return [t.decode('utf-8', errors='replace') for t in re.findall(b'[^\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff]{5,}', data)]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def extract_json_from_bytes(data: bytes):
|
|
58
|
+
if not data: return None
|
|
59
|
+
json_match = re.search(b'\{[^\}]{5,}[^\}]*\}', data)
|
|
60
|
+
if json_match:
|
|
61
|
+
try:
|
|
62
|
+
return json.loads(json_match.group().decode('utf-8'))
|
|
63
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
64
|
+
pass
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def parse_step_payload(payload: bytes) -> dict:
|
|
69
|
+
if not payload: return {}
|
|
70
|
+
result = {}
|
|
71
|
+
parsed = parse_protobuf(payload)
|
|
72
|
+
for field_tag, value in parsed.items():
|
|
73
|
+
if field_tag == 5 and isinstance(value, bytes):
|
|
74
|
+
nested = parse_protobuf(value)
|
|
75
|
+
result['nested'] = nested
|
|
76
|
+
if 4 in nested and isinstance(nested[4], bytes):
|
|
77
|
+
field4_text = extract_text_from_bytes(nested[4])
|
|
78
|
+
result['tool_names'] = field4_text
|
|
79
|
+
result['json'] = extract_json_from_bytes(nested[4])
|
|
80
|
+
if result.get('json'):
|
|
81
|
+
j = result['json']
|
|
82
|
+
result['tool_action'] = j.get('toolAction', '')
|
|
83
|
+
result['tool_summary'] = j.get('toolSummary', '')
|
|
84
|
+
result['command_line'] = j.get('CommandLine', '')
|
|
85
|
+
result['cwd'] = j.get('Cwd', '')
|
|
86
|
+
result['pattern'] = j.get('Pattern', '')
|
|
87
|
+
result['search_directory'] = j.get('SearchDirectory', '')
|
|
88
|
+
result['directory_path'] = j.get('DirectoryPath', '')
|
|
89
|
+
result['excludes'] = j.get('Excludes', [])
|
|
90
|
+
if 9 in nested and isinstance(nested[9], bytes):
|
|
91
|
+
result['session_info'] = extract_text_from_bytes(nested[9])
|
|
92
|
+
elif field_tag == 4 and isinstance(value, bytes):
|
|
93
|
+
result['field4_text'] = extract_text_from_bytes(value)
|
|
94
|
+
result['json'] = extract_json_from_bytes(value)
|
|
95
|
+
return result
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def parse_metadata(metadata: bytes) -> dict:
|
|
99
|
+
if not metadata: return {}
|
|
100
|
+
result = {}
|
|
101
|
+
result['all_text'] = extract_text_from_bytes(metadata)
|
|
102
|
+
call_match = re.search(b'call_[a-zA-Z0-9_]+', metadata)
|
|
103
|
+
if call_match:
|
|
104
|
+
result['call_id'] = call_match.group().decode('utf-8')
|
|
105
|
+
result['json'] = extract_json_from_bytes(metadata)
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def parse_executor_metadata(data: bytes) -> dict:
|
|
110
|
+
result = {}
|
|
111
|
+
parsed = parse_protobuf(data)
|
|
112
|
+
for field_tag, value in parsed.items():
|
|
113
|
+
if field_tag == 10 and isinstance(value, bytes):
|
|
114
|
+
nested = parse_protobuf(value)
|
|
115
|
+
for ft2, fv2 in nested.items():
|
|
116
|
+
if ft2 == 1 and isinstance(fv2, bytes):
|
|
117
|
+
result['content_segments'] = extract_text_segments(fv2)
|
|
118
|
+
result['json'] = extract_json_from_bytes(fv2)
|
|
119
|
+
elif ft2 == 3 and isinstance(fv2, bytes):
|
|
120
|
+
result['tool_definitions'] = extract_text_segments(fv2)
|
|
121
|
+
elif ft2 == 4 and isinstance(fv2, bytes):
|
|
122
|
+
result['system_prompt'] = extract_text_segments(fv2)
|
|
123
|
+
return result
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
STEP_TYPE_NAMES = {
|
|
127
|
+
14: 'user', 15: 'assistant', 17: 'system',
|
|
128
|
+
23: 'assistant', 101: 'assistant', 132: 'tool_call',
|
|
129
|
+
}
|
|
130
|
+
STATUS_NAMES = {3: 'completed', 5: 'completed', 6: 'error', 7: 'error'}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def extract_paren_content(text, prefix):
|
|
134
|
+
"""Extract content after prefix up to matching closing paren."""
|
|
135
|
+
if not text.startswith(prefix):
|
|
136
|
+
return None
|
|
137
|
+
start = len(prefix)
|
|
138
|
+
depth = 1
|
|
139
|
+
pos = start
|
|
140
|
+
while pos < len(text) and depth > 0:
|
|
141
|
+
if text[pos] == '(':
|
|
142
|
+
depth += 1
|
|
143
|
+
elif text[pos] == ')':
|
|
144
|
+
depth -= 1
|
|
145
|
+
pos += 1
|
|
146
|
+
return text[start:pos - 1]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def convert_db(db_path: str, output_path: str = None):
|
|
150
|
+
conn = sqlite3.connect(db_path)
|
|
151
|
+
conn.row_factory = sqlite3.Row
|
|
152
|
+
|
|
153
|
+
trajectory_meta = conn.execute('SELECT * FROM trajectory_meta LIMIT 1').fetchone()
|
|
154
|
+
if trajectory_meta:
|
|
155
|
+
trajectory_id, cascade_id = trajectory_meta['trajectory_id'], trajectory_meta['cascade_id']
|
|
156
|
+
trajectory_type, source = trajectory_meta['trajectory_type'], trajectory_meta['source']
|
|
157
|
+
else:
|
|
158
|
+
trajectory_id = cascade_id = trajectory_type = source = None
|
|
159
|
+
|
|
160
|
+
steps = conn.execute('SELECT idx, step_type, status, step_payload, metadata FROM steps ORDER BY idx').fetchall()
|
|
161
|
+
|
|
162
|
+
# Load executor_metadata - use only first entry to avoid duplication
|
|
163
|
+
executor_content = []
|
|
164
|
+
em_cur = conn.execute('SELECT data FROM executor_metadata ORDER BY idx LIMIT 1')
|
|
165
|
+
em_row = em_cur.fetchone()
|
|
166
|
+
if em_row:
|
|
167
|
+
em_result = parse_executor_metadata(em_row[0])
|
|
168
|
+
if 'content_segments' in em_result:
|
|
169
|
+
executor_content = em_result['content_segments']
|
|
170
|
+
|
|
171
|
+
# Get unique content segments (deduplicate)
|
|
172
|
+
seen = set()
|
|
173
|
+
unique_segments = []
|
|
174
|
+
for s in executor_content:
|
|
175
|
+
s = s.strip()
|
|
176
|
+
if len(s) > 3 and s not in seen:
|
|
177
|
+
seen.add(s)
|
|
178
|
+
unique_segments.append(s)
|
|
179
|
+
|
|
180
|
+
lines = []
|
|
181
|
+
lines.append('# Conversation Export')
|
|
182
|
+
lines.append('')
|
|
183
|
+
|
|
184
|
+
if trajectory_meta:
|
|
185
|
+
lines.append('## Trajectory Metadata')
|
|
186
|
+
lines.append('')
|
|
187
|
+
lines.append(f'- **Trajectory ID**: `{trajectory_id}`')
|
|
188
|
+
lines.append(f'- **Cascade ID**: `{cascade_id}`')
|
|
189
|
+
lines.append(f'- **Trajectory Type**: {trajectory_type}')
|
|
190
|
+
lines.append(f'- **Source**: {source}')
|
|
191
|
+
lines.append(f'- **Total Steps**: {len(steps)}')
|
|
192
|
+
lines.append('')
|
|
193
|
+
lines.append('---')
|
|
194
|
+
lines.append('')
|
|
195
|
+
|
|
196
|
+
# --- Executor Metadata: Command Summary ---
|
|
197
|
+
if unique_segments:
|
|
198
|
+
lines.append('## Commands & Actions Summary')
|
|
199
|
+
lines.append('')
|
|
200
|
+
lines.append('Extracted from executor metadata:')
|
|
201
|
+
lines.append('')
|
|
202
|
+
|
|
203
|
+
for text in unique_segments:
|
|
204
|
+
text = text.strip()
|
|
205
|
+
if len(text) < 4: continue
|
|
206
|
+
|
|
207
|
+
# Skip configuration/setting lines
|
|
208
|
+
skip_prefixes = [
|
|
209
|
+
'enable-', 'jetski-', 'antigravity_', 'teamwork_',
|
|
210
|
+
'message_continue_check', 'terminal_step_check',
|
|
211
|
+
'max_generator_invocations_check', 'empty_output_continuation_check',
|
|
212
|
+
'force_invocation', 'no_tool_call_check', 'battle_mode_tool_args',
|
|
213
|
+
'request_artifact_feedback_stop', 'idle_subagent_guard',
|
|
214
|
+
'augmented_intent', 'subagent_reminder', 'running_tasks_reminder',
|
|
215
|
+
'bash_command_reminder', 'message_delivery', 'read_url_content',
|
|
216
|
+
'replace_file_content', 'manage_subagents', 'communication_style',
|
|
217
|
+
'terminal_sandbox', 'planning_mode_artifacts', 'conversation_transcript',
|
|
218
|
+
'json-hooks-enabled', 'enable-file-diff-accumulator',
|
|
219
|
+
'enable-customization-skills', 'use-component-rewrite',
|
|
220
|
+
'enable-markdown-agents', 'agy-customizations',
|
|
221
|
+
'antigravity_guide', 'generative_ui', 'jetski-autonomous-mode',
|
|
222
|
+
'enable-cost-accumulator', 'enable-url-artifacts',
|
|
223
|
+
'enable-background-task-accumulator', 'enable-permissioned-github',
|
|
224
|
+
'enable-skill-accumulator', 'enable-teamwork-subagent',
|
|
225
|
+
'enable-owl-slash-command', 'enable-generative-hooks',
|
|
226
|
+
'teamwork_preview', 'DeepInvestigator', 'user_information',
|
|
227
|
+
'#timeout_long_running_search_command',
|
|
228
|
+
'request_artifact_feedback_stop',
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
should_skip = False
|
|
232
|
+
for prefix in skip_prefixes:
|
|
233
|
+
if text.startswith(prefix):
|
|
234
|
+
should_skip = True
|
|
235
|
+
break
|
|
236
|
+
if should_skip:
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# System messages
|
|
240
|
+
if text.startswith('As IDE feedback'):
|
|
241
|
+
lines.append('### 💡 System Prompt')
|
|
242
|
+
lines.append('')
|
|
243
|
+
lines.append(text[:1000])
|
|
244
|
+
lines.append('')
|
|
245
|
+
elif text.startswith('planning_mode:'):
|
|
246
|
+
lines.append('### 📋 Planning Mode')
|
|
247
|
+
lines.append('')
|
|
248
|
+
lines.append(text[:500])
|
|
249
|
+
lines.append('')
|
|
250
|
+
elif text.startswith('gemini-'):
|
|
251
|
+
lines.append(f'### 🤖 Model')
|
|
252
|
+
lines.append('')
|
|
253
|
+
lines.append(text)
|
|
254
|
+
lines.append('')
|
|
255
|
+
elif text.startswith('bash_command_reminder'):
|
|
256
|
+
lines.append('### ⚠️ Bash Command Reminder')
|
|
257
|
+
lines.append('')
|
|
258
|
+
lines.append(text[:500])
|
|
259
|
+
lines.append('')
|
|
260
|
+
elif text.startswith('running_tasks_reminder'):
|
|
261
|
+
lines.append('### 📋 Running Tasks Reminder')
|
|
262
|
+
lines.append('')
|
|
263
|
+
lines.append(text[:500])
|
|
264
|
+
lines.append('')
|
|
265
|
+
elif text.startswith('unsandboxed('):
|
|
266
|
+
content = extract_paren_content(text, 'unsandboxed(')
|
|
267
|
+
if content:
|
|
268
|
+
lines.append('### 🔓 Unsandboxed Command')
|
|
269
|
+
lines.append('')
|
|
270
|
+
if content.startswith('cat <<') or '{' in content[:20]:
|
|
271
|
+
lines.append('```')
|
|
272
|
+
lines.append(content[:3000])
|
|
273
|
+
lines.append('```')
|
|
274
|
+
else:
|
|
275
|
+
lines.append(f'```\n{content}\n```')
|
|
276
|
+
lines.append('')
|
|
277
|
+
elif text.startswith('gunsandboxed('):
|
|
278
|
+
content = extract_paren_content(text, 'gunsandboxed(')
|
|
279
|
+
if content:
|
|
280
|
+
lines.append('### 🔫 Gunsandboxed Command')
|
|
281
|
+
lines.append('')
|
|
282
|
+
lines.append(f'```\n{content}\n```')
|
|
283
|
+
lines.append('')
|
|
284
|
+
elif text.startswith('command('):
|
|
285
|
+
content = extract_paren_content(text, 'command(')
|
|
286
|
+
if content:
|
|
287
|
+
lines.append('### 💬 Command')
|
|
288
|
+
lines.append('')
|
|
289
|
+
lines.append(f'```\n{content}\n```')
|
|
290
|
+
lines.append('')
|
|
291
|
+
elif text.startswith('read_file('):
|
|
292
|
+
content = extract_paren_content(text, 'read_file(')
|
|
293
|
+
if content:
|
|
294
|
+
lines.append('### 📄 Read File')
|
|
295
|
+
lines.append('')
|
|
296
|
+
lines.append(f'```\n{content}\n```')
|
|
297
|
+
lines.append('')
|
|
298
|
+
elif text.startswith('write_file('):
|
|
299
|
+
content = extract_paren_content(text, 'write_file(')
|
|
300
|
+
if content:
|
|
301
|
+
lines.append('### ✏️ Write File')
|
|
302
|
+
lines.append('')
|
|
303
|
+
lines.append(f'```\n{content}\n```')
|
|
304
|
+
lines.append('')
|
|
305
|
+
elif text.startswith('read_url('):
|
|
306
|
+
content = extract_paren_content(text, 'read_url(')
|
|
307
|
+
if content:
|
|
308
|
+
lines.append('### 🔗 Read URL')
|
|
309
|
+
lines.append('')
|
|
310
|
+
lines.append(f'```\n{content}\n```')
|
|
311
|
+
lines.append('')
|
|
312
|
+
elif text.startswith('run_command('):
|
|
313
|
+
content = extract_paren_content(text, 'run_command(')
|
|
314
|
+
if content:
|
|
315
|
+
lines.append('### 🏃 Run Command')
|
|
316
|
+
lines.append('')
|
|
317
|
+
lines.append(f'```\n{content}\n```')
|
|
318
|
+
lines.append('')
|
|
319
|
+
elif text.startswith('find_by_name('):
|
|
320
|
+
content = extract_paren_content(text, 'find_by_name(')
|
|
321
|
+
if content:
|
|
322
|
+
lines.append('### 🔍 Find by Name')
|
|
323
|
+
lines.append('')
|
|
324
|
+
lines.append(f'```\n{content}\n```')
|
|
325
|
+
lines.append('')
|
|
326
|
+
elif text.startswith('list_dir('):
|
|
327
|
+
content = extract_paren_content(text, 'list_dir(')
|
|
328
|
+
if content:
|
|
329
|
+
lines.append('### 📁 List Directory')
|
|
330
|
+
lines.append('')
|
|
331
|
+
lines.append(f'```\n{content}\n```')
|
|
332
|
+
lines.append('')
|
|
333
|
+
elif text.startswith('$') or text.startswith('!') or text.startswith('#') or text.startswith('%') or text.startswith('=') or text.startswith('>'):
|
|
334
|
+
# Result markers - skip
|
|
335
|
+
pass
|
|
336
|
+
elif text.startswith('mcp('):
|
|
337
|
+
content = extract_paren_content(text, 'mcp(')
|
|
338
|
+
if content:
|
|
339
|
+
lines.append('### 🧩 MCP Call')
|
|
340
|
+
lines.append('')
|
|
341
|
+
lines.append(f'```\n{content}\n```')
|
|
342
|
+
lines.append('')
|
|
343
|
+
elif len(text) > 20 and ('{' in text[:50] or 'EOF' in text or 'package.json' in text or 'tsconfig' in text):
|
|
344
|
+
lines.append('### 📝 Configuration')
|
|
345
|
+
lines.append('')
|
|
346
|
+
lines.append('```')
|
|
347
|
+
lines.append(text[:3000])
|
|
348
|
+
lines.append('```')
|
|
349
|
+
lines.append('')
|
|
350
|
+
elif len(text) > 15:
|
|
351
|
+
lines.append(f'### {text[:100]}')
|
|
352
|
+
lines.append('')
|
|
353
|
+
|
|
354
|
+
lines.append('---')
|
|
355
|
+
lines.append('')
|
|
356
|
+
|
|
357
|
+
# --- Tool Calls from steps table ---
|
|
358
|
+
tool_calls = []
|
|
359
|
+
assistant_msgs = []
|
|
360
|
+
user_msgs = []
|
|
361
|
+
other_steps = []
|
|
362
|
+
|
|
363
|
+
for step in steps:
|
|
364
|
+
step_type = step['step_type']
|
|
365
|
+
status = step['status']
|
|
366
|
+
idx = step['idx']
|
|
367
|
+
step_name = STEP_TYPE_NAMES.get(step_type, f'unknown({step_type})')
|
|
368
|
+
status_name = STATUS_NAMES.get(status, str(status))
|
|
369
|
+
|
|
370
|
+
payload_info = parse_step_payload(step['step_payload']) if step['step_payload'] else {}
|
|
371
|
+
meta_info = parse_metadata(step['metadata']) if step['metadata'] else {}
|
|
372
|
+
|
|
373
|
+
step_data = {
|
|
374
|
+
'idx': idx, 'step_type': step_type, 'step_name': step_name,
|
|
375
|
+
'status': status, 'status_name': status_name, **meta_info, **payload_info,
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if step_type == 132: tool_calls.append(step_data)
|
|
379
|
+
elif step_type in (15, 23, 101): assistant_msgs.append(step_data)
|
|
380
|
+
elif step_type == 14: user_msgs.append(step_data)
|
|
381
|
+
elif step_type == 17: other_steps.append(step_data)
|
|
382
|
+
else: other_steps.append(step_data)
|
|
383
|
+
|
|
384
|
+
if tool_calls:
|
|
385
|
+
lines.append('## Tool Calls (Detailed)')
|
|
386
|
+
lines.append('')
|
|
387
|
+
lines.append(f'Total: {len(tool_calls)}')
|
|
388
|
+
lines.append('')
|
|
389
|
+
|
|
390
|
+
for tc in tool_calls:
|
|
391
|
+
idx = tc['idx']
|
|
392
|
+
call_id = tc.get('call_id', '')
|
|
393
|
+
all_text = tc.get('all_text', [])
|
|
394
|
+
json_data = tc.get('json', {}) or tc.get('json') or {}
|
|
395
|
+
|
|
396
|
+
tool_name = ''
|
|
397
|
+
for i, t in enumerate(all_text):
|
|
398
|
+
if t.startswith('call_') and i + 1 < len(all_text):
|
|
399
|
+
tool_name = all_text[i + 1]
|
|
400
|
+
break
|
|
401
|
+
|
|
402
|
+
lines.append(f'### Tool Call #{idx}')
|
|
403
|
+
lines.append('')
|
|
404
|
+
lines.append(f'- **Call ID**: `{call_id}`')
|
|
405
|
+
lines.append(f'- **Tool**: `{tool_name}`')
|
|
406
|
+
lines.append(f'- **Status**: {tc["status_name"]}')
|
|
407
|
+
|
|
408
|
+
if json_data:
|
|
409
|
+
lines.append(f'- **Action**: {json_data.get("toolAction", "")}')
|
|
410
|
+
lines.append(f'- **Summary**: {json_data.get("toolSummary", "")}')
|
|
411
|
+
lines.append(f'- **Parameters**:')
|
|
412
|
+
lines.append('')
|
|
413
|
+
lines.append('```json')
|
|
414
|
+
lines.append(json.dumps(json_data, indent=2))
|
|
415
|
+
lines.append('```')
|
|
416
|
+
else:
|
|
417
|
+
json_match = re.search(r'\{[^\}]{5,}[^\}]*\}', ' '.join(all_text))
|
|
418
|
+
if json_match:
|
|
419
|
+
try:
|
|
420
|
+
j = json.loads(json_match.group())
|
|
421
|
+
lines.append(f'- **Parameters**:')
|
|
422
|
+
lines.append('')
|
|
423
|
+
lines.append('```json')
|
|
424
|
+
lines.append(json.dumps(j, indent=2))
|
|
425
|
+
lines.append('```')
|
|
426
|
+
except: pass
|
|
427
|
+
|
|
428
|
+
if json_data:
|
|
429
|
+
for key in ['CommandLine', 'Cwd', 'Pattern', 'SearchDirectory', 'DirectoryPath', 'Excludes']:
|
|
430
|
+
val = json_data.get(key)
|
|
431
|
+
if val is not None:
|
|
432
|
+
if key == 'Excludes':
|
|
433
|
+
lines.append(f'- **{key}**: `{", ".join(val)}`')
|
|
434
|
+
else:
|
|
435
|
+
lines.append(f'- **{key}**: `{val}`')
|
|
436
|
+
|
|
437
|
+
session_info = tc.get('session_info', [])
|
|
438
|
+
if session_info:
|
|
439
|
+
lines.append(f'- **Session**: `{session_info[0]}`')
|
|
440
|
+
lines.append('')
|
|
441
|
+
|
|
442
|
+
lines.append('## Summary')
|
|
443
|
+
lines.append('')
|
|
444
|
+
lines.append(f'- **Total Steps**: {len(steps)}')
|
|
445
|
+
lines.append(f'- **User Messages**: {len(user_msgs)}')
|
|
446
|
+
lines.append(f'- **Assistant Messages**: {len(assistant_msgs)}')
|
|
447
|
+
lines.append(f'- **Tool Calls**: {len(tool_calls)}')
|
|
448
|
+
lines.append(f'- **System Steps**: {len(other_steps)}')
|
|
449
|
+
lines.append('')
|
|
450
|
+
|
|
451
|
+
markdown_content = '\n'.join(lines)
|
|
452
|
+
|
|
453
|
+
if output_path is None:
|
|
454
|
+
output_path = 'conversation.md'
|
|
455
|
+
|
|
456
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
457
|
+
f.write(markdown_content)
|
|
458
|
+
|
|
459
|
+
print(f'Converted {len(steps)} steps to {output_path}')
|
|
460
|
+
print(f' Tool calls: {len(tool_calls)}')
|
|
461
|
+
print(f' Assistant messages: {len(assistant_msgs)}')
|
|
462
|
+
print(f' User messages: {len(user_msgs)}')
|
|
463
|
+
print(f' Other: {len(other_steps)}')
|
|
464
|
+
print(f' Unique command segments: {len(unique_segments)}')
|
|
465
|
+
|
|
466
|
+
conn.close()
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
if __name__ == '__main__':
|
|
470
|
+
db_path = sys.argv[1] if len(sys.argv) > 1 else None
|
|
471
|
+
output_path = sys.argv[2] if len(sys.argv) > 2 else None
|
|
472
|
+
|
|
473
|
+
if db_path is None:
|
|
474
|
+
db_path = os.path.join(
|
|
475
|
+
os.path.expanduser('~'),
|
|
476
|
+
'.gemini', 'antigravity', 'conversations',
|
|
477
|
+
'094b96f4-9751-4978-b473-18144c70a431.db'
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
if not os.path.exists(db_path):
|
|
481
|
+
print(f'Error: Database not found at {db_path}', file=sys.stderr)
|
|
482
|
+
sys.exit(1)
|
|
483
|
+
|
|
484
|
+
convert_db(db_path, output_path)
|
package/dist/plugin.zip
CHANGED
|
Binary file
|