shotgun-sh 0.1.0.dev25__py3-none-any.whl → 0.1.0.dev26__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 +24 -8
- shotgun/agents/common.py +112 -27
- shotgun/agents/conversation_history.py +53 -3
- shotgun/agents/models.py +17 -0
- shotgun/prompts/agents/export.j2 +128 -20
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +1 -1
- shotgun/prompts/agents/plan.j2 +102 -31
- shotgun/prompts/agents/state/system_state.j2 +5 -3
- shotgun/tui/screens/chat.py +10 -14
- shotgun/tui/screens/chat_screen/hint_message.py +40 -0
- shotgun/tui/screens/chat_screen/history.py +6 -2
- {shotgun_sh-0.1.0.dev25.dist-info → shotgun_sh-0.1.0.dev26.dist-info}/METADATA +1 -1
- {shotgun_sh-0.1.0.dev25.dist-info → shotgun_sh-0.1.0.dev26.dist-info}/RECORD +16 -15
- {shotgun_sh-0.1.0.dev25.dist-info → shotgun_sh-0.1.0.dev26.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.1.0.dev25.dist-info → shotgun_sh-0.1.0.dev26.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.1.0.dev25.dist-info → shotgun_sh-0.1.0.dev26.dist-info}/licenses/LICENSE +0 -0
shotgun/agents/agent_manager.py
CHANGED
|
@@ -37,6 +37,7 @@ from textual.widget import Widget
|
|
|
37
37
|
|
|
38
38
|
from shotgun.agents.common import add_system_prompt_message, add_system_status_message
|
|
39
39
|
from shotgun.agents.models import AgentType, FileOperation
|
|
40
|
+
from shotgun.tui.screens.chat_screen.hint_message import HintMessage
|
|
40
41
|
|
|
41
42
|
from .export import create_export_agent
|
|
42
43
|
from .history.compaction import apply_persistent_compaction
|
|
@@ -54,7 +55,7 @@ class MessageHistoryUpdated(Message):
|
|
|
54
55
|
|
|
55
56
|
def __init__(
|
|
56
57
|
self,
|
|
57
|
-
messages: list[ModelMessage],
|
|
58
|
+
messages: list[ModelMessage | HintMessage],
|
|
58
59
|
agent_type: AgentType,
|
|
59
60
|
file_operations: list[FileOperation] | None = None,
|
|
60
61
|
) -> None:
|
|
@@ -143,7 +144,7 @@ class AgentManager(Widget):
|
|
|
143
144
|
self._current_agent_type: AgentType = initial_type
|
|
144
145
|
|
|
145
146
|
# Maintain shared message history
|
|
146
|
-
self.ui_message_history: list[ModelMessage] = []
|
|
147
|
+
self.ui_message_history: list[ModelMessage | HintMessage] = []
|
|
147
148
|
self.message_history: list[ModelMessage] = []
|
|
148
149
|
self.recently_change_files: list[FileOperation] = []
|
|
149
150
|
self._stream_state: _PartialStreamState | None = None
|
|
@@ -461,8 +462,8 @@ class AgentManager(Widget):
|
|
|
461
462
|
)
|
|
462
463
|
|
|
463
464
|
def _filter_system_prompts(
|
|
464
|
-
self, messages: list[ModelMessage]
|
|
465
|
-
) -> list[ModelMessage]:
|
|
465
|
+
self, messages: list[ModelMessage | HintMessage]
|
|
466
|
+
) -> list[ModelMessage | HintMessage]:
|
|
466
467
|
"""Filter out system prompts from messages for UI display.
|
|
467
468
|
|
|
468
469
|
Args:
|
|
@@ -473,8 +474,12 @@ class AgentManager(Widget):
|
|
|
473
474
|
"""
|
|
474
475
|
from pydantic_ai.messages import SystemPromptPart
|
|
475
476
|
|
|
476
|
-
filtered_messages: list[ModelMessage] = []
|
|
477
|
+
filtered_messages: list[ModelMessage | HintMessage] = []
|
|
477
478
|
for msg in messages:
|
|
479
|
+
if isinstance(msg, HintMessage):
|
|
480
|
+
filtered_messages.append(msg)
|
|
481
|
+
continue
|
|
482
|
+
|
|
478
483
|
parts: Sequence[ModelRequestPart] | Sequence[ModelResponsePart] | None = (
|
|
479
484
|
msg.parts if hasattr(msg, "parts") else None
|
|
480
485
|
)
|
|
@@ -514,6 +519,7 @@ class AgentManager(Widget):
|
|
|
514
519
|
|
|
515
520
|
return ConversationState(
|
|
516
521
|
agent_messages=self.message_history.copy(),
|
|
522
|
+
ui_messages=self.ui_message_history.copy(),
|
|
517
523
|
agent_type=self._current_agent_type.value,
|
|
518
524
|
)
|
|
519
525
|
|
|
@@ -524,10 +530,16 @@ class AgentManager(Widget):
|
|
|
524
530
|
state: ConversationState object to restore
|
|
525
531
|
"""
|
|
526
532
|
# Restore message history for agents (includes system prompts)
|
|
527
|
-
|
|
533
|
+
non_hint_messages = [
|
|
534
|
+
msg for msg in state.agent_messages if not isinstance(msg, HintMessage)
|
|
535
|
+
]
|
|
536
|
+
self.message_history = non_hint_messages
|
|
528
537
|
|
|
529
|
-
# Filter out system prompts for UI display
|
|
530
|
-
|
|
538
|
+
# Filter out system prompts for UI display while keeping hints
|
|
539
|
+
ui_source = state.ui_messages or cast(
|
|
540
|
+
list[ModelMessage | HintMessage], state.agent_messages
|
|
541
|
+
)
|
|
542
|
+
self.ui_message_history = self._filter_system_prompts(ui_source)
|
|
531
543
|
|
|
532
544
|
# Restore agent type
|
|
533
545
|
self._current_agent_type = AgentType(state.agent_type)
|
|
@@ -535,6 +547,10 @@ class AgentManager(Widget):
|
|
|
535
547
|
# Notify listeners about the restored messages
|
|
536
548
|
self._post_messages_updated()
|
|
537
549
|
|
|
550
|
+
def add_hint_message(self, message: HintMessage) -> None:
|
|
551
|
+
self.ui_message_history.append(message)
|
|
552
|
+
self._post_messages_updated()
|
|
553
|
+
|
|
538
554
|
|
|
539
555
|
# Re-export AgentType for backward compatibility
|
|
540
556
|
__all__ = [
|
shotgun/agents/common.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
from collections.abc import Callable
|
|
5
|
+
from pathlib import Path
|
|
5
6
|
from typing import Any
|
|
6
7
|
|
|
7
8
|
from pydantic_ai import (
|
|
@@ -28,7 +29,7 @@ from shotgun.utils.file_system_utils import get_shotgun_base_path
|
|
|
28
29
|
|
|
29
30
|
from .history import token_limit_compactor
|
|
30
31
|
from .history.compaction import apply_persistent_compaction
|
|
31
|
-
from .models import AgentDeps, AgentRuntimeOptions
|
|
32
|
+
from .models import AgentDeps, AgentRuntimeOptions, PipelineConfigEntry
|
|
32
33
|
from .tools import (
|
|
33
34
|
append_file,
|
|
34
35
|
ask_user,
|
|
@@ -197,33 +198,24 @@ def create_base_agent(
|
|
|
197
198
|
return agent, deps
|
|
198
199
|
|
|
199
200
|
|
|
200
|
-
def
|
|
201
|
-
|
|
201
|
+
def _extract_file_toc_content(
|
|
202
|
+
file_path: Path, max_depth: int | None = None, max_chars: int = 500
|
|
203
|
+
) -> str | None:
|
|
204
|
+
"""Extract TOC from a single file with depth and character limits.
|
|
202
205
|
|
|
203
206
|
Args:
|
|
204
|
-
|
|
207
|
+
file_path: Path to the markdown file
|
|
208
|
+
max_depth: Maximum heading depth (1=#, 2=##, None=all)
|
|
209
|
+
max_chars: Maximum characters for the TOC
|
|
205
210
|
|
|
206
211
|
Returns:
|
|
207
|
-
Formatted TOC string
|
|
212
|
+
Formatted TOC string or None if file doesn't exist
|
|
208
213
|
"""
|
|
209
|
-
|
|
210
|
-
if (
|
|
211
|
-
not agent_mode
|
|
212
|
-
or agent_mode == AgentType.EXPORT
|
|
213
|
-
or agent_mode not in AGENT_DIRECTORIES
|
|
214
|
-
):
|
|
215
|
-
return None
|
|
216
|
-
|
|
217
|
-
base_path = get_shotgun_base_path()
|
|
218
|
-
md_file = AGENT_DIRECTORIES[agent_mode]
|
|
219
|
-
md_path = base_path / md_file
|
|
220
|
-
|
|
221
|
-
# Check if the markdown file exists
|
|
222
|
-
if not md_path.exists():
|
|
214
|
+
if not file_path.exists():
|
|
223
215
|
return None
|
|
224
216
|
|
|
225
217
|
try:
|
|
226
|
-
content =
|
|
218
|
+
content = file_path.read_text(encoding="utf-8")
|
|
227
219
|
lines = content.split("\n")
|
|
228
220
|
|
|
229
221
|
# Extract headings
|
|
@@ -239,6 +231,10 @@ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
|
|
|
239
231
|
else:
|
|
240
232
|
break
|
|
241
233
|
|
|
234
|
+
# Skip if exceeds max_depth
|
|
235
|
+
if max_depth and level > max_depth:
|
|
236
|
+
continue
|
|
237
|
+
|
|
242
238
|
# Get the heading text (remove the # symbols and clean up)
|
|
243
239
|
heading_text = stripped[level:].strip()
|
|
244
240
|
if heading_text:
|
|
@@ -246,21 +242,110 @@ def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
|
|
|
246
242
|
indent = " " * (level - 1)
|
|
247
243
|
toc_lines.append(f"{indent}{'#' * level} {heading_text}")
|
|
248
244
|
|
|
245
|
+
# Check if we're approaching the character limit
|
|
246
|
+
current_length = sum(len(line) + 1 for line in toc_lines)
|
|
247
|
+
if current_length > max_chars:
|
|
248
|
+
# Remove the last line and add ellipsis
|
|
249
|
+
toc_lines.pop()
|
|
250
|
+
if toc_lines:
|
|
251
|
+
toc_lines.append(" ...")
|
|
252
|
+
break
|
|
253
|
+
|
|
249
254
|
if not toc_lines:
|
|
250
255
|
return None
|
|
251
256
|
|
|
252
|
-
|
|
253
|
-
toc = "\n".join(toc_lines)
|
|
254
|
-
if len(toc) > 2000:
|
|
255
|
-
toc = toc[:1997] + "..."
|
|
256
|
-
|
|
257
|
-
return toc
|
|
257
|
+
return "\n".join(toc_lines)
|
|
258
258
|
|
|
259
259
|
except Exception as e:
|
|
260
|
-
logger.debug(f"Failed to extract TOC from {
|
|
260
|
+
logger.debug(f"Failed to extract TOC from {file_path}: {e}")
|
|
261
261
|
return None
|
|
262
262
|
|
|
263
263
|
|
|
264
|
+
def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
|
|
265
|
+
"""Extract TOCs from current and prior agents' files in the pipeline.
|
|
266
|
+
|
|
267
|
+
Shows full TOC of agent's own file and high-level summaries of prior agents'
|
|
268
|
+
files to maintain context awareness while keeping context window tight.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
agent_mode: The agent mode to extract TOC for
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Formatted multi-file TOC string or None if not applicable
|
|
275
|
+
"""
|
|
276
|
+
# Skip if no mode
|
|
277
|
+
if not agent_mode:
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
# Define pipeline order and dependencies
|
|
281
|
+
pipeline_config: dict[AgentType, PipelineConfigEntry] = {
|
|
282
|
+
AgentType.RESEARCH: PipelineConfigEntry(
|
|
283
|
+
own_file="research.md",
|
|
284
|
+
prior_files=[], # First in pipeline
|
|
285
|
+
),
|
|
286
|
+
AgentType.SPECIFY: PipelineConfigEntry(
|
|
287
|
+
own_file="specification.md",
|
|
288
|
+
prior_files=["research.md"],
|
|
289
|
+
),
|
|
290
|
+
AgentType.PLAN: PipelineConfigEntry(
|
|
291
|
+
own_file="plan.md",
|
|
292
|
+
prior_files=["research.md", "specification.md"],
|
|
293
|
+
),
|
|
294
|
+
AgentType.TASKS: PipelineConfigEntry(
|
|
295
|
+
own_file="tasks.md",
|
|
296
|
+
prior_files=["research.md", "specification.md", "plan.md"],
|
|
297
|
+
),
|
|
298
|
+
AgentType.EXPORT: PipelineConfigEntry(
|
|
299
|
+
own_file=None, # Export uses directory
|
|
300
|
+
prior_files=["research.md", "specification.md", "plan.md", "tasks.md"],
|
|
301
|
+
),
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# Get configuration for current agent
|
|
305
|
+
if agent_mode not in pipeline_config:
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
config = pipeline_config[agent_mode]
|
|
309
|
+
base_path = get_shotgun_base_path()
|
|
310
|
+
toc_sections: list[str] = []
|
|
311
|
+
|
|
312
|
+
# Extract TOCs from prior files (high-level only)
|
|
313
|
+
for prior_file in config.prior_files:
|
|
314
|
+
file_path = base_path / prior_file
|
|
315
|
+
# Only show # and ## headings from prior files, max 500 chars each
|
|
316
|
+
prior_toc = _extract_file_toc_content(file_path, max_depth=2, max_chars=500)
|
|
317
|
+
if prior_toc:
|
|
318
|
+
# Add section header
|
|
319
|
+
file_label = prior_file.replace(".md", "").replace("_", " ").title()
|
|
320
|
+
toc_sections.append(
|
|
321
|
+
f"=== Prior Context: {file_label} (summary) ===\n{prior_toc}"
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# Extract TOC from own file (full detail)
|
|
325
|
+
if config.own_file:
|
|
326
|
+
own_path = base_path / config.own_file
|
|
327
|
+
own_toc = _extract_file_toc_content(own_path, max_depth=None, max_chars=2000)
|
|
328
|
+
if own_toc:
|
|
329
|
+
file_label = config.own_file.replace(".md", "").replace("_", " ").title()
|
|
330
|
+
# Put own file TOC at the beginning
|
|
331
|
+
toc_sections.insert(
|
|
332
|
+
0, f"=== Your Current Document: {file_label} ===\n{own_toc}"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Combine all sections
|
|
336
|
+
if not toc_sections:
|
|
337
|
+
return None
|
|
338
|
+
|
|
339
|
+
combined_toc = "\n\n".join(toc_sections)
|
|
340
|
+
|
|
341
|
+
# Final truncation if needed (should rarely happen with our limits)
|
|
342
|
+
max_total = 3500 # Conservative total limit
|
|
343
|
+
if len(combined_toc) > max_total:
|
|
344
|
+
combined_toc = combined_toc[: max_total - 3] + "..."
|
|
345
|
+
|
|
346
|
+
return combined_toc
|
|
347
|
+
|
|
348
|
+
|
|
264
349
|
def get_agent_existing_files(agent_mode: AgentType | None = None) -> list[str]:
|
|
265
350
|
"""Get list of existing files for the given agent mode.
|
|
266
351
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Models and utilities for persisting TUI conversation history."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, cast
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, ConfigDict, Field
|
|
7
7
|
from pydantic_ai.messages import (
|
|
@@ -10,11 +10,16 @@ from pydantic_ai.messages import (
|
|
|
10
10
|
)
|
|
11
11
|
from pydantic_core import to_jsonable_python
|
|
12
12
|
|
|
13
|
+
from shotgun.tui.screens.chat_screen.hint_message import HintMessage
|
|
14
|
+
|
|
15
|
+
SerializedMessage = dict[str, Any]
|
|
16
|
+
|
|
13
17
|
|
|
14
18
|
class ConversationState(BaseModel):
|
|
15
19
|
"""Represents the complete state of a conversation in memory."""
|
|
16
20
|
|
|
17
21
|
agent_messages: list[ModelMessage]
|
|
22
|
+
ui_messages: list[ModelMessage | HintMessage] = Field(default_factory=list)
|
|
18
23
|
agent_type: str # Will store AgentType.value
|
|
19
24
|
|
|
20
25
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
@@ -24,9 +29,12 @@ class ConversationHistory(BaseModel):
|
|
|
24
29
|
"""Persistent conversation history for TUI sessions."""
|
|
25
30
|
|
|
26
31
|
version: int = 1
|
|
27
|
-
agent_history: list[
|
|
32
|
+
agent_history: list[SerializedMessage] = Field(
|
|
33
|
+
default_factory=list
|
|
34
|
+
) # Stores serialized ModelMessage objects
|
|
35
|
+
ui_history: list[SerializedMessage] = Field(
|
|
28
36
|
default_factory=list
|
|
29
|
-
) #
|
|
37
|
+
) # Stores serialized ModelMessage and HintMessage objects
|
|
30
38
|
last_agent_model: str = "research"
|
|
31
39
|
updated_at: datetime = Field(default_factory=datetime.now)
|
|
32
40
|
|
|
@@ -43,6 +51,25 @@ class ConversationHistory(BaseModel):
|
|
|
43
51
|
messages, fallback=lambda x: str(x), exclude_none=True
|
|
44
52
|
)
|
|
45
53
|
|
|
54
|
+
def set_ui_messages(self, messages: list[ModelMessage | HintMessage]) -> None:
|
|
55
|
+
"""Set ui_history from a list of UI messages."""
|
|
56
|
+
|
|
57
|
+
def _serialize_message(
|
|
58
|
+
message: ModelMessage | HintMessage,
|
|
59
|
+
) -> Any:
|
|
60
|
+
if isinstance(message, HintMessage):
|
|
61
|
+
data = message.model_dump()
|
|
62
|
+
data["message_type"] = "hint"
|
|
63
|
+
return data
|
|
64
|
+
payload = to_jsonable_python(
|
|
65
|
+
message, fallback=lambda x: str(x), exclude_none=True
|
|
66
|
+
)
|
|
67
|
+
if isinstance(payload, dict):
|
|
68
|
+
payload.setdefault("message_type", "model")
|
|
69
|
+
return payload
|
|
70
|
+
|
|
71
|
+
self.ui_history = [_serialize_message(msg) for msg in messages]
|
|
72
|
+
|
|
46
73
|
def get_agent_messages(self) -> list[ModelMessage]:
|
|
47
74
|
"""Get agent_history as a list of ModelMessage objects.
|
|
48
75
|
|
|
@@ -54,3 +81,26 @@ class ConversationHistory(BaseModel):
|
|
|
54
81
|
|
|
55
82
|
# Deserialize from JSON format back to ModelMessage objects
|
|
56
83
|
return ModelMessagesTypeAdapter.validate_python(self.agent_history)
|
|
84
|
+
|
|
85
|
+
def get_ui_messages(self) -> list[ModelMessage | HintMessage]:
|
|
86
|
+
"""Get ui_history as a list of Model or hint messages."""
|
|
87
|
+
|
|
88
|
+
if not self.ui_history:
|
|
89
|
+
# Fallback for older conversation files without UI history
|
|
90
|
+
return cast(list[ModelMessage | HintMessage], self.get_agent_messages())
|
|
91
|
+
|
|
92
|
+
messages: list[ModelMessage | HintMessage] = []
|
|
93
|
+
for item in self.ui_history:
|
|
94
|
+
message_type = item.get("message_type") if isinstance(item, dict) else None
|
|
95
|
+
if message_type == "hint":
|
|
96
|
+
messages.append(HintMessage.model_validate(item))
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
# Backwards compatibility: data may not include the type marker
|
|
100
|
+
payload = item
|
|
101
|
+
if isinstance(payload, dict):
|
|
102
|
+
payload = {k: v for k, v in payload.items() if k != "message_type"}
|
|
103
|
+
deserialized = ModelMessagesTypeAdapter.validate_python([payload])
|
|
104
|
+
messages.append(deserialized[0])
|
|
105
|
+
|
|
106
|
+
return messages
|
shotgun/agents/models.py
CHANGED
|
@@ -27,6 +27,23 @@ class AgentType(StrEnum):
|
|
|
27
27
|
EXPORT = "export"
|
|
28
28
|
|
|
29
29
|
|
|
30
|
+
class PipelineConfigEntry(BaseModel):
|
|
31
|
+
"""Configuration for each agent in the pipeline.
|
|
32
|
+
|
|
33
|
+
This model defines what files an agent can write to and what
|
|
34
|
+
files from prior agents it should read for context.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
own_file: str | None = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description="The file this agent writes to (None for export agent)",
|
|
40
|
+
)
|
|
41
|
+
prior_files: list[str] = Field(
|
|
42
|
+
default_factory=list,
|
|
43
|
+
description="Files from prior agents in pipeline to read for context",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
30
47
|
class UserAnswer(BaseModel):
|
|
31
48
|
"""A answer from the user."""
|
|
32
49
|
|
shotgun/prompts/agents/export.j2
CHANGED
|
@@ -26,14 +26,31 @@ Your job is to help export project documentation and findings to various formats
|
|
|
26
26
|
|
|
27
27
|
## EXPORT WORKFLOW
|
|
28
28
|
|
|
29
|
-
For
|
|
30
|
-
1. **
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
For AGENTS.md exports:
|
|
30
|
+
1. **MANDATORY: Read ALL pipeline files**:
|
|
31
|
+
- `tasks.md` - Extract actionable tasks with inputs/outputs
|
|
32
|
+
- `specification.md` - Get primary objective and constraints
|
|
33
|
+
- `plan.md` - Convert stages to success criteria
|
|
34
|
+
- `research.md` - Extract codebase context and open questions
|
|
35
|
+
2. **Map content to template sections**:
|
|
36
|
+
- Primary Objective: From specification.md's main goal
|
|
37
|
+
- Tasks: From tasks.md, formatted as action items
|
|
38
|
+
- Research Needed: Unresolved questions from research.md
|
|
39
|
+
- Codebase Context: Technical findings from research.md
|
|
40
|
+
- Success Criteria: Plan.md stages as measurable outcomes
|
|
41
|
+
- Important Notes: Critical constraints from all files
|
|
42
|
+
- References: Documentation links from research.md
|
|
43
|
+
3. **Transform content**: Convert to action-oriented language for AI agents
|
|
44
|
+
4. **Create export**: Save as `exports/AGENTS.md` or `exports/AGENTS_[timestamp].md`
|
|
45
|
+
5. **Validate**: Ensure every section has actionable content
|
|
46
|
+
|
|
47
|
+
For other export tasks:
|
|
48
|
+
1. **Check existing files**: Read relevant source files
|
|
49
|
+
2. **Understand requirements**: Determine format and scope
|
|
50
|
+
3. **Read source content**: Load all necessary information
|
|
51
|
+
4. **Transform and format**: Convert to requested format
|
|
52
|
+
5. **Create export files**: Save in exports/ folder
|
|
53
|
+
6. **Validate output**: Verify proper formatting
|
|
37
54
|
|
|
38
55
|
## SUPPORTED EXPORT FORMATS
|
|
39
56
|
|
|
@@ -41,18 +58,109 @@ For export tasks:
|
|
|
41
58
|
- **Markdown (.md)**: Nicely formatted Markdown file with everything the user wants to export
|
|
42
59
|
- **Multiple files**: Can create multiple export files in the exports/ folder as needed
|
|
43
60
|
|
|
44
|
-
### AGENTS.md
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
### AGENTS.md - Primary Export Format for AI Agents
|
|
62
|
+
|
|
63
|
+
**CRITICAL**: The AGENTS.md file is THE primary deliverable for AI coding agents. It MUST follow this exact template:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
# [Project Name] Implementation Guide for AI Agents
|
|
67
|
+
|
|
68
|
+
## Primary Objective
|
|
69
|
+
[1-2 sentences describing WHAT needs to be built, not HOW]
|
|
70
|
+
|
|
71
|
+
## Tasks
|
|
72
|
+
|
|
73
|
+
### Task 1: [Action-oriented title]
|
|
74
|
+
**Input**: [What files/data this task needs]
|
|
75
|
+
**Output**: [What files/code this task produces]
|
|
76
|
+
**Constraints**: [Security, performance, or architectural requirements]
|
|
77
|
+
|
|
78
|
+
### Task 2: [Next action]
|
|
79
|
+
[Continue pattern...]
|
|
80
|
+
|
|
81
|
+
## Research Needed
|
|
82
|
+
- [ ] [Specific technical question to investigate]
|
|
83
|
+
- [ ] [API endpoint to understand]
|
|
84
|
+
- [ ] [Integration pattern to review]
|
|
85
|
+
|
|
86
|
+
## Codebase Context
|
|
87
|
+
- **Language**: [Primary language and version]
|
|
88
|
+
- **Key Files**: [Critical files to read first]
|
|
89
|
+
- **Patterns**: [Design patterns used in codebase]
|
|
90
|
+
- **Dependencies**: [Important libraries/frameworks]
|
|
91
|
+
|
|
92
|
+
## Success Criteria
|
|
93
|
+
- [ ] [Measurable outcome 1]
|
|
94
|
+
- [ ] [Measurable outcome 2]
|
|
95
|
+
- [ ] [Tests pass / Coverage meets X%]
|
|
96
|
+
|
|
97
|
+
## Important Notes
|
|
98
|
+
- [Critical constraint or requirement]
|
|
99
|
+
- [Security consideration]
|
|
100
|
+
- [Performance requirement]
|
|
101
|
+
|
|
102
|
+
## References
|
|
103
|
+
- [Link to API documentation]
|
|
104
|
+
- [Link to architecture diagrams]
|
|
105
|
+
- [Link to test requirements]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Export Requirements**:
|
|
109
|
+
- Extract Primary Objective from specification.md
|
|
110
|
+
- Map tasks from tasks.md into action-oriented Task sections
|
|
111
|
+
- Pull research questions from research.md that are still relevant
|
|
112
|
+
- Include codebase context from research findings
|
|
113
|
+
- Convert plan stages into Success Criteria
|
|
114
|
+
- Add implementation constraints from specifications
|
|
115
|
+
- Format ALL content for immediate AI agent execution
|
|
116
|
+
- NO educational content, NO "learn X" instructions
|
|
117
|
+
- Every section must be actionable by Claude Code
|
|
118
|
+
|
|
119
|
+
**Example of GOOD AGENTS.md Content**:
|
|
120
|
+
```markdown
|
|
121
|
+
# E-commerce API Implementation Guide for AI Agents
|
|
122
|
+
|
|
123
|
+
## Primary Objective
|
|
124
|
+
Build a REST API for product catalog management with authentication, supporting CRUD operations and search functionality.
|
|
125
|
+
|
|
126
|
+
## Tasks
|
|
127
|
+
|
|
128
|
+
### Task 1: Implement Database Models
|
|
129
|
+
**Input**: requirements.md, existing database schema
|
|
130
|
+
**Output**: models/product.py, models/user.py, migrations/
|
|
131
|
+
**Constraints**: Use SQLAlchemy ORM, maintain backward compatibility
|
|
132
|
+
|
|
133
|
+
### Task 2: Create Authentication Endpoints
|
|
134
|
+
**Input**: models/user.py, auth requirements
|
|
135
|
+
**Output**: api/auth.py, middleware/auth.py
|
|
136
|
+
**Constraints**: JWT tokens, 15-minute expiry, refresh token support
|
|
137
|
+
|
|
138
|
+
## Research Needed
|
|
139
|
+
- [ ] Check if existing auth middleware supports JWT refresh tokens
|
|
140
|
+
- [ ] Verify PostgreSQL full-text search capabilities for product search
|
|
141
|
+
- [ ] Review rate limiting implementation in current middleware
|
|
142
|
+
|
|
143
|
+
## Codebase Context
|
|
144
|
+
- **Language**: Python 3.11
|
|
145
|
+
- **Key Files**: src/api/__init__.py, src/models/base.py
|
|
146
|
+
- **Patterns**: Repository pattern, dependency injection
|
|
147
|
+
- **Dependencies**: FastAPI, SQLAlchemy, Pydantic
|
|
148
|
+
|
|
149
|
+
## Success Criteria
|
|
150
|
+
- [ ] All CRUD endpoints return correct status codes
|
|
151
|
+
- [ ] Authentication enforced on protected routes
|
|
152
|
+
- [ ] Test coverage exceeds 80%
|
|
153
|
+
- [ ] API response times under 200ms
|
|
154
|
+
|
|
155
|
+
## Important Notes
|
|
156
|
+
- Database migrations must be reversible
|
|
157
|
+
- All endpoints require input validation
|
|
158
|
+
- Search must support pagination
|
|
159
|
+
|
|
160
|
+
## References
|
|
161
|
+
- FastAPI docs: https://fastapi.tiangolo.com
|
|
162
|
+
- Project API spec: docs/openapi.yaml
|
|
163
|
+
```
|
|
56
164
|
|
|
57
165
|
|
|
58
166
|
## EXPORT PRINCIPLES
|
|
@@ -7,7 +7,7 @@ Your extensive expertise spans, among other things:
|
|
|
7
7
|
## KEY RULES
|
|
8
8
|
|
|
9
9
|
{% if interactive_mode %}
|
|
10
|
-
0. Always ask
|
|
10
|
+
0. Always ask CLARIFYING QUESTIONS if the user's request is ambiguous or lacks sufficient detail. Do not make assumptions about what the user wants.
|
|
11
11
|
{% endif %}
|
|
12
12
|
1. Above all, prefer using tools to do the work and NEVER respond with text.
|
|
13
13
|
2. IMPORTANT: Always ask for review and go ahead to move forward after using write_file().
|
shotgun/prompts/agents/plan.j2
CHANGED
|
@@ -2,72 +2,143 @@ You are an experienced Project Manager and Strategic Planner.
|
|
|
2
2
|
|
|
3
3
|
Your job is to help create comprehensive, actionable plans for software projects and maintain the plan.md file.
|
|
4
4
|
|
|
5
|
+
⚠️ **CRITICAL RULE**: If you use ANY time references (Week, Day, Month, Hour, Quarter, Year), your plan is INVALID and will be rejected. Use ONLY Stage/Step numbering based on technical dependencies.
|
|
6
|
+
|
|
5
7
|
{% include 'agents/partials/common_agent_system_prompt.j2' %}
|
|
6
8
|
|
|
7
9
|
## MEMORY MANAGEMENT PROTOCOL
|
|
8
10
|
|
|
9
11
|
- You have exclusive write access to: `plan.md`
|
|
10
|
-
-
|
|
12
|
+
- MUST READ `research.md` and `specification.md` BEFORE asking questions or creating plans
|
|
13
|
+
- Cannot write to research.md or specification.md - they are read-only context
|
|
11
14
|
- This is your persistent memory store - ALWAYS load it first
|
|
12
15
|
- Compress content regularly to stay within context limits
|
|
13
16
|
- Keep your file updated as you work - it's your memory across sessions
|
|
14
|
-
- When adding new plans, archive completed
|
|
17
|
+
- When adding new plans, archive completed stages and compress old plans
|
|
15
18
|
- Keep active plan at top, archived/completed plans compressed at bottom
|
|
16
19
|
|
|
17
20
|
## AI AGENT PIPELINE AWARENESS
|
|
18
21
|
|
|
19
22
|
**CRITICAL**: Your output will be consumed by AI coding agents (Claude Code, Cursor, Windsurf, etc.)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
|
|
23
|
+
|
|
24
|
+
**NEVER INCLUDE:**
|
|
25
|
+
- Weeks, days, hours, months, quarters, or ANY time measurements
|
|
26
|
+
- Timeline overviews, Gantt charts, or project schedules
|
|
27
|
+
- "Phase 1 (Week 1-2)" or "Day 1-2" style headers
|
|
28
|
+
- Calendar-based planning or duration estimates
|
|
29
|
+
- "8-10 weeks to complete" or similar time predictions
|
|
30
|
+
|
|
31
|
+
**ALWAYS USE:**
|
|
32
|
+
- "Stage 1", "Stage 2" or "Step 1", "Step 2" (NO time references)
|
|
33
|
+
- Prerequisites and technical dependencies
|
|
34
|
+
- High-level components and architecture
|
|
35
|
+
- Success criteria without implementation details
|
|
36
|
+
- Purpose-driven descriptions (WHY each stage exists)
|
|
37
|
+
|
|
38
|
+
**REMEMBER:**
|
|
39
|
+
- Plans should be HIGH-LEVEL architecture and approach
|
|
40
|
+
- Leave specific file paths and function names to the task agent
|
|
41
|
+
- Focus on WHAT to build, not HOW to code it
|
|
42
|
+
- AI agents execute immediately based on dependencies, not schedules
|
|
28
43
|
|
|
29
44
|
## PLANNING WORKFLOW
|
|
30
45
|
|
|
31
46
|
For PLANNING tasks:
|
|
32
47
|
1. **Load existing plan**: ALWAYS first use `read_file("plan.md")` to see what plans already exist (if the file exists)
|
|
33
|
-
2. **
|
|
34
|
-
3. **Analyze requirements**: Understand project goals and constraints from
|
|
35
|
-
4. **Define specifications**: Create detailed technical and functional plans
|
|
36
|
-
5. **
|
|
48
|
+
2. **MANDATORY: Load context files**: You MUST read `specification.md` and `research.md` (if they exist) BEFORE doing anything else, including asking questions
|
|
49
|
+
3. **Analyze requirements**: Understand project goals and constraints from the loaded documentation
|
|
50
|
+
4. **Define specifications**: Create detailed technical and functional plans based on the context
|
|
51
|
+
5. **REQUIRED: Write the plan**: You MUST use `write_file("plan.md", content)` to save the plan. NOT writing the plan means FAILURE.
|
|
52
|
+
|
|
53
|
+
**CRITICAL RULES**:
|
|
54
|
+
- You CANNOT ask questions until you have first attempted to read both `research.md` and `specification.md`
|
|
55
|
+
- You MUST write your plan to `plan.md` - not writing the file means you have failed your task
|
|
56
|
+
- ANY time reference (week, day, month) makes the plan INVALID
|
|
57
|
+
|
|
58
|
+
## PLAN FORMAT EXAMPLE
|
|
59
|
+
|
|
60
|
+
**CORRECT high-level plan format:**
|
|
61
|
+
|
|
62
|
+
```markdown
|
|
63
|
+
# API Implementation Plan
|
|
64
|
+
|
|
65
|
+
## Stage 1: Database Layer
|
|
66
|
+
### Purpose: Establish data persistence
|
|
67
|
+
### Prerequisites: None
|
|
68
|
+
### Key Components:
|
|
69
|
+
- User and session models
|
|
70
|
+
- Database schema and migrations
|
|
71
|
+
### Success Criteria:
|
|
72
|
+
- Data models support all required operations
|
|
73
|
+
- Database can be reliably initialized
|
|
74
|
+
|
|
75
|
+
## Stage 2: Business Logic
|
|
76
|
+
### Purpose: Implement core functionality
|
|
77
|
+
### Prerequisites: Database layer complete
|
|
78
|
+
### Key Components:
|
|
79
|
+
- Authentication service
|
|
80
|
+
- Validation rules
|
|
81
|
+
- Business logic layer
|
|
82
|
+
### Success Criteria:
|
|
83
|
+
- All business rules enforced
|
|
84
|
+
- Services handle edge cases properly
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**NEVER write plans like this:**
|
|
88
|
+
- "Week 1-2: Setup database"
|
|
89
|
+
- "Phase 1 (5 days): Foundation"
|
|
90
|
+
- "Timeline: 8-10 weeks"
|
|
91
|
+
- "Day 1: Initialize project"
|
|
37
92
|
|
|
38
93
|
## PLANNING PRINCIPLES
|
|
39
94
|
|
|
40
95
|
- Build on existing research and previous plans
|
|
41
|
-
- Create
|
|
42
|
-
- Break down complex objectives into
|
|
43
|
-
- Consider dependencies between
|
|
44
|
-
- Include
|
|
45
|
-
- Focus on
|
|
46
|
-
- Consider feasibility and prioritize high-impact
|
|
96
|
+
- Create clear goals that are Specific, Measurable, Achievable, and Relevant
|
|
97
|
+
- Break down complex objectives into sequential implementation stages
|
|
98
|
+
- Consider dependencies between components and potential risks
|
|
99
|
+
- Include technical requirements and success criteria
|
|
100
|
+
- Focus on high-level architecture rather than implementation details
|
|
101
|
+
- Consider feasibility and prioritize high-impact components
|
|
47
102
|
- Keep plan.md as the single source of truth
|
|
48
|
-
- Organize plans by
|
|
103
|
+
- Organize plans by implementation order, dependencies, or components
|
|
49
104
|
|
|
50
105
|
IMPORTANT RULES:
|
|
51
106
|
- Make at most 1 plan file write per request
|
|
52
107
|
- Always base plans on available specifications and research when relevant
|
|
53
|
-
-
|
|
54
|
-
-
|
|
108
|
+
- **ABSOLUTELY NO TIME REFERENCES** (weeks, days, months, hours, quarters, years)
|
|
109
|
+
- If you write "Week", "Day", "Month" or any time unit, the plan is INVALID
|
|
110
|
+
- Structure ONLY by Stages/Steps with technical dependencies
|
|
111
|
+
- Focus on high-level architecture, not specific file implementations
|
|
112
|
+
- Leave detailed tasks for the task agent to create
|
|
55
113
|
- Be concise but comprehensive
|
|
56
114
|
|
|
57
115
|
{% if interactive_mode %}
|
|
58
116
|
USER INTERACTION - REDUCE UNCERTAINTY:
|
|
59
117
|
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
-
|
|
118
|
+
- FIRST read `research.md` and `specification.md` before asking ANY questions
|
|
119
|
+
- ONLY ask clarifying questions AFTER reading the context files
|
|
120
|
+
- Questions should be about gaps not covered in research/specification
|
|
121
|
+
- Use ask_user tool to gather specific details about:
|
|
122
|
+
- Information not found in the context files
|
|
123
|
+
- Clarifications on ambiguous specifications
|
|
124
|
+
- Priorities when multiple options exist
|
|
65
125
|
- Better to ask 2-3 targeted questions than create a generic plan
|
|
66
126
|
- Confirm major changes to existing plans before proceeding
|
|
67
127
|
{% else %}
|
|
68
128
|
NON-INTERACTIVE MODE - MAKE REASONABLE ASSUMPTIONS:
|
|
69
129
|
|
|
70
130
|
- Focus on creating a practical, actionable plan
|
|
71
|
-
- Include
|
|
72
|
-
-
|
|
73
|
-
{% endif %}
|
|
131
|
+
- Include logical implementation steps and considerations
|
|
132
|
+
- Structure by dependencies and execution order, not calendar time
|
|
133
|
+
{% endif %}
|
|
134
|
+
|
|
135
|
+
## FINAL VALIDATION BEFORE WRITING
|
|
136
|
+
|
|
137
|
+
Before writing to plan.md, verify:
|
|
138
|
+
✅ NO time references (week, day, month, hour, year, quarter)
|
|
139
|
+
✅ Uses Stage/Step numbering only
|
|
140
|
+
✅ Based on technical dependencies, not schedules
|
|
141
|
+
✅ High-level architecture focus (details for task agent)
|
|
142
|
+
✅ Follows the example format shown above
|
|
143
|
+
|
|
144
|
+
If your plan contains ANY time reference, DELETE IT and rewrite using Stages.
|
|
@@ -21,11 +21,13 @@ No files currently exist in your allowed directories. You can create:
|
|
|
21
21
|
{% endif %}
|
|
22
22
|
|
|
23
23
|
{% if markdown_toc %}
|
|
24
|
-
## Document
|
|
24
|
+
## Document Table of Contents - READ THE ENTIRE FILE TO UNDERSTAND MORE
|
|
25
25
|
|
|
26
|
-
The current document contains the following sections:
|
|
27
26
|
```
|
|
28
27
|
{{ markdown_toc }}
|
|
29
28
|
```
|
|
30
|
-
|
|
29
|
+
|
|
30
|
+
**IMPORTANT**: The above shows ONLY the Table of Contents from prior stages in the pipeline. Review this context before asking questions or creating new content.
|
|
31
|
+
{% else %}
|
|
32
|
+
Review the existing documents above before adding new content to avoid duplication.
|
|
31
33
|
{% endif %}
|
shotgun/tui/screens/chat.py
CHANGED
|
@@ -43,6 +43,7 @@ from shotgun.sdk.codebase import CodebaseSDK
|
|
|
43
43
|
from shotgun.sdk.exceptions import CodebaseNotFoundError, InvalidPathError
|
|
44
44
|
from shotgun.sdk.services import get_codebase_service
|
|
45
45
|
from shotgun.tui.commands import CommandHandler
|
|
46
|
+
from shotgun.tui.screens.chat_screen.hint_message import HintMessage
|
|
46
47
|
from shotgun.tui.screens.chat_screen.history import ChatHistory
|
|
47
48
|
|
|
48
49
|
from ..components.prompt_input import PromptInput
|
|
@@ -322,7 +323,7 @@ class ChatScreen(Screen[None]):
|
|
|
322
323
|
value = reactive("")
|
|
323
324
|
mode = reactive(AgentType.RESEARCH)
|
|
324
325
|
history: PromptHistory = PromptHistory()
|
|
325
|
-
messages = reactive(list[ModelMessage]())
|
|
326
|
+
messages = reactive(list[ModelMessage | HintMessage]())
|
|
326
327
|
working = reactive(False)
|
|
327
328
|
question: reactive[UserQuestion | None] = reactive(None)
|
|
328
329
|
indexing_job: reactive[CodebaseIndexSelection | None] = reactive(None)
|
|
@@ -375,7 +376,7 @@ class ChatScreen(Screen[None]):
|
|
|
375
376
|
dir.is_dir() and dir.name in ["__pycache__", ".git", ".shotgun"]
|
|
376
377
|
for dir in cur_dir.iterdir()
|
|
377
378
|
)
|
|
378
|
-
if is_empty:
|
|
379
|
+
if is_empty or self.continue_session:
|
|
379
380
|
return
|
|
380
381
|
|
|
381
382
|
# Check if the current directory has any accessible codebases
|
|
@@ -417,7 +418,7 @@ class ChatScreen(Screen[None]):
|
|
|
417
418
|
spinner.set_classes("" if is_working else "hidden")
|
|
418
419
|
spinner.display = is_working
|
|
419
420
|
|
|
420
|
-
def watch_messages(self, messages: list[ModelMessage]) -> None:
|
|
421
|
+
def watch_messages(self, messages: list[ModelMessage | HintMessage]) -> None:
|
|
421
422
|
"""Update the chat history when messages change."""
|
|
422
423
|
if self.is_mounted:
|
|
423
424
|
chat_history = self.query_one(ChatHistory)
|
|
@@ -479,14 +480,8 @@ class ChatScreen(Screen[None]):
|
|
|
479
480
|
yield Static("", id="indexing-job-display")
|
|
480
481
|
|
|
481
482
|
def mount_hint(self, markdown: str) -> None:
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return
|
|
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
|
-
)
|
|
483
|
+
hint = HintMessage(message=markdown)
|
|
484
|
+
self.agent_manager.add_hint_message(hint)
|
|
490
485
|
|
|
491
486
|
@on(PartialResponseMessage)
|
|
492
487
|
def handle_partial_response(self, event: PartialResponseMessage) -> None:
|
|
@@ -543,9 +538,7 @@ class ChatScreen(Screen[None]):
|
|
|
543
538
|
f"📁 Modified {num_files} files in: `{path_obj.parent}`"
|
|
544
539
|
)
|
|
545
540
|
|
|
546
|
-
|
|
547
|
-
file_info_widget = Markdown(message)
|
|
548
|
-
chat_history.vertical_tail.mount(file_info_widget)
|
|
541
|
+
self.mount_hint(message)
|
|
549
542
|
|
|
550
543
|
@on(PromptInput.Submitted)
|
|
551
544
|
async def handle_submit(self, message: PromptInput.Submitted) -> None:
|
|
@@ -730,6 +723,7 @@ class ChatScreen(Screen[None]):
|
|
|
730
723
|
last_agent_model=state.agent_type,
|
|
731
724
|
)
|
|
732
725
|
conversation.set_agent_messages(state.agent_messages)
|
|
726
|
+
conversation.set_ui_messages(state.ui_messages)
|
|
733
727
|
|
|
734
728
|
# Save to file
|
|
735
729
|
self.conversation_manager.save(conversation)
|
|
@@ -742,10 +736,12 @@ class ChatScreen(Screen[None]):
|
|
|
742
736
|
|
|
743
737
|
# Restore agent state
|
|
744
738
|
agent_messages = conversation.get_agent_messages()
|
|
739
|
+
ui_messages = conversation.get_ui_messages()
|
|
745
740
|
|
|
746
741
|
# Create ConversationState for restoration
|
|
747
742
|
state = ConversationState(
|
|
748
743
|
agent_messages=agent_messages,
|
|
744
|
+
ui_messages=ui_messages,
|
|
749
745
|
agent_type=conversation.last_agent_model,
|
|
750
746
|
)
|
|
751
747
|
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from textual.app import ComposeResult
|
|
5
|
+
from textual.widget import Widget
|
|
6
|
+
from textual.widgets import Markdown
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class HintMessage(BaseModel):
|
|
10
|
+
message: str
|
|
11
|
+
kind: Literal["hint"] = "hint"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HintMessageWidget(Widget):
|
|
15
|
+
"""A message for the user generated by shotgun, not the agent.."""
|
|
16
|
+
|
|
17
|
+
DEFAULT_CSS = """
|
|
18
|
+
HintMessageWidget {
|
|
19
|
+
background: $secondary-background-darken-1;
|
|
20
|
+
height: auto;
|
|
21
|
+
margin: 1;
|
|
22
|
+
margin-left: 1;
|
|
23
|
+
padding: 1;
|
|
24
|
+
|
|
25
|
+
* > Markdown {
|
|
26
|
+
height: auto;
|
|
27
|
+
offset-x: -1;
|
|
28
|
+
padding: 0;
|
|
29
|
+
margin: 0;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, message: HintMessage) -> None:
|
|
36
|
+
super().__init__()
|
|
37
|
+
self.message = message
|
|
38
|
+
|
|
39
|
+
def compose(self) -> ComposeResult:
|
|
40
|
+
yield Markdown(markdown=f"{self.message.message}")
|
|
@@ -19,6 +19,7 @@ from textual.widget import Widget
|
|
|
19
19
|
from textual.widgets import Markdown
|
|
20
20
|
|
|
21
21
|
from shotgun.tui.components.vertical_tail import VerticalTail
|
|
22
|
+
from shotgun.tui.screens.chat_screen.hint_message import HintMessage, HintMessageWidget
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class PartialResponseWidget(Widget): # TODO: doesn't work lol
|
|
@@ -75,16 +76,19 @@ class ChatHistory(Widget):
|
|
|
75
76
|
|
|
76
77
|
def __init__(self) -> None:
|
|
77
78
|
super().__init__()
|
|
78
|
-
self.items: list[ModelMessage] = []
|
|
79
|
+
self.items: list[ModelMessage | HintMessage] = []
|
|
79
80
|
self.vertical_tail: VerticalTail | None = None
|
|
80
81
|
self.partial_response = None
|
|
81
82
|
|
|
82
83
|
def compose(self) -> ComposeResult:
|
|
83
84
|
self.vertical_tail = VerticalTail()
|
|
85
|
+
|
|
84
86
|
with self.vertical_tail:
|
|
85
87
|
for item in self.items:
|
|
86
88
|
if isinstance(item, ModelRequest):
|
|
87
89
|
yield UserQuestionWidget(item)
|
|
90
|
+
elif isinstance(item, HintMessage):
|
|
91
|
+
yield HintMessageWidget(item)
|
|
88
92
|
elif isinstance(item, ModelResponse):
|
|
89
93
|
yield AgentResponseWidget(item)
|
|
90
94
|
yield PartialResponseWidget(self.partial_response).data_bind(
|
|
@@ -94,7 +98,7 @@ class ChatHistory(Widget):
|
|
|
94
98
|
def watch_partial_response(self, _partial_response: ModelMessage | None) -> None:
|
|
95
99
|
self.call_after_refresh(self.autoscroll)
|
|
96
100
|
|
|
97
|
-
def update_messages(self, messages: list[ModelMessage]) -> None:
|
|
101
|
+
def update_messages(self, messages: list[ModelMessage | HintMessage]) -> None:
|
|
98
102
|
"""Update the displayed messages without recomposing."""
|
|
99
103
|
if not self.vertical_tail:
|
|
100
104
|
return
|
|
@@ -7,12 +7,12 @@ shotgun/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
7
7
|
shotgun/sentry_telemetry.py,sha256=3r9on0GQposn9aX6Dkb9mrfaVQl_dIZzhu9BjE838AU,2854
|
|
8
8
|
shotgun/telemetry.py,sha256=aBwCRFU97oiIK5K13OhT7yYCQUAVQyrvnoG-aX3k2ZE,3109
|
|
9
9
|
shotgun/agents/__init__.py,sha256=8Jzv1YsDuLyNPFJyckSr_qI4ehTVeDyIMDW4omsfPGc,25
|
|
10
|
-
shotgun/agents/agent_manager.py,sha256=
|
|
11
|
-
shotgun/agents/common.py,sha256=
|
|
12
|
-
shotgun/agents/conversation_history.py,sha256=
|
|
10
|
+
shotgun/agents/agent_manager.py,sha256=mIRGf5xaIvMuvUQRzPa3IqP-VlGCwVoxS1ftNgbeFPU,20943
|
|
11
|
+
shotgun/agents/common.py,sha256=C5-hpIgyeQ5n22fIiSouJkdzbiLXAtvVxo176FIyeYM,19034
|
|
12
|
+
shotgun/agents/conversation_history.py,sha256=5J8_1yxdZiiWTq22aDio88DkBDZ4_Lh_p5Iy5_ENszc,3898
|
|
13
13
|
shotgun/agents/conversation_manager.py,sha256=fxAvXbEl3Cl2ugJ4N9aWXaqZtkrnfj3QzwjWC4LFXwI,3514
|
|
14
14
|
shotgun/agents/export.py,sha256=Zke952DbJ_lOBUmN-TPHw7qmjbfqsFu1uycBRQI_pkg,2969
|
|
15
|
-
shotgun/agents/models.py,sha256=
|
|
15
|
+
shotgun/agents/models.py,sha256=fljmjHoCzNXPWOHSA-1KmQk1epk8ovowqDM7j5zZKB8,7853
|
|
16
16
|
shotgun/agents/plan.py,sha256=s-WfILBOW4l8kY59RUOVtX5MJSuSzFm1nGp6b17If78,3030
|
|
17
17
|
shotgun/agents/research.py,sha256=lYG7Rytcitop8mXs3isMI3XvYzzI3JH9u0VZz6K9zfo,3274
|
|
18
18
|
shotgun/agents/specify.py,sha256=7MoMxfIn34G27mw6wrp_F0i2O5rid476L3kHFONDCd0,3137
|
|
@@ -73,16 +73,16 @@ shotgun/codebase/core/parser_loader.py,sha256=LZRrDS8Sp518jIu3tQW-BxdwJ86lnsTteI
|
|
|
73
73
|
shotgun/prompts/__init__.py,sha256=RswUm0HMdfm2m2YKUwUsEdRIwoczdbI7zlucoEvHYRo,132
|
|
74
74
|
shotgun/prompts/loader.py,sha256=jy24-E02pCSmz2651aCT2NgHfRrHAGMYvKrD6gs0Er8,4424
|
|
75
75
|
shotgun/prompts/agents/__init__.py,sha256=YRIJMbzpArojNX1BP5gfxxois334z_GQga8T-xyWMbY,39
|
|
76
|
-
shotgun/prompts/agents/export.j2,sha256
|
|
77
|
-
shotgun/prompts/agents/plan.j2,sha256=
|
|
76
|
+
shotgun/prompts/agents/export.j2,sha256=-6lpRmIkSiDqm1G0Va_cf0JG3sJ14cQKQ8beQ0pBh94,8422
|
|
77
|
+
shotgun/prompts/agents/plan.j2,sha256=MyZDyOS21V-zrHNzbIhIdzcESGh_3KVbA4qh9rZR2_E,6086
|
|
78
78
|
shotgun/prompts/agents/research.j2,sha256=JBtjXaMVDRuNTt7-Ai8gUb2InfolfqCkQoEkn9PsQZk,3929
|
|
79
79
|
shotgun/prompts/agents/specify.j2,sha256=AP7XrA3KE7GZsCvW4guASxZHBM2mnrMw3irdZ3RUOBs,2808
|
|
80
80
|
shotgun/prompts/agents/tasks.j2,sha256=Vm6nFSgYzdwRBySUzwMzWHhfYR12Cb_xKPqif7yMuhs,4909
|
|
81
81
|
shotgun/prompts/agents/partials/codebase_understanding.j2,sha256=7WH-PVd-TRBFQUdOdKkwwn9hAUaJznFZMAGHhO7IGGU,5633
|
|
82
|
-
shotgun/prompts/agents/partials/common_agent_system_prompt.j2,sha256=
|
|
82
|
+
shotgun/prompts/agents/partials/common_agent_system_prompt.j2,sha256=eFuc3z1pSJzQtPJfjMIDNHv5XX9lP6YVrmKcbbskJj8,1877
|
|
83
83
|
shotgun/prompts/agents/partials/content_formatting.j2,sha256=MG0JB7SSp8YV5akDWpbs2f9DcdREIYqLp38NnoWLeQ0,1854
|
|
84
84
|
shotgun/prompts/agents/partials/interactive_mode.j2,sha256=9sYPbyc46HXg3k1FT_LugIQvOyNDnMQwsMIgOgN-_aY,1100
|
|
85
|
-
shotgun/prompts/agents/state/system_state.j2,sha256=
|
|
85
|
+
shotgun/prompts/agents/state/system_state.j2,sha256=XYAlf2_5PiIcJqdu-dBG4zyQBfD5h4rmjpI1fEIUW8c,1080
|
|
86
86
|
shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2,sha256=U-hy-H9bPwV0sYIHTZ5TESxc5EOCtntI8GUZOmJipJw,601
|
|
87
87
|
shotgun/prompts/codebase/__init__.py,sha256=NYuPMtmYM2ptuwf3YxVuotNlJOUq0hnjmwlzKcJkGK4,42
|
|
88
88
|
shotgun/prompts/codebase/cypher_query_patterns.j2,sha256=ufTx_xT3VoS76KcVUbIgGQx-bJoJHx3bBE3dagAXv18,8913
|
|
@@ -107,22 +107,23 @@ shotgun/tui/components/prompt_input.py,sha256=Ss-htqraHZAPaehGE4x86ij0veMjc4Ugad
|
|
|
107
107
|
shotgun/tui/components/spinner.py,sha256=ovTDeaJ6FD6chZx_Aepia6R3UkPOVJ77EKHfRmn39MY,2427
|
|
108
108
|
shotgun/tui/components/splash.py,sha256=vppy9vEIEvywuUKRXn2y11HwXSRkQZHLYoVjhDVdJeU,1267
|
|
109
109
|
shotgun/tui/components/vertical_tail.py,sha256=kkCH0WjAh54jDvRzIaOffRZXUKn_zHFZ_ichfUpgzaE,1071
|
|
110
|
-
shotgun/tui/screens/chat.py,sha256=
|
|
110
|
+
shotgun/tui/screens/chat.py,sha256=474e6W6r2QgQZvWwZoFds9Y7YmhjNukR_bXvwutHetM,27690
|
|
111
111
|
shotgun/tui/screens/chat.tcss,sha256=2Yq3E23jxsySYsgZf4G1AYrYVcpX0UDW6kNNI0tDmtM,437
|
|
112
112
|
shotgun/tui/screens/directory_setup.py,sha256=lIZ1J4A6g5Q2ZBX8epW7BhR96Dmdcg22CyiM5S-I5WU,3237
|
|
113
113
|
shotgun/tui/screens/provider_config.py,sha256=A_tvDHF5KLP5PV60LjMJ_aoOdT3TjI6_g04UIUqGPqM,7126
|
|
114
114
|
shotgun/tui/screens/splash.py,sha256=E2MsJihi3c9NY1L28o_MstDxGwrCnnV7zdq00MrGAsw,706
|
|
115
115
|
shotgun/tui/screens/chat_screen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
116
|
shotgun/tui/screens/chat_screen/command_providers.py,sha256=55JIH9T8QnyHRsMoXhOi87FiVM-d6o7OKpCe82uDP9I,7840
|
|
117
|
-
shotgun/tui/screens/chat_screen/
|
|
117
|
+
shotgun/tui/screens/chat_screen/hint_message.py,sha256=WOpbk8q7qt7eOHTyyHvh_IQIaublVDeJGaLpsxEk9FA,933
|
|
118
|
+
shotgun/tui/screens/chat_screen/history.py,sha256=PKUqj3kYk0NqQyQRoM7fTn4lsivfTOlKqm_77cO5RG4,7930
|
|
118
119
|
shotgun/tui/utils/__init__.py,sha256=cFjDfoXTRBq29wgP7TGRWUu1eFfiIG-LLOzjIGfadgI,150
|
|
119
120
|
shotgun/tui/utils/mode_progress.py,sha256=lseRRo7kMWLkBzI3cU5vqJmS2ZcCjyRYf9Zwtvc-v58,10931
|
|
120
121
|
shotgun/utils/__init__.py,sha256=WinIEp9oL2iMrWaDkXz2QX4nYVPAm8C9aBSKTeEwLtE,198
|
|
121
122
|
shotgun/utils/env_utils.py,sha256=8QK5aw_f_V2AVTleQQlcL0RnD4sPJWXlDG46fsHu0d8,1057
|
|
122
123
|
shotgun/utils/file_system_utils.py,sha256=l-0p1bEHF34OU19MahnRFdClHufThfGAjQ431teAIp0,1004
|
|
123
124
|
shotgun/utils/update_checker.py,sha256=Xf-7w3Pos3etzCoT771gJe2HLkA8_V2GrqWy7ni9UqA,11373
|
|
124
|
-
shotgun_sh-0.1.0.
|
|
125
|
-
shotgun_sh-0.1.0.
|
|
126
|
-
shotgun_sh-0.1.0.
|
|
127
|
-
shotgun_sh-0.1.0.
|
|
128
|
-
shotgun_sh-0.1.0.
|
|
125
|
+
shotgun_sh-0.1.0.dev26.dist-info/METADATA,sha256=YyEZvsV43Hgvv34CVTQU9i_CRlt9DagOoz3oy3c1WXY,11197
|
|
126
|
+
shotgun_sh-0.1.0.dev26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
127
|
+
shotgun_sh-0.1.0.dev26.dist-info/entry_points.txt,sha256=asZxLU4QILneq0MWW10saVCZc4VWhZfb0wFZvERnzfA,45
|
|
128
|
+
shotgun_sh-0.1.0.dev26.dist-info/licenses/LICENSE,sha256=YebsZl590zCHrF_acCU5pmNt0pnAfD2DmAnevJPB1tY,1065
|
|
129
|
+
shotgun_sh-0.1.0.dev26.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|