soothe-cli 0.4.3__tar.gz → 0.4.4__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.
Files changed (125) hide show
  1. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/PKG-INFO +1 -1
  2. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/main.py +1 -1
  3. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/config_loader.py +1 -1
  4. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/_env_vars.py +1 -4
  5. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/app.py +3 -92
  6. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/command_registry.py +0 -5
  7. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/config.py +15 -258
  8. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/model_config.py +6 -6
  9. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/.gitignore +0 -0
  10. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/README.md +0 -0
  11. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/pyproject.toml +0 -0
  12. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/__init__.py +0 -0
  13. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/__init__.py +0 -0
  14. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/commands/__init__.py +0 -0
  15. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
  16. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
  17. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
  18. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/commands/thread_cmd.py +0 -0
  19. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/execution/__init__.py +0 -0
  20. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/execution/daemon.py +0 -0
  21. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/execution/headless.py +0 -0
  22. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
  23. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/execution/launcher.py +0 -0
  24. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/stream/__init__.py +0 -0
  25. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/stream/context.py +0 -0
  26. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/stream/display_line.py +0 -0
  27. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/stream/formatter.py +0 -0
  28. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/stream/pipeline.py +0 -0
  29. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/cli/stream/task_scope.py +0 -0
  30. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/config/__init__.py +0 -0
  31. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/config/cli_config.py +0 -0
  32. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/plan/__init__.py +0 -0
  33. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/plan/rich_tree.py +0 -0
  34. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/__init__.py +0 -0
  35. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/commands/__init__.py +0 -0
  36. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/commands/command_router.py +0 -0
  37. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/commands/slash_commands.py +0 -0
  38. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/commands/subagent_routing.py +0 -0
  39. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/core/__init__.py +0 -0
  40. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/core/event_processor.py +0 -0
  41. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/core/presentation_engine.py +0 -0
  42. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/core/processor_state.py +0 -0
  43. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/core/renderer_protocol.py +0 -0
  44. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/events/__init__.py +0 -0
  45. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/events/display_policy.py +0 -0
  46. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/events/essential_events.py +0 -0
  47. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/events/explore_task_display.py +0 -0
  48. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/events/stream_accumulator.py +0 -0
  49. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/events/tui_trace_log.py +0 -0
  50. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/presentation_engine.py +0 -0
  51. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/renderer_base.py +0 -0
  52. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/rendering/__init__.py +0 -0
  53. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/rendering/async_renderer_protocol.py +0 -0
  54. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/rendering/renderer_base.py +0 -0
  55. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/stream_accumulator.py +0 -0
  56. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/subagent_routing.py +0 -0
  57. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/__init__.py +0 -0
  58. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/message_processing.py +0 -0
  59. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/rendering.py +0 -0
  60. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_call_resolution.py +0 -0
  61. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_card_payload.py +0 -0
  62. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_card_visibility.py +0 -0
  63. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/__init__.py +0 -0
  64. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/base.py +0 -0
  65. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/execution.py +0 -0
  66. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/fallback.py +0 -0
  67. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/file_ops.py +0 -0
  68. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/goal_formatter.py +0 -0
  69. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/media.py +0 -0
  70. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/structured.py +0 -0
  71. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/subagent.py +0 -0
  72. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_formatters/web.py +0 -0
  73. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_message_format.py +0 -0
  74. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/shared/tools/tool_output_formatter.py +0 -0
  75. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/__init__.py +0 -0
  76. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/_ask_user_types.py +0 -0
  77. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/_cli_context.py +0 -0
  78. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/_session_stats.py +0 -0
  79. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/_version.py +0 -0
  80. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/app.tcss +0 -0
  81. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/daemon_session.py +0 -0
  82. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/file_ops.py +0 -0
  83. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/formatting.py +0 -0
  84. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/hooks.py +0 -0
  85. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/input.py +0 -0
  86. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/media_utils.py +0 -0
  87. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/message_display_filter.py +0 -0
  88. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/output.py +0 -0
  89. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/preview_limits.py +0 -0
  90. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/project_utils.py +0 -0
  91. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/sessions.py +0 -0
  92. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/skills/__init__.py +0 -0
  93. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/skills/invocation.py +0 -0
  94. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/skills/load.py +0 -0
  95. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/textual_adapter.py +0 -0
  96. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/theme.py +0 -0
  97. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/tool_display.py +0 -0
  98. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/unicode_security.py +0 -0
  99. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/update_check.py +0 -0
  100. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/__init__.py +0 -0
  101. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/_links.py +0 -0
  102. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/approval.py +0 -0
  103. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
  104. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
  105. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
  106. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
  107. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
  108. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
  109. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/diff.py +0 -0
  110. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/editor.py +0 -0
  111. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/history.py +0 -0
  112. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/loading.py +0 -0
  113. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
  114. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
  115. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/message_store.py +0 -0
  116. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/messages.py +0 -0
  117. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
  118. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
  119. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/status.py +0 -0
  120. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
  121. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/thread_selector.py +0 -0
  122. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
  123. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
  124. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/tools.py +0 -0
  125. {soothe_cli-0.4.3 → soothe_cli-0.4.4}/src/soothe_cli/tui/widgets/welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soothe-cli
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: Soothe CLI client - communicates with daemon via WebSocket
5
5
  Project-URL: Homepage, https://github.com/OpenSoothe/soothe
