shotgun-sh 0.2.10.dev8__py3-none-any.whl → 0.2.10.dev10__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.

@@ -32,6 +32,34 @@ logger = get_logger(__name__)
32
32
  _model_cache: dict[tuple[ProviderType, KeyProvider, ModelName, str], Model] = {}
33
33
 
34
34
 
35
+ def get_default_model_for_provider(config: ShotgunConfig) -> ModelName:
36
+ """Get the default model based on which provider/account is configured.
37
+
38
+ Checks API keys in priority order and returns appropriate default model.
39
+ Treats Shotgun Account as a provider context.
40
+
41
+ Args:
42
+ config: Shotgun configuration containing API keys
43
+
44
+ Returns:
45
+ Default ModelName for the configured provider/account
46
+ """
47
+ # Priority 1: Shotgun Account
48
+ if _get_api_key(config.shotgun.api_key):
49
+ return ModelName.CLAUDE_HAIKU_4_5
50
+
51
+ # Priority 2: Individual provider keys
52
+ if _get_api_key(config.anthropic.api_key):
53
+ return ModelName.CLAUDE_HAIKU_4_5
54
+ if _get_api_key(config.openai.api_key):
55
+ return ModelName.GPT_5
56
+ if _get_api_key(config.google.api_key):
57
+ return ModelName.GEMINI_2_5_PRO
58
+
59
+ # Fallback: system-wide default
60
+ return ModelName.CLAUDE_HAIKU_4_5
61
+
62
+
35
63
  def get_or_create_model(
36
64
  provider: ProviderType,
37
65
  key_provider: "KeyProvider",
@@ -39,7 +39,10 @@ from shotgun.agents.models import (
39
39
  AgentType,
40
40
  FileOperationTracker,
41
41
  )
42
- from shotgun.codebase.core.manager import CodebaseAlreadyIndexedError
42
+ from shotgun.codebase.core.manager import (
43
+ CodebaseAlreadyIndexedError,
44
+ CodebaseGraphManager,
45
+ )
43
46
  from shotgun.codebase.models import IndexProgress, ProgressPhase
44
47
  from shotgun.posthog_telemetry import track_event
45
48
  from shotgun.sdk.codebase import CodebaseSDK
@@ -785,6 +788,25 @@ class ChatScreen(Screen[None]):
785
788
  except Exception as exc: # pragma: no cover - defensive UI path
786
789
  self.notify(f"Failed to delete codebase: {exc}", severity="error")
787
790
 
791
+ def _is_kuzu_corruption_error(self, exception: Exception) -> bool:
792
+ """Check if error is related to kuzu database corruption.
793
+
794
+ Args:
795
+ exception: The exception to check
796
+
797
+ Returns:
798
+ True if the error indicates kuzu database corruption
799
+ """
800
+ error_str = str(exception).lower()
801
+ error_indicators = [
802
+ "not a directory",
803
+ "errno 20",
804
+ "corrupted",
805
+ ".kuzu",
806
+ "ioexception",
807
+ ]
808
+ return any(indicator in error_str for indicator in error_indicators)
809
+
788
810
  @work
789
811
  async def index_codebase(self, selection: CodebaseIndexSelection) -> None:
790
812
  label = self.query_one("#indexing-job-display", Static)
@@ -853,58 +875,92 @@ class ChatScreen(Screen[None]):
853
875
  # Start progress animation timer (10 fps = 100ms interval)
854
876
  progress_timer = self.set_interval(0.1, update_progress_display)
855
877
 
856
- try:
857
- # Pass the current working directory as the indexed_from_cwd
858
- logger.debug(
859
- f"Starting indexing - repo_path: {selection.repo_path}, "
860
- f"name: {selection.name}, cwd: {Path.cwd().resolve()}"
861
- )
862
- result = await self.codebase_sdk.index_codebase(
863
- selection.repo_path,
864
- selection.name,
865
- indexed_from_cwd=str(Path.cwd().resolve()),
866
- progress_callback=progress_callback,
867
- )
878
+ # Retry logic for handling kuzu corruption
879
+ max_retries = 3
880
+
881
+ for attempt in range(max_retries):
882
+ try:
883
+ # Clean up corrupted DBs before retry (skip on first attempt)
884
+ if attempt > 0:
885
+ logger.info(
886
+ f"Retry attempt {attempt + 1}/{max_retries} - cleaning up corrupted databases"
887
+ )
888
+ manager = CodebaseGraphManager(
889
+ self.codebase_sdk.service.storage_dir
890
+ )
891
+ cleaned = await manager.cleanup_corrupted_databases()
892
+ logger.info(f"Cleaned up {len(cleaned)} corrupted database(s)")
893
+ self.notify(
894
+ f"Retrying indexing after cleanup (attempt {attempt + 1}/{max_retries})...",
895
+ severity="information",
896
+ )
868
897
 
869
- # Stop progress animation
870
- progress_timer.stop()
898
+ # Pass the current working directory as the indexed_from_cwd
899
+ logger.debug(
900
+ f"Starting indexing - repo_path: {selection.repo_path}, "
901
+ f"name: {selection.name}, cwd: {Path.cwd().resolve()}"
902
+ )
903
+ result = await self.codebase_sdk.index_codebase(
904
+ selection.repo_path,
905
+ selection.name,
906
+ indexed_from_cwd=str(Path.cwd().resolve()),
907
+ progress_callback=progress_callback,
908
+ )
871
909
 
872
- # Show 100% completion after indexing finishes
873
- final_bar = create_progress_bar(100.0)
874
- label.update(f"[$foreground-muted]Indexing codebase: {final_bar} 100%[/]")
875
- label.refresh()
910
+ # Success! Stop progress animation
911
+ progress_timer.stop()
876
912
 
877
- logger.info(
878
- f"Successfully indexed codebase '{result.name}' (ID: {result.graph_id})"
879
- )
880
- self.notify(
881
- f"Indexed codebase '{result.name}' (ID: {result.graph_id})",
882
- severity="information",
883
- timeout=8,
884
- )
913
+ # Show 100% completion after indexing finishes
914
+ final_bar = create_progress_bar(100.0)
915
+ label.update(
916
+ f"[$foreground-muted]Indexing codebase: {final_bar} 100%[/]"
917
+ )
918
+ label.refresh()
885
919
 
886
- except CodebaseAlreadyIndexedError as exc:
887
- progress_timer.stop()
888
- logger.warning(f"Codebase already indexed: {exc}")
889
- self.notify(str(exc), severity="warning")
890
- return
891
- except InvalidPathError as exc:
892
- progress_timer.stop()
893
- logger.error(f"Invalid path error: {exc}")
894
- self.notify(str(exc), severity="error")
920
+ logger.info(
921
+ f"Successfully indexed codebase '{result.name}' (ID: {result.graph_id})"
922
+ )
923
+ self.notify(
924
+ f"Indexed codebase '{result.name}' (ID: {result.graph_id})",
925
+ severity="information",
926
+ timeout=8,
927
+ )
928
+ break # Success - exit retry loop
929
+
930
+ except CodebaseAlreadyIndexedError as exc:
931
+ progress_timer.stop()
932
+ logger.warning(f"Codebase already indexed: {exc}")
933
+ self.notify(str(exc), severity="warning")
934
+ return
935
+ except InvalidPathError as exc:
936
+ progress_timer.stop()
937
+ logger.error(f"Invalid path error: {exc}")
938
+ self.notify(str(exc), severity="error")
939
+ return
940
+
941
+ except Exception as exc: # pragma: no cover - defensive UI path
942
+ # Check if this is a kuzu corruption error and we have retries left
943
+ if attempt < max_retries - 1 and self._is_kuzu_corruption_error(exc):
944
+ logger.warning(
945
+ f"Kuzu corruption detected on attempt {attempt + 1}/{max_retries}: {exc}. "
946
+ f"Will retry after cleanup..."
947
+ )
948
+ # Exponential backoff: 1s, 2s
949
+ await asyncio.sleep(2**attempt)
950
+ continue
951
+
952
+ # Either final retry failed OR not a corruption error - show error
953
+ logger.exception(
954
+ f"Failed to index codebase after {attempt + 1} attempts - "
955
+ f"repo_path: {selection.repo_path}, name: {selection.name}, error: {exc}"
956
+ )
957
+ self.notify(f"Failed to index codebase: {exc}", severity="error")
958
+ break
895
959
 
896
- except Exception as exc: # pragma: no cover - defensive UI path
897
- # Log full exception details with stack trace
898
- logger.exception(
899
- f"Failed to index codebase - repo_path: {selection.repo_path}, "
900
- f"name: {selection.name}, error: {exc}"
901
- )
902
- self.notify(f"Failed to index codebase: {exc}", severity="error")
903
- finally:
904
- # Always stop the progress timer
905
- progress_timer.stop()
906
- label.update("")
907
- label.refresh()
960
+ # Always stop the progress timer and clean up label
961
+ progress_timer.stop()
962
+ label.update("")
963
+ label.refresh()
908
964
 
909
965
  @work
910
966
  async def run_agent(self, message: str) -> None:
@@ -13,6 +13,7 @@ from textual.widgets import Button, Label, ListItem, ListView, Static
13
13
 
14
14
  from shotgun.agents.config import ConfigManager
15
15
  from shotgun.agents.config.models import MODEL_SPECS, ModelName, ShotgunConfig
16
+ from shotgun.agents.config.provider import get_default_model_for_provider
16
17
  from shotgun.logging_config import get_logger
17
18
 
18
19
  if TYPE_CHECKING:
@@ -111,7 +112,7 @@ class ModelPickerScreen(Screen[None]):
111
112
  config_manager._provider_has_api_key(config.shotgun),
112
113
  )
