voice-vibecoder 2.6.0__tar.gz → 2.7.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.
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/CHANGELOG.md +3 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/PKG-INFO +1 -1
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/pyproject.toml +1 -1
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/__init__.py +1 -1
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/claude.py +54 -46
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/instances.py +10 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/tools/file_tree.py +6 -2
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/tools/handlers.py +2 -1
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/panels.py +73 -32
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/screen.py +12 -7
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/.github/workflows/ci.yml +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/.github/workflows/release.yml +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/.gitignore +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/LICENSE +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/README.md +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/app.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/index.html +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/app_config.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/audio.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/cli_serve.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/__init__.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/cursor.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/registry.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/config.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/server.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/session.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/tools/__init__.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/tools/agent_task.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/tools/definitions.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/tools/dispatch.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/__init__.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/callbacks.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/diff_view.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/help_modal.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/setup.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/state.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/styles.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/update_modal.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/ui/wizard.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/voice_providers/__init__.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/voice_providers/openai.py +0 -0
- {voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/worktrees.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voice-vibecoder
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Olaf The Vibecoder — a voice-controlled coding assistant using OpenAI Realtime API + Claude Code
|
|
5
5
|
Project-URL: Homepage, https://github.com/snokam/voice-vibecoder
|
|
6
6
|
Project-URL: Repository, https://github.com/snokam/voice-vibecoder
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "voice-vibecoder"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.7.0"
|
|
8
8
|
description = "Olaf The Vibecoder — a voice-controlled coding assistant using OpenAI Realtime API + Claude Code"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
{voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/claude.py
RENAMED
|
@@ -242,6 +242,7 @@ class ClaudeRunner:
|
|
|
242
242
|
) -> AgentResult:
|
|
243
243
|
result_text = ""
|
|
244
244
|
result_session_id: str | None = None
|
|
245
|
+
got_content = False
|
|
245
246
|
|
|
246
247
|
async def _prompt():
|
|
247
248
|
yield {
|
|
@@ -251,54 +252,61 @@ class ClaudeRunner:
|
|
|
251
252
|
"session_id": session_id,
|
|
252
253
|
}
|
|
253
254
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if isinstance(block, TextBlock):
|
|
258
|
-
text = block.text.strip()
|
|
259
|
-
if text and on_output:
|
|
260
|
-
for line in text.splitlines():
|
|
261
|
-
if line.strip():
|
|
262
|
-
on_output(AgentOutput(
|
|
263
|
-
category="text", content=line,
|
|
264
|
-
))
|
|
265
|
-
|
|
266
|
-
elif isinstance(block, ToolUseBlock):
|
|
267
|
-
if not block.name:
|
|
268
|
-
continue
|
|
269
|
-
desc = describe_tool_call(block.name, block.input)
|
|
270
|
-
if on_output:
|
|
271
|
-
category = (
|
|
272
|
-
"file_edit" if block.name in ("Edit", "Write")
|
|
273
|
-
else "bash" if block.name == "Bash"
|
|
274
|
-
else "tool_call"
|
|
275
|
-
)
|
|
276
|
-
on_output(AgentOutput(
|
|
277
|
-
category=category, content=desc,
|
|
278
|
-
))
|
|
279
|
-
if block.name in ("Edit", "Write"):
|
|
280
|
-
_emit_edit_diff(on_output, block.name, block.input)
|
|
281
|
-
|
|
282
|
-
elif isinstance(block, ToolResultBlock):
|
|
283
|
-
content = block.content or ""
|
|
284
|
-
if isinstance(content, str) and content.strip() and on_output:
|
|
285
|
-
preview = content[:200] + ("..." if len(content) > 200 else "")
|
|
286
|
-
on_output(AgentOutput(
|
|
287
|
-
category="tool_result", content=preview,
|
|
288
|
-
))
|
|
289
|
-
|
|
290
|
-
elif isinstance(msg, UserMessage):
|
|
291
|
-
if isinstance(msg.content, list):
|
|
255
|
+
try:
|
|
256
|
+
async for msg in query(prompt=_prompt(), options=options):
|
|
257
|
+
if isinstance(msg, AssistantMessage):
|
|
292
258
|
for block in msg.content:
|
|
293
|
-
if isinstance(block,
|
|
259
|
+
if isinstance(block, TextBlock):
|
|
260
|
+
text = block.text.strip()
|
|
261
|
+
if text and on_output:
|
|
262
|
+
got_content = True
|
|
263
|
+
for line in text.splitlines():
|
|
264
|
+
if line.strip():
|
|
265
|
+
on_output(AgentOutput(
|
|
266
|
+
category="text", content=line,
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
elif isinstance(block, ToolUseBlock):
|
|
270
|
+
if not block.name:
|
|
271
|
+
continue
|
|
272
|
+
got_content = True
|
|
273
|
+
desc = describe_tool_call(block.name, block.input)
|
|
274
|
+
if on_output:
|
|
275
|
+
category = (
|
|
276
|
+
"file_edit" if block.name in ("Edit", "Write")
|
|
277
|
+
else "bash" if block.name == "Bash"
|
|
278
|
+
else "tool_call"
|
|
279
|
+
)
|
|
280
|
+
on_output(AgentOutput(
|
|
281
|
+
category=category, content=desc,
|
|
282
|
+
))
|
|
283
|
+
if block.name in ("Edit", "Write"):
|
|
284
|
+
_emit_edit_diff(on_output, block.name, block.input)
|
|
285
|
+
|
|
286
|
+
elif isinstance(block, ToolResultBlock):
|
|
294
287
|
content = block.content or ""
|
|
295
|
-
if isinstance(content, str) and content.strip():
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
288
|
+
if isinstance(content, str) and content.strip() and on_output:
|
|
289
|
+
preview = content[:200] + ("..." if len(content) > 200 else "")
|
|
290
|
+
on_output(AgentOutput(
|
|
291
|
+
category="tool_result", content=preview,
|
|
292
|
+
))
|
|
293
|
+
|
|
294
|
+
elif isinstance(msg, UserMessage):
|
|
295
|
+
if isinstance(msg.content, list):
|
|
296
|
+
for block in msg.content:
|
|
297
|
+
if isinstance(block, ToolResultBlock):
|
|
298
|
+
content = block.content or ""
|
|
299
|
+
if isinstance(content, str) and content.strip():
|
|
300
|
+
pass # Already routed via AssistantMessage
|
|
301
|
+
|
|
302
|
+
elif isinstance(msg, ResultMessage):
|
|
303
|
+
result_text = msg.result or ""
|
|
304
|
+
if msg.session_id:
|
|
305
|
+
result_session_id = msg.session_id
|
|
306
|
+
except ProcessError:
|
|
307
|
+
if not got_content and not result_text:
|
|
308
|
+
raise
|
|
309
|
+
logger.warning("CLI process error after content was already streamed")
|
|
302
310
|
|
|
303
311
|
return AgentResult(text=result_text, session_id=result_session_id)
|
|
304
312
|
|
|
@@ -125,6 +125,9 @@ class InstanceRegistry:
|
|
|
125
125
|
self.on_status_change(instance.instance_id, instance.status)
|
|
126
126
|
if self.on_instance_change:
|
|
127
127
|
self.on_instance_change()
|
|
128
|
+
# Show file tree immediately when panel appears
|
|
129
|
+
from voice_vibecoder.tools.file_tree import start_file_tree_updates
|
|
130
|
+
start_file_tree_updates(instance.instance_id, worktree_path, self)
|
|
128
131
|
return instance
|
|
129
132
|
|
|
130
133
|
def create_session(self, name: str, agent_type: str = "claude") -> AgentInstance:
|
|
@@ -416,4 +419,11 @@ class InstanceRegistry:
|
|
|
416
419
|
self._branch_index[branch] = instance.instance_id
|
|
417
420
|
loaded.append(instance)
|
|
418
421
|
|
|
422
|
+
# Start file tree updates for restored code instances
|
|
423
|
+
from voice_vibecoder.tools.file_tree import start_file_tree_updates
|
|
424
|
+
for inst in loaded:
|
|
425
|
+
if inst.instance_type == InstanceType.SESSION:
|
|
426
|
+
continue
|
|
427
|
+
start_file_tree_updates(inst.instance_id, inst.worktree_path, self)
|
|
428
|
+
|
|
419
429
|
return loaded
|
|
@@ -188,14 +188,18 @@ def get_file_tree(worktree_path: str) -> dict:
|
|
|
188
188
|
if status in summary:
|
|
189
189
|
summary[status] += 1
|
|
190
190
|
|
|
191
|
-
|
|
191
|
+
# Flat entries list for the TUI sidebar
|
|
192
|
+
entries = [{"path": p, "status": s} for p, s in sorted(all_paths.items())]
|
|
193
|
+
|
|
194
|
+
return {"root": root, "tree": tree, "summary": summary, "entries": entries}
|
|
192
195
|
|
|
193
196
|
except Exception as e:
|
|
194
197
|
logger.warning("Failed to build file tree: %s", e)
|
|
195
198
|
return {
|
|
196
199
|
"root": "unknown",
|
|
197
200
|
"tree": {"name": "unknown", "path": "", "type": "folder", "children": [], "expanded": True},
|
|
198
|
-
"summary": {"modified": 0, "new": 0, "deleted": 0, "unchanged": 0}
|
|
201
|
+
"summary": {"modified": 0, "new": 0, "deleted": 0, "unchanged": 0},
|
|
202
|
+
"entries": [],
|
|
199
203
|
}
|
|
200
204
|
|
|
201
205
|
|
|
@@ -395,7 +395,8 @@ def _handle_show_diff(branch: str | None = None, file: str | None = None) -> str
|
|
|
395
395
|
return f"No changes on '{instance.branch}' vs main."
|
|
396
396
|
if matched_file:
|
|
397
397
|
return f"Showing diff for '{matched_file}' on '{instance.branch}'."
|
|
398
|
-
|
|
398
|
+
file_list = ", ".join(f.path for f in files[:15])
|
|
399
|
+
return f"Showing diff for '{instance.branch}': {file_count} changed file(s): {file_list}"
|
|
399
400
|
|
|
400
401
|
|
|
401
402
|
def _handle_show_output(branch: str | None = None, session_name: str | None = None) -> str:
|
|
@@ -4,10 +4,13 @@ Handles creating, updating, toggling fullscreen, and removing agent
|
|
|
4
4
|
instance panels in the grid layout.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import logging
|
|
7
8
|
import math
|
|
8
9
|
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
9
12
|
from textual.containers import Grid, Horizontal, Vertical
|
|
10
|
-
from textual.widgets import Static, RichLog
|
|
13
|
+
from textual.widgets import Static, RichLog, Tree
|
|
11
14
|
|
|
12
15
|
from voice_vibecoder.code_providers.registry import get_badge
|
|
13
16
|
from voice_vibecoder.instances import AgentInstance, InstanceStatus
|
|
@@ -29,30 +32,68 @@ _FILE_STATUS_COLORS = {
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
class FileTreeWidget(Tree):
|
|
36
|
+
"""Interactive file tree sidebar with clickable expand/collapse folders."""
|
|
37
|
+
|
|
38
|
+
show_root = False
|
|
39
|
+
|
|
40
|
+
DEFAULT_CSS = """
|
|
41
|
+
FileTreeWidget {
|
|
42
|
+
scrollbar-size: 1 1;
|
|
43
|
+
}
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, tree_data: dict, **kwargs):
|
|
47
|
+
super().__init__(tree_data.get("name", "files"), **kwargs)
|
|
48
|
+
self._user_toggled: dict[str, bool] = {}
|
|
49
|
+
self._rebuilding = False
|
|
50
|
+
self._build_from_data(tree_data)
|
|
51
|
+
|
|
52
|
+
def _build_from_data(self, tree_data: dict) -> None:
|
|
53
|
+
self._rebuilding = True
|
|
54
|
+
self.clear()
|
|
55
|
+
self.root.expand()
|
|
56
|
+
for child in tree_data.get("children", []):
|
|
57
|
+
self._insert_node(self.root, child)
|
|
58
|
+
self._rebuilding = False
|
|
59
|
+
|
|
60
|
+
def _insert_node(self, parent, node_data: dict) -> None:
|
|
61
|
+
path = node_data.get("path", "")
|
|
62
|
+
name = node_data.get("name", "")
|
|
63
|
+
|
|
64
|
+
if node_data["type"] == "folder":
|
|
65
|
+
if path in self._user_toggled:
|
|
66
|
+
should_expand = self._user_toggled[path]
|
|
67
|
+
else:
|
|
68
|
+
should_expand = node_data.get("expanded", False)
|
|
69
|
+
|
|
70
|
+
label = f"[bold dim]{name}/[/bold dim]"
|
|
71
|
+
folder_node = parent.add(label, data={"path": path, "type": "folder"}, expand=should_expand)
|
|
72
|
+
|
|
73
|
+
for child in node_data.get("children", []):
|
|
74
|
+
self._insert_node(folder_node, child)
|
|
41
75
|
else:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
lines = []
|
|
46
|
-
for folder in sorted(tree.keys()):
|
|
47
|
-
if folder:
|
|
48
|
-
lines.append(f"[bold dim]{folder}/[/bold dim]")
|
|
49
|
-
for name, status in sorted(tree[folder], key=lambda x: x[0]):
|
|
50
|
-
icon = _FILE_STATUS_ICONS.get(status, "[dim]?[/dim]")
|
|
76
|
+
status = node_data.get("status", "unchanged")
|
|
77
|
+
icon = _FILE_STATUS_ICONS.get(status, "[dim]·[/dim]")
|
|
51
78
|
color = _FILE_STATUS_COLORS.get(status, "dim")
|
|
52
|
-
|
|
53
|
-
|
|
79
|
+
label = f"{icon} [{color}]{name}[/{color}]"
|
|
80
|
+
parent.add_leaf(label, data={"path": path, "type": "file", "status": status})
|
|
81
|
+
|
|
82
|
+
def rebuild(self, tree_data: dict) -> None:
|
|
83
|
+
"""Rebuild tree from new data, preserving user expand/collapse state."""
|
|
84
|
+
self._build_from_data(tree_data)
|
|
54
85
|
|
|
55
|
-
|
|
86
|
+
def on_tree_node_expanded(self, event) -> None:
|
|
87
|
+
if self._rebuilding:
|
|
88
|
+
return
|
|
89
|
+
if event.node.data and event.node.data.get("type") == "folder":
|
|
90
|
+
self._user_toggled[event.node.data["path"]] = True
|
|
91
|
+
|
|
92
|
+
def on_tree_node_collapsed(self, event) -> None:
|
|
93
|
+
if self._rebuilding:
|
|
94
|
+
return
|
|
95
|
+
if event.node.data and event.node.data.get("type") == "folder":
|
|
96
|
+
self._user_toggled[event.node.data["path"]] = False
|
|
56
97
|
|
|
57
98
|
|
|
58
99
|
def _format_header(inst: AgentInstance, status: InstanceStatus | None = None) -> str:
|
|
@@ -284,24 +325,23 @@ class PanelMixin:
|
|
|
284
325
|
grid.styles.grid_size_rows = 1
|
|
285
326
|
|
|
286
327
|
def _update_file_tree(self, instance_id: str, data: dict) -> None:
|
|
287
|
-
"""Update (or create) the file tree sidebar in a panel."""
|
|
328
|
+
"""Update (or create) the interactive file tree sidebar in a panel."""
|
|
288
329
|
if not self._session:
|
|
289
330
|
return
|
|
290
331
|
inst = self._session.registry.get_by_id(instance_id)
|
|
291
332
|
if not inst:
|
|
292
333
|
return
|
|
293
334
|
|
|
294
|
-
|
|
295
|
-
if not
|
|
335
|
+
tree_data = data.get("tree")
|
|
336
|
+
if not tree_data or not tree_data.get("children"):
|
|
296
337
|
return
|
|
297
338
|
|
|
298
|
-
formatted = _format_file_tree(entries)
|
|
299
339
|
sidebar_id = f"file-tree-{instance_id}"
|
|
300
340
|
|
|
301
|
-
# Try to update existing
|
|
341
|
+
# Try to update existing tree widget
|
|
302
342
|
try:
|
|
303
|
-
|
|
304
|
-
|
|
343
|
+
tree_widget = self.query_one(f"#{sidebar_id}", FileTreeWidget)
|
|
344
|
+
tree_widget.rebuild(tree_data)
|
|
305
345
|
return
|
|
306
346
|
except Exception:
|
|
307
347
|
pass
|
|
@@ -315,8 +355,8 @@ class PanelMixin:
|
|
|
315
355
|
except Exception:
|
|
316
356
|
return
|
|
317
357
|
|
|
318
|
-
|
|
319
|
-
|
|
358
|
+
tree_widget = FileTreeWidget(
|
|
359
|
+
tree_data,
|
|
320
360
|
classes="file-tree-sidebar",
|
|
321
361
|
id=sidebar_id,
|
|
322
362
|
)
|
|
@@ -326,13 +366,14 @@ class PanelMixin:
|
|
|
326
366
|
try:
|
|
327
367
|
output.remove()
|
|
328
368
|
container = Horizontal(
|
|
329
|
-
|
|
369
|
+
tree_widget,
|
|
330
370
|
output,
|
|
331
371
|
classes="file-tree-container",
|
|
332
372
|
id=f"file-tree-wrap-{instance_id}",
|
|
333
373
|
)
|
|
334
374
|
panel.mount(container)
|
|
335
375
|
except Exception:
|
|
376
|
+
logger.exception("Failed to mount file tree for %s", instance_id)
|
|
336
377
|
# If something fails, try to restore output directly
|
|
337
378
|
try:
|
|
338
379
|
panel.mount(output)
|
|
@@ -361,6 +361,13 @@ class VoiceCodingScreen(PanelMixin, CallbackMixin, Screen):
|
|
|
361
361
|
asyncio.set_event_loop(self._loop)
|
|
362
362
|
self._loop.run_until_complete(self._session_lifecycle())
|
|
363
363
|
|
|
364
|
+
def _call_ui(self, callback, *args) -> None:
|
|
365
|
+
"""Safely call a UI callback from a background thread."""
|
|
366
|
+
try:
|
|
367
|
+
self.app.call_from_thread(callback, *args)
|
|
368
|
+
except Exception:
|
|
369
|
+
pass # App not active (shutting down or not yet ready)
|
|
370
|
+
|
|
364
371
|
async def _session_lifecycle(self) -> None:
|
|
365
372
|
try:
|
|
366
373
|
await self._session.connect()
|
|
@@ -371,12 +378,10 @@ class VoiceCodingScreen(PanelMixin, CallbackMixin, Screen):
|
|
|
371
378
|
self._audio = AudioManager(on_audio_chunk=self._send_audio_chunk)
|
|
372
379
|
self._audio.start()
|
|
373
380
|
|
|
374
|
-
self.
|
|
381
|
+
self._call_ui(self._log, "[green]Connected — start speaking[/green]")
|
|
375
382
|
if self._log_path:
|
|
376
|
-
self.
|
|
377
|
-
|
|
378
|
-
)
|
|
379
|
-
self.app.call_from_thread(self._update_status_bar, "ready")
|
|
383
|
+
self._call_ui(self._log, f"[dim]Log: {self._log_path}[/dim]")
|
|
384
|
+
self._call_ui(self._update_status_bar, "ready")
|
|
380
385
|
|
|
381
386
|
# Run custom startup callback if configured
|
|
382
387
|
if self._config.on_startup:
|
|
@@ -384,10 +389,10 @@ class VoiceCodingScreen(PanelMixin, CallbackMixin, Screen):
|
|
|
384
389
|
|
|
385
390
|
await self._session.handle_messages()
|
|
386
391
|
except Exception as e:
|
|
387
|
-
self.
|
|
392
|
+
self._call_ui(
|
|
388
393
|
self._log, f"[bold red]Connection error: {e}[/bold red]"
|
|
389
394
|
)
|
|
390
|
-
self.
|
|
395
|
+
self._call_ui(
|
|
391
396
|
self._set_status,
|
|
392
397
|
f"[red]Error[/red] | [dim]s[/dim] settings | [dim]q[/dim] quit",
|
|
393
398
|
)
|
|
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
|
{voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/__init__.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/cursor.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/code_providers/registry.py
RENAMED
|
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
|
{voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/voice_providers/__init__.py
RENAMED
|
File without changes
|
{voice_vibecoder-2.6.0 → voice_vibecoder-2.7.0}/src/voice_vibecoder/voice_providers/openai.py
RENAMED
|
File without changes
|
|
File without changes
|