6
6
  Project-URL: Documentation, https://soothe.readthedocs.io
@@ -1,7 +1,7 @@
1
1
  """Main CLI entry point using Typer."""
2
2
 
3
3
  # Load environment variables from .env file BEFORE any langchain imports
4
- # This is required for LangSmith tracing to be activated at import time
4
+ # so provider API keys and other env-backed config are visible at import time.
5
5
  from dotenv import load_dotenv
6
6
 
7
7
  load_dotenv()
@@ -29,7 +29,7 @@ def load_config(config_path: str | None = None) -> CLIConfig:
29
29
  A ``CLIConfig`` instance.
30
30
  """
31
31
  # Load environment variables from .env file
32
- # This ensures LangSmith and other env vars are available
32
+ # This ensures API keys and other env vars from .env are available
33
33
  load_dotenv()
34
34
 
35
35
  if config_path is not None:
@@ -40,9 +40,6 @@ DEBUG_FILE = "SOOTHE_CLI_DEBUG_FILE"
40
40
  EXTRA_SKILLS_DIRS = "SOOTHE_CLI_EXTRA_SKILLS_DIRS"
41
41
  """Colon-separated paths added to the skill containment allowlist."""
42
42
 
43
- LANGSMITH_PROJECT = "SOOTHE_CLI_LANGSMITH_PROJECT"
44
- """Override LangSmith project name for agent traces."""
45
-
46
43
  NO_UPDATE_CHECK = "SOOTHE_CLI_NO_UPDATE_CHECK"
47
44
  """Disable automatic update checking when set."""
48
45
 
@@ -53,4 +50,4 @@ SHELL_ALLOW_LIST = "SOOTHE_CLI_SHELL_ALLOW_LIST"
53
50
  """Comma-separated shell commands to allow (or 'recommended'/'all')."""
54
51
 
55
52
  USER_ID = "SOOTHE_CLI_USER_ID"
56
- """Attach a user identifier to LangSmith trace metadata."""
53
+ """Attach a user identifier to stream metadata (when set)."""
@@ -2640,104 +2640,17 @@ class SootheApp(App):
2640
2640
 
2641
2641
  @staticmethod
2642
2642
  async def _build_thread_message(prefix: str, thread_id: str) -> str | Content:
2643
- """Build a thread status message, hyperlinking the ID when possible.
2644
-
2645
- Attempts to resolve the LangSmith thread URL with a short timeout.
2646
- Falls back to plain text if tracing is not configured or resolution
2647
- fails.
2643
+ """Build a thread status message with the thread id.
2648
2644
 
2649
2645
  Args:
2650
2646
  prefix: Label before the thread ID (e.g. `'Resumed thread'`).
2651
2647
  thread_id: The thread identifier.
2652
2648
 
2653
2649
  Returns:
2654
- `Content` with a clickable thread ID, or a plain string.
2650
+ Plain status line.
2655
2651
  """
2656
- from soothe_cli.tui.config import build_langsmith_thread_url
2657
-
2658
- try:
2659
- url = await asyncio.wait_for(
2660
- asyncio.to_thread(build_langsmith_thread_url, thread_id),
2661
- timeout=2.0,
2662
- )
2663
- except (TimeoutError, Exception): # noqa: BLE001 # Resilient non-interactive mode error handling
2664
- url = None
2665
-
2666
- if url:
2667
- return Content.assemble(
2668
- f"{prefix}: ",
2669
- (thread_id, TStyle(link=url)),
2670
- )
2671
2652
  return f"{prefix}: {thread_id}"
