shotgun-sh 0.1.4__tar.gz → 0.1.6__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.
Potentially problematic release.
This version of shotgun-sh might be problematic. Click here for more details.
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/PKG-INFO +1 -1
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/pyproject.toml +1 -1
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/agent_manager.py +0 -11
- shotgun_sh-0.1.6/src/shotgun/codebase/core/cypher_models.py +46 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/nl_query.py +180 -39
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/service.py +17 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/cypher_system.j2 +15 -1
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat.py +5 -11
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat_screen/history.py +5 -4
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/utils/update_checker.py +16 -4
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/.gitignore +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/LICENSE +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/README.md +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/hatch_build.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/common.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/config/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/config/constants.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/config/manager.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/config/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/config/provider.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/conversation_history.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/conversation_manager.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/export.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/compaction.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/constants.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/context_extraction.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/history_building.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/history_processors.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/message_utils.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/token_counting.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/history/token_estimation.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/messages.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/plan.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/research.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/specify.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tasks.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/codebase_shell.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/directory_lister.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/file_read.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/query_graph.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/codebase/retrieve_code.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/file_management.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/user_interaction.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/web_search/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/web_search/anthropic.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/web_search/gemini.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/web_search/openai.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/agents/tools/web_search/utils.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/build_constants.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/codebase/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/codebase/commands.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/codebase/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/config.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/export.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/plan.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/research.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/specify.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/tasks.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/update.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/cli/utils.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/change_detector.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/code_retrieval.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/ingestor.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/language_config.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/manager.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/core/parser_loader.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/codebase/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/logging_config.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/main.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/posthog_telemetry.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/export.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/codebase_understanding.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/content_formatting.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/interactive_mode.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/plan.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/research.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/specify.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/state/system_state.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/tasks.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/cypher_query_patterns.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/enhanced_query_context.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/partials/cypher_rules.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/partials/graph_schema.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/partials/temporal_context.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/history/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/history/incremental_summarization.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/history/summarization.j2 +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/loader.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/py.typed +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/sdk/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/sdk/codebase.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/sdk/exceptions.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/sdk/models.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/sdk/services.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/sentry_telemetry.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/telemetry.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/app.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/commands/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/components/prompt_input.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/components/spinner.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/components/splash.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/components/vertical_tail.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat.tcss +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat_screen/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat_screen/command_providers.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat_screen/hint_message.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/directory_setup.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/provider_config.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/splash.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/styles.tcss +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/utils/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/utils/mode_progress.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/utils/__init__.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/utils/env_utils.py +0 -0
- {shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/utils/file_system_utils.py +0 -0
|
@@ -362,17 +362,6 @@ class AgentManager(Widget):
|
|
|
362
362
|
**kwargs,
|
|
363
363
|
)
|
|
364
364
|
finally:
|
|
365
|
-
# If the stream ended unexpectedly without a final result, clear accumulated state.
|
|
366
|
-
# state = self._stream_state
|
|
367
|
-
# if state is not None:
|
|
368
|
-
# pending_response = state.current_response
|
|
369
|
-
# if pending_response is not None:
|
|
370
|
-
# already_recorded = (
|
|
371
|
-
# bool(state.messages) and state.messages[-1] is pending_response
|
|
372
|
-
# )
|
|
373
|
-
# if not already_recorded:
|
|
374
|
-
# self._post_partial_message(pending_response, True)
|
|
375
|
-
# state.messages.append(pending_response)
|
|
376
365
|
self._stream_state = None
|
|
377
366
|
|
|
378
367
|
self.ui_message_history = original_messages + cast(
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Pydantic models and exceptions for Cypher query generation."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CypherGenerationResponse(BaseModel):
|
|
9
|
+
"""Structured response from LLM for Cypher query generation.
|
|
10
|
+
|
|
11
|
+
This model ensures the LLM explicitly indicates whether it can generate
|
|
12
|
+
a valid Cypher query and provides a reason if it cannot.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
cypher_query: str | None = Field(
|
|
16
|
+
default=None,
|
|
17
|
+
description="The generated Cypher query, or None if generation not possible",
|
|
18
|
+
)
|
|
19
|
+
can_generate_valid_cypher: bool = Field(
|
|
20
|
+
description="Whether a valid Cypher query can be generated for this request"
|
|
21
|
+
)
|
|
22
|
+
reason_cannot_generate: str | None = Field(
|
|
23
|
+
default=None,
|
|
24
|
+
description="Explanation why query cannot be generated (if applicable)",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def model_post_init(self, __context: Any) -> None:
|
|
28
|
+
"""Validate that reason is provided when query cannot be generated."""
|
|
29
|
+
if not self.can_generate_valid_cypher and not self.reason_cannot_generate:
|
|
30
|
+
self.reason_cannot_generate = "No reason provided"
|
|
31
|
+
if self.can_generate_valid_cypher and not self.cypher_query:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
"cypher_query must be provided when can_generate_valid_cypher is True"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CypherGenerationNotPossibleError(Exception):
|
|
38
|
+
"""Raised when LLM cannot generate valid Cypher for the query.
|
|
39
|
+
|
|
40
|
+
This typically happens when the query is conceptual rather than structural,
|
|
41
|
+
or when it requires interpretation beyond what can be expressed in Cypher.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, reason: str):
|
|
45
|
+
self.reason = reason
|
|
46
|
+
super().__init__(f"Cannot generate Cypher query: {reason}")
|
|
@@ -4,15 +4,13 @@ import time
|
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
-
from pydantic_ai
|
|
8
|
-
ModelRequest,
|
|
9
|
-
SystemPromptPart,
|
|
10
|
-
TextPart,
|
|
11
|
-
UserPromptPart,
|
|
12
|
-
)
|
|
7
|
+
from pydantic_ai import Agent
|
|
13
8
|
|
|
14
9
|
from shotgun.agents.config import get_provider_model
|
|
15
|
-
from shotgun.
|
|
10
|
+
from shotgun.codebase.core.cypher_models import (
|
|
11
|
+
CypherGenerationNotPossibleError,
|
|
12
|
+
CypherGenerationResponse,
|
|
13
|
+
)
|
|
16
14
|
from shotgun.logging_config import get_logger
|
|
17
15
|
from shotgun.prompts import PromptLoader
|
|
18
16
|
|
|
@@ -25,42 +23,52 @@ logger = get_logger(__name__)
|
|
|
25
23
|
prompt_loader = PromptLoader()
|
|
26
24
|
|
|
27
25
|
|
|
28
|
-
async def llm_cypher_prompt(
|
|
29
|
-
|
|
26
|
+
async def llm_cypher_prompt(
|
|
27
|
+
system_prompt: str, user_prompt: str
|
|
28
|
+
) -> CypherGenerationResponse:
|
|
29
|
+
"""Generate a Cypher query from a natural language prompt using structured output.
|
|
30
30
|
|
|
31
31
|
Args:
|
|
32
32
|
system_prompt: The system prompt defining the behavior and context for the LLM
|
|
33
33
|
user_prompt: The user's natural language query
|
|
34
34
|
Returns:
|
|
35
|
-
|
|
35
|
+
CypherGenerationResponse with cypher_query, can_generate flag, and reason if not
|
|
36
36
|
"""
|
|
37
37
|
model_config = get_provider_model()
|
|
38
|
-
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ModelRequest(
|
|
45
|
-
parts=[
|
|
46
|
-
SystemPromptPart(content=system_prompt),
|
|
47
|
-
UserPromptPart(content=user_prompt),
|
|
48
|
-
]
|
|
49
|
-
),
|
|
50
|
-
],
|
|
51
|
-
max_tokens=2000, # Cypher queries are short, 2000 tokens is plenty
|
|
38
|
+
|
|
39
|
+
# Create an agent with structured output for Cypher generation
|
|
40
|
+
cypher_agent = Agent(
|
|
41
|
+
model=model_config.model_instance,
|
|
42
|
+
output_type=CypherGenerationResponse,
|
|
43
|
+
retries=2,
|
|
52
44
|
)
|
|
53
45
|
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
# Combine system and user prompts
|
|
47
|
+
combined_prompt = f"{system_prompt}\n\nUser Query: {user_prompt}"
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
# Run the agent to get structured response
|
|
51
|
+
result = await cypher_agent.run(combined_prompt)
|
|
52
|
+
response = result.output
|
|
53
|
+
|
|
54
|
+
# Log the structured response for debugging
|
|
55
|
+
logger.debug(
|
|
56
|
+
"Cypher generation response - can_generate: %s, query: %s, reason: %s",
|
|
57
|
+
response.can_generate_valid_cypher,
|
|
58
|
+
response.cypher_query[:50] if response.cypher_query else None,
|
|
59
|
+
response.reason_cannot_generate,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return response
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
except Exception as e:
|
|
65
|
+
logger.error("Failed to generate Cypher query with structured output: %s", e)
|
|
66
|
+
# Return a failure response
|
|
67
|
+
return CypherGenerationResponse(
|
|
68
|
+
cypher_query=None,
|
|
69
|
+
can_generate_valid_cypher=False,
|
|
70
|
+
reason_cannot_generate=f"LLM error: {str(e)}",
|
|
71
|
+
)
|
|
64
72
|
|
|
65
73
|
|
|
66
74
|
async def generate_cypher(natural_language_query: str) -> str:
|
|
@@ -71,6 +79,10 @@ async def generate_cypher(natural_language_query: str) -> str:
|
|
|
71
79
|
|
|
72
80
|
Returns:
|
|
73
81
|
Generated Cypher query
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
CypherGenerationNotPossibleError: If the query cannot be converted to Cypher
|
|
85
|
+
RuntimeError: If there's an error during generation
|
|
74
86
|
"""
|
|
75
87
|
# Get current time for context
|
|
76
88
|
current_timestamp = int(time.time())
|
|
@@ -88,8 +100,30 @@ async def generate_cypher(natural_language_query: str) -> str:
|
|
|
88
100
|
)
|
|
89
101
|
|
|
90
102
|
try:
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
response = await llm_cypher_prompt(system_prompt, enhanced_query)
|
|
104
|
+
|
|
105
|
+
# Check if the LLM could generate a valid Cypher query
|
|
106
|
+
if not response.can_generate_valid_cypher:
|
|
107
|
+
logger.info(
|
|
108
|
+
"Cannot generate Cypher for query '%s': %s",
|
|
109
|
+
natural_language_query,
|
|
110
|
+
response.reason_cannot_generate,
|
|
111
|
+
)
|
|
112
|
+
raise CypherGenerationNotPossibleError(
|
|
113
|
+
response.reason_cannot_generate or "Query cannot be converted to Cypher"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
if not response.cypher_query:
|
|
117
|
+
raise ValueError("LLM indicated success but provided no query")
|
|
118
|
+
|
|
119
|
+
cleaned_query = clean_cypher_response(response.cypher_query)
|
|
120
|
+
|
|
121
|
+
# Validate Cypher keywords
|
|
122
|
+
is_valid, validation_error = validate_cypher_keywords(cleaned_query)
|
|
123
|
+
if not is_valid:
|
|
124
|
+
logger.warning(f"Generated query has invalid syntax: {validation_error}")
|
|
125
|
+
logger.warning(f"Problematic query: {cleaned_query}")
|
|
126
|
+
raise ValueError(f"Generated query validation failed: {validation_error}")
|
|
93
127
|
|
|
94
128
|
# Validate UNION ALL queries
|
|
95
129
|
is_valid, validation_error = validate_union_query(cleaned_query)
|
|
@@ -100,6 +134,8 @@ async def generate_cypher(natural_language_query: str) -> str:
|
|
|
100
134
|
|
|
101
135
|
return cleaned_query
|
|
102
136
|
|
|
137
|
+
except CypherGenerationNotPossibleError:
|
|
138
|
+
raise # Re-raise as-is
|
|
103
139
|
except Exception as e:
|
|
104
140
|
raise RuntimeError(f"Failed to generate Cypher query: {e}") from e
|
|
105
141
|
|
|
@@ -170,8 +206,31 @@ MATCH (f:Function) RETURN f.name, f.qualified_name // WRONG: missing third colu
|
|
|
170
206
|
base_system_prompt=prompt_loader.render("codebase/cypher_system.j2"),
|
|
171
207
|
)
|
|
172
208
|
|
|
173
|
-
|
|
174
|
-
|
|
209
|
+
response = await llm_cypher_prompt(enhanced_system_prompt, enhanced_query)
|
|
210
|
+
|
|
211
|
+
# Check if the LLM could generate a valid Cypher query
|
|
212
|
+
if not response.can_generate_valid_cypher:
|
|
213
|
+
logger.info(
|
|
214
|
+
"Cannot generate Cypher for retry query '%s': %s",
|
|
215
|
+
natural_language_query,
|
|
216
|
+
response.reason_cannot_generate,
|
|
217
|
+
)
|
|
218
|
+
raise CypherGenerationNotPossibleError(
|
|
219
|
+
response.reason_cannot_generate
|
|
220
|
+
or "Query cannot be converted to Cypher even with error context"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if not response.cypher_query:
|
|
224
|
+
raise ValueError("LLM indicated success but provided no query on retry")
|
|
225
|
+
|
|
226
|
+
cleaned_query = clean_cypher_response(response.cypher_query)
|
|
227
|
+
|
|
228
|
+
# Validate Cypher keywords
|
|
229
|
+
is_valid, validation_error = validate_cypher_keywords(cleaned_query)
|
|
230
|
+
if not is_valid:
|
|
231
|
+
logger.warning(f"Generated query has invalid syntax: {validation_error}")
|
|
232
|
+
logger.warning(f"Problematic query: {cleaned_query}")
|
|
233
|
+
raise ValueError(f"Generated query validation failed: {validation_error}")
|
|
175
234
|
|
|
176
235
|
# Validate UNION ALL queries
|
|
177
236
|
is_valid, validation_error = validate_union_query(cleaned_query)
|
|
@@ -182,6 +241,8 @@ MATCH (f:Function) RETURN f.name, f.qualified_name // WRONG: missing third colu
|
|
|
182
241
|
|
|
183
242
|
return cleaned_query
|
|
184
243
|
|
|
244
|
+
except CypherGenerationNotPossibleError:
|
|
245
|
+
raise # Re-raise as-is
|
|
185
246
|
except Exception as e:
|
|
186
247
|
raise RuntimeError(
|
|
187
248
|
f"Failed to generate Cypher query with error context: {e}"
|
|
@@ -202,6 +263,10 @@ async def generate_cypher_openai_async(
|
|
|
202
263
|
|
|
203
264
|
Returns:
|
|
204
265
|
Generated Cypher query
|
|
266
|
+
|
|
267
|
+
Raises:
|
|
268
|
+
CypherGenerationNotPossibleError: If the query cannot be converted to Cypher
|
|
269
|
+
RuntimeError: If there's an error during generation
|
|
205
270
|
"""
|
|
206
271
|
# Get current time for context
|
|
207
272
|
current_timestamp = int(time.time())
|
|
@@ -219,9 +284,26 @@ async def generate_cypher_openai_async(
|
|
|
219
284
|
)
|
|
220
285
|
|
|
221
286
|
try:
|
|
222
|
-
|
|
223
|
-
|
|
287
|
+
response = await llm_cypher_prompt(system_prompt, enhanced_query)
|
|
288
|
+
|
|
289
|
+
# Check if the LLM could generate a valid Cypher query
|
|
290
|
+
if not response.can_generate_valid_cypher:
|
|
291
|
+
logger.info(
|
|
292
|
+
"Cannot generate Cypher for query '%s': %s",
|
|
293
|
+
natural_language_query,
|
|
294
|
+
response.reason_cannot_generate,
|
|
295
|
+
)
|
|
296
|
+
raise CypherGenerationNotPossibleError(
|
|
297
|
+
response.reason_cannot_generate or "Query cannot be converted to Cypher"
|
|
298
|
+
)
|
|
224
299
|
|
|
300
|
+
if not response.cypher_query:
|
|
301
|
+
raise ValueError("LLM indicated success but provided no query")
|
|
302
|
+
|
|
303
|
+
return clean_cypher_response(response.cypher_query)
|
|
304
|
+
|
|
305
|
+
except CypherGenerationNotPossibleError:
|
|
306
|
+
raise # Re-raise as-is
|
|
225
307
|
except Exception as e:
|
|
226
308
|
logger.error(f"OpenAI API error: {e}")
|
|
227
309
|
raise RuntimeError(f"Failed to generate Cypher query: {e}") from e
|
|
@@ -288,6 +370,65 @@ def validate_union_query(cypher_query: str) -> tuple[bool, str]:
|
|
|
288
370
|
return True, ""
|
|
289
371
|
|
|
290
372
|
|
|
373
|
+
def validate_cypher_keywords(query: str) -> tuple[bool, str]:
|
|
374
|
+
"""Validate that a query starts with valid Kuzu Cypher keywords.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
query: The Cypher query to validate
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
Tuple of (is_valid, error_message)
|
|
381
|
+
"""
|
|
382
|
+
# Valid Kuzu Cypher starting keywords based on parser expectations
|
|
383
|
+
valid_cypher_keywords = {
|
|
384
|
+
"ALTER",
|
|
385
|
+
"ATTACH",
|
|
386
|
+
"BEGIN",
|
|
387
|
+
"CALL",
|
|
388
|
+
"CHECKPOINT",
|
|
389
|
+
"COMMENT",
|
|
390
|
+
"COMMIT",
|
|
391
|
+
"COPY",
|
|
392
|
+
"CREATE",
|
|
393
|
+
"DELETE",
|
|
394
|
+
"DETACH",
|
|
395
|
+
"DROP",
|
|
396
|
+
"EXPLAIN",
|
|
397
|
+
"EXPORT",
|
|
398
|
+
"FORCE",
|
|
399
|
+
"IMPORT",
|
|
400
|
+
"INSTALL",
|
|
401
|
+
"LOAD",
|
|
402
|
+
"MATCH",
|
|
403
|
+
"MERGE",
|
|
404
|
+
"OPTIONAL",
|
|
405
|
+
"PROFILE",
|
|
406
|
+
"RETURN",
|
|
407
|
+
"ROLLBACK",
|
|
408
|
+
"SET",
|
|
409
|
+
"UNWIND",
|
|
410
|
+
"UNINSTALL",
|
|
411
|
+
"UPDATE",
|
|
412
|
+
"USE",
|
|
413
|
+
"WITH",
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
query = query.strip()
|
|
417
|
+
if not query:
|
|
418
|
+
return False, "Empty query"
|
|
419
|
+
|
|
420
|
+
# Get the first word
|
|
421
|
+
first_word = query.upper().split()[0] if query else ""
|
|
422
|
+
|
|
423
|
+
if first_word not in valid_cypher_keywords:
|
|
424
|
+
return (
|
|
425
|
+
False,
|
|
426
|
+
f"Query doesn't start with valid Cypher keyword. Found: '{first_word}'",
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
return True, ""
|
|
430
|
+
|
|
431
|
+
|
|
291
432
|
def clean_cypher_response(response_text: str) -> str:
|
|
292
433
|
"""Clean up common LLM formatting artifacts from a Cypher query.
|
|
293
434
|
|
|
@@ -4,6 +4,7 @@ import time
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
7
|
+
from shotgun.codebase.core.cypher_models import CypherGenerationNotPossibleError
|
|
7
8
|
from shotgun.codebase.core.manager import CodebaseGraphManager
|
|
8
9
|
from shotgun.codebase.core.nl_query import generate_cypher
|
|
9
10
|
from shotgun.codebase.models import CodebaseGraph, QueryResult, QueryType
|
|
@@ -190,6 +191,22 @@ class CodebaseService:
|
|
|
190
191
|
error=None,
|
|
191
192
|
)
|
|
192
193
|
|
|
194
|
+
except CypherGenerationNotPossibleError as e:
|
|
195
|
+
# Handle queries that cannot be converted to Cypher
|
|
196
|
+
execution_time = (time.time() - start_time) * 1000
|
|
197
|
+
logger.info(f"Query cannot be converted to Cypher: {e.reason}")
|
|
198
|
+
|
|
199
|
+
return QueryResult(
|
|
200
|
+
query=query,
|
|
201
|
+
cypher_query=None,
|
|
202
|
+
results=[],
|
|
203
|
+
column_names=[],
|
|
204
|
+
row_count=0,
|
|
205
|
+
execution_time_ms=execution_time,
|
|
206
|
+
success=False,
|
|
207
|
+
error=f"This query cannot be converted to Cypher: {e.reason}",
|
|
208
|
+
)
|
|
209
|
+
|
|
193
210
|
except Exception as e:
|
|
194
211
|
execution_time = (time.time() - start_time) * 1000
|
|
195
212
|
logger.error(f"Query execution failed: {e}")
|
|
@@ -25,4 +25,18 @@ Your goal is to return appropriate properties for each node type. Common propert
|
|
|
25
25
|
{% include 'codebase/partials/temporal_context.j2' %}
|
|
26
26
|
|
|
27
27
|
**6. Output Format**
|
|
28
|
-
|
|
28
|
+
You must return a structured JSON response with the following fields:
|
|
29
|
+
- `cypher_query`: The generated Cypher query string (or null if not possible)
|
|
30
|
+
- `can_generate_valid_cypher`: Boolean indicating if a valid Cypher query can be generated
|
|
31
|
+
- `reason_cannot_generate`: String explaining why generation isn't possible (or null if successful)
|
|
32
|
+
|
|
33
|
+
**IMPORTANT:** Some queries cannot be expressed in Cypher:
|
|
34
|
+
- Conceptual questions requiring interpretation (e.g., "What is the main purpose of this codebase?")
|
|
35
|
+
- Questions about code quality or best practices
|
|
36
|
+
- Questions requiring semantic understanding beyond structure
|
|
37
|
+
|
|
38
|
+
For these, set `can_generate_valid_cypher` to false and provide a clear explanation in `reason_cannot_generate`.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
- Query: "Show all classes" → can_generate_valid_cypher: true, cypher_query: "MATCH (c:Class) RETURN c.name, c.qualified_name;"
|
|
42
|
+
- Query: "What is the main purpose of this codebase?" → can_generate_valid_cypher: false, reason_cannot_generate: "This is a conceptual question requiring interpretation and analysis of the code's overall design and intent, rather than a structural query about specific code elements."
|
|
@@ -411,7 +411,7 @@ class ChatScreen(Screen[None]):
|
|
|
411
411
|
await self.codebase_sdk.list_codebases_for_directory()
|
|
412
412
|
).graphs
|
|
413
413
|
if accessible_graphs:
|
|
414
|
-
self.mount_hint(help_text_with_codebase())
|
|
414
|
+
self.mount_hint(help_text_with_codebase(already_indexed=True))
|
|
415
415
|
return
|
|
416
416
|
|
|
417
417
|
should_index = await self.app.push_screen_wait(CodebaseIndexPromptScreen())
|
|
@@ -419,6 +419,8 @@ class ChatScreen(Screen[None]):
|
|
|
419
419
|
self.mount_hint(help_text_empty_dir())
|
|
420
420
|
return
|
|
421
421
|
|
|
422
|
+
self.mount_hint(help_text_with_codebase(already_indexed=False))
|
|
423
|
+
|
|
422
424
|
self.index_codebase_command()
|
|
423
425
|
|
|
424
426
|
def watch_mode(self, new_mode: AgentType) -> None:
|
|
@@ -692,7 +694,6 @@ class ChatScreen(Screen[None]):
|
|
|
692
694
|
timeout=8,
|
|
693
695
|
)
|
|
694
696
|
|
|
695
|
-
self.mount_hint(codebase_indexed_hint(selection.name))
|
|
696
697
|
except CodebaseAlreadyIndexedError as exc:
|
|
697
698
|
logger.warning(f"Codebase already indexed: {exc}")
|
|
698
699
|
self.notify(str(exc), severity="warning")
|
|
@@ -796,17 +797,10 @@ class ChatScreen(Screen[None]):
|
|
|
796
797
|
self.mode = AgentType(conversation.last_agent_model)
|
|
797
798
|
|
|
798
799
|
|
|
799
|
-
def
|
|
800
|
-
return (
|
|
801
|
-
f"Codebase **{codebase_name}** indexed successfully. You can now use it in your chat.\n\n"
|
|
802
|
-
+ help_text_with_codebase()
|
|
803
|
-
)
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
def help_text_with_codebase() -> str:
|
|
800
|
+
def help_text_with_codebase(already_indexed: bool = False) -> str:
|
|
807
801
|
return (
|
|
808
802
|
"Howdy! Welcome to Shotgun - the context tool for software engineering. \n\nYou can research, build specs, plan, create tasks, and export context to your favorite code-gen agents.\n\n"
|
|
809
|
-
"I can help with:\n\n"
|
|
803
|
+
f"{'' if already_indexed else 'Once your codebase is indexed, '}I can help with:\n\n"
|
|
810
804
|
"- Speccing out a new feature\n"
|
|
811
805
|
"- Onboarding you onto this project\n"
|
|
812
806
|
"- Helping with a refactor spec\n"
|
|
@@ -136,7 +136,8 @@ class UserQuestionWidget(Widget):
|
|
|
136
136
|
if part.tool_name == "ask_user" and isinstance(part.content, dict):
|
|
137
137
|
acc += f"**>** {part.content['answer']}\n\n"
|
|
138
138
|
else:
|
|
139
|
-
acc += " ∟ finished\n\n" # let's not show anything yet
|
|
139
|
+
# acc += " ∟ finished\n\n" # let's not show anything yet
|
|
140
|
+
pass
|
|
140
141
|
elif isinstance(part, UserPromptPart):
|
|
141
142
|
acc += f"**>** {part.content}\n\n"
|
|
142
143
|
return acc
|
|
@@ -152,7 +153,7 @@ class AgentResponseWidget(Widget):
|
|
|
152
153
|
if self.item is None:
|
|
153
154
|
yield Markdown(markdown="")
|
|
154
155
|
else:
|
|
155
|
-
yield Markdown(markdown=
|
|
156
|
+
yield Markdown(markdown=self.compute_output())
|
|
156
157
|
|
|
157
158
|
def compute_output(self) -> str:
|
|
158
159
|
acc = ""
|
|
@@ -160,10 +161,10 @@ class AgentResponseWidget(Widget):
|
|
|
160
161
|
return ""
|
|
161
162
|
for idx, part in enumerate(self.item.parts):
|
|
162
163
|
if isinstance(part, TextPart):
|
|
163
|
-
acc += f"{part.content}\n\n"
|
|
164
|
+
acc += f"**⏺** {part.content}\n\n"
|
|
164
165
|
elif isinstance(part, ToolCallPart):
|
|
165
166
|
parts_str = self._format_tool_call_part(part)
|
|
166
|
-
acc +=
|
|
167
|
+
acc += parts_str + "\n\n"
|
|
167
168
|
elif isinstance(part, BuiltinToolCallPart):
|
|
168
169
|
acc += f"{part.tool_name}({part.args})\n\n"
|
|
169
170
|
elif isinstance(part, BuiltinToolReturnPart):
|
|
@@ -288,7 +288,12 @@ def perform_update(force: bool = False) -> tuple[bool, str]:
|
|
|
288
288
|
|
|
289
289
|
# Verify actual installation by checking version
|
|
290
290
|
update_successful = False
|
|
291
|
-
|
|
291
|
+
|
|
292
|
+
# For pipx with return code 0, trust it succeeded
|
|
293
|
+
if method == "pipx" and result.returncode == 0:
|
|
294
|
+
update_successful = True
|
|
295
|
+
logger.debug("Pipx returned 0, trusting update succeeded")
|
|
296
|
+
elif result.returncode == 0 or pipx_success:
|
|
292
297
|
# Give the system a moment to update the package metadata
|
|
293
298
|
import time
|
|
294
299
|
|
|
@@ -360,8 +365,9 @@ def perform_update(force: bool = False) -> tuple[bool, str]:
|
|
|
360
365
|
)
|
|
361
366
|
except Exception as e:
|
|
362
367
|
logger.debug(f"Version verification failed: {e}")
|
|
363
|
-
# If verification fails
|
|
364
|
-
|
|
368
|
+
# If verification fails but initial command succeeded, trust it
|
|
369
|
+
if not update_successful:
|
|
370
|
+
update_successful = result.returncode == 0 or pipx_success
|
|
365
371
|
|
|
366
372
|
if update_successful:
|
|
367
373
|
message = f"Successfully updated from {current_version} to {latest}"
|
|
@@ -374,7 +380,13 @@ def perform_update(force: bool = False) -> tuple[bool, str]:
|
|
|
374
380
|
|
|
375
381
|
return True, message
|
|
376
382
|
else:
|
|
377
|
-
|
|
383
|
+
# Only use stderr for error message, stdout often contains normal progress
|
|
384
|
+
if result.stderr:
|
|
385
|
+
error_msg = f"Update failed: {result.stderr}"
|
|
386
|
+
elif result.returncode != 0:
|
|
387
|
+
error_msg = f"Update failed with exit code {result.returncode}: {result.stdout or 'No output'}"
|
|
388
|
+
else:
|
|
389
|
+
error_msg = "Update verification failed but command may have succeeded"
|
|
378
390
|
logger.error(error_msg)
|
|
379
391
|
return False, error_msg
|
|
380
392
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/codebase_understanding.j2
RENAMED
|
File without changes
|
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/content_formatting.j2
RENAMED
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/agents/partials/interactive_mode.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/enhanced_query_context.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/codebase/partials/temporal_context.j2
RENAMED
|
File without changes
|
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/prompts/history/incremental_summarization.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{shotgun_sh-0.1.4 → shotgun_sh-0.1.6}/src/shotgun/tui/screens/chat_screen/command_providers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|