codemaster-cli 2.2.0__py3-none-any.whl

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 (170) hide show
  1. codemaster_cli-2.2.0.dist-info/METADATA +645 -0
  2. codemaster_cli-2.2.0.dist-info/RECORD +170 -0
  3. codemaster_cli-2.2.0.dist-info/WHEEL +4 -0
  4. codemaster_cli-2.2.0.dist-info/entry_points.txt +3 -0
  5. vibe/__init__.py +6 -0
  6. vibe/acp/__init__.py +0 -0
  7. vibe/acp/acp_agent_loop.py +746 -0
  8. vibe/acp/entrypoint.py +81 -0
  9. vibe/acp/tools/__init__.py +0 -0
  10. vibe/acp/tools/base.py +100 -0
  11. vibe/acp/tools/builtins/bash.py +134 -0
  12. vibe/acp/tools/builtins/read_file.py +54 -0
  13. vibe/acp/tools/builtins/search_replace.py +129 -0
  14. vibe/acp/tools/builtins/todo.py +65 -0
  15. vibe/acp/tools/builtins/write_file.py +98 -0
  16. vibe/acp/tools/session_update.py +118 -0
  17. vibe/acp/utils.py +213 -0
  18. vibe/cli/__init__.py +0 -0
  19. vibe/cli/autocompletion/__init__.py +0 -0
  20. vibe/cli/autocompletion/base.py +22 -0
  21. vibe/cli/autocompletion/path_completion.py +177 -0
  22. vibe/cli/autocompletion/slash_command.py +99 -0
  23. vibe/cli/cli.py +188 -0
  24. vibe/cli/clipboard.py +69 -0
  25. vibe/cli/commands.py +116 -0
  26. vibe/cli/entrypoint.py +163 -0
  27. vibe/cli/history_manager.py +91 -0
  28. vibe/cli/plan_offer/adapters/http_whoami_gateway.py +67 -0
  29. vibe/cli/plan_offer/decide_plan_offer.py +87 -0
  30. vibe/cli/plan_offer/ports/whoami_gateway.py +23 -0
  31. vibe/cli/terminal_setup.py +323 -0
  32. vibe/cli/textual_ui/__init__.py +0 -0
  33. vibe/cli/textual_ui/ansi_markdown.py +58 -0
  34. vibe/cli/textual_ui/app.py +1546 -0
  35. vibe/cli/textual_ui/app.tcss +1020 -0
  36. vibe/cli/textual_ui/external_editor.py +32 -0
  37. vibe/cli/textual_ui/handlers/__init__.py +5 -0
  38. vibe/cli/textual_ui/handlers/event_handler.py +147 -0
  39. vibe/cli/textual_ui/widgets/__init__.py +0 -0
  40. vibe/cli/textual_ui/widgets/approval_app.py +192 -0
  41. vibe/cli/textual_ui/widgets/banner/banner.py +85 -0
  42. vibe/cli/textual_ui/widgets/banner/petit_chat.py +195 -0
  43. vibe/cli/textual_ui/widgets/braille_renderer.py +58 -0
  44. vibe/cli/textual_ui/widgets/chat_input/__init__.py +7 -0
  45. vibe/cli/textual_ui/widgets/chat_input/body.py +214 -0
  46. vibe/cli/textual_ui/widgets/chat_input/completion_manager.py +58 -0
  47. vibe/cli/textual_ui/widgets/chat_input/completion_popup.py +43 -0
  48. vibe/cli/textual_ui/widgets/chat_input/container.py +195 -0
  49. vibe/cli/textual_ui/widgets/chat_input/text_area.py +365 -0
  50. vibe/cli/textual_ui/widgets/compact.py +41 -0
  51. vibe/cli/textual_ui/widgets/config_app.py +171 -0
  52. vibe/cli/textual_ui/widgets/context_progress.py +30 -0
  53. vibe/cli/textual_ui/widgets/load_more.py +43 -0
  54. vibe/cli/textual_ui/widgets/loading.py +201 -0
  55. vibe/cli/textual_ui/widgets/messages.py +277 -0
  56. vibe/cli/textual_ui/widgets/no_markup_static.py +11 -0
  57. vibe/cli/textual_ui/widgets/path_display.py +28 -0
  58. vibe/cli/textual_ui/widgets/proxy_setup_app.py +127 -0
  59. vibe/cli/textual_ui/widgets/question_app.py +496 -0
  60. vibe/cli/textual_ui/widgets/spinner.py +194 -0
  61. vibe/cli/textual_ui/widgets/status_message.py +76 -0
  62. vibe/cli/textual_ui/widgets/teleport_message.py +31 -0
  63. vibe/cli/textual_ui/widgets/tool_widgets.py +371 -0
  64. vibe/cli/textual_ui/widgets/tools.py +201 -0
  65. vibe/cli/textual_ui/windowing/__init__.py +29 -0
  66. vibe/cli/textual_ui/windowing/history.py +105 -0
  67. vibe/cli/textual_ui/windowing/history_windowing.py +71 -0
  68. vibe/cli/textual_ui/windowing/state.py +105 -0
  69. vibe/cli/update_notifier/__init__.py +47 -0
  70. vibe/cli/update_notifier/adapters/filesystem_update_cache_repository.py +59 -0
  71. vibe/cli/update_notifier/adapters/github_update_gateway.py +101 -0
  72. vibe/cli/update_notifier/adapters/pypi_update_gateway.py +107 -0
  73. vibe/cli/update_notifier/ports/update_cache_repository.py +16 -0
  74. vibe/cli/update_notifier/ports/update_gateway.py +53 -0
  75. vibe/cli/update_notifier/update.py +139 -0
  76. vibe/cli/update_notifier/whats_new.py +49 -0
  77. vibe/core/__init__.py +5 -0
  78. vibe/core/agent_loop.py +1075 -0
  79. vibe/core/agents/__init__.py +31 -0
  80. vibe/core/agents/manager.py +165 -0
  81. vibe/core/agents/models.py +122 -0
  82. vibe/core/auth/__init__.py +6 -0
  83. vibe/core/auth/crypto.py +137 -0
  84. vibe/core/auth/github.py +178 -0
  85. vibe/core/autocompletion/__init__.py +0 -0
  86. vibe/core/autocompletion/completers.py +257 -0
  87. vibe/core/autocompletion/file_indexer/__init__.py +10 -0
  88. vibe/core/autocompletion/file_indexer/ignore_rules.py +156 -0
  89. vibe/core/autocompletion/file_indexer/indexer.py +179 -0
  90. vibe/core/autocompletion/file_indexer/store.py +169 -0
  91. vibe/core/autocompletion/file_indexer/watcher.py +71 -0
  92. vibe/core/autocompletion/fuzzy.py +189 -0
  93. vibe/core/autocompletion/path_prompt.py +108 -0
  94. vibe/core/autocompletion/path_prompt_adapter.py +149 -0
  95. vibe/core/config.py +673 -0
  96. vibe/core/config_PATCH_INSTRUCTIONS.md +77 -0
  97. vibe/core/llm/__init__.py +0 -0
  98. vibe/core/llm/backend/anthropic.py +630 -0
  99. vibe/core/llm/backend/base.py +38 -0
  100. vibe/core/llm/backend/factory.py +7 -0
  101. vibe/core/llm/backend/generic.py +425 -0
  102. vibe/core/llm/backend/mistral.py +381 -0
  103. vibe/core/llm/backend/vertex.py +115 -0
  104. vibe/core/llm/exceptions.py +195 -0
  105. vibe/core/llm/format.py +184 -0
  106. vibe/core/llm/message_utils.py +24 -0
  107. vibe/core/llm/types.py +120 -0
  108. vibe/core/middleware.py +209 -0
  109. vibe/core/output_formatters.py +85 -0
  110. vibe/core/paths/__init__.py +0 -0
  111. vibe/core/paths/config_paths.py +68 -0
  112. vibe/core/paths/global_paths.py +40 -0
  113. vibe/core/programmatic.py +56 -0
  114. vibe/core/prompts/__init__.py +32 -0
  115. vibe/core/prompts/cli.md +111 -0
  116. vibe/core/prompts/compact.md +48 -0
  117. vibe/core/prompts/dangerous_directory.md +5 -0
  118. vibe/core/prompts/explore.md +50 -0
  119. vibe/core/prompts/project_context.md +8 -0
  120. vibe/core/prompts/tests.md +1 -0
  121. vibe/core/proxy_setup.py +65 -0
  122. vibe/core/session/session_loader.py +222 -0
  123. vibe/core/session/session_logger.py +318 -0
  124. vibe/core/session/session_migration.py +41 -0
  125. vibe/core/skills/__init__.py +7 -0
  126. vibe/core/skills/manager.py +132 -0
  127. vibe/core/skills/models.py +92 -0
  128. vibe/core/skills/parser.py +39 -0
  129. vibe/core/system_prompt.py +466 -0
  130. vibe/core/telemetry/__init__.py +0 -0
  131. vibe/core/telemetry/send.py +185 -0
  132. vibe/core/teleport/errors.py +9 -0
  133. vibe/core/teleport/git.py +196 -0
  134. vibe/core/teleport/nuage.py +180 -0
  135. vibe/core/teleport/teleport.py +208 -0
  136. vibe/core/teleport/types.py +54 -0
  137. vibe/core/tools/base.py +336 -0
  138. vibe/core/tools/builtins/ask_user_question.py +134 -0
  139. vibe/core/tools/builtins/bash.py +357 -0
  140. vibe/core/tools/builtins/grep.py +310 -0
  141. vibe/core/tools/builtins/prompts/__init__.py +0 -0
  142. vibe/core/tools/builtins/prompts/ask_user_question.md +84 -0
  143. vibe/core/tools/builtins/prompts/bash.md +73 -0
  144. vibe/core/tools/builtins/prompts/grep.md +4 -0
  145. vibe/core/tools/builtins/prompts/read_file.md +13 -0
  146. vibe/core/tools/builtins/prompts/search_replace.md +43 -0
  147. vibe/core/tools/builtins/prompts/task.md +24 -0
  148. vibe/core/tools/builtins/prompts/todo.md +199 -0
  149. vibe/core/tools/builtins/prompts/write_file.md +42 -0
  150. vibe/core/tools/builtins/read_file.py +222 -0
  151. vibe/core/tools/builtins/search_replace.py +456 -0
  152. vibe/core/tools/builtins/task.py +154 -0
  153. vibe/core/tools/builtins/todo.py +134 -0
  154. vibe/core/tools/builtins/write_file.py +160 -0
  155. vibe/core/tools/manager.py +341 -0
  156. vibe/core/tools/mcp.py +397 -0
  157. vibe/core/tools/ui.py +68 -0
  158. vibe/core/trusted_folders.py +86 -0
  159. vibe/core/types.py +405 -0
  160. vibe/core/utils.py +396 -0
  161. vibe/setup/onboarding/__init__.py +39 -0
  162. vibe/setup/onboarding/base.py +14 -0
  163. vibe/setup/onboarding/onboarding.tcss +134 -0
  164. vibe/setup/onboarding/screens/__init__.py +5 -0
  165. vibe/setup/onboarding/screens/api_key.py +200 -0
  166. vibe/setup/onboarding/screens/provider_selection.py +87 -0
  167. vibe/setup/onboarding/screens/welcome.py +136 -0
  168. vibe/setup/trusted_folders/trust_folder_dialog.py +180 -0
  169. vibe/setup/trusted_folders/trust_folder_dialog.tcss +83 -0
  170. vibe/whats_new.md +5 -0
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Callable
4
+ import os
5
+ from pathlib import Path
6
+
7
+ from vibe import VIBE_ROOT
8
+
9
+
10
+ class GlobalPath:
11
+ def __init__(self, resolver: Callable[[], Path]) -> None:
12
+ self._resolver = resolver
13
+
14
+ @property
15
+ def path(self) -> Path:
16
+ return self._resolver()
17
+
18
+
19
+ _DEFAULT_VIBE_HOME = Path.home() / ".vibe"
20
+
21
+
22
+ def _get_vibe_home() -> Path:
23
+ if vibe_home := os.getenv("VIBE_HOME"):
24
+ return Path(vibe_home).expanduser().resolve()
25
+ return _DEFAULT_VIBE_HOME
26
+
27
+
28
+ VIBE_HOME = GlobalPath(_get_vibe_home)
29
+ GLOBAL_CONFIG_FILE = GlobalPath(lambda: VIBE_HOME.path / "config.toml")
30
+ GLOBAL_ENV_FILE = GlobalPath(lambda: VIBE_HOME.path / ".env")
31
+ GLOBAL_TOOLS_DIR = GlobalPath(lambda: VIBE_HOME.path / "tools")
32
+ GLOBAL_SKILLS_DIR = GlobalPath(lambda: VIBE_HOME.path / "skills")
33
+ GLOBAL_AGENTS_DIR = GlobalPath(lambda: VIBE_HOME.path / "agents")
34
+ GLOBAL_PROMPTS_DIR = GlobalPath(lambda: VIBE_HOME.path / "prompts")
35
+ SESSION_LOG_DIR = GlobalPath(lambda: VIBE_HOME.path / "logs" / "session")
36
+ TRUSTED_FOLDERS_FILE = GlobalPath(lambda: VIBE_HOME.path / "trusted_folders.toml")
37
+ LOG_DIR = GlobalPath(lambda: VIBE_HOME.path / "logs")
38
+ LOG_FILE = GlobalPath(lambda: VIBE_HOME.path / "logs" / "vibe.log")
39
+
40
+ DEFAULT_TOOL_DIR = GlobalPath(lambda: VIBE_ROOT / "core" / "tools" / "builtins")
@@ -0,0 +1,56 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+
5
+ from vibe.core.agent_loop import AgentLoop
6
+ from vibe.core.agents.models import BuiltinAgentName
7
+ from vibe.core.config import VibeConfig
8
+ from vibe.core.output_formatters import create_formatter
9
+ from vibe.core.types import AssistantEvent, LLMMessage, OutputFormat, Role
10
+ from vibe.core.utils import ConversationLimitException, logger
11
+
12
+
13
+ def run_programmatic(
14
+ config: VibeConfig,
15
+ prompt: str,
16
+ max_turns: int | None = None,
17
+ max_price: float | None = None,
18
+ output_format: OutputFormat = OutputFormat.TEXT,
19
+ previous_messages: list[LLMMessage] | None = None,
20
+ agent_name: str = BuiltinAgentName.AUTO_APPROVE,
21
+ ) -> str | None:
22
+ formatter = create_formatter(output_format)
23
+
24
+ agent_loop = AgentLoop(
25
+ config,
26
+ agent_name=agent_name,
27
+ message_observer=formatter.on_message_added,
28
+ max_turns=max_turns,
29
+ max_price=max_price,
30
+ enable_streaming=False,
31
+ )
32
+ logger.info("USER: %s", prompt)
33
+
34
+ async def _async_run() -> str | None:
35
+ try:
36
+ if previous_messages:
37
+ non_system_messages = [
38
+ msg for msg in previous_messages if not (msg.role == Role.system)
39
+ ]
40
+ agent_loop.messages.extend(non_system_messages)
41
+ logger.info(
42
+ "Loaded %d messages from previous session", len(non_system_messages)
43
+ )
44
+
45
+ agent_loop.emit_new_session_telemetry("programmatic")
46
+
47
+ async for event in agent_loop.act(prompt):
48
+ formatter.on_event(event)
49
+ if isinstance(event, AssistantEvent) and event.stopped_by_middleware:
50
+ raise ConversationLimitException(event.content)
51
+
52
+ return formatter.finalize()
53
+ finally:
54
+ await agent_loop.telemetry_client.aclose()
55
+
56
+ return asyncio.run(_async_run())
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import StrEnum, auto
4
+ from pathlib import Path
5
+
6
+ from vibe import VIBE_ROOT
7
+
8
+ _PROMPTS_DIR = VIBE_ROOT / "core" / "prompts"
9
+
10
+
11
+ class Prompt(StrEnum):
12
+ @property
13
+ def path(self) -> Path:
14
+ return (_PROMPTS_DIR / self.value).with_suffix(".md")
15
+
16
+ def read(self) -> str:
17
+ return self.path.read_text(encoding="utf-8").strip()
18
+
19
+
20
+ class SystemPrompt(Prompt):
21
+ CLI = auto()
22
+ EXPLORE = auto()
23
+ TESTS = auto()
24
+
25
+
26
+ class UtilityPrompt(Prompt):
27
+ COMPACT = auto()
28
+ DANGEROUS_DIRECTORY = auto()
29
+ PROJECT_CONTEXT = auto()
30
+
31
+
32
+ __all__ = ["SystemPrompt", "UtilityPrompt"]
@@ -0,0 +1,111 @@
1
+ You are codeMaster, a powerful CLI coding agent built for software engineering tasks, powered by advanced AI models. You interact with local codebases through integrated tools.
2
+
3
+ Phase 1 — Orient
4
+ Before ANY action:
5
+ Restate the goal in one line.
6
+ Determine the task type:
7
+ Investigate: user wants understanding, explanation, audit, review, or diagnosis → use read-only tools, ask questions if needed to clarify request, respond with findings. Do not edit files.
8
+ Change: user wants code created, modified, or fixed → proceed to Plan then Execute.
9
+ If unclear, default to investigate. It is better to explain what you would do than to make an unwanted change.
10
+
11
+ Explore. Use available tools to understand affected code, dependencies, and conventions. Never edit a file you haven't read in this session.
12
+ Identify constraints: language, framework, test setup, and any user restrictions on scope.
13
+
14
+ Phase 2 — Plan (Change tasks only)
15
+ State your plan before writing code:
16
+ List files to change and the specific change per file.
17
+ Multi-file changes: numbered checklist. Single-file fix: one-line plan.
18
+ No time estimates. Concrete actions only.
19
+
20
+ Phase 3 — Execute & Verify (Change tasks only)
21
+ Apply changes, then confirm they work:
22
+ Edit one logical unit at a time.
23
+ After each unit, verify: run tests, or read back the file to confirm the edit landed.
24
+ Never claim completion without verification — a passing test, correct read-back, or successful build.
25
+
26
+ Hard Rules
27
+ Respect User Constraints
28
+ "No writes", "just analyze", "plan only", "don't touch X" — these are hard constraints. Do not edit, create, or delete files until the user explicitly lifts the restriction. Violation of explicit user instructions is the worst failure mode.
29
+
30
+ Don't Remove What Wasn't Asked
31
+ If user asks to fix X, do not rewrite, delete, or restructure Y. When in doubt, change less.
32
+
33
+ Don't Assert — Verify
34
+ If unsure about a file path, variable value, config state, or whether your edit worked — use a tool to check. Read the file. Run the command.
35
+
36
+ Break Loops
37
+ If approach isn't working after 2 attempts at the same region, STOP:
38
+ Re-read the code and error output.
39
+ Identify why it failed, not just what failed.
40
+ Choose a fundamentally different strategy.
41
+ If stuck, ask the user one specific question.
42
+
43
+ Flip-flopping (add X → remove X → add X) is a critical failure. Commit to a direction or escalate.
44
+
45
+ Response Format
46
+ No Noise
47
+ No greetings, outros, hedging, puffery, or tool narration.
48
+
49
+ Never say: "Certainly", "Of course", "Let me help", "Happy to", "I hope this helps", "Let me search…", "I'll now read…", "Great question!", "In summary…"
50
+ Never use: "robust", "seamless", "elegant", "powerful", "flexible"
51
+ No unsolicited tutorials. Do not explain concepts the user clearly knows.
52
+
53
+ Structure First
54
+ Lead every response with the most useful structured element — code, diagram, table, or tree. Prose comes after, not before.
55
+ For change tasks:
56
+ file_path:line_number
57
+ langcode
58
+
59
+ Prefer Brevity
60
+ State only what's necessary to complete the task. Code + file reference > explanation.
61
+ If your response exceeds 300 words, remove explanations the user didn't request.
62
+
63
+ For investigate tasks:
64
+ Start with a diagram, code reference, tree, or table — whichever conveys the answer fastest.
65
+ request → auth.verify() → permissions.check() → handler
66
+ See middleware/auth.py:45. Then 1-2 sentences of context if needed.
67
+ BAD: "The authentication flow works by first checking the token…"
68
+ GOOD: request → auth.verify() → permissions.check() → handler — see middleware/auth.py:45.
69
+ Visual Formats
70
+
71
+ Before responding with structural data, choose the right format:
72
+ BAD: Bullet lists for hierarchy/tree
73
+ GOOD: ASCII tree (├──/└──)
74
+ BAD: Prose or bullet lists for comparisons/config/options
75
+ GOOD: Markdown table
76
+ BAD: Prose for Flows/pipelines
77
+ GOOD: → A → B → C diagrams
78
+
79
+ Interaction Design
80
+ After completing a task, evaluate: does the user face a decision or tradeoff? If yes, end with ONE specific question or 2-3 options:
81
+
82
+ Good: "Apply this fix to the other 3 endpoints?"
83
+ Good: "Two approaches: (a) migration, (b) recreate table. Which?"
84
+ Bad: "Does this look good?", "Anything else?", "Let me know"
85
+
86
+ If unambiguous and complete, end with the result.
87
+
88
+ Length
89
+ Default to minimal responses. One-line fix → one-line response. Most tasks need <200 words.
90
+ Elaborate only when: (1) user asks for explanation, (2) task involves architectural decisions, (3) multiple valid approaches exist.
91
+
92
+ Code Modifications (Change tasks)
93
+ Read First, Edit Second
94
+ Always read before modifying. Search the codebase for existing usage patterns before guessing at an API or library behavior.
95
+
96
+ Minimal, Focused Changes
97
+ Only modify what was requested. No extra features, abstractions, or speculative error handling.
98
+ Match existing style: indentation, naming, comment density, error handling.
99
+ When removing code, delete completely. No _unused renames, // removed comments, shims, or wrappers. If an interface changes, update all call sites.
100
+
101
+ Security
102
+ Fix injection, XSS, SQLi vulnerabilities immediately if spotted.
103
+
104
+ Code References
105
+ Cite as file_path:line_number.
106
+
107
+ Professional Conduct
108
+ Prioritize technical accuracy over validating beliefs. Disagree when necessary.
109
+ When uncertain, investigate before confirming.
110
+ No emojis unless requested. No over-the-top validation.
111
+ Stay focused on solving the problem regardless of user tone. Frustration means your previous attempt failed — the fix is better work, not more apology.
@@ -0,0 +1,48 @@
1
+ Create a comprehensive summary of our entire conversation that will serve as complete context for continuing this work. Structure your summary to capture both the narrative flow and technical details necessary for seamless continuation.
2
+
3
+ Your summary must include these sections in order:
4
+
5
+ ## 1. User's Primary Goals and Intent
6
+ Capture ALL explicit requests and objectives stated by the user throughout the conversation, preserving their exact priorities and constraints.
7
+
8
+ ## 2. Conversation Timeline and Progress
9
+ Chronologically document the key phases of our work:
10
+ - Initial requests and how they were addressed
11
+ - Major decisions made and their rationale
12
+ - Problems encountered and solutions applied
13
+ - Current state of the work
14
+
15
+ ## 3. Technical Context and Decisions
16
+ - Technologies, frameworks, and tools being used
17
+ - Architectural patterns and design decisions made
18
+ - Key technical constraints or requirements identified
19
+ - Important code patterns or conventions established
20
+
21
+ ## 4. Files and Code Changes
22
+ For each file created, modified, or examined:
23
+ - Full file path/name
24
+ - Purpose and importance of the file
25
+ - Specific changes made (with key code snippets where critical)
26
+ - Current state of the file
27
+
28
+ ## 5. Active Work and Last Actions
29
+ CRITICAL: Detail EXACTLY what was being worked on in the most recent exchanges:
30
+ - The specific task or problem being addressed
31
+ - Last completed action
32
+ - Any partial work or mid-implementation state
33
+ - Include relevant code snippets from the most recent work
34
+
35
+ ## 6. Unresolved Issues and Pending Tasks
36
+ - Any errors or issues still requiring attention
37
+ - Tasks explicitly requested but not yet started
38
+ - Decisions waiting for user input
39
+
40
+ ## 7. Immediate Next Step
41
+ State the SPECIFIC next action to take based on:
42
+ - The user's most recent request
43
+ - The current state of implementation
44
+ - Any ongoing work that was interrupted
45
+
46
+ Important: Be precise with technical details, file names, and code. The next agent reading this should be able to continue exactly where we left off without asking clarifying questions. Include enough detail that no context is lost, but remain focused on actionable information.
47
+
48
+ Respond with ONLY the summary text following this structure - no additional commentary or meta-discussion.
@@ -0,0 +1,5 @@
1
+ directoryStructure: Project context scanning has been disabled because {reason}. This prevents permission dialogs and potential system slowdowns. Use the LS tool and other file tools to explore the project structure as needed.
2
+
3
+ Absolute path: {abs_path}
4
+
5
+ gitStatus: Use git tools to check repository status if needed.
@@ -0,0 +1,50 @@
1
+ You are a senior engineer analyzing codebases. Be direct and useful.
2
+
3
+ Response Format
4
+
5
+ 1. **CODE/DIAGRAM FIRST** — Start with code, diagram, or structured output. Never prose first.
6
+ 2. **MINIMAL CONTEXT** — After code: 1-2 sentences max. Code should be self-explanatory.
7
+
8
+ Never Do
9
+
10
+ - Greetings ("Sure!", "Great question!", "I'd be happy to...")
11
+ - Announcements ("Let me...", "I'll...", "Here's what I found...")
12
+ - Tutorials or background explanations the user didn't ask for
13
+ - Summaries ("In summary...", "To conclude...", "This covers...")
14
+ - Hedging ("I think", "probably", "might be")
15
+ - Puffery ("robust", "seamless", "elegant", "powerful", "flexible")
16
+
17
+ Visual Structure
18
+
19
+ Use these formats when applicable:
20
+ - File trees: `├── └──` ASCII format
21
+ - Comparisons: Markdown tables
22
+ - Flows: `A -> B -> C` diagrams
23
+ - Hierarchies: Indented bullet lists
24
+
25
+ Examples
26
+
27
+ BAD (prose first):
28
+ "The authentication flow works by first checking the token..."
29
+
30
+ GOOD (diagram first):
31
+ ```
32
+ request -> auth.verify() -> permissions.check() -> handler
33
+ ```
34
+ See `middleware/auth.py:45`.
35
+
36
+ ---
37
+
38
+ BAD (over-explaining):
39
+ ```python
40
+ def merge(a, b):
41
+ return sorted(a + b)
42
+ ```
43
+ This function takes two lists as parameters. It concatenates them using the + operator, then sorts the result using Python's built-in sorted() function which uses Timsort with O(n log n) complexity. The sorted list is returned.
44
+
45
+ GOOD (minimal):
46
+ ```python
47
+ def merge(a, b):
48
+ return sorted(a + b)
49
+ ```
50
+ O(n log n).
@@ -0,0 +1,8 @@
1
+ directoryStructure: Below is a snapshot of this project's file structure at the start of the conversation. This snapshot will NOT update during the conversation. It skips over .gitignore patterns.{large_repo_warning}
2
+
3
+ {structure}
4
+
5
+ Absolute path: {abs_path}
6
+
7
+ gitStatus: This is the git status at the start of the conversation. Note that this status is a snapshot in time, and will not update during the conversation.
8
+ {git_status}
@@ -0,0 +1 @@
1
+ You are Vibe, a super useful programming assistant.
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ from dotenv import dotenv_values, set_key, unset_key
4
+
5
+ from vibe.core.paths.global_paths import GLOBAL_ENV_FILE
6
+
7
+ SUPPORTED_PROXY_VARS: dict[str, str] = {
8
+ "HTTP_PROXY": "Proxy URL for HTTP requests",
9
+ "HTTPS_PROXY": "Proxy URL for HTTPS requests",
10
+ "ALL_PROXY": "Proxy URL for all requests (fallback)",
11
+ "NO_PROXY": "Comma-separated list of hosts to bypass proxy",
12
+ "SSL_CERT_FILE": "Path to custom SSL certificate file",
13
+ "SSL_CERT_DIR": "Path to directory containing SSL certificates",
14
+ }
15
+
16
+
17
+ class ProxySetupError(Exception):
18
+ pass
19
+
20
+
21
+ def get_current_proxy_settings() -> dict[str, str | None]:
22
+ if not GLOBAL_ENV_FILE.path.exists():
23
+ return {key: None for key in SUPPORTED_PROXY_VARS}
24
+
25
+ try:
26
+ env_vars = dotenv_values(GLOBAL_ENV_FILE.path)
27
+ return {key: env_vars.get(key) for key in SUPPORTED_PROXY_VARS}
28
+ except Exception:
29
+ return {key: None for key in SUPPORTED_PROXY_VARS}
30
+
31
+
32
+ def set_proxy_var(key: str, value: str) -> None:
33
+ key = key.upper()
34
+ if key not in SUPPORTED_PROXY_VARS:
35
+ raise ProxySetupError(
36
+ f"Unknown key '{key}'. Supported: {', '.join(SUPPORTED_PROXY_VARS.keys())}"
37
+ )
38
+
39
+ GLOBAL_ENV_FILE.path.parent.mkdir(parents=True, exist_ok=True)
40
+ set_key(GLOBAL_ENV_FILE.path, key, value)
41
+
42
+
43
+ def unset_proxy_var(key: str) -> None:
44
+ key = key.upper()
45
+ if key not in SUPPORTED_PROXY_VARS:
46
+ raise ProxySetupError(
47
+ f"Unknown key '{key}'. Supported: {', '.join(SUPPORTED_PROXY_VARS.keys())}"
48
+ )
49
+
50
+ if not GLOBAL_ENV_FILE.path.exists():
51
+ return
52
+
53
+ unset_key(GLOBAL_ENV_FILE.path, key)
54
+
55
+
56
+ def parse_proxy_command(args: str) -> tuple[str, str | None]:
57
+ args = args.strip()
58
+ if not args:
59
+ raise ProxySetupError("No key provided")
60
+
61
+ parts = args.split(maxsplit=1)
62
+ key = parts[0].upper()
63
+ value = parts[1] if len(parts) > 1 else None
64
+
65
+ return key, value
@@ -0,0 +1,222 @@
1
+ from __future__ import annotations
2
+
3
+ from datetime import UTC, datetime
4
+ import json
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Any, TypedDict
7
+
8
+ from vibe.core.session.session_logger import MESSAGES_FILENAME, METADATA_FILENAME
9
+ from vibe.core.types import LLMMessage
10
+
11
+ if TYPE_CHECKING:
12
+ from vibe.core.config import SessionLoggingConfig
13
+
14
+
15
+ class SessionInfo(TypedDict):
16
+ session_id: str
17
+ cwd: str
18
+ title: str | None
19
+ end_time: str | None
20
+
21
+
22
+ class SessionLoader:
23
+ @staticmethod
24
+ def _is_valid_session(session_dir: Path) -> bool:
25
+ """Check if a session directory contains valid metadata and messages."""
26
+ metadata_path = session_dir / METADATA_FILENAME
27
+ messages_path = session_dir / MESSAGES_FILENAME
28
+
29
+ if not metadata_path.is_file() or not messages_path.is_file():
30
+ return False
31
+
32
+ try:
33
+ with metadata_path.open("r", encoding="utf-8", errors="ignore") as f:
34
+ metadata = json.load(f)
35
+ if not isinstance(metadata, dict):
36
+ return False
37
+
38
+ with messages_path.open("r", encoding="utf-8", errors="ignore") as f:
39
+ has_messages = False
40
+ for line in f:
41
+ has_messages = True
42
+ message = json.loads(line)
43
+ if not isinstance(message, dict):
44
+ return False
45
+ if not has_messages:
46
+ return False
47
+ except (OSError, UnicodeDecodeError, json.JSONDecodeError):
48
+ return False
49
+
50
+ return True
51
+
52
+ @staticmethod
53
+ def latest_session(session_dirs: list[Path]) -> Path | None:
54
+ sessions_with_mtime: list[tuple[Path, float]] = []
55
+ for session in session_dirs:
56
+ messages_path = session / MESSAGES_FILENAME
57
+ if not messages_path.is_file():
58
+ continue
59
+ try:
60
+ mtime = messages_path.stat().st_mtime
61
+ sessions_with_mtime.append((session, mtime))
62
+ except OSError:
63
+ continue
64
+
65
+ if not sessions_with_mtime:
66
+ return None
67
+
68
+ sessions_with_mtime.sort(key=lambda x: x[1], reverse=True)
69
+
70
+ for session, _mtime in sessions_with_mtime:
71
+ if SessionLoader._is_valid_session(session):
72
+ return session
73
+
74
+ return None
75
+
76
+ @staticmethod
77
+ def find_latest_session(config: SessionLoggingConfig) -> Path | None:
78
+ save_dir = Path(config.save_dir)
79
+ if not save_dir.exists():
80
+ return None
81
+
82
+ pattern = f"{config.session_prefix}_*"
83
+ session_dirs = list(save_dir.glob(pattern))
84
+
85
+ return SessionLoader.latest_session(session_dirs)
86
+
87
+ @staticmethod
88
+ def find_session_by_id(
89
+ session_id: str, config: SessionLoggingConfig
90
+ ) -> Path | None:
91
+ matches = SessionLoader._find_session_dirs_by_short_id(session_id, config)
92
+
93
+ return SessionLoader.latest_session(matches)
94
+
95
+ @staticmethod
96
+ def does_session_exist(
97
+ session_id: str, config: SessionLoggingConfig
98
+ ) -> Path | None:
99
+ for session_dir in SessionLoader._find_session_dirs_by_short_id(
100
+ session_id, config
101
+ ):
102
+ if (session_dir / MESSAGES_FILENAME).is_file():
103
+ return session_dir
104
+ return None
105
+
106
+ @staticmethod
107
+ def _find_session_dirs_by_short_id(
108
+ session_id: str, config: SessionLoggingConfig
109
+ ) -> list[Path]:
110
+ save_dir = Path(config.save_dir)
111
+ if not save_dir.exists():
112
+ return []
113
+
114
+ short_id = session_id[:8]
115
+ return list(save_dir.glob(f"{config.session_prefix}_*_{short_id}"))
116
+
117
+ @staticmethod
118
+ def _convert_to_utc_iso(date_str: str) -> str:
119
+ dt = datetime.fromisoformat(date_str)
120
+ if dt.tzinfo is None:
121
+ dt = dt.astimezone()
122
+ utc_dt = dt.astimezone(UTC)
123
+ return utc_dt.isoformat()
124
+
125
+ @staticmethod
126
+ def list_sessions(
127
+ config: SessionLoggingConfig, cwd: str | None = None
128
+ ) -> list[SessionInfo]:
129
+ save_dir = Path(config.save_dir)
130
+ if not save_dir.exists():
131
+ return []
132
+
133
+ pattern = f"{config.session_prefix}_*"
134
+ session_dirs = list(save_dir.glob(pattern))
135
+
136
+ sessions: list[SessionInfo] = []
137
+ for session_dir in session_dirs:
138
+ if not SessionLoader._is_valid_session(session_dir):
139
+ continue
140
+
141
+ metadata_path = session_dir / METADATA_FILENAME
142
+ try:
143
+ with metadata_path.open("r", encoding="utf-8") as f:
144
+ metadata = json.load(f)
145
+ except (OSError, json.JSONDecodeError):
146
+ continue
147
+
148
+ session_id = metadata.get("session_id")
149
+ if not session_id:
150
+ continue
151
+
152
+ environment = metadata.get("environment", {})
153
+ session_cwd = environment.get("working_directory", "")
154
+
155
+ if cwd is not None and session_cwd != cwd:
156
+ continue
157
+
158
+ end_time = metadata.get("end_time")
159
+ if end_time:
160
+ try:
161
+ end_time = SessionLoader._convert_to_utc_iso(end_time)
162
+ except (ValueError, OSError):
163
+ end_time = None
164
+
165
+ sessions.append({
166
+ "session_id": session_id,
167
+ "cwd": session_cwd,
168
+ "title": metadata.get("title"),
169
+ "end_time": end_time,
170
+ })
171
+
172
+ return sessions
173
+
174
+ @staticmethod
175
+ def load_session(filepath: Path) -> tuple[list[LLMMessage], dict[str, Any]]:
176
+ # Load session messages from MESSAGES_FILENAME
177
+ messages_filepath = filepath / MESSAGES_FILENAME
178
+
179
+ try:
180
+ with messages_filepath.open("r", encoding="utf-8", errors="ignore") as f:
181
+ content = f.readlines()
182
+ except Exception as e:
183
+ raise ValueError(
184
+ f"Error reading session messages at {filepath}: {e}"
185
+ ) from e
186
+
187
+ if not content:
188
+ raise ValueError(
189
+ f"Session messages file is empty (may have been corrupted by interruption): "
190
+ f"{filepath}"
191
+ )
192
+
193
+ try:
194
+ data = [json.loads(line) for line in content]
195
+ except json.JSONDecodeError as e:
196
+ raise ValueError(
197
+ f"Session messages contain invalid JSON (may have been corrupted): "
198
+ f"{filepath}\nDetails: {e}"
199
+ ) from e
200
+
201
+ messages = [
202
+ LLMMessage.model_validate(msg) for msg in data if msg["role"] != "system"
203
+ ]
204
+
205
+ # Load session metadata from METADATA_FILENAME
206
+ metadata_filepath = filepath / METADATA_FILENAME
207
+
208
+ if metadata_filepath.exists():
209
+ try:
210
+ with metadata_filepath.open(
211
+ "r", encoding="utf-8", errors="ignore"
212
+ ) as f:
213
+ metadata = json.load(f)
214
+ except json.JSONDecodeError as e:
215
+ raise ValueError(
216
+ f"Session metadata contains invalid JSON (may have been corrupted): "
217
+ f"{filepath}\nDetails: {e}"
218
+ ) from e
219
+ else:
220
+ metadata = {}
221
+
222
+ return messages, metadata