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.
- codemaster_cli-2.2.0.dist-info/METADATA +645 -0
- codemaster_cli-2.2.0.dist-info/RECORD +170 -0
- codemaster_cli-2.2.0.dist-info/WHEEL +4 -0
- codemaster_cli-2.2.0.dist-info/entry_points.txt +3 -0
- vibe/__init__.py +6 -0
- vibe/acp/__init__.py +0 -0
- vibe/acp/acp_agent_loop.py +746 -0
- vibe/acp/entrypoint.py +81 -0
- vibe/acp/tools/__init__.py +0 -0
- vibe/acp/tools/base.py +100 -0
- vibe/acp/tools/builtins/bash.py +134 -0
- vibe/acp/tools/builtins/read_file.py +54 -0
- vibe/acp/tools/builtins/search_replace.py +129 -0
- vibe/acp/tools/builtins/todo.py +65 -0
- vibe/acp/tools/builtins/write_file.py +98 -0
- vibe/acp/tools/session_update.py +118 -0
- vibe/acp/utils.py +213 -0
- vibe/cli/__init__.py +0 -0
- vibe/cli/autocompletion/__init__.py +0 -0
- vibe/cli/autocompletion/base.py +22 -0
- vibe/cli/autocompletion/path_completion.py +177 -0
- vibe/cli/autocompletion/slash_command.py +99 -0
- vibe/cli/cli.py +188 -0
- vibe/cli/clipboard.py +69 -0
- vibe/cli/commands.py +116 -0
- vibe/cli/entrypoint.py +163 -0
- vibe/cli/history_manager.py +91 -0
- vibe/cli/plan_offer/adapters/http_whoami_gateway.py +67 -0
- vibe/cli/plan_offer/decide_plan_offer.py +87 -0
- vibe/cli/plan_offer/ports/whoami_gateway.py +23 -0
- vibe/cli/terminal_setup.py +323 -0
- vibe/cli/textual_ui/__init__.py +0 -0
- vibe/cli/textual_ui/ansi_markdown.py +58 -0
- vibe/cli/textual_ui/app.py +1546 -0
- vibe/cli/textual_ui/app.tcss +1020 -0
- vibe/cli/textual_ui/external_editor.py +32 -0
- vibe/cli/textual_ui/handlers/__init__.py +5 -0
- vibe/cli/textual_ui/handlers/event_handler.py +147 -0
- vibe/cli/textual_ui/widgets/__init__.py +0 -0
- vibe/cli/textual_ui/widgets/approval_app.py +192 -0
- vibe/cli/textual_ui/widgets/banner/banner.py +85 -0
- vibe/cli/textual_ui/widgets/banner/petit_chat.py +195 -0
- vibe/cli/textual_ui/widgets/braille_renderer.py +58 -0
- vibe/cli/textual_ui/widgets/chat_input/__init__.py +7 -0
- vibe/cli/textual_ui/widgets/chat_input/body.py +214 -0
- vibe/cli/textual_ui/widgets/chat_input/completion_manager.py +58 -0
- vibe/cli/textual_ui/widgets/chat_input/completion_popup.py +43 -0
- vibe/cli/textual_ui/widgets/chat_input/container.py +195 -0
- vibe/cli/textual_ui/widgets/chat_input/text_area.py +365 -0
- vibe/cli/textual_ui/widgets/compact.py +41 -0
- vibe/cli/textual_ui/widgets/config_app.py +171 -0
- vibe/cli/textual_ui/widgets/context_progress.py +30 -0
- vibe/cli/textual_ui/widgets/load_more.py +43 -0
- vibe/cli/textual_ui/widgets/loading.py +201 -0
- vibe/cli/textual_ui/widgets/messages.py +277 -0
- vibe/cli/textual_ui/widgets/no_markup_static.py +11 -0
- vibe/cli/textual_ui/widgets/path_display.py +28 -0
- vibe/cli/textual_ui/widgets/proxy_setup_app.py +127 -0
- vibe/cli/textual_ui/widgets/question_app.py +496 -0
- vibe/cli/textual_ui/widgets/spinner.py +194 -0
- vibe/cli/textual_ui/widgets/status_message.py +76 -0
- vibe/cli/textual_ui/widgets/teleport_message.py +31 -0
- vibe/cli/textual_ui/widgets/tool_widgets.py +371 -0
- vibe/cli/textual_ui/widgets/tools.py +201 -0
- vibe/cli/textual_ui/windowing/__init__.py +29 -0
- vibe/cli/textual_ui/windowing/history.py +105 -0
- vibe/cli/textual_ui/windowing/history_windowing.py +71 -0
- vibe/cli/textual_ui/windowing/state.py +105 -0
- vibe/cli/update_notifier/__init__.py +47 -0
- vibe/cli/update_notifier/adapters/filesystem_update_cache_repository.py +59 -0
- vibe/cli/update_notifier/adapters/github_update_gateway.py +101 -0
- vibe/cli/update_notifier/adapters/pypi_update_gateway.py +107 -0
- vibe/cli/update_notifier/ports/update_cache_repository.py +16 -0
- vibe/cli/update_notifier/ports/update_gateway.py +53 -0
- vibe/cli/update_notifier/update.py +139 -0
- vibe/cli/update_notifier/whats_new.py +49 -0
- vibe/core/__init__.py +5 -0
- vibe/core/agent_loop.py +1075 -0
- vibe/core/agents/__init__.py +31 -0
- vibe/core/agents/manager.py +165 -0
- vibe/core/agents/models.py +122 -0
- vibe/core/auth/__init__.py +6 -0
- vibe/core/auth/crypto.py +137 -0
- vibe/core/auth/github.py +178 -0
- vibe/core/autocompletion/__init__.py +0 -0
- vibe/core/autocompletion/completers.py +257 -0
- vibe/core/autocompletion/file_indexer/__init__.py +10 -0
- vibe/core/autocompletion/file_indexer/ignore_rules.py +156 -0
- vibe/core/autocompletion/file_indexer/indexer.py +179 -0
- vibe/core/autocompletion/file_indexer/store.py +169 -0
- vibe/core/autocompletion/file_indexer/watcher.py +71 -0
- vibe/core/autocompletion/fuzzy.py +189 -0
- vibe/core/autocompletion/path_prompt.py +108 -0
- vibe/core/autocompletion/path_prompt_adapter.py +149 -0
- vibe/core/config.py +673 -0
- vibe/core/config_PATCH_INSTRUCTIONS.md +77 -0
- vibe/core/llm/__init__.py +0 -0
- vibe/core/llm/backend/anthropic.py +630 -0
- vibe/core/llm/backend/base.py +38 -0
- vibe/core/llm/backend/factory.py +7 -0
- vibe/core/llm/backend/generic.py +425 -0
- vibe/core/llm/backend/mistral.py +381 -0
- vibe/core/llm/backend/vertex.py +115 -0
- vibe/core/llm/exceptions.py +195 -0
- vibe/core/llm/format.py +184 -0
- vibe/core/llm/message_utils.py +24 -0
- vibe/core/llm/types.py +120 -0
- vibe/core/middleware.py +209 -0
- vibe/core/output_formatters.py +85 -0
- vibe/core/paths/__init__.py +0 -0
- vibe/core/paths/config_paths.py +68 -0
- vibe/core/paths/global_paths.py +40 -0
- vibe/core/programmatic.py +56 -0
- vibe/core/prompts/__init__.py +32 -0
- vibe/core/prompts/cli.md +111 -0
- vibe/core/prompts/compact.md +48 -0
- vibe/core/prompts/dangerous_directory.md +5 -0
- vibe/core/prompts/explore.md +50 -0
- vibe/core/prompts/project_context.md +8 -0
- vibe/core/prompts/tests.md +1 -0
- vibe/core/proxy_setup.py +65 -0
- vibe/core/session/session_loader.py +222 -0
- vibe/core/session/session_logger.py +318 -0
- vibe/core/session/session_migration.py +41 -0
- vibe/core/skills/__init__.py +7 -0
- vibe/core/skills/manager.py +132 -0
- vibe/core/skills/models.py +92 -0
- vibe/core/skills/parser.py +39 -0
- vibe/core/system_prompt.py +466 -0
- vibe/core/telemetry/__init__.py +0 -0
- vibe/core/telemetry/send.py +185 -0
- vibe/core/teleport/errors.py +9 -0
- vibe/core/teleport/git.py +196 -0
- vibe/core/teleport/nuage.py +180 -0
- vibe/core/teleport/teleport.py +208 -0
- vibe/core/teleport/types.py +54 -0
- vibe/core/tools/base.py +336 -0
- vibe/core/tools/builtins/ask_user_question.py +134 -0
- vibe/core/tools/builtins/bash.py +357 -0
- vibe/core/tools/builtins/grep.py +310 -0
- vibe/core/tools/builtins/prompts/__init__.py +0 -0
- vibe/core/tools/builtins/prompts/ask_user_question.md +84 -0
- vibe/core/tools/builtins/prompts/bash.md +73 -0
- vibe/core/tools/builtins/prompts/grep.md +4 -0
- vibe/core/tools/builtins/prompts/read_file.md +13 -0
- vibe/core/tools/builtins/prompts/search_replace.md +43 -0
- vibe/core/tools/builtins/prompts/task.md +24 -0
- vibe/core/tools/builtins/prompts/todo.md +199 -0
- vibe/core/tools/builtins/prompts/write_file.md +42 -0
- vibe/core/tools/builtins/read_file.py +222 -0
- vibe/core/tools/builtins/search_replace.py +456 -0
- vibe/core/tools/builtins/task.py +154 -0
- vibe/core/tools/builtins/todo.py +134 -0
- vibe/core/tools/builtins/write_file.py +160 -0
- vibe/core/tools/manager.py +341 -0
- vibe/core/tools/mcp.py +397 -0
- vibe/core/tools/ui.py +68 -0
- vibe/core/trusted_folders.py +86 -0
- vibe/core/types.py +405 -0
- vibe/core/utils.py +396 -0
- vibe/setup/onboarding/__init__.py +39 -0
- vibe/setup/onboarding/base.py +14 -0
- vibe/setup/onboarding/onboarding.tcss +134 -0
- vibe/setup/onboarding/screens/__init__.py +5 -0
- vibe/setup/onboarding/screens/api_key.py +200 -0
- vibe/setup/onboarding/screens/provider_selection.py +87 -0
- vibe/setup/onboarding/screens/welcome.py +136 -0
- vibe/setup/trusted_folders/trust_folder_dialog.py +180 -0
- vibe/setup/trusted_folders/trust_folder_dialog.tcss +83 -0
- 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"]
|
vibe/core/prompts/cli.md
ADDED
|
@@ -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.
|
vibe/core/proxy_setup.py
ADDED
|
@@ -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
|