shotgun-sh 0.1.0.dev22__py3-none-any.whl → 0.1.0.dev24__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.
Potentially problematic release.
This version of shotgun-sh might be problematic. Click here for more details.
- shotgun/agents/agent_manager.py +95 -15
- shotgun/agents/common.py +143 -25
- shotgun/agents/conversation_history.py +56 -0
- shotgun/agents/conversation_manager.py +105 -0
- shotgun/agents/export.py +5 -2
- shotgun/agents/models.py +16 -7
- shotgun/agents/plan.py +2 -1
- shotgun/agents/research.py +2 -1
- shotgun/agents/specify.py +2 -1
- shotgun/agents/tasks.py +5 -2
- shotgun/agents/tools/file_management.py +67 -2
- shotgun/codebase/core/ingestor.py +1 -1
- shotgun/codebase/core/manager.py +106 -4
- shotgun/codebase/models.py +4 -0
- shotgun/codebase/service.py +60 -2
- shotgun/main.py +9 -1
- shotgun/prompts/agents/export.j2 +14 -11
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +6 -9
- shotgun/prompts/agents/plan.j2 +9 -13
- shotgun/prompts/agents/research.j2 +11 -14
- shotgun/prompts/agents/specify.j2 +9 -12
- shotgun/prompts/agents/state/system_state.j2 +27 -5
- shotgun/prompts/agents/tasks.j2 +12 -12
- shotgun/sdk/codebase.py +26 -2
- shotgun/sdk/services.py +0 -14
- shotgun/tui/app.py +9 -4
- shotgun/tui/screens/chat.py +80 -19
- shotgun/tui/screens/chat_screen/command_providers.py +1 -1
- shotgun/tui/screens/chat_screen/history.py +6 -0
- shotgun/tui/utils/mode_progress.py +111 -78
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/METADATA +8 -9
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/RECORD +35 -54
- shotgun/agents/artifact_state.py +0 -58
- shotgun/agents/tools/artifact_management.py +0 -481
- shotgun/artifacts/__init__.py +0 -17
- shotgun/artifacts/exceptions.py +0 -89
- shotgun/artifacts/manager.py +0 -530
- shotgun/artifacts/models.py +0 -334
- shotgun/artifacts/service.py +0 -463
- shotgun/artifacts/templates/__init__.py +0 -10
- shotgun/artifacts/templates/loader.py +0 -252
- shotgun/artifacts/templates/models.py +0 -136
- shotgun/artifacts/templates/plan/delivery_and_release_plan.yaml +0 -66
- shotgun/artifacts/templates/research/market_research.yaml +0 -585
- shotgun/artifacts/templates/research/sdk_comparison.yaml +0 -257
- shotgun/artifacts/templates/specify/prd.yaml +0 -331
- shotgun/artifacts/templates/specify/product_spec.yaml +0 -301
- shotgun/artifacts/utils.py +0 -76
- shotgun/prompts/agents/partials/artifact_system.j2 +0 -32
- shotgun/prompts/agents/state/artifact_templates_available.j2 +0 -20
- shotgun/prompts/agents/state/existing_artifacts_available.j2 +0 -25
- shotgun/sdk/artifact_models.py +0 -186
- shotgun/sdk/artifacts.py +0 -448
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.1.0.dev22.dist-info → shotgun_sh-0.1.0.dev24.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,24 +1,19 @@
|
|
|
1
1
|
You are an experienced Research Assistant.
|
|
2
2
|
|
|
3
|
-
Your job is to help the user research various subjects related to their software project.
|
|
3
|
+
Your job is to help the user research various subjects related to their software project and keep the research.md file up to date.
|
|
4
4
|
|
|
5
5
|
{% include 'agents/partials/common_agent_system_prompt.j2' %}
|
|
6
6
|
|
|
7
7
|
## RESEARCH WORKFLOW
|
|
8
8
|
|
|
9
9
|
For research tasks:
|
|
10
|
-
1. **
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
7. **Search strategically**: Use web search to fill knowledge gaps (max 3 searches per session)
|
|
18
|
-
8. **Synthesize findings**: Combine existing and new information
|
|
19
|
-
9. **Create structured artifacts**: Use `write_artifact_section()` to organize findings
|
|
20
|
-
|
|
21
|
-
Use meaningful artifact IDs like: "api-design-patterns", "microservices-study", "database-comparison"
|
|
10
|
+
1. **Load previous research**: ALWAYS first use `read_file("research.md")` to see what research already exists (if the file exists)
|
|
11
|
+
2. **Analyze existing work**: Understand what has been researched previously
|
|
12
|
+
3. **Identify gaps**: Determine what additional research is needed
|
|
13
|
+
4. DO NOT ASSUME YOU KNOW THE ANSWER TO THE QUERY. ALWAYS START WITH GENERIC SEARCHES FIRST, NOT USING NAMES OF THINGS SUGGESTIVE OF THE ANSWER.
|
|
14
|
+
5. **Search strategically**: Use web search to fill knowledge gaps (max 3 searches per session)
|
|
15
|
+
6. **Synthesize findings**: Combine existing and new information
|
|
16
|
+
7. **Update research.md**: Use `write_file("research.md", content)` to save comprehensive, organized research
|
|
22
17
|
|
|
23
18
|
## RESEARCH PRINCIPLES
|
|
24
19
|
|
|
@@ -31,11 +26,13 @@ Use meaningful artifact IDs like: "api-design-patterns", "microservices-study",
|
|
|
31
26
|
- Validate information from multiple sources
|
|
32
27
|
- Document assumptions and limitations
|
|
33
28
|
- Avoid analysis paralysis - be decisive with available information
|
|
34
|
-
- Multi-Source Research -
|
|
29
|
+
- Multi-Source Research - Cross-reference multiple sources for accuracy and completeness
|
|
35
30
|
- Critical Analysis - Evaluate trade-offs, limitations, and real-world applicability
|
|
36
31
|
- Actionable Insights - Provide concrete recommendations and next steps when you're done
|
|
37
32
|
- Comprehensive Citations - Include detailed source citations for verification
|
|
38
33
|
- AVOID combining multiple topics into single search query. In fact, try similar queries across different providers/tools.
|
|
34
|
+
- Keep research.md as the single source of truth
|
|
35
|
+
- Organize findings by topic/category for easy reference
|
|
39
36
|
|
|
40
37
|
## Response Standards
|
|
41
38
|
Your responses should always be:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
You are an experienced Specification Analyst.
|
|
2
2
|
|
|
3
|
-
Your job is to help the user create comprehensive software specifications for their projects.
|
|
3
|
+
Your job is to help the user create comprehensive software specifications for their projects and maintain the specification.md file.
|
|
4
4
|
|
|
5
5
|
Transform requirements into detailed, actionable specifications that development teams can implement.
|
|
6
6
|
|
|
@@ -9,16 +9,11 @@ Transform requirements into detailed, actionable specifications that development
|
|
|
9
9
|
## SPECIFICATION WORKFLOW
|
|
10
10
|
|
|
11
11
|
For specification tasks:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
4. **Analyze requirements**: Understand the functional and non-functional requirements
|
|
18
|
-
5. **Define specifications**: Create detailed technical and functional specifications
|
|
19
|
-
6. **Structure documentation**: Use `write_artifact_section()` to organize specifications
|
|
20
|
-
|
|
21
|
-
Use meaningful artifact IDs like: "user-auth-spec", "api-gateway-spec", "data-model-spec"
|
|
12
|
+
1. **Load existing specifications**: ALWAYS first use `read_file("specification.md")` to see what specifications already exist (if the file exists)
|
|
13
|
+
2. **Check research**: Read `research.md` if it exists to understand technical context and findings
|
|
14
|
+
3. **Analyze requirements**: Understand the functional and non-functional requirements
|
|
15
|
+
4. **Define specifications**: Create detailed technical and functional specifications
|
|
16
|
+
5. **Structure documentation**: Use `write_file("specification.md", content)` to save comprehensive specifications
|
|
22
17
|
|
|
23
18
|
## SPECIFICATION PRINCIPLES
|
|
24
19
|
|
|
@@ -28,4 +23,6 @@ Use meaningful artifact IDs like: "user-auth-spec", "api-gateway-spec", "data-mo
|
|
|
28
23
|
- **Traceability**: Link specifications back to original requirements and business needs
|
|
29
24
|
- **Testability**: Define clear acceptance criteria and testing approaches
|
|
30
25
|
- **Maintainability**: Structure specifications for easy updates and modifications
|
|
31
|
-
- **Stakeholder Focus**: Consider different audiences (developers, testers, business users)
|
|
26
|
+
- **Stakeholder Focus**: Consider different audiences (developers, testers, business users)
|
|
27
|
+
- Keep specification.md as the single source of truth
|
|
28
|
+
- Organize specifications by features, components, or user stories
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
{% if current_date %}
|
|
2
1
|
## System Status
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
{% include 'agents/state/codebase/codebase_graphs_available.j2' %}
|
|
4
|
+
|
|
5
|
+
## Available Files
|
|
5
6
|
|
|
7
|
+
{% if existing_files %}
|
|
8
|
+
The following files already exist in the .shotgun/ directory.
|
|
9
|
+
Before doing a web search check the information information in these files before continuing.
|
|
10
|
+
Your working files are:
|
|
11
|
+
{% for file in existing_files %}
|
|
12
|
+
- `{{ file }}`
|
|
13
|
+
{% endfor %}
|
|
14
|
+
{% else %}
|
|
15
|
+
No files currently exist in your allowed directories. You can create:
|
|
16
|
+
- `research.md` - Research findings and analysis
|
|
17
|
+
- `plan.md` - Project plans and roadmaps
|
|
18
|
+
- `tasks.md` - Task lists and management
|
|
19
|
+
- `specification.md` - Technical specifications
|
|
20
|
+
- `exports/` folder - For exported documents
|
|
6
21
|
{% endif %}
|
|
7
|
-
|
|
8
|
-
{%
|
|
9
|
-
|
|
22
|
+
|
|
23
|
+
{% if markdown_toc %}
|
|
24
|
+
## Document Structure
|
|
25
|
+
|
|
26
|
+
The current document contains the following sections:
|
|
27
|
+
```
|
|
28
|
+
{{ markdown_toc }}
|
|
29
|
+
```
|
|
30
|
+
Review these existing sections before adding new content to avoid duplication.
|
|
31
|
+
{% endif %}
|
shotgun/prompts/agents/tasks.j2
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
You are an experienced Project Manager and Task Coordinator.
|
|
2
2
|
|
|
3
|
-
Your job is to help create and manage actionable tasks for software projects.
|
|
3
|
+
Your job is to help create and manage actionable tasks for software projects and maintain the tasks.md file.
|
|
4
4
|
|
|
5
5
|
{% include 'agents/partials/common_agent_system_prompt.j2' %}
|
|
6
6
|
|
|
7
7
|
## TASK MANAGEMENT WORKFLOW
|
|
8
8
|
|
|
9
9
|
For task management:
|
|
10
|
-
1. **
|
|
11
|
-
2. **Review context**:
|
|
10
|
+
1. **Load existing tasks**: ALWAYS first use `read_file("tasks.md")` to see what tasks already exist (if the file exists)
|
|
11
|
+
2. **Review context**: Read `plan.md` and `specification.md` if they exist to understand project context
|
|
12
12
|
3. **Analyze requirements**: Understand the current situation and user's task requirements
|
|
13
|
-
4. **Create structured tasks**: Use `
|
|
13
|
+
4. **Create structured tasks**: Use `write_file("tasks.md", content)` to save organized tasks
|
|
14
14
|
5. **Build incrementally**: Update and refine tasks based on new information
|
|
15
15
|
|
|
16
|
-
## TASK
|
|
16
|
+
## TASK FILE STRUCTURE
|
|
17
17
|
|
|
18
|
-
Organize tasks into logical sections:
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
- **
|
|
23
|
-
|
|
24
|
-
Use meaningful artifact IDs like: "feature-development", "migration-tasks", "testing-checklist"
|
|
18
|
+
Organize tasks into logical sections in tasks.md:
|
|
19
|
+
- **Backlog** - Prioritized list of tasks to be done
|
|
20
|
+
- **In Progress** - Current active tasks and status
|
|
21
|
+
- **Done** - Completed tasks for reference
|
|
22
|
+
- **Blocked** - Tasks waiting on dependencies or decisions
|
|
25
23
|
|
|
26
24
|
## TASK CREATION PRINCIPLES
|
|
27
25
|
|
|
@@ -33,6 +31,8 @@ Use meaningful artifact IDs like: "feature-development", "migration-tasks", "tes
|
|
|
33
31
|
- Align with goals and steps from project plans
|
|
34
32
|
- Include both development and testing/validation tasks
|
|
35
33
|
- Break down complex work into manageable chunks
|
|
34
|
+
- Keep tasks.md as the single source of truth
|
|
35
|
+
- Use clear task IDs or numbers for tracking
|
|
36
36
|
|
|
37
37
|
{% if interactive_mode %}
|
|
38
38
|
USER INTERACTION - ASK CLARIFYING QUESTIONS:
|
shotgun/sdk/codebase.py
CHANGED
|
@@ -43,12 +43,30 @@ class CodebaseSDK:
|
|
|
43
43
|
graphs = await self.service.list_graphs()
|
|
44
44
|
return ListResult(graphs=graphs)
|
|
45
45
|
|
|
46
|
-
async def
|
|
46
|
+
async def list_codebases_for_directory(
|
|
47
|
+
self, directory: Path | None = None
|
|
48
|
+
) -> ListResult:
|
|
49
|
+
"""List codebases accessible from a specific directory.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
directory: Directory to filter by. If None, uses current working directory.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
ListResult containing filtered list of codebases
|
|
56
|
+
"""
|
|
57
|
+
graphs = await self.service.list_graphs_for_directory(directory)
|
|
58
|
+
return ListResult(graphs=graphs)
|
|
59
|
+
|
|
60
|
+
async def index_codebase(
|
|
61
|
+
self, path: Path, name: str, indexed_from_cwd: str | None = None
|
|
62
|
+
) -> IndexResult:
|
|
47
63
|
"""Index a new codebase.
|
|
48
64
|
|
|
49
65
|
Args:
|
|
50
66
|
path: Path to the repository to index
|
|
51
67
|
name: Human-readable name for the codebase
|
|
68
|
+
indexed_from_cwd: Working directory from which indexing was initiated.
|
|
69
|
+
If None, uses current working directory.
|
|
52
70
|
|
|
53
71
|
Returns:
|
|
54
72
|
IndexResult with indexing details
|
|
@@ -60,7 +78,13 @@ class CodebaseSDK:
|
|
|
60
78
|
if not resolved_path.exists():
|
|
61
79
|
raise InvalidPathError(f"Path does not exist: {resolved_path}")
|
|
62
80
|
|
|
63
|
-
|
|
81
|
+
# Default to current working directory if not specified
|
|
82
|
+
if indexed_from_cwd is None:
|
|
83
|
+
indexed_from_cwd = str(Path.cwd().resolve())
|
|
84
|
+
|
|
85
|
+
graph = await self.service.create_graph(
|
|
86
|
+
resolved_path, name, indexed_from_cwd=indexed_from_cwd
|
|
87
|
+
)
|
|
64
88
|
file_count = sum(graph.language_stats.values()) if graph.language_stats else 0
|
|
65
89
|
|
|
66
90
|
return IndexResult(
|
shotgun/sdk/services.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
-
from shotgun.artifacts.service import ArtifactService
|
|
6
5
|
from shotgun.codebase.service import CodebaseService
|
|
7
6
|
from shotgun.utils import get_shotgun_home
|
|
8
7
|
|
|
@@ -22,16 +21,3 @@ def get_codebase_service(storage_dir: Path | str | None = None) -> CodebaseServi
|
|
|
22
21
|
elif isinstance(storage_dir, str):
|
|
23
22
|
storage_dir = Path(storage_dir)
|
|
24
23
|
return CodebaseService(storage_dir)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def get_artifact_service(base_path: Path | None = None) -> ArtifactService:
|
|
28
|
-
"""Get ArtifactService instance with configurable base path.
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
base_path: Optional base path for artifacts.
|
|
32
|
-
Defaults to .shotgun in current directory.
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
Configured ArtifactService instance
|
|
36
|
-
"""
|
|
37
|
-
return ArtifactService(base_path)
|
shotgun/tui/app.py
CHANGED
|
@@ -29,10 +29,13 @@ class ShotgunApp(App[None]):
|
|
|
29
29
|
]
|
|
30
30
|
CSS_PATH = "styles.tcss"
|
|
31
31
|
|
|
32
|
-
def __init__(
|
|
32
|
+
def __init__(
|
|
33
|
+
self, no_update_check: bool = False, continue_session: bool = False
|
|
34
|
+
) -> None:
|
|
33
35
|
super().__init__()
|
|
34
36
|
self.config_manager: ConfigManager = get_config_manager()
|
|
35
37
|
self.no_update_check = no_update_check
|
|
38
|
+
self.continue_session = continue_session
|
|
36
39
|
self.update_notification: str | None = None
|
|
37
40
|
|
|
38
41
|
# Start async update check
|
|
@@ -77,7 +80,8 @@ class ShotgunApp(App[None]):
|
|
|
77
80
|
|
|
78
81
|
if isinstance(self.screen, ChatScreen):
|
|
79
82
|
return
|
|
80
|
-
|
|
83
|
+
# Pass continue_session flag to ChatScreen
|
|
84
|
+
self.push_screen(ChatScreen(continue_session=self.continue_session))
|
|
81
85
|
|
|
82
86
|
def check_local_shotgun_directory_exists(self) -> bool:
|
|
83
87
|
shotgun_dir = get_shotgun_base_path()
|
|
@@ -97,13 +101,14 @@ class ShotgunApp(App[None]):
|
|
|
97
101
|
return [] # we don't want any system commands
|
|
98
102
|
|
|
99
103
|
|
|
100
|
-
def run(no_update_check: bool = False) -> None:
|
|
104
|
+
def run(no_update_check: bool = False, continue_session: bool = False) -> None:
|
|
101
105
|
"""Run the TUI application.
|
|
102
106
|
|
|
103
107
|
Args:
|
|
104
108
|
no_update_check: If True, disable automatic update checks.
|
|
109
|
+
continue_session: If True, continue from previous conversation.
|
|
105
110
|
"""
|
|
106
|
-
app = ShotgunApp(no_update_check=no_update_check)
|
|
111
|
+
app = ShotgunApp(no_update_check=no_update_check, continue_session=continue_session)
|
|
107
112
|
app.run(inline_no_clear=True)
|
|
108
113
|
|
|
109
114
|
|
shotgun/tui/screens/chat.py
CHANGED
|
@@ -22,13 +22,18 @@ from textual.widgets import Button, DirectoryTree, Input, Label, Markdown, Stati
|
|
|
22
22
|
|
|
23
23
|
from shotgun.agents.agent_manager import (
|
|
24
24
|
AgentManager,
|
|
25
|
-
AgentType,
|
|
26
25
|
MessageHistoryUpdated,
|
|
27
26
|
PartialResponseMessage,
|
|
28
27
|
)
|
|
29
28
|
from shotgun.agents.config import get_provider_model
|
|
29
|
+
from shotgun.agents.conversation_history import (
|
|
30
|
+
ConversationHistory,
|
|
31
|
+
ConversationState,
|
|
32
|
+
)
|
|
33
|
+
from shotgun.agents.conversation_manager import ConversationManager
|
|
30
34
|
from shotgun.agents.models import (
|
|
31
35
|
AgentDeps,
|
|
36
|
+
AgentType,
|
|
32
37
|
FileOperationTracker,
|
|
33
38
|
UserAnswer,
|
|
34
39
|
UserQuestion,
|
|
@@ -36,7 +41,7 @@ from shotgun.agents.models import (
|
|
|
36
41
|
from shotgun.codebase.core.manager import CodebaseAlreadyIndexedError
|
|
37
42
|
from shotgun.sdk.codebase import CodebaseSDK
|
|
38
43
|
from shotgun.sdk.exceptions import CodebaseNotFoundError, InvalidPathError
|
|
39
|
-
from shotgun.sdk.services import
|
|
44
|
+
from shotgun.sdk.services import get_codebase_service
|
|
40
45
|
from shotgun.tui.commands import CommandHandler
|
|
41
46
|
from shotgun.tui.screens.chat_screen.history import ChatHistory
|
|
42
47
|
|
|
@@ -323,12 +328,11 @@ class ChatScreen(Screen[None]):
|
|
|
323
328
|
indexing_job: reactive[CodebaseIndexSelection | None] = reactive(None)
|
|
324
329
|
partial_message: reactive[ModelMessage | None] = reactive(None)
|
|
325
330
|
|
|
326
|
-
def __init__(self) -> None:
|
|
331
|
+
def __init__(self, continue_session: bool = False) -> None:
|
|
327
332
|
super().__init__()
|
|
328
333
|
# Get the model configuration and services
|
|
329
334
|
model_config = get_provider_model()
|
|
330
335
|
codebase_service = get_codebase_service()
|
|
331
|
-
artifact_service = get_artifact_service()
|
|
332
336
|
self.codebase_sdk = CodebaseSDK()
|
|
333
337
|
|
|
334
338
|
# Create shared deps without system_prompt_fn (agents provide their own)
|
|
@@ -343,17 +347,23 @@ class ChatScreen(Screen[None]):
|
|
|
343
347
|
is_tui_context=True,
|
|
344
348
|
llm_model=model_config,
|
|
345
349
|
codebase_service=codebase_service,
|
|
346
|
-
artifact_service=artifact_service,
|
|
347
350
|
system_prompt_fn=_placeholder_system_prompt_fn,
|
|
348
351
|
)
|
|
349
352
|
self.agent_manager = AgentManager(deps=self.deps, initial_type=self.mode)
|
|
350
353
|
self.command_handler = CommandHandler()
|
|
351
354
|
self.placeholder_hints = PlaceholderHints()
|
|
355
|
+
self.conversation_manager = ConversationManager()
|
|
356
|
+
self.continue_session = continue_session
|
|
352
357
|
|
|
353
358
|
def on_mount(self) -> None:
|
|
354
359
|
self.query_one(PromptInput).focus(scroll_visible=True)
|
|
355
360
|
# Hide spinner initially
|
|
356
361
|
self.query_one("#spinner").display = False
|
|
362
|
+
|
|
363
|
+
# Load conversation history if --continue flag was provided
|
|
364
|
+
if self.continue_session and self.conversation_manager.exists():
|
|
365
|
+
self._load_conversation()
|
|
366
|
+
|
|
357
367
|
self.call_later(self.check_if_codebase_is_indexed)
|
|
358
368
|
# Start the question listener worker to handle ask_user interactions
|
|
359
369
|
self.call_later(self.add_question_listener)
|
|
@@ -368,16 +378,11 @@ class ChatScreen(Screen[None]):
|
|
|
368
378
|
if is_empty:
|
|
369
379
|
return
|
|
370
380
|
|
|
371
|
-
#
|
|
372
|
-
|
|
373
|
-
(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if cur_dir.is_relative_to(Path(dir.repo_path).resolve())
|
|
377
|
-
),
|
|
378
|
-
None,
|
|
379
|
-
)
|
|
380
|
-
if directory_indexed:
|
|
381
|
+
# Check if the current directory has any accessible codebases
|
|
382
|
+
accessible_graphs = (
|
|
383
|
+
await self.codebase_sdk.list_codebases_for_directory()
|
|
384
|
+
).graphs
|
|
385
|
+
if accessible_graphs:
|
|
381
386
|
self.mount_hint(help_text_with_codebase())
|
|
382
387
|
return
|
|
383
388
|
|
|
@@ -478,6 +483,10 @@ class ChatScreen(Screen[None]):
|
|
|
478
483
|
if not chat_history.vertical_tail:
|
|
479
484
|
return
|
|
480
485
|
chat_history.vertical_tail.mount(Markdown(markdown))
|
|
486
|
+
# Scroll to bottom after mounting hint
|
|
487
|
+
chat_history.vertical_tail.call_after_refresh(
|
|
488
|
+
chat_history.vertical_tail.scroll_end, animate=False
|
|
489
|
+
)
|
|
481
490
|
|
|
482
491
|
@on(PartialResponseMessage)
|
|
483
492
|
def handle_partial_response(self, event: PartialResponseMessage) -> None:
|
|
@@ -590,9 +599,7 @@ class ChatScreen(Screen[None]):
|
|
|
590
599
|
Returns:
|
|
591
600
|
Dynamic placeholder hint based on mode and progress.
|
|
592
601
|
"""
|
|
593
|
-
return self.placeholder_hints.get_placeholder_for_mode(
|
|
594
|
-
mode, force_new=force_new
|
|
595
|
-
)
|
|
602
|
+
return self.placeholder_hints.get_placeholder_for_mode(mode)
|
|
596
603
|
|
|
597
604
|
def index_codebase_command(self) -> None:
|
|
598
605
|
start_path = Path.cwd()
|
|
@@ -639,8 +646,18 @@ class ChatScreen(Screen[None]):
|
|
|
639
646
|
)
|
|
640
647
|
label.refresh()
|
|
641
648
|
try:
|
|
649
|
+
# Pass the current working directory as the indexed_from_cwd
|
|
650
|
+
logger.debug(
|
|
651
|
+
f"Starting indexing - repo_path: {selection.repo_path}, "
|
|
652
|
+
f"name: {selection.name}, cwd: {Path.cwd().resolve()}"
|
|
653
|
+
)
|
|
642
654
|
result = await self.codebase_sdk.index_codebase(
|
|
643
|
-
selection.repo_path,
|
|
655
|
+
selection.repo_path,
|
|
656
|
+
selection.name,
|
|
657
|
+
indexed_from_cwd=str(Path.cwd().resolve()),
|
|
658
|
+
)
|
|
659
|
+
logger.info(
|
|
660
|
+
f"Successfully indexed codebase '{result.name}' (ID: {result.graph_id})"
|
|
644
661
|
)
|
|
645
662
|
self.notify(
|
|
646
663
|
f"Indexed codebase '{result.name}' (ID: {result.graph_id})",
|
|
@@ -650,12 +667,19 @@ class ChatScreen(Screen[None]):
|
|
|
650
667
|
|
|
651
668
|
self.mount_hint(codebase_indexed_hint(selection.name))
|
|
652
669
|
except CodebaseAlreadyIndexedError as exc:
|
|
670
|
+
logger.warning(f"Codebase already indexed: {exc}")
|
|
653
671
|
self.notify(str(exc), severity="warning")
|
|
654
672
|
return
|
|
655
673
|
except InvalidPathError as exc:
|
|
674
|
+
logger.error(f"Invalid path error: {exc}")
|
|
656
675
|
self.notify(str(exc), severity="error")
|
|
657
676
|
|
|
658
677
|
except Exception as exc: # pragma: no cover - defensive UI path
|
|
678
|
+
# Log full exception details with stack trace
|
|
679
|
+
logger.exception(
|
|
680
|
+
f"Failed to index codebase - repo_path: {selection.repo_path}, "
|
|
681
|
+
f"name: {selection.name}, error: {exc}"
|
|
682
|
+
)
|
|
659
683
|
self.notify(f"Failed to index codebase: {exc}", severity="error")
|
|
660
684
|
finally:
|
|
661
685
|
label.update("")
|
|
@@ -690,9 +714,46 @@ class ChatScreen(Screen[None]):
|
|
|
690
714
|
)
|
|
691
715
|
self.working = False
|
|
692
716
|
|
|
717
|
+
# Save conversation after each interaction
|
|
718
|
+
self._save_conversation()
|
|
719
|
+
|
|
693
720
|
prompt_input = self.query_one(PromptInput)
|
|
694
721
|
prompt_input.focus()
|
|
695
722
|
|
|
723
|
+
def _save_conversation(self) -> None:
|
|
724
|
+
"""Save the current conversation to persistent storage."""
|
|
725
|
+
# Get conversation state from agent manager
|
|
726
|
+
state = self.agent_manager.get_conversation_state()
|
|
727
|
+
|
|
728
|
+
# Create conversation history object
|
|
729
|
+
conversation = ConversationHistory(
|
|
730
|
+
last_agent_model=state.agent_type,
|
|
731
|
+
)
|
|
732
|
+
conversation.set_agent_messages(state.agent_messages)
|
|
733
|
+
|
|
734
|
+
# Save to file
|
|
735
|
+
self.conversation_manager.save(conversation)
|
|
736
|
+
|
|
737
|
+
def _load_conversation(self) -> None:
|
|
738
|
+
"""Load conversation from persistent storage."""
|
|
739
|
+
conversation = self.conversation_manager.load()
|
|
740
|
+
if conversation is None:
|
|
741
|
+
return
|
|
742
|
+
|
|
743
|
+
# Restore agent state
|
|
744
|
+
agent_messages = conversation.get_agent_messages()
|
|
745
|
+
|
|
746
|
+
# Create ConversationState for restoration
|
|
747
|
+
state = ConversationState(
|
|
748
|
+
agent_messages=agent_messages,
|
|
749
|
+
agent_type=conversation.last_agent_model,
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
self.agent_manager.restore_conversation_state(state)
|
|
753
|
+
|
|
754
|
+
# Update the current mode
|
|
755
|
+
self.mode = AgentType(conversation.last_agent_model)
|
|
756
|
+
|
|
696
757
|
|
|
697
758
|
def codebase_indexed_hint(codebase_name: str) -> str:
|
|
698
759
|
return (
|
|
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, cast
|
|
|
3
3
|
|
|
4
4
|
from textual.command import DiscoveryHit, Hit, Provider
|
|
5
5
|
|
|
6
|
-
from shotgun.agents.
|
|
6
|
+
from shotgun.agents.models import AgentType
|
|
7
7
|
from shotgun.codebase.models import CodebaseGraph
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
@@ -187,6 +187,12 @@ class AgentResponseWidget(Widget):
|
|
|
187
187
|
def _format_tool_call_part(self, part: ToolCallPart) -> str:
|
|
188
188
|
if part.tool_name == "ask_user":
|
|
189
189
|
return self._format_ask_user_part(part)
|
|
190
|
+
# write_file
|
|
191
|
+
if part.tool_name == "write_file" or part.tool_name == "append_file":
|
|
192
|
+
if isinstance(part.args, dict) and "filename" in part.args:
|
|
193
|
+
return f"{part.tool_name}({part.args['filename']})"
|
|
194
|
+
else:
|
|
195
|
+
return f"{part.tool_name}()"
|
|
190
196
|
if part.tool_name == "write_artifact_section":
|
|
191
197
|
if isinstance(part.args, dict) and "section_title" in part.args:
|
|
192
198
|
return f"{part.tool_name}({part.args['section_title']})"
|