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.
Files changed (150) hide show
  1. shotgun/agents/agent_manager.py +219 -37
  2. shotgun/agents/common.py +79 -78
  3. shotgun/agents/config/README.md +89 -0
  4. shotgun/agents/config/__init__.py +10 -1
  5. shotgun/agents/config/manager.py +364 -53
  6. shotgun/agents/config/models.py +101 -21
  7. shotgun/agents/config/provider.py +51 -13
  8. shotgun/agents/config/streaming_test.py +119 -0
  9. shotgun/agents/context_analyzer/analyzer.py +6 -2
  10. shotgun/agents/conversation/__init__.py +18 -0
  11. shotgun/agents/conversation/filters.py +164 -0
  12. shotgun/agents/conversation/history/chunking.py +278 -0
  13. shotgun/agents/{history → conversation/history}/compaction.py +27 -1
  14. shotgun/agents/{history → conversation/history}/constants.py +5 -0
  15. shotgun/agents/conversation/history/file_content_deduplication.py +239 -0
  16. shotgun/agents/{history → conversation/history}/history_processors.py +267 -3
  17. shotgun/agents/{history → conversation/history}/token_counting/anthropic.py +8 -0
  18. shotgun/agents/{conversation_manager.py → conversation/manager.py} +1 -1
  19. shotgun/agents/{conversation_history.py → conversation/models.py} +8 -94
  20. shotgun/agents/error/__init__.py +11 -0
  21. shotgun/agents/error/models.py +19 -0
  22. shotgun/agents/export.py +12 -13
  23. shotgun/agents/models.py +66 -1
  24. shotgun/agents/plan.py +12 -13
  25. shotgun/agents/research.py +13 -10
  26. shotgun/agents/router/__init__.py +47 -0
  27. shotgun/agents/router/models.py +376 -0
  28. shotgun/agents/router/router.py +185 -0
  29. shotgun/agents/router/tools/__init__.py +18 -0
  30. shotgun/agents/router/tools/delegation_tools.py +503 -0
  31. shotgun/agents/router/tools/plan_tools.py +322 -0
  32. shotgun/agents/runner.py +230 -0
  33. shotgun/agents/specify.py +12 -13
  34. shotgun/agents/tasks.py +12 -13
  35. shotgun/agents/tools/file_management.py +49 -1
  36. shotgun/agents/tools/registry.py +2 -0
  37. shotgun/agents/tools/web_search/__init__.py +1 -2
  38. shotgun/agents/tools/web_search/gemini.py +1 -3
  39. shotgun/agents/tools/web_search/openai.py +1 -1
  40. shotgun/build_constants.py +2 -2
  41. shotgun/cli/clear.py +1 -1
  42. shotgun/cli/compact.py +5 -3
  43. shotgun/cli/context.py +44 -1
  44. shotgun/cli/error_handler.py +24 -0
  45. shotgun/cli/export.py +34 -34
  46. shotgun/cli/plan.py +34 -34
  47. shotgun/cli/research.py +17 -9
  48. shotgun/cli/spec/__init__.py +5 -0
  49. shotgun/cli/spec/backup.py +81 -0
  50. shotgun/cli/spec/commands.py +132 -0
  51. shotgun/cli/spec/models.py +48 -0
  52. shotgun/cli/spec/pull_service.py +219 -0
  53. shotgun/cli/specify.py +20 -19
  54. shotgun/cli/tasks.py +34 -34
  55. shotgun/codebase/core/change_detector.py +1 -1
  56. shotgun/codebase/core/ingestor.py +154 -8
  57. shotgun/codebase/core/manager.py +1 -1
  58. shotgun/codebase/models.py +2 -0
  59. shotgun/exceptions.py +325 -0
  60. shotgun/llm_proxy/__init__.py +17 -0
  61. shotgun/llm_proxy/client.py +215 -0
  62. shotgun/llm_proxy/models.py +137 -0
  63. shotgun/logging_config.py +42 -0
  64. shotgun/main.py +4 -0
  65. shotgun/posthog_telemetry.py +1 -1
  66. shotgun/prompts/agents/export.j2 +2 -0
  67. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +23 -3
  68. shotgun/prompts/agents/partials/interactive_mode.j2 +3 -3
  69. shotgun/prompts/agents/partials/router_delegation_mode.j2 +36 -0
  70. shotgun/prompts/agents/plan.j2 +29 -1
  71. shotgun/prompts/agents/research.j2 +75 -23
  72. shotgun/prompts/agents/router.j2 +440 -0
  73. shotgun/prompts/agents/specify.j2 +80 -4
  74. shotgun/prompts/agents/state/system_state.j2 +15 -8
  75. shotgun/prompts/agents/tasks.j2 +63 -23
  76. shotgun/prompts/history/chunk_summarization.j2 +34 -0
  77. shotgun/prompts/history/combine_summaries.j2 +53 -0
  78. shotgun/sdk/codebase.py +14 -3
  79. shotgun/settings.py +5 -0
  80. shotgun/shotgun_web/__init__.py +67 -1
  81. shotgun/shotgun_web/client.py +42 -1
  82. shotgun/shotgun_web/constants.py +46 -0
  83. shotgun/shotgun_web/exceptions.py +29 -0
  84. shotgun/shotgun_web/models.py +390 -0
  85. shotgun/shotgun_web/shared_specs/__init__.py +32 -0
  86. shotgun/shotgun_web/shared_specs/file_scanner.py +175 -0
  87. shotgun/shotgun_web/shared_specs/hasher.py +83 -0
  88. shotgun/shotgun_web/shared_specs/models.py +71 -0
  89. shotgun/shotgun_web/shared_specs/upload_pipeline.py +329 -0
  90. shotgun/shotgun_web/shared_specs/utils.py +34 -0
  91. shotgun/shotgun_web/specs_client.py +703 -0
  92. shotgun/shotgun_web/supabase_client.py +31 -0
  93. shotgun/tui/app.py +78 -15
  94. shotgun/tui/components/mode_indicator.py +120 -25
  95. shotgun/tui/components/status_bar.py +2 -2
  96. shotgun/tui/containers.py +1 -1
  97. shotgun/tui/dependencies.py +64 -9
  98. shotgun/tui/layout.py +5 -0
  99. shotgun/tui/protocols.py +37 -0
  100. shotgun/tui/screens/chat/chat.tcss +9 -1
  101. shotgun/tui/screens/chat/chat_screen.py +1015 -106
  102. shotgun/tui/screens/chat/codebase_index_prompt_screen.py +196 -17
  103. shotgun/tui/screens/chat_screen/command_providers.py +13 -89
  104. shotgun/tui/screens/chat_screen/hint_message.py +76 -1
  105. shotgun/tui/screens/chat_screen/history/agent_response.py +7 -3
  106. shotgun/tui/screens/chat_screen/history/chat_history.py +12 -0
  107. shotgun/tui/screens/chat_screen/history/formatters.py +53 -15
  108. shotgun/tui/screens/chat_screen/history/partial_response.py +11 -1
  109. shotgun/tui/screens/chat_screen/messages.py +219 -0
  110. shotgun/tui/screens/confirmation_dialog.py +40 -0
  111. shotgun/tui/screens/directory_setup.py +45 -41
  112. shotgun/tui/screens/feedback.py +10 -3
  113. shotgun/tui/screens/github_issue.py +11 -2
  114. shotgun/tui/screens/model_picker.py +28 -8
  115. shotgun/tui/screens/onboarding.py +179 -26
  116. shotgun/tui/screens/pipx_migration.py +58 -6
  117. shotgun/tui/screens/provider_config.py +66 -8
  118. shotgun/tui/screens/shared_specs/__init__.py +21 -0
  119. shotgun/tui/screens/shared_specs/create_spec_dialog.py +273 -0
  120. shotgun/tui/screens/shared_specs/models.py +56 -0
  121. shotgun/tui/screens/shared_specs/share_specs_dialog.py +390 -0
  122. shotgun/tui/screens/shared_specs/upload_progress_screen.py +452 -0
  123. shotgun/tui/screens/shotgun_auth.py +110 -16
  124. shotgun/tui/screens/spec_pull.py +288 -0
  125. shotgun/tui/screens/welcome.py +123 -0
  126. shotgun/tui/services/conversation_service.py +5 -2
  127. shotgun/tui/utils/mode_progress.py +20 -86
  128. shotgun/tui/widgets/__init__.py +2 -1
  129. shotgun/tui/widgets/approval_widget.py +152 -0
  130. shotgun/tui/widgets/cascade_confirmation_widget.py +203 -0
  131. shotgun/tui/widgets/plan_panel.py +129 -0
  132. shotgun/tui/widgets/step_checkpoint_widget.py +180 -0
  133. shotgun/tui/widgets/widget_coordinator.py +1 -1
  134. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/METADATA +11 -4
  135. shotgun_sh-0.4.0.dev1.dist-info/RECORD +242 -0
  136. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/WHEEL +1 -1
  137. shotgun_sh-0.2.17.dist-info/RECORD +0 -194
  138. /shotgun/agents/{history → conversation/history}/__init__.py +0 -0
  139. /shotgun/agents/{history → conversation/history}/context_extraction.py +0 -0
  140. /shotgun/agents/{history → conversation/history}/history_building.py +0 -0
  141. /shotgun/agents/{history → conversation/history}/message_utils.py +0 -0
  142. /shotgun/agents/{history → conversation/history}/token_counting/__init__.py +0 -0
  143. /shotgun/agents/{history → conversation/history}/token_counting/base.py +0 -0
  144. /shotgun/agents/{history → conversation/history}/token_counting/openai.py +0 -0
  145. /shotgun/agents/{history → conversation/history}/token_counting/sentencepiece_counter.py +0 -0
  146. /shotgun/agents/{history → conversation/history}/token_counting/tokenizer_cache.py +0 -0
  147. /shotgun/agents/{history → conversation/history}/token_counting/utils.py +0 -0
  148. /shotgun/agents/{history → conversation/history}/token_estimation.py +0 -0
  149. {shotgun_sh-0.2.17.dist-info → shotgun_sh-0.4.0.dev1.dist-info}/entry_points.txt +0 -0
  150. {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 AgentResponse, AgentType
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[Agent[AgentDeps, AgentResponse], AgentDeps]:
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 for the given agent mode.
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: The agent mode to check files for. If None, lists all files.
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
- # If no agent mode, list all files in base path and first level subdirectories
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
- # Handle specific agent modes
381
- if agent_mode not in AGENT_DIRECTORIES:
382
- return []
383
-
384
- if agent_mode == AgentType.EXPORT:
385
- # For export agent, list all files in exports directory
386
- exports_dir = base_path / "exports"
387
- if exports_dir.exists():
388
- for file_path in exports_dir.rglob("*"):
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
- result = prompt_loader.render(
462
- f"agents/{agent_type}.j2",
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: Agent[AgentDeps, AgentResponse],
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 ConfigManager, get_config_manager
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",