2672
2653
 
2673
- async def _handle_trace_command(self, command: str) -> None:
2674
- """Open the current thread in LangSmith.
2675
-
2676
- Resolves the URL and opens the browser immediately regardless of busy
2677
- state. When the app is busy, chat output (user echo + clickable link)
2678
- is deferred until the current task finishes. Error conditions (no
2679
- session, URL failure, tracing not configured) render immediately
2680
- regardless of busy state.
2681
-
2682
- Args:
2683
- command: The raw command text (displayed as user message).
2684
- """
2685
- from soothe_cli.tui.config import build_langsmith_thread_url
2686
-
2687
- if not self._session_state:
2688
- await self._mount_message(UserMessage(command))
2689
- await self._mount_message(AppMessage("No active session."))
2690
- return
2691
- thread_id = self._session_state.loop_id
2692
- try:
2693
- url = await asyncio.to_thread(build_langsmith_thread_url, thread_id)
2694
- except Exception:
2695
- logger.exception("Failed to build LangSmith thread URL for %s", thread_id)
2696
- await self._mount_message(UserMessage(command))
2697
- await self._mount_message(AppMessage("Failed to resolve LangSmith thread URL."))
2698
- return
2699
- if not url:
2700
- await self._mount_message(UserMessage(command))
2701
- await self._mount_message(
2702
- AppMessage(
2703
- "LangSmith tracing is not configured. Set LANGSMITH_API_KEY and LANGSMITH_TRACING=true to enable."
2704
- )
2705
- )
2706
- return
2707
-
2708
- def _open_browser() -> None:
2709
- try:
2710
- webbrowser.open(url)
2711
- except Exception:
2712
- logger.debug("Could not open browser for URL: %s", url, exc_info=True)
2713
-
2714
- asyncio.get_running_loop().run_in_executor(None, _open_browser)
2715
-
2716
- # Defer chat output while a turn is in progress — rendering the user
2717
- # echo + link immediately would splice it into the middle of the
2718
- # streaming assistant response
2719
- if self._agent_running or self._shell_running:
2720
- queued_widget = QueuedUserMessage(command)
2721
- self._queued_widgets.append(queued_widget)
2722
- await self._mount_message(queued_widget)
2723
-
2724
- async def _mount_output() -> None:
2725
- if queued_widget in self._queued_widgets:
2726
- self._queued_widgets.remove(queued_widget)
2727
- with suppress(Exception):
2728
- await queued_widget.remove()
2729
- await self._mount_message(UserMessage(command))
2730
- link = Content.styled(url, TStyle(dim=True, italic=True, link=url))
2731
- await self._mount_message(AppMessage(link))
2732
-
2733
- # Append directly — no dedup; each /trace invocation gets its own output.
2734
- self._deferred_actions.append(DeferredAction(kind="chat_output", execute=_mount_output))
2735
- return
2736
-
2737
- await self._mount_message(UserMessage(command))
2738
- link = Content.styled(url, TStyle(dim=True, italic=True, link=url))
2739
- await self._mount_message(AppMessage(link))
2740
-
2741
2654
  async def _handle_command(self, command: str) -> None:
2742
2655
  """Handle a slash command.
2743
2656
 
@@ -2785,7 +2698,7 @@ class SootheApp(App):
2785
2698
  "Commands: /quit, /clear, /editor, /autopilot, /mcp, "
2786
2699
  "/model [--model-params JSON] [--default], /notifications, "
2787
2700
  "/reload, /skill:<name>, /remember, /theme, "
2788
- "/tokens, /loops, /trace, "
2701
+ "/tokens, /loops, "
2789
2702
  "/browser, /claude, /research, /explore, /plan (subagent routing), "
2790
2703
  "/update, /auto-update, /changelog, /docs, /feedback, /help\n\n"
2791
2704
  "Interactive Features:\n"
@@ -2869,8 +2782,6 @@ class SootheApp(App):
2869
2782
  await self.action_open_editor()
2870
2783
  elif cmd == "/loops":
2871
2784
  await self._show_loop_selector()
2872
- elif cmd == "/trace":
2873
- await self._handle_trace_command(command)
2874
2785
  elif cmd == "/update":
2875
2786
  await self._handle_update_command()
2876
2787
  elif cmd == "/auto-update":
@@ -100,11 +100,6 @@ COMMANDS: tuple[SlashCommand, ...] = (
100
100
  bypass_tier=BypassTier.IMMEDIATE_UI,
101
101
  hidden_keywords="continue history sessions",
102
102
  ),
103
- SlashCommand(
104
- name="/trace",
105
- description="Open current thread in LangSmith",
106
- bypass_tier=BypassTier.SIDE_EFFECT_FREE,
107
- ),
108
103
  SlashCommand(
109
104
  name="/browser",
110
105
  description="Route prompt to Browser subagent (usage: /browser <query>)",
@@ -24,10 +24,10 @@ from soothe_cli.tui._version import __version__
24
24
  logger = logging.getLogger(__name__)
25
25
 
26
26
  # ---------------------------------------------------------------------------
27
- # Lazy bootstrap: dotenv loading, LANGSMITH_PROJECT override, and start-path
28
- # detection are deferred until first access of `settings` (via module
29
- # `__getattr__`). This avoids disk I/O and path traversal during import for
30
- # callers that never touch `settings` (e.g. `Soothe --help`).
27
+ # Lazy bootstrap: dotenv loading and start-path detection are deferred until
28
+ # first access of `settings` (via module `__getattr__`). This avoids disk I/O
29
+ # and path traversal during import for callers that never touch `settings`
30
+ # (e.g. `Soothe --help`).
31
31
  # ---------------------------------------------------------------------------
32
32
 
33
33
  _bootstrap_done = False
@@ -43,13 +43,6 @@ _singleton_lock = threading.Lock()
43
43
  _bootstrap_start_path: Path | None = None
44
44
  """Working directory captured at bootstrap time for dotenv and project discovery."""
45
45
 
46
- _original_langsmith_project: str | None = None
47
- """Caller's `LANGSMITH_PROJECT` value before the CLI overrides it for agent traces.
48
-
49
- Captured inside `_ensure_bootstrap()` after dotenv loading but before the
50
- `LANGSMITH_PROJECT` override, so `.env`-only values are visible.
51
- """
52
-
53
46
 
54
47
  def _find_dotenv_from_start_path(start_path: Path) -> Path | None:
55
48
  """Find the nearest `.env` file from an explicit start path upward.
@@ -149,7 +142,7 @@ def _load_dotenv(*, start_path: Path | None = None) -> bool:
149
142
 
150
143
 
151
144
  def _ensure_bootstrap() -> None:
152
- """Run one-time bootstrap: dotenv loading and `LANGSMITH_PROJECT` override.
145
+ """Run one-time bootstrap: dotenv loading from project and global paths.
153
146
 
154
147
  Idempotent and thread-safe — subsequent calls are no-ops. Called
155
148
  automatically by `_get_settings()` when `settings` is first accessed.
@@ -159,7 +152,7 @@ def _ensure_bootstrap() -> None:
159
152
  loops. Exceptions are caught and logged at ERROR level; the CLI proceeds
160
153
  with the environment as-is.