113
114
 
114
- current_model = config.selected_model or ModelName.CLAUDE_SONNET_4_5
115
+ current_model = config.selected_model or get_default_model_for_provider(config)
115
116
  self.selected_model = current_model
116
117
  logger.debug("Current selected model: %s", current_model)
117
118
 
@@ -193,7 +194,7 @@ class ModelPickerScreen(Screen[None]):
193
194
  """
194
195
  # Load config once with force_reload
195
196
  config = self.config_manager.load(force_reload=True)
196
- current_model = config.selected_model or ModelName.CLAUDE_SONNET_4_5
197
+ current_model = config.selected_model or get_default_model_for_provider(config)
197
198
 
198
199
  # Update labels for available models only
199
200
  for model_name in AVAILABLE_MODELS:
@@ -101,7 +101,7 @@ Or install permanently: `uv tool install shotgun-sh`
101
101
 
102
102
  **Discord:** https://discord.gg/5RmY6J2N7s
103
103
 
104
- **Full Migration Guide:** https://github.com/shotgun-sh/shotgun-sdk/blob/main/PIPX_MIGRATION.md
104
+ **Full Migration Guide:** https://github.com/shotgun-sh/shotgun/blob/main/PIPX_MIGRATION.md
105
105
  """
106
106
  )
107
107
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shotgun-sh
3
- Version: 0.2.10.dev8
3
+ Version: 0.2.10.dev10
4
4
  Summary: AI-powered research, planning, and task management CLI tool
