shotgun-sh 0.2.17__py3-none-any.whl → 0.4.0.dev1__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.
- shotgun/agents/agent_manager.py +219 -37
- shotgun/agents/common.py +79 -78
- shotgun/agents/config/README.md +89 -0
- shotgun/agents/config/__init__.py +10 -1
- shotgun/agents/config/manager.py +364 -53
- shotgun/agents/config/models.py +101 -21
- shotgun/agents/config/provider.py +51 -13
- shotgun/agents/config/streaming_test.py +119 -0
- shotgun/agents/context_analyzer/analyzer.py +6 -2
- shotgun/agents/conversation/__init__.py +18 -0
- shotgun/agents/conversation/filters.py +164 -0
- shotgun/agents/conversation/history/chunking.py +278 -0
- shotgun/agents/{history → conversation/history}/compaction.py +27 -1
- shotgun/agents/{history → conversation/history}/constants.py +5 -0
- shotgun/agents/conversation/history/file_content_deduplication.py +239 -0
- shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
- shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +8 -0
- shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
- shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
- shotgun/agents/error/__init__.py +11 -0
- shotgun/agents/error/models.py +19 -0
- shotgun/agents/export.py +12 -13
- shotgun/agents/models.py +66 -1
- shotgun/agents/plan.py +12 -13
- shotgun/agents/research.py +13 -10
- shotgun/agents/router/__init__.py +47 -0
- shotgun/agents/router/models.py +376 -0
- shotgun/agents/router/router.py +185 -0
- shotgun/agents/router/tools/__init__.py +18 -0
- shotgun/agents/router/tools/delegation_tools.py +503 -0
- shotgun/agents/router/tools/plan_tools.py +322 -0
- shotgun/agents/runner.py +230 -0
- shotgun/agents/specify.py +12 -13
- shotgun/agents/tasks.py +12 -13
- shotgun/agents/tools/file_management.py +49 -1
- shotgun/agents/tools/registry.py +2 -0
- shotgun/agents/tools/web_search/__init__.py +1 -2
- shotgun/agents/tools/web_search/gemini.py +1 -3
- shotgun/agents/tools/web_search/openai.py +1 -1
- shotgun/build_constants.py +2 -2
- shotgun/cli/clear.py +1 -1
- shotgun/cli/compact.py +5 -3
- shotgun/cli/context.py +44 -1
- shotgun/cli/error_handler.py +24 -0
- shotgun/cli/export.py +34 -34
- shotgun/cli/plan.py +34 -34
- shotgun/cli/research.py +17 -9
- shotgun/cli/spec/__init__.py +5 -0
- shotgun/cli/spec/backup.py +81 -0
- shotgun/cli/spec/commands.py +132 -0
- shotgun/cli/spec/models.py +48 -0
- shotgun/cli/spec/pull_service.py +219 -0
- shotgun/cli/specify.py +20 -19
- shotgun/cli/tasks.py +34 -34
- shotgun/codebase/core/change_detector.py +1 -1
- shotgun/codebase/core/ingestor.py +154 -8
- shotgun/codebase/core/manager.py +1 -1
- shotgun/codebase/models.py +2 -0
- shotgun/exceptions.py +325 -0
- shotgun/llm_proxy/__init__.py +17 -0
- shotgun/llm_proxy/client.py +215 -0
- shotgun/llm_proxy/models.py +137 -0
- shotgun/logging_config.py +42 -0
- shotgun/main.py +4 -0
- shotgun/posthog_telemetry.py +1 -1
- shotgun/prompts/agents/export.j2 +2 -0
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +23 -3
- shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
- shotgun/prompts/agents/partials/router_delegation_mode.j2 +36 -0
- shotgun/prompts/agents/plan.j2 +29 -1
- shotgun/prompts/agents/research.j2 +75 -23
- shotgun/prompts/agents/router.j2 +440 -0
- shotgun/prompts/agents/specify.j2 +80 -4
- shotgun/prompts/agents/state/system_state.j2 +15 -8
- shotgun/prompts/agents/tasks.j2 +63 -23
- shotgun/prompts/history/chunk_summarization.j2 +34 -0
- shotgun/prompts/history/combine_summaries.j2 +53 -0
- shotgun/sdk/codebase.py +14 -3
- shotgun/settings.py +5 -0
- shotgun/shotgun_web/__init__.py +67 -1
- shotgun/shotgun_web/client.py +42 -1
- shotgun/shotgun_web/constants.py +46 -0
- shotgun/shotgun_web/exceptions.py +29 -0
- shotgun/shotgun_web/models.py +390 -0
- shotgun/shotgun_web/shared_specs/__init__.py +32 -0
- shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
- shotgun/shotgun_web/shared_specs/hasher.py +83 -0
- shotgun/shotgun_web/shared_specs/models.py +71 -0
- shotgun/shotgun_web/shared_specs/upload_pipeline.py +329 -0
- shotgun/shotgun_web/shared_specs/utils.py +34 -0
- shotgun/shotgun_web/specs_client.py +703 -0
- shotgun/shotgun_web/supabase_client.py +31 -0
- shotgun/tui/app.py +78 -15
- shotgun/tui/components/mode_indicator.py +120 -25
- shotgun/tui/components/status_bar.py +2 -2
- shotgun/tui/containers.py +1 -1
- shotgun/tui/dependencies.py +64 -9
- shotgun/tui/layout.py +5 -0
- shotgun/tui/protocols.py +37 -0
- shotgun/tui/screens/chat/chat.tcss +9 -1
- shotgun/tui/screens/chat/chat_screen.py +1015 -106
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +196 -17
- shotgun/tui/screens/chat_screen/command_providers.py +13 -89
- shotgun/tui/screens/chat_screen/hint_message.py +76 -1
- shotgun/tui/screens/chat_screen/history/agent_response.py +7 -3
- shotgun/tui/screens/chat_screen/history/chat_history.py +12 -0
- shotgun/tui/screens/chat_screen/history/formatters.py +53 -15
- shotgun/tui/screens/chat_screen/history/partial_response.py +11 -1
- shotgun/tui/screens/chat_screen/messages.py +219 -0
- shotgun/tui/screens/confirmation_dialog.py +40 -0
- shotgun/tui/screens/directory_setup.py +45 -41
- shotgun/tui/screens/feedback.py +10 -3
- shotgun/tui/screens/github_issue.py +11 -2
- shotgun/tui/screens/model_picker.py +28 -8
- shotgun/tui/screens/onboarding.py +179 -26
- shotgun/tui/screens/pipx_migration.py +58 -6
- shotgun/tui/screens/provider_config.py +66 -8
- shotgun/tui/screens/shared_specs/__init__.py +21 -0
- shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
- shotgun/tui/screens/shared_specs/models.py +56 -0
- shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
- shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
- shotgun/tui/screens/shotgun_auth.py +110 -16
- shotgun/tui/screens/spec_pull.py +288 -0
- shotgun/tui/screens/welcome.py +123 -0
- shotgun/tui/services/conversation_service.py +5 -2
- shotgun/tui/utils/mode_progress.py +20 -86
- shotgun/tui/widgets/__init__.py +2 -1
- shotgun/tui/widgets/approval_widget.py +152 -0
- shotgun/tui/widgets/cascade_confirmation_widget.py +203 -0
- shotgun/tui/widgets/plan_panel.py +129 -0
- shotgun/tui/widgets/step_checkpoint_widget.py +180 -0
- shotgun/tui/widgets/widget_coordinator.py +1 -1
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/METADATA +11 -4
- shotgun_sh-0.4.0.dev1.dist-info/RECORD +242 -0
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/WHEEL +1 -1
- shotgun_sh-0.2.17.dist-info/RECORD +0 -194
- /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
- /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
- /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
- /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/base.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/openai.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -0
- /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/licenses/LICENSE +0 -0
shotgun/agents/common.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Common utilities for agent creation and management."""
|
|
2
2
|
|
|
3
|
-
from collections.abc import Callable
|
|
3
|
+
from collections.abc import AsyncIterable, Awaitable, Callable
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Any
|
|
6
6
|
|
|
@@ -17,7 +17,12 @@ from pydantic_ai.messages import (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
from shotgun.agents.config import ProviderType, get_provider_model
|
|
20
|
-
from shotgun.agents.models import
|
|
20
|
+
from shotgun.agents.models import (
|
|
21
|
+
AgentResponse,
|
|
22
|
+
AgentSystemPromptContext,
|
|
23
|
+
AgentType,
|
|
24
|
+
ShotgunAgent,
|
|
25
|
+
)
|
|
21
26
|
from shotgun.logging_config import get_logger
|
|
22
27
|
from shotgun.prompts import PromptLoader
|
|
23
28
|
from shotgun.sdk.services import get_codebase_service
|
|
@@ -25,7 +30,7 @@ from shotgun.utils import ensure_shotgun_directory_exists
|
|
|
25
30
|
from shotgun.utils.datetime_utils import get_datetime_context
|
|
26
31
|
from shotgun.utils.file_system_utils import get_shotgun_base_path
|
|
27
32
|
|
|
28
|
-
from .history import token_limit_compactor
|
|
33
|
+
from .conversation.history import token_limit_compactor
|
|
29
34
|
from .messages import AgentSystemPrompt, SystemStatusPrompt
|
|
30
35
|
from .models import AgentDeps, AgentRuntimeOptions, PipelineConfigEntry
|
|
31
36
|
from .tools import (
|
|
@@ -38,7 +43,6 @@ from .tools import (
|
|
|
38
43
|
retrieve_code,
|
|
39
44
|
write_file,
|
|
40
45
|
)
|
|
41
|
-
from .tools.file_management import AGENT_DIRECTORIES
|
|
42
46
|
|
|
43
47
|
logger = get_logger(__name__)
|
|
44
48
|
|
|
@@ -74,6 +78,19 @@ async def add_system_status_message(
|
|
|
74
78
|
# Get current datetime with timezone information
|
|
75
79
|
dt_context = get_datetime_context()
|
|
76
80
|
|
|
81
|
+
# Get execution plan and pending approval state if this is the Router agent
|
|
82
|
+
execution_plan = None
|
|
83
|
+
pending_approval = False
|
|
84
|
+
if deps.agent_mode == AgentType.ROUTER:
|
|
85
|
+
# Import here to avoid circular imports
|
|
86
|
+
from shotgun.agents.router.models import RouterDeps
|
|
87
|
+
|
|
88
|
+
if isinstance(deps, RouterDeps):
|
|
89
|
+
if deps.current_plan is not None:
|
|
90
|
+
execution_plan = deps.current_plan.format_for_display()
|
|
91
|
+
# Check if plan is pending approval (multi-step plan in Planning mode)
|
|
92
|
+
pending_approval = deps.pending_approval is not None
|
|
93
|
+
|
|
77
94
|
system_state = prompt_loader.render(
|
|
78
95
|
"agents/state/system_state.j2",
|
|
79
96
|
codebase_understanding_graphs=codebase_understanding_graphs,
|
|
@@ -83,6 +100,8 @@ async def add_system_status_message(
|
|
|
83
100
|
current_datetime=dt_context.datetime_formatted,
|
|
84
101
|
timezone_name=dt_context.timezone_name,
|
|
85
102
|
utc_offset=dt_context.utc_offset,
|
|
103
|
+
execution_plan=execution_plan,
|
|
104
|
+
pending_approval=pending_approval,
|
|
86
105
|
)
|
|
87
106
|
|
|
88
107
|
message_history.append(
|
|
@@ -102,7 +121,7 @@ async def create_base_agent(
|
|
|
102
121
|
additional_tools: list[Any] | None = None,
|
|
103
122
|
provider: ProviderType | None = None,
|
|
104
123
|
agent_mode: AgentType | None = None,
|
|
105
|
-
) -> tuple[
|
|
124
|
+
) -> tuple[ShotgunAgent, AgentDeps]:
|
|
106
125
|
"""Create a base agent with common configuration.
|
|
107
126
|
|
|
108
127
|
Args:
|
|
@@ -352,86 +371,33 @@ async def extract_markdown_toc(agent_mode: AgentType | None) -> str | None:
|
|
|
352
371
|
|
|
353
372
|
|
|
354
373
|
def get_agent_existing_files(agent_mode: AgentType | None = None) -> list[str]:
|
|
355
|
-
"""Get list of existing files
|
|
374
|
+
"""Get list of all existing files in .shotgun directory.
|
|
375
|
+
|
|
376
|
+
All agents can read any file in .shotgun/, so we list all files regardless
|
|
377
|
+
of agent mode. This includes user-added files that agents should be aware of.
|
|
356
378
|
|
|
357
379
|
Args:
|
|
358
|
-
agent_mode:
|
|
380
|
+
agent_mode: Unused, kept for backwards compatibility.
|
|
359
381
|
|
|
360
382
|
Returns:
|
|
361
383
|
List of existing file paths relative to .shotgun directory
|
|
362
384
|
"""
|
|
363
385
|
base_path = get_shotgun_base_path()
|
|
364
|
-
existing_files = []
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if agent_mode is None:
|
|
368
|
-
# List files in the root .shotgun directory
|
|
369
|
-
for item in base_path.iterdir():
|
|
370
|
-
if item.is_file():
|
|
371
|
-
existing_files.append(item.name)
|
|
372
|
-
elif item.is_dir():
|
|
373
|
-
# List files in first-level subdirectories
|
|
374
|
-
for subitem in item.iterdir():
|
|
375
|
-
if subitem.is_file():
|
|
376
|
-
relative_path = subitem.relative_to(base_path)
|
|
377
|
-
existing_files.append(str(relative_path))
|
|
386
|
+
existing_files: list[str] = []
|
|
387
|
+
|
|
388
|
+
if not base_path.exists():
|
|
378
389
|
return existing_files
|
|
379
390
|
|
|
380
|
-
#
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if file_path.is_file():
|
|
390
|
-
relative_path = file_path.relative_to(base_path)
|
|
391
|
+
# List all files in .shotgun directory and subdirectories
|
|
392
|
+
for item in base_path.iterdir():
|
|
393
|
+
if item.is_file():
|
|
394
|
+
existing_files.append(item.name)
|
|
395
|
+
elif item.is_dir():
|
|
396
|
+
# List files in subdirectories (one level deep to avoid too much noise)
|
|
397
|
+
for subitem in item.iterdir():
|
|
398
|
+
if subitem.is_file():
|
|
399
|
+
relative_path = subitem.relative_to(base_path)
|
|
391
400
|
existing_files.append(str(relative_path))
|
|
392
|
-
else:
|
|
393
|
-
# For other agents, check files/directories they have access to
|
|
394
|
-
allowed_paths_raw = AGENT_DIRECTORIES[agent_mode]
|
|
395
|
-
|
|
396
|
-
# Convert single Path/string to list of Paths for uniform handling
|
|
397
|
-
if isinstance(allowed_paths_raw, str):
|
|
398
|
-
# Special case: "*" means export agent (shouldn't reach here but handle it)
|
|
399
|
-
allowed_paths = (
|
|
400
|
-
[Path(allowed_paths_raw)] if allowed_paths_raw != "*" else []
|
|
401
|
-
)
|
|
402
|
-
elif isinstance(allowed_paths_raw, Path):
|
|
403
|
-
allowed_paths = [allowed_paths_raw]
|
|
404
|
-
else:
|
|
405
|
-
# Already a list
|
|
406
|
-
allowed_paths = allowed_paths_raw
|
|
407
|
-
|
|
408
|
-
# Check each allowed path
|
|
409
|
-
for allowed_path in allowed_paths:
|
|
410
|
-
allowed_str = str(allowed_path)
|
|
411
|
-
|
|
412
|
-
# Check if it's a directory (no .md suffix)
|
|
413
|
-
if not allowed_path.suffix or not allowed_str.endswith(".md"):
|
|
414
|
-
# It's a directory - list all files within it
|
|
415
|
-
dir_path = base_path / allowed_str
|
|
416
|
-
if dir_path.exists() and dir_path.is_dir():
|
|
417
|
-
for file_path in dir_path.rglob("*"):
|
|
418
|
-
if file_path.is_file():
|
|
419
|
-
relative_path = file_path.relative_to(base_path)
|
|
420
|
-
existing_files.append(str(relative_path))
|
|
421
|
-
else:
|
|
422
|
-
# It's a file - check if it exists
|
|
423
|
-
file_path = base_path / allowed_str
|
|
424
|
-
if file_path.exists():
|
|
425
|
-
existing_files.append(allowed_str)
|
|
426
|
-
|
|
427
|
-
# Also check for associated directory (e.g., research/ for research.md)
|
|
428
|
-
base_name = allowed_str.replace(".md", "")
|
|
429
|
-
dir_path = base_path / base_name
|
|
430
|
-
if dir_path.exists() and dir_path.is_dir():
|
|
431
|
-
for file_path in dir_path.rglob("*"):
|
|
432
|
-
if file_path.is_file():
|
|
433
|
-
relative_path = file_path.relative_to(base_path)
|
|
434
|
-
existing_files.append(str(relative_path))
|
|
435
401
|
|
|
436
402
|
return existing_files
|
|
437
403
|
|
|
@@ -458,10 +424,24 @@ def build_agent_system_prompt(
|
|
|
458
424
|
logger.debug("🔧 Building research agent system prompt...")
|
|
459
425
|
logger.debug("Interactive mode: %s", ctx.deps.interactive_mode)
|
|
460
426
|
|
|
461
|
-
|
|
462
|
-
|
|
427
|
+
# Build template context using Pydantic model for type safety and testability
|
|
428
|
+
# Import here to avoid circular imports (same pattern as add_system_status_message)
|
|
429
|
+
from shotgun.agents.router.models import RouterDeps
|
|
430
|
+
|
|
431
|
+
router_mode = None
|
|
432
|
+
if isinstance(ctx.deps, RouterDeps):
|
|
433
|
+
router_mode = ctx.deps.router_mode.value
|
|
434
|
+
|
|
435
|
+
template_context = AgentSystemPromptContext(
|
|
463
436
|
interactive_mode=ctx.deps.interactive_mode,
|
|
464
437
|
mode=agent_type,
|
|
438
|
+
sub_agent_context=ctx.deps.sub_agent_context,
|
|
439
|
+
router_mode=router_mode,
|
|
440
|
+
)
|
|
441
|
+
|
|
442
|
+
result = prompt_loader.render(
|
|
443
|
+
f"agents/{agent_type}.j2",
|
|
444
|
+
**template_context.model_dump(),
|
|
465
445
|
)
|
|
466
446
|
|
|
467
447
|
if agent_type == "research":
|
|
@@ -525,13 +505,33 @@ async def add_system_prompt_message(
|
|
|
525
505
|
return message_history
|
|
526
506
|
|
|
527
507
|
|
|
508
|
+
EventStreamHandler = Callable[
|
|
509
|
+
[RunContext[AgentDeps], AsyncIterable[Any]], Awaitable[None]
|
|
510
|
+
]
|
|
511
|
+
|
|
512
|
+
|
|
528
513
|
async def run_agent(
|
|
529
|
-
agent:
|
|
514
|
+
agent: ShotgunAgent,
|
|
530
515
|
prompt: str,
|
|
531
516
|
deps: AgentDeps,
|
|
532
517
|
message_history: list[ModelMessage] | None = None,
|
|
533
518
|
usage_limits: UsageLimits | None = None,
|
|
519
|
+
event_stream_handler: EventStreamHandler | None = None,
|
|
534
520
|
) -> AgentRunResult[AgentResponse]:
|
|
521
|
+
"""Run an agent with optional streaming support.
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
agent: The agent to run.
|
|
525
|
+
prompt: The prompt to send to the agent.
|
|
526
|
+
deps: Agent dependencies.
|
|
527
|
+
message_history: Optional message history to continue from.
|
|
528
|
+
usage_limits: Optional usage limits for the run.
|
|
529
|
+
event_stream_handler: Optional callback for streaming events.
|
|
530
|
+
When provided, enables real-time streaming of agent responses.
|
|
531
|
+
|
|
532
|
+
Returns:
|
|
533
|
+
The agent run result.
|
|
534
|
+
"""
|
|
535
535
|
# Clear file tracker for new run
|
|
536
536
|
deps.file_tracker.clear()
|
|
537
537
|
logger.debug("🔧 Cleared file tracker for new agent run")
|
|
@@ -544,6 +544,7 @@ async def run_agent(
|
|
|
544
544
|
deps=deps,
|
|
545
545
|
usage_limits=usage_limits,
|
|
546
546
|
message_history=message_history,
|
|
547
|
+
event_stream_handler=event_stream_handler,
|
|
547
548
|
)
|
|
548
549
|
|
|
549
550
|
# Log file operations summary if any files were modified
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Configuration Management
|
|
2
|
+
|
|
3
|
+
This directory contains the configuration management system for Shotgun, including models, migrations, and provider integration.
|
|
4
|
+
|
|
5
|
+
## Config Version History
|
|
6
|
+
|
|
7
|
+
### Version 1 (Config Versioning Introduced)
|
|
8
|
+
|
|
9
|
+
- **Commit**: `f36defc` (Sep 19, 2025)
|
|
10
|
+
- **Title**: "feat: add Sentry error tracking with anonymous user identification"
|
|
11
|
+
- **Key Fields**: `user_id`, `config_version: 1`
|
|
12
|
+
- **Note**: First version to include explicit versioning
|
|
13
|
+
|
|
14
|
+
### Version 2 (Shotgun Account Provider)
|
|
15
|
+
|
|
16
|
+
- **Commit**: `37a5add` (Oct 3, 2025)
|
|
17
|
+
- **Title**: "feat: add Shotgun Account provider with LiteLLM proxy support"
|
|
18
|
+
- **Key Fields**: `user_id`, `config_version: 2`, added `shotgun` provider config
|
|
19
|
+
- **Note**: Configs without a version field default to v2 during migration
|
|
20
|
+
|
|
21
|
+
### Version 3 (OAuth Authentication)
|
|
22
|
+
|
|
23
|
+
- **Commit**: `39d2af9` (Oct 6, 2025)
|
|
24
|
+
- **Title**: "feat: implement OAuth-style authentication flow for Shotgun Account"
|
|
25
|
+
- **Key Changes**:
|
|
26
|
+
- Renamed `user_id` → `shotgun_instance_id`
|
|
27
|
+
- Added `supabase_jwt` field to Shotgun Account config
|
|
28
|
+
- **Git Tags**: Both `0.2.11.dev1` and `0.2.11.dev2` are at this version
|
|
29
|
+
|
|
30
|
+
### Version 4 (Marketing Messages)
|
|
31
|
+
|
|
32
|
+
- **Commit**: `8638a6d` (Nov 4, 2025)
|
|
33
|
+
- **Title**: "feat: add marketing message system for GitHub star promotion"
|
|
34
|
+
- **Key Changes**:
|
|
35
|
+
- Added `marketing` configuration with message tracking
|
|
36
|
+
- Added `shown_welcome_screen` field (set to `False` for existing BYOK users)
|
|
37
|
+
|
|
38
|
+
### Version 5 (Streaming Detection) - CURRENT
|
|
39
|
+
|
|
40
|
+
- **Commit**: `fded351` (Nov 6, 2025)
|
|
41
|
+
- **Title**: "feat: add config migration for streaming capability field (v4->v5)"
|
|
42
|
+
- **Key Changes**:
|
|
43
|
+
- Added `supports_streaming` field to OpenAI config
|
|
44
|
+
- Added `shown_onboarding_popup` timestamp field
|
|
45
|
+
- Added `supabase_jwt` to Shotgun Account config
|
|
46
|
+
|
|
47
|
+
## Migration System
|
|
48
|
+
|
|
49
|
+
The migration system is designed to be sequential and idempotent. Migrations are defined in `manager.py`:
|
|
50
|
+
|
|
51
|
+
- `_migrate_v2_to_v3()`: Renames `user_id` to `shotgun_instance_id`
|
|
52
|
+
- `_migrate_v3_to_v4()`: Adds marketing config and welcome screen flag
|
|
53
|
+
- `_migrate_v4_to_v5()`: Adds streaming support fields
|
|
54
|
+
|
|
55
|
+
All migrations preserve user data (API keys, settings) and can be safely run multiple times.
|
|
56
|
+
|
|
57
|
+
## Adding a New Config Version
|
|
58
|
+
|
|
59
|
+
When adding a new config version:
|
|
60
|
+
|
|
61
|
+
1. **Update `models.py`**:
|
|
62
|
+
- Increment `CURRENT_CONFIG_VERSION` constant
|
|
63
|
+
- Add new fields to appropriate config models
|
|
64
|
+
|
|
65
|
+
2. **Create migration function in `manager.py`**:
|
|
66
|
+
```python
|
|
67
|
+
def _migrate_vN_to_vN+1(data: dict[str, Any]) -> dict[str, Any]:
|
|
68
|
+
"""Migrate config from version N to N+1."""
|
|
69
|
+
data["config_version"] = N + 1
|
|
70
|
+
# Add migration logic
|
|
71
|
+
return data
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
3. **Register migration**:
|
|
75
|
+
- Add to `migrations` dict in `_apply_migrations()`
|
|
76
|
+
|
|
77
|
+
4. **Add tests in `test/unit/test_config_migrations.py`**:
|
|
78
|
+
- Create example config for version N
|
|
79
|
+
- Test individual migration function
|
|
80
|
+
- Test sequential migration from version N to current
|
|
81
|
+
- Test with populated configs (non-empty API keys, etc.)
|
|
82
|
+
- Test edge cases
|
|
83
|
+
|
|
84
|
+
## Files
|
|
85
|
+
|
|
86
|
+
- **`models.py`**: Pydantic models for configuration schema
|
|
87
|
+
- **`manager.py`**: ConfigManager class and migration functions
|
|
88
|
+
- **`provider.py`**: LLM provider integration and model creation
|
|
89
|
+
- **`streaming_test.py`**: OpenAI streaming capability detection
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
"""Configuration module for Shotgun CLI."""
|
|
2
2
|
|
|
3
|
-
from .manager import
|
|
3
|
+
from .manager import (
|
|
4
|
+
BACKUP_DIR_NAME,
|
|
5
|
+
ConfigManager,
|
|
6
|
+
ConfigMigrationError,
|
|
7
|
+
get_backup_dir,
|
|
8
|
+
get_config_manager,
|
|
9
|
+
)
|
|
4
10
|
from .models import ProviderType, ShotgunConfig
|
|
5
11
|
from .provider import get_provider_model
|
|
6
12
|
|
|
7
13
|
__all__ = [
|
|
14
|
+
"BACKUP_DIR_NAME",
|
|
8
15
|
"ConfigManager",
|
|
16
|
+
"ConfigMigrationError",
|
|
17
|
+
"get_backup_dir",
|
|
9
18
|
"get_config_manager",
|
|
10
19
|
"ProviderType",
|
|
11
20
|
"ShotgunConfig",
|