161
154
  """
162
- global _bootstrap_done, _bootstrap_start_path, _original_langsmith_project # noqa: PLW0603
155
+ global _bootstrap_done, _bootstrap_start_path # noqa: PLW0603
163
156
 
164
157
  if _bootstrap_done:
165
158
  return
@@ -176,54 +169,10 @@ def _ensure_bootstrap() -> None:
176
169
  ctx = _get_server_project_context()
177
170
  _bootstrap_start_path = ctx.user_cwd if ctx else None
178
171
  _load_dotenv(start_path=_bootstrap_start_path)
179
-
180
- # Capture AFTER dotenv loading so .env-only values are visible,
181
- # but BEFORE the override below replaces it.
182
- _original_langsmith_project = os.environ.get("LANGSMITH_PROJECT")
183
-
184
- # CRITICAL: Override LANGSMITH_PROJECT to route agent traces to a
185
- # separate project. LangSmith reads LANGSMITH_PROJECT at invocation
186
- # time, so we override it here and preserve the user's original
187
- # value for shell commands.
188
- from soothe_cli.tui._env_vars import LANGSMITH_PROJECT
189
-
190
- soothe_project = os.environ.get(LANGSMITH_PROJECT)
191
- if soothe_project:
192
- os.environ["LANGSMITH_PROJECT"] = soothe_project
193
-
194
- # Propagate prefixed LangSmith env vars to canonical names.
195
- # The CLI resolves prefixed vars via resolve_env_var(), but the
196
- # LangSmith SDK reads os.environ directly and has no knowledge
197
- # of the SOOTHE_ prefix. Setting canonical vars here
198
- # bridges that gap.
199
- from soothe_cli.tui.model_config import _ENV_PREFIX
200
-
201
- for canonical in (
202
- "LANGSMITH_API_KEY",
203
- "LANGCHAIN_API_KEY",
204
- "LANGSMITH_TRACING",
205
- "LANGCHAIN_TRACING_V2",
206
- ):
207
- prefixed = f"{_ENV_PREFIX}{canonical}"
208
- if prefixed not in os.environ:
209
- continue
210
- prefixed_val = os.environ[prefixed]
211
- if canonical not in os.environ:
212
- # Propagate (including empty string for explicit disable).
213
- os.environ[canonical] = prefixed_val
214
- elif os.environ[canonical] != prefixed_val:
215
- os.environ[canonical] = prefixed_val
216
- logger.warning(
217
- "Both %s and %s are set with different values; using %s. Unset %s to silence this warning.",
218
- canonical,
219
- prefixed,
220
- prefixed,
221
- canonical,
222
- )
223
172
  except Exception:
224
173
  logger.exception(
225
- "Bootstrap failed; .env values and LANGSMITH_PROJECT override "
226
- "may be missing. The CLI will proceed with environment as-is.",
174
+ "Bootstrap failed; project .env may not be loaded. "
175
+ "The CLI will proceed with environment as-is.",
227
176
  )
228
177
  finally:
229
178
  _bootstrap_done = True
@@ -375,15 +324,6 @@ _glyphs_cache: Glyphs | None = None
375
324
  _editable_cache: tuple[bool, str | None] | None = None
376
325
  """Module-level cache for editable install info: (is_editable, source_path)."""
377
326
 
378
- _langsmith_url_cache: tuple[str, str] | None = None
379
- """Module-level cache for successful LangSmith project URL lookups."""
380
-
381
- _LANGSMITH_URL_LOOKUP_TIMEOUT_SECONDS = 2.0
382
- """Max seconds to wait for LangSmith project URL lookup.
383
-
384
- Kept short so tracing metadata can never stall CLI flows.
385
- """
386
-
387
327
 
388
328
  def _resolve_editable_info() -> tuple[bool, str | None]:
389
329
  """Parse PEP 610 `direct_url.json` once and cache both results.
@@ -613,8 +553,8 @@ def build_stream_config(
613
553
  ) -> RunnableConfig:
614
554
  """Build the LangGraph stream config dict.
615
555
 
616
- Injects CLI and SDK versions into `metadata["versions"]` so LangSmith traces
617
- can be correlated with specific releases.
556
+ Injects CLI and SDK versions into `metadata["versions"]` so runs can be
557
+ correlated with specific releases.
618
558
 
619
559
  Why the CLI sets *both* versions:
620
560
 
@@ -627,9 +567,6 @@ def build_stream_config(
627
567
  version would be lost.
628
568
  * Including the SDK version here ensures it survives the merge.
629
569
 
630
- Includes `ls_integration` metadata so LangSmith traces originating from the CLI
631
- are distinguishable from bare SDK usage.
632
-
633
570
  Args:
634
571
  thread_id: The CLI session thread identifier.
635
572
  assistant_id: The agent/assistant identifier, if any.
@@ -660,7 +597,6 @@ def build_stream_config(
660
597
 
661
598
  metadata: dict[str, Any] = {
662
599
  "versions": versions,
663
- "ls_integration": "Soothe",
664
600
  }
665
601
  from soothe_cli.tui._env_vars import USER_ID
666
602
 
@@ -883,12 +819,6 @@ class Settings:
883
819
  google_cloud_project: str | None
884
820
  """Google Cloud project ID for VertexAI authentication."""
885
821
 
886
- soothe_langchain_project: str | None
887
- """LangSmith project name for Soothe agent tracing."""
888
-
889
- user_langchain_project: str | None
890
- """Original `LANGSMITH_PROJECT` from environment (for user code)."""
891
-
892
822
  model_name: str | None = None
893
823
  """Currently active model name, set after model creation."""
894
824
 
@@ -940,23 +870,11 @@ class Settings:
940
870
  tavily_key = resolve_env_var("TAVILY_API_KEY")
941
871
  google_cloud_project = resolve_env_var("GOOGLE_CLOUD_PROJECT")
942
872
 
943
- # Detect LangSmith configuration
944
- # SOOTHE_CLI_LANGSMITH_PROJECT: Project for Soothe agent tracing
945
- # user_langchain_project: User's ORIGINAL LANGSMITH_PROJECT (before override)
946
- # When accessed via the module-level `settings` singleton,
947
- # _ensure_bootstrap() has already run and may have overridden
948
- # LANGSMITH_PROJECT. We use the saved original value, not the
949
- # current os.environ value. Direct callers should ensure
950
- # bootstrap has run if they depend on the override.
951
873
  from soothe_cli.tui._env_vars import (
952
874
  EXTRA_SKILLS_DIRS,
953
- LANGSMITH_PROJECT,
954
875
  SHELL_ALLOW_LIST,
955
876
  )
956
877
 
957
- soothe_langchain_project = resolve_env_var(LANGSMITH_PROJECT)
958
- user_langchain_project = _original_langsmith_project # Use saved original!
959
-
960
878
  # Detect project
961
879
  from soothe_cli.tui.project_utils import find_project_root
962
880
 
@@ -983,8 +901,6 @@ class Settings:
983
901
  nvidia_api_key=nvidia_key,
984
902
  tavily_api_key=tavily_key,
985
903
  google_cloud_project=google_cloud_project,
986
- soothe_langchain_project=soothe_langchain_project,
987
- user_langchain_project=user_langchain_project,
988
904
  project_root=project_root,
989
905
  shell_allow_list=shell_allow_list,
990
906
  extra_skills_dirs=extra_skills_dirs,
@@ -994,13 +910,11 @@ class Settings:
994
910
  """Reload selected settings from environment variables and project files.
995
911
 
996
912
  This refreshes only fields that are expected to change at runtime
997
- (API keys, Google Cloud project, project root, shell allow-list, and
998
- LangSmith tracing project).
913
+ (API keys, Google Cloud project, project root, and shell allow-list).
999
914
 
1000
915
  Runtime model state (`model_name`, `model_provider`,
1001
- `model_context_limit`) and the original user LangSmith project
1002
- (`user_langchain_project`) are intentionally preserved -- they are
1003
- not in `reloadable_fields` and are never touched by this method.
916
+ `model_context_limit`) is intentionally preserved it is not in
917
+ `reloadable_fields` and is never touched by this method.
1004
918
 
1005
919
  !!! note
1006
920
 
@@ -1034,7 +948,6 @@ class Settings:
1034
948
  "nvidia_api_key",
1035
949
  "tavily_api_key",
1036
950
  "google_cloud_project",
1037
- "soothe_langchain_project",
1038
951
  "project_root",
1039
952
  "shell_allow_list",
1040
953
  "extra_skills_dirs",
@@ -1042,15 +955,14 @@ class Settings:
1042
955
  """Fields refreshed on `/reload`.
1043
956
 
1044
957
  Runtime model state (`model_name`, `model_provider`, `model_context_limit`)
1045
- and the original user LangSmith project are intentionally excluded
1046
- they are set once and should not change across reloads.
958
+ is intentionally excluded it is set once and should not change across
959
+ reloads.
1047
960
  """
1048
961
 
1049
962
  previous = {field: getattr(self, field) for field in reloadable_fields}
1050
963
 
1051
964
  from soothe_cli.tui._env_vars import (
1052
965
  EXTRA_SKILLS_DIRS,
1053
- LANGSMITH_PROJECT,
1054
966
  SHELL_ALLOW_LIST,
1055
967
  )
1056
968
 
@@ -1080,7 +992,6 @@ class Settings:
1080
992
  "nvidia_api_key": resolve_env_var("NVIDIA_API_KEY"),
1081
993
  "tavily_api_key": resolve_env_var("TAVILY_API_KEY"),
1082
994
  "google_cloud_project": resolve_env_var("GOOGLE_CLOUD_PROJECT"),
1083
- "soothe_langchain_project": resolve_env_var(LANGSMITH_PROJECT),
1084
995
  "project_root": project_root,
1085
996
  "shell_allow_list": shell_allow_list,