5
5
  Project-URL: Homepage, https://shotgun.sh/
6
6
  Project-URL: Repository, https://github.com/shotgun-sh/shotgun
@@ -25,7 +25,7 @@ shotgun/agents/config/__init__.py,sha256=Fl8K_81zBpm-OfOW27M_WWLSFdaHHek6lWz95iD
25
25
  shotgun/agents/config/constants.py,sha256=JNuLpeBUKikEsxGSjwX3RVWUQpbCKnDKstF2NczuDqk,932
26
26
  shotgun/agents/config/manager.py,sha256=Z8EQoWPQM7uj3MyklmxHQ1GR6va1uv9BjT5DDvAv3pY,18920
27
27
  shotgun/agents/config/models.py,sha256=tM0JnMf-hWWzJCmOsJBA6tIiwrdFTQI_4B0zYFNg6CU,5736
28
- shotgun/agents/config/provider.py,sha256=tqxcVGCOy7j1fakL9M994TE0v4dCG1OuySrGkbUnSQI,12493
28
+ shotgun/agents/config/provider.py,sha256=8TLiyTowkjT5p0zjocv9zGjem5hgQKnNuiN1nESguok,13412
29
29
  shotgun/agents/history/__init__.py,sha256=XFQj2a6fxDqVg0Q3juvN9RjV_RJbgvFZtQOCOjVJyp4,147
30
30
  shotgun/agents/history/compaction.py,sha256=9RMpG0aY_7L4TecbgwHSOkGtbd9W5XZTg-MbzZmNl00,3515
31
31
  shotgun/agents/history/constants.py,sha256=yWY8rrTZarLA3flCCMB_hS2NMvUDRDTwP4D4j7MIh1w,446
@@ -127,12 +127,12 @@ shotgun/tui/components/prompt_input.py,sha256=Ss-htqraHZAPaehGE4x86ij0veMjc4Ugad
127
127
  shotgun/tui/components/spinner.py,sha256=ovTDeaJ6FD6chZx_Aepia6R3UkPOVJ77EKHfRmn39MY,2427
128
128
  shotgun/tui/components/splash.py,sha256=vppy9vEIEvywuUKRXn2y11HwXSRkQZHLYoVjhDVdJeU,1267