1086
997
  "extra_skills_dirs": _parse_extra_skills_dirs(
@@ -1092,18 +1003,6 @@ class Settings:
1092
1003
  for field, value in refreshed.items():
1093
1004
  setattr(self, field, value)
1094
1005
 
1095
- # Sync the LANGSMITH_PROJECT env var so LangSmith tracing picks up
1096
- # the change
1097
- new_project = refreshed["soothe_langchain_project"]
1098
- if new_project:
1099
- os.environ["LANGSMITH_PROJECT"] = new_project
1100
- elif previous["soothe_langchain_project"]:
1101
- # Override was previously active but new value is unset; restore.
1102
- if _original_langsmith_project:
1103
- os.environ["LANGSMITH_PROJECT"] = _original_langsmith_project
1104
- else:
1105
- os.environ.pop("LANGSMITH_PROJECT", None)
1106
-
1107
1006
  def _display(field: str, value: object) -> str:
1108
1007
  if field in api_key_fields:
1109
1008
  return "set" if value else "unset"
@@ -1612,148 +1511,6 @@ def is_shell_command_allowed(command: str, allow_list: list[str] | None) -> bool
1612
1511
  return found_command
1613
1512
 
1614
1513
 
1615
- def get_langsmith_project_name() -> str | None:
1616
- """Resolve the LangSmith project name if tracing is configured.
1617
-
1618
- Checks for the required API key and tracing environment variables.
1619
- When both are present, resolves the project name with priority:
1620
- `settings.soothe_langchain_project` (from
1621
- `SOOTHE_CLI_LANGSMITH_PROJECT`), then `LANGSMITH_PROJECT` from the
1622
- environment (note: this may already have been overridden at bootstrap time
1623
- to match `SOOTHE_CLI_LANGSMITH_PROJECT`), then `'Soothe'`.
1624
-
1625
- Returns:
1626
- Project name string when LangSmith tracing is active, None otherwise.
1627
- """
1628
- from soothe_cli.tui.model_config import resolve_env_var
1629
-
1630
- langsmith_key = resolve_env_var("LANGSMITH_API_KEY") or resolve_env_var("LANGCHAIN_API_KEY")
1631
- langsmith_tracing = resolve_env_var("LANGSMITH_TRACING") or resolve_env_var(
1632
- "LANGCHAIN_TRACING_V2"
1633
- )
1634
- if not (langsmith_key and langsmith_tracing):
1635
- return None
1636
-
1637
- return (
1638
- _get_settings().soothe_langchain_project or os.environ.get("LANGSMITH_PROJECT") or "Soothe"
1639
- )
1640
-
1641
-
1642
- def fetch_langsmith_project_url(project_name: str) -> str | None:
1643
- """Fetch the LangSmith project URL via the LangSmith client.
1644
-
1645
- Successful results are cached at module level so repeated calls do not
1646
- make additional network requests.
1647
-
1648
- The network call runs in a daemon thread with a hard timeout of
1649
- `_LANGSMITH_URL_LOOKUP_TIMEOUT_SECONDS`, so this function blocks the
1650
- calling thread for at most that duration even if LangSmith is unreachable.
1651
-
1652
- Returns None (with a debug log) on any failure: missing `langsmith` package,
1653
- network errors, invalid project names, client initialization issues,
1654
- or timeouts.
1655
-
1656
- Args:
1657
- project_name: LangSmith project name to look up.
1658
-
1659
- Returns:
1660
- Project URL string if found, None otherwise.
1661
- """
1662
- global _langsmith_url_cache # noqa: PLW0603 # Module-level cache requires global statement
1663
-
1664
- if _langsmith_url_cache is not None:
1665
- cached_name, cached_url = _langsmith_url_cache
1666
- if cached_name == project_name:
1667
- return cached_url
1668
- # Different project name — fall through to fetch.
1669
-
1670
- try:
1671
- from langsmith import Client
1672
- except ImportError:
1673
- logger.debug(
1674
- "Could not fetch LangSmith project URL for '%s'",
1675
- project_name,
1676
- exc_info=True,
1677
- )
1678
- return None
1679
-
1680
- result: str | None = None
1681
- lookup_error: Exception | None = None
1682
- done = threading.Event()
1683
-
1684
- def _lookup_url() -> None:
1685
- nonlocal result, lookup_error
1686
- try:
1687
- from soothe_cli.tui.model_config import resolve_env_var
1688
-
1689
- # Explicit api_key because Client() reads os.environ directly
1690
- # and doesn't know about the SOOTHE_ prefix.
1691
- api_key = resolve_env_var("LANGSMITH_API_KEY") or resolve_env_var("LANGCHAIN_API_KEY")
1692
- project = Client(api_key=api_key).read_project(project_name=project_name)
1693
- result = project.url or None
1694
- except Exception as exc: # noqa: BLE001 # LangSmith SDK error types are not stable
1695
- lookup_error = exc
1696
- finally:
1697
- done.set()
1698
-
1699
- thread = threading.Thread(target=_lookup_url, daemon=True)
1700
- thread.start()
1701
-
1702
- if not done.wait(_LANGSMITH_URL_LOOKUP_TIMEOUT_SECONDS):
1703
- logger.debug(
1704
- "Timed out fetching LangSmith project URL for '%s' after %.1fs",
1705
- project_name,
1706
- _LANGSMITH_URL_LOOKUP_TIMEOUT_SECONDS,
1707
- )
1708
- return None
1709
-
1710
- if lookup_error is not None:
1711
- logger.debug(
1712
- "Could not fetch LangSmith project URL for '%s'",
1713
- project_name,
1714
- exc_info=(
1715
- type(lookup_error),
1716
- lookup_error,
1717
- lookup_error.__traceback__,
1718
- ),
1719
- )
1720
- return None
1721
-
1722
- if result is not None:
1723
- _langsmith_url_cache = (project_name, result)
1724
- return result
1725
-
1726
-
1727
- def build_langsmith_thread_url(thread_id: str) -> str | None:
1728
- """Build a full LangSmith thread URL if tracing is configured.
1729
-
1730
- Combines `get_langsmith_project_name` and `fetch_langsmith_project_url`
1731
- into a single convenience helper.
1732
-
1733
- Args:
1734
- thread_id: Thread identifier to build the URL for.
1735
-
1736
- Returns:
1737
- Full thread URL string, or `None` if unavailable (LangSmith is not
1738
- configured or the project URL cannot be resolved.)
1739
- """
1740
- project_name = get_langsmith_project_name()
1741
- if not project_name:
1742
- return None
1743
-
1744
- project_url = fetch_langsmith_project_url(project_name)
1745
- if not project_url:
1746
- return None
1747
-
1748
- return f"{project_url.rstrip('/')}/t/{thread_id}?utm_source=Soothe"
1749
-
1750
-
1751
- def reset_langsmith_url_cache() -> None:
1752
- """Reset the LangSmith URL cache (for testing)."""
1753
- global _langsmith_url_cache # noqa: PLW0603 # Module-level cache requires global statement
1754
- _langsmith_url_cache = None
1755
-
1756
-
1757
1514
  def get_default_coding_instructions() -> str:
1758
1515
  """Get the default coding agent instructions.
1759
1516
 
@@ -171,15 +171,15 @@ def resolve_env_var(var_name: str) -> str:
171
171
  """Resolve environment variable with SOOTHE_ prefix support.
172
172
 
173
173
  This function handles two scenarios:
174
- 1. Direct env var lookup: resolve_env_var("LANGSMITH_API_KEY")
175
- - First checks SOOTHE_LANGSMITH_API_KEY
176
- - Falls back to LANGSMITH_API_KEY
177
- 2. Pattern resolution: resolve_env_var("${LANGSMITH_API_KEY}")
174
+ 1. Direct env var lookup: resolve_env_var("OPENAI_API_KEY")
175
+ - First checks SOOTHE_OPENAI_API_KEY
176
+ - Falls back to OPENAI_API_KEY
177
+ 2. Pattern resolution: resolve_env_var("${OPENAI_API_KEY}")
178
178
  - Resolves ${VAR} patterns within strings
179
179
 
180
180
  Args:
181
- var_name: Environment variable name (e.g., "LANGSMITH_API_KEY")
182
- or pattern string (e.g., "${LANGSMITH_API_KEY}")
181
+ var_name: Environment variable name (e.g., "OPENAI_API_KEY")
182
+ or pattern string (e.g., "${OPENAI_API_KEY}")
183
183
 
184
184
  Returns:
185
185
  Resolved value from environment, or empty string if not found.
File without changes
File without changes
File without changes