129
129
  shotgun/tui/components/vertical_tail.py,sha256=kROwTaRjUwVB7H35dtmNcUVPQqNYvvfq7K2tXBKEb6c,638
130
- shotgun/tui/screens/chat.py,sha256=iBiTxbQw2wvnyRe5-H_hUYUvIAxMnh8IM0bTgqAkQW4,39549
130
+ shotgun/tui/screens/chat.py,sha256=uvXBRlbXNK5wGGF705KYyW60O3PCL2VpP-0e89nMTN8,41914
131
131
  shotgun/tui/screens/chat.tcss,sha256=2Yq3E23jxsySYsgZf4G1AYrYVcpX0UDW6kNNI0tDmtM,437
132
132
  shotgun/tui/screens/directory_setup.py,sha256=lIZ1J4A6g5Q2ZBX8epW7BhR96Dmdcg22CyiM5S-I5WU,3237
133
133
  shotgun/tui/screens/feedback.py,sha256=VxpW0PVxMp22ZvSfQkTtgixNrpEOlfWtekjqlVfYEjA,5708
134
- shotgun/tui/screens/model_picker.py,sha256=G-EvalpxgHKk0W3FgHMcxIr817VwZyEgh_ZadSQiRwo,11831
135
- shotgun/tui/screens/pipx_migration.py,sha256=Tsm650AT80aaP1nBeMj32UI-zZXNEu8xlbu1NZY3ha8,4325
134
+ shotgun/tui/screens/model_picker.py,sha256=kPvBnMK20SJrAGAC0HHM4Yi8n7biHraz58eiE8jiPxg,11927
135
+ shotgun/tui/screens/pipx_migration.py,sha256=BY6R1Z__htCLjWwffXbHUpxfAk1nnLQnzGRUCmXfwiY,4321
136
136
  shotgun/tui/screens/provider_config.py,sha256=UCnAzjXPoP7Y73gsXxZF2PNA4LdSgpgoGYwiOd6fERA,10902
137
137
  shotgun/tui/screens/shotgun_auth.py,sha256=Y--7LZewV6gfDkucxymfAO7BCd7eI2C3H1ClDMztVio,10663
138
138
  shotgun/tui/screens/splash.py,sha256=E2MsJihi3c9NY1L28o_MstDxGwrCnnV7zdq00MrGAsw,706
@@ -149,8 +149,8 @@ shotgun/utils/env_utils.py,sha256=ulM3BRi9ZhS7uC-zorGeDQm4SHvsyFuuU9BtVPqdrHY,14
149
149
  shotgun/utils/file_system_utils.py,sha256=l-0p1bEHF34OU19MahnRFdClHufThfGAjQ431teAIp0,1004
150
150
  shotgun/utils/source_detection.py,sha256=Co6Q03R3fT771TF3RzB-70stfjNP2S4F_ArZKibwzm8,454
151
151
  shotgun/utils/update_checker.py,sha256=5pK9ZXEjgnE-BQLvibm9Fj6SJHVYeG0U-WspRf0bJec,9660
152
- shotgun_sh-0.2.10.dev8.dist-info/METADATA,sha256=YTOOomif6XHudm8MJyeJLK_7iDZgAFCl8Bv_RGTVEyA,4670
153
- shotgun_sh-0.2.10.dev8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
154
- shotgun_sh-0.2.10.dev8.dist-info/entry_points.txt,sha256=GQmtjKaPtviqYOuB3C0SMGlG5RZS9-VDDIKxV_IVHmY,75
155
- shotgun_sh-0.2.10.dev8.dist-info/licenses/LICENSE,sha256=YebsZl590zCHrF_acCU5pmNt0pnAfD2DmAnevJPB1tY,1065
156
- shotgun_sh-0.2.10.dev8.dist-info/RECORD,,
152
+ shotgun_sh-0.2.10.dev10.dist-info/METADATA,sha256=dF-vALHIz2ejuSzp-8g5wMbaiPLe7ng0FU4sJjiIjjo,4671
153
+ shotgun_sh-0.2.10.dev10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
154
+ shotgun_sh-0.2.10.dev10.dist-info/entry_points.txt,sha256=GQmtjKaPtviqYOuB3C0SMGlG5RZS9-VDDIKxV_IVHmY,75
155
+ shotgun_sh-0.2.10.dev10.dist-info/licenses/LICENSE,sha256=YebsZl590zCHrF_acCU5pmNt0pnAfD2DmAnevJPB1tY,1065
156
+ shotgun_sh-0.2.10.dev10.dist-info/RECORD,,