git-juggler 0.2.12__tar.gz → 0.2.15__tar.gz

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 (214) hide show
  1. {git_juggler-0.2.12 → git_juggler-0.2.15}/.github/workflows/build-package.yml +41 -2
  2. {git_juggler-0.2.12 → git_juggler-0.2.15}/PKG-INFO +1 -1
  3. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/hooks.py +141 -14
  4. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/agents.py +28 -2
  5. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/config.py +4 -0
  6. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/config.py +13 -0
  7. git_juggler-0.2.15/src/backend/git_juggler/frontend_dist/assets/index-0n6Gh9f_.css +1 -0
  8. git_juggler-0.2.15/src/backend/git_juggler/frontend_dist/assets/index-CB9Jhv3C.js +23 -0
  9. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/frontend_dist/index.html +2 -2
  10. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/data.py +26 -4
  11. git_juggler-0.2.15/src/backend/git_juggler/git/detail.py +106 -0
  12. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/refs.py +6 -1
  13. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/schemas.py +12 -0
  14. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/conftest.py +1 -0
  15. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_agent_hooks.py +58 -0
  16. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_ci_poll_route.py +7 -6
  17. git_juggler-0.2.15/src/backend/tests/test_commit_detail.py +120 -0
  18. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_git_data.py +29 -0
  19. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_opencode_sessions.py +8 -2
  20. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_preferences.py +13 -0
  21. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/App.css +65 -166
  22. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/api/client.ts +13 -2
  23. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/api/types.ts +12 -0
  24. git_juggler-0.2.15/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +67 -0
  25. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +4 -6
  26. git_juggler-0.2.15/src/frontend/src/components/GraphToolbar/GraphToolbar.tsx +284 -0
  27. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/AgentsSettings.tsx +12 -4
  28. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/RepoPathsSettings.tsx +17 -0
  29. git_juggler-0.2.15/src/frontend/src/components/Search/SearchBox.tsx +133 -0
  30. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Sidebar/RepoList.tsx +2 -7
  31. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Tabs/TabsBar.tsx +8 -6
  32. git_juggler-0.2.15/src/frontend/src/components/Tags/CreateTagDialog.tsx +178 -0
  33. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Terminal/TerminalPanel.tsx +3 -2
  34. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/agents.ts +1 -0
  35. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/graph.ts +10 -1
  36. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/repos.ts +6 -0
  37. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/storeCore.ts +922 -216
  38. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/styles/sidebar.css +0 -7
  39. git_juggler-0.2.12/src/backend/git_juggler/frontend_dist/assets/index-C6HbHDsB.css +0 -1
  40. git_juggler-0.2.12/src/backend/git_juggler/frontend_dist/assets/index-YHzN7wEh.js +0 -23
  41. git_juggler-0.2.12/src/backend/git_juggler/git/detail.py +0 -70
  42. git_juggler-0.2.12/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -129
  43. git_juggler-0.2.12/src/frontend/src/components/Search/SearchBox.tsx +0 -142
  44. git_juggler-0.2.12/src/frontend/src/components/Search/TriSwitch.tsx +0 -123
  45. {git_juggler-0.2.12 → git_juggler-0.2.15}/.gitignore +0 -0
  46. {git_juggler-0.2.12 → git_juggler-0.2.15}/CLAUDE.md +0 -0
  47. {git_juggler-0.2.12 → git_juggler-0.2.15}/LICENSE +0 -0
  48. {git_juggler-0.2.12 → git_juggler-0.2.15}/README.md +0 -0
  49. {git_juggler-0.2.12 → git_juggler-0.2.15}/notes.txt +0 -0
  50. {git_juggler-0.2.12 → git_juggler-0.2.15}/pyproject.toml +0 -0
  51. {git_juggler-0.2.12 → git_juggler-0.2.15}/resources/git-juggler-demo-1.gif +0 -0
  52. {git_juggler-0.2.12 → git_juggler-0.2.15}/resources/git-juggler-demo-2.png +0 -0
  53. {git_juggler-0.2.12 → git_juggler-0.2.15}/resources/git-juggler-demo-3.png +0 -0
  54. {git_juggler-0.2.12 → git_juggler-0.2.15}/resources/git-juggler-demo-4.png +0 -0
  55. {git_juggler-0.2.12 → git_juggler-0.2.15}/scripts/build-package.sh +0 -0
  56. {git_juggler-0.2.12 → git_juggler-0.2.15}/scripts/dev.sh +0 -0
  57. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/__init__.py +0 -0
  58. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/__init__.py +0 -0
  59. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/hook_events.py +0 -0
  60. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/__init__.py +0 -0
  61. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/activity_models.py +0 -0
  62. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/claude_sessions.py +0 -0
  63. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/claude_transcripts.py +0 -0
  64. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/git_resolver.py +0 -0
  65. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/opencode_sessions.py +0 -0
  66. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/paths.py +0 -0
  67. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/agents/tracking/text.py +0 -0
  68. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/__init__.py +0 -0
  69. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/ci.py +0 -0
  70. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/git.py +0 -0
  71. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/repos.py +0 -0
  72. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/api/terminal.py +0 -0
  73. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/app.py +0 -0
  74. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ci/__init__.py +0 -0
  75. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ci/github_actions.py +0 -0
  76. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ci/http.py +0 -0
  77. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ci/jenkins.py +0 -0
  78. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ci/run_cache.py +0 -0
  79. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/dev_app.py +0 -0
  80. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/frontend_dist/agent-logos/claude.png +0 -0
  81. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/frontend_dist/agent-logos/opencode.webp +0 -0
  82. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/frontend_dist/ci-logos/jenkins.webp +0 -0
  83. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/frontend_dist/git-juggler.png +0 -0
  84. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/frontend_dist/logo.png +0 -0
  85. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/__init__.py +0 -0
  86. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/command.py +0 -0
  87. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/diff.py +0 -0
  88. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/gitfiles.py +0 -0
  89. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/status.py +0 -0
  90. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/git/utils.py +0 -0
  91. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/main.py +0 -0
  92. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/repo_discovery.py +0 -0
  93. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/terminal.py +0 -0
  94. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/themes.py +0 -0
  95. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ui/__init__.py +0 -0
  96. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/git_juggler/ui/folder_picker.py +0 -0
  97. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/hook_event_files.py +0 -0
  98. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_agent_tracking.py +0 -0
  99. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_ci.py +0 -0
  100. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_ci_poll.py +0 -0
  101. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_claude_sessions_data.py +0 -0
  102. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_file_diff.py +0 -0
  103. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_github_actions.py +0 -0
  104. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_jenkins.py +0 -0
  105. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_pick_folder.py +0 -0
  106. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_repos.py +0 -0
  107. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_run_cache.py +0 -0
  108. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/backend/tests/test_themes.py +0 -0
  109. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/.gitignore +0 -0
  110. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/README.md +0 -0
  111. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/index.html +0 -0
  112. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/package-lock.json +0 -0
  113. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/package.json +0 -0
  114. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/public/agent-logos/claude.png +0 -0
  115. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/public/agent-logos/opencode.webp +0 -0
  116. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/public/ci-logos/jenkins.webp +0 -0
  117. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/public/git-juggler.png +0 -0
  118. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/public/logo.png +0 -0
  119. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/App.tsx +0 -0
  120. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -0
  121. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Agents/AgentHoverCard.tsx +0 -0
  122. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Agents/AgentStatusPopover.tsx +0 -0
  123. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Agents/agentFormat.ts +0 -0
  124. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
  125. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
  126. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
  127. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
  128. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Badges/ciStatus.tsx +0 -0
  129. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
  130. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/CIRunPopover.tsx +0 -0
  131. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/CommandPalette/CommandPalette.tsx +0 -0
  132. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
  133. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Commits/CommitList.tsx +0 -0
  134. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Commits/CommitRow.tsx +0 -0
  135. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Commits/LineCounts.tsx +0 -0
  136. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Commits/UncommittedRow.tsx +0 -0
  137. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +0 -0
  138. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +0 -0
  139. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/ContextMenu/CreateBranchModal.tsx +0 -0
  140. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
  141. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Diff/FileDiffModal.tsx +0 -0
  142. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Graph/GraphPanel.tsx +0 -0
  143. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Graph/branchColor.ts +0 -0
  144. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
  145. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Graph/laneWidth.ts +0 -0
  146. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/AgentQuickPicks.tsx +0 -0
  147. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/CiPollField.tsx +0 -0
  148. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
  149. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/GitHubSettings.tsx +0 -0
  150. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/JenkinsSettings.tsx +0 -0
  151. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/MainMenu.tsx +0 -0
  152. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/ThemePicker.tsx +0 -0
  153. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Menu/ThemeQuickPicks.tsx +0 -0
  154. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +0 -0
  155. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Search/MultiSelect.tsx +0 -0
  156. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Sidebar/NewGroupModal.tsx +0 -0
  157. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Sidebar/Sidebar.tsx +0 -0
  158. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Sidebar/panelKit.tsx +0 -0
  159. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
  160. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/Welcome/WelcomeWizard.tsx +0 -0
  161. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/icons.tsx +0 -0
  162. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/inputs/NumberField.tsx +0 -0
  163. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/inputs/TextField.tsx +0 -0
  164. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/inputs/ToggleField.tsx +0 -0
  165. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/components/inputs/styles.ts +0 -0
  166. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/index.css +0 -0
  167. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/index.tsx +0 -0
  168. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/agentRings.ts +0 -0
  169. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/agentStatusPopoverData.ts +0 -0
  170. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/appTheme.ts +0 -0
  171. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/branchVisibility.ts +0 -0
  172. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/bundledThemes.ts +0 -0
  173. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/ciPoll.ts +0 -0
  174. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/ciRunPopoverData.ts +0 -0
  175. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/color.ts +0 -0
  176. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
  177. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/flip.ts +0 -0
  178. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/formatDate.ts +0 -0
  179. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/preferenceSender.ts +0 -0
  180. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/scrollToCommit.ts +0 -0
  181. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/sideBySide.ts +0 -0
  182. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/stageWindow.ts +0 -0
  183. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/stripAnsi.ts +0 -0
  184. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/swallowNextClick.ts +0 -0
  185. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/lib/vscodeTheme.ts +0 -0
  186. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/ci.ts +0 -0
  187. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/overlayStack.ts +0 -0
  188. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/overlays.ts +0 -0
  189. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/preferenceSync.ts +0 -0
  190. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/preferences.ts +0 -0
  191. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/settings.ts +0 -0
  192. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/store.ts +0 -0
  193. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/tabs.ts +0 -0
  194. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/terminal.ts +0 -0
  195. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/state/themes.ts +0 -0
  196. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/styles/ci.css +0 -0
  197. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/styles/commits.css +0 -0
  198. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/styles/graph.css +0 -0
  199. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/styles/menu.css +0 -0
  200. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/src/styles/terminal.css +0 -0
  201. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/agentFormat.test.ts +0 -0
  202. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/agentRings.test.ts +0 -0
  203. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/agentStatusPopoverData.test.ts +0 -0
  204. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/ciPoll.test.ts +0 -0
  205. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/ciRunPopoverData.test.ts +0 -0
  206. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/computeColumns.test.ts +0 -0
  207. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/preferenceSender.test.ts +0 -0
  208. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/sideBySide.test.ts +0 -0
  209. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/stageWindow.test.ts +0 -0
  210. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tests/vscodeTheme.test.ts +0 -0
  211. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tsconfig.app.json +0 -0
  212. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tsconfig.json +0 -0
  213. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/tsconfig.node.json +0 -0
  214. {git_juggler-0.2.12 → git_juggler-0.2.15}/src/frontend/vite.config.ts +0 -0
@@ -3,7 +3,6 @@ name: Build Python package
3
3
  on:
4
4
  push:
5
5
  tags: ["*"]
6
- workflow_dispatch:
7
6
 
8
7
  jobs:
9
8
  validate-tag:
@@ -18,10 +17,49 @@ jobs:
18
17
  fi
19
18
  fi
20
19
 
21
- build:
20
+ test:
22
21
  needs: validate-tag
23
22
  runs-on: ubuntu-latest
24
23
 
24
+ steps:
25
+ - name: Check out repository
26
+ uses: actions/checkout@v4
27
+ with:
28
+ fetch-depth: 0 # full history + tags, needed by hatch-vcs to derive the version
29
+
30
+ - name: Set up Node.js
31
+ uses: actions/setup-node@v4
32
+ with:
33
+ node-version: 22
34
+ cache: npm
35
+ cache-dependency-path: src/frontend/package-lock.json
36
+
37
+ - name: Set up Python
38
+ uses: actions/setup-python@v5
39
+ with:
40
+ python-version: "3.12"
41
+
42
+ - name: Install Python test tooling
43
+ run: python -m pip install --upgrade pip pytest
44
+
45
+ - name: Install backend test dependencies
46
+ run: python -m pip install -e .
47
+
48
+ - name: Run backend tests
49
+ run: python -m pytest src/backend/tests
50
+
51
+ - name: Install frontend dependencies
52
+ working-directory: src/frontend
53
+ run: npm ci
54
+
55
+ - name: Run frontend tests
56
+ working-directory: src/frontend
57
+ run: npm test
58
+
59
+ build:
60
+ needs: test
61
+ runs-on: ubuntu-latest
62
+
25
63
  steps:
26
64
  - name: Check out repository
27
65
  uses: actions/checkout@v4
@@ -54,6 +92,7 @@ jobs:
54
92
  if-no-files-found: error
55
93
 
56
94
  publish:
95
+ if: github.ref_type == 'tag'
57
96
  needs: build
58
97
  runs-on: ubuntu-latest
59
98
  environment:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: git-juggler
3
- Version: 0.2.12
3
+ Version: 0.2.15
4
4
  Summary: A local Git dashboard for people juggling agents and too many repos.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -19,7 +19,9 @@ SESSIONS_DIR = DATA_DIR / "agent-sessions"
19
19
  EVENT_PATH = DATA_DIR / "agent-events.jsonl"
20
20
  RECORDER_PATH = DATA_DIR / "agent-hook-recorder.py"
21
21
  CLAUDE_SETTINGS_PATH = Path.home() / ".claude" / "settings.json"
22
- OPENCODE_PLUGIN_PATH = Path.home() / ".config" / "opencode" / "plugins" / "git-juggler.js"
22
+ OPENCODE_PLUGIN_PATH = (
23
+ Path.home() / ".config" / "opencode" / "plugins" / "git-juggler.js"
24
+ )
23
25
 
24
26
  # Shared by the reader and both hooks (injected into their source below).
25
27
  PROMPT_PHASES = ("userpromptsubmit",)
@@ -31,7 +33,17 @@ SESSION_EVENT_LIMIT = 300
31
33
  COMPACT_BYTES = 1024 * 1024
32
34
  # The payload fields the reader uses. Everything else (tool output, file
33
35
  # contents, the prompt text, ...) is never written.
34
- RAW_KEYS = ("session_id", "sessionId", "sessionID", "cwd", "worktree", "directory", "tool_name", "tool", "command")
36
+ RAW_KEYS = (
37
+ "session_id",
38
+ "sessionId",
39
+ "sessionID",
40
+ "cwd",
41
+ "worktree",
42
+ "directory",
43
+ "tool_name",
44
+ "tool",
45
+ "command",
46
+ )
35
47
  INPUT_KEYS = ("command", "file_path", "filePath", "path")
36
48
 
37
49
 
@@ -58,7 +70,11 @@ def slim_raw(raw: dict) -> dict:
58
70
  kept[container] = inputs
59
71
  event = raw.get("event")
60
72
  if isinstance(event, dict):
61
- slim_event = {key: event[key] for key in ("type", "sessionID", "session_id", "sessionId") if key in event}
73
+ slim_event = {
74
+ key: event[key]
75
+ for key in ("type", "sessionID", "session_id", "sessionId")
76
+ if key in event
77
+ }
62
78
  properties = event.get("properties")
63
79
  if isinstance(properties, dict):
64
80
  slim_properties: dict = {}
@@ -231,8 +247,16 @@ def _claude_snippet_dict() -> dict:
231
247
  "hooks": {
232
248
  "SessionStart": [_claude_hook_entry("SessionStart")],
233
249
  "UserPromptSubmit": [_claude_hook_entry("UserPromptSubmit")],
234
- "PreToolUse": [_claude_hook_entry("PreToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS|Task")],
235
- "PostToolUse": [_claude_hook_entry("PostToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS|Task")],
250
+ "PreToolUse": [
251
+ _claude_hook_entry(
252
+ "PreToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS|Task"
253
+ )
254
+ ],
255
+ "PostToolUse": [
256
+ _claude_hook_entry(
257
+ "PostToolUse", "Bash|Edit|MultiEdit|Write|Read|Glob|Grep|LS|Task"
258
+ )
259
+ ],
236
260
  "SessionEnd": [_claude_hook_entry("SessionEnd")],
237
261
  }
238
262
  }
@@ -246,7 +270,7 @@ OPENCODE_PLUGIN_MARKER = "git-juggler-plugin v4"
246
270
 
247
271
  # Loaded once per OpenCode process (no spawn per event); it writes the same
248
272
  # per-session files with the same trimming as the recorder.
249
- _OPENCODE_PLUGIN_TEMPLATE = r'''// git-juggler global activity hook. Managed by git-juggler. __MARKER__
273
+ _OPENCODE_PLUGIN_TEMPLATE = r"""// git-juggler global activity hook. Managed by git-juggler. __MARKER__
250
274
  import fs from "node:fs"
251
275
  import os from "node:os"
252
276
  import path from "node:path"
@@ -358,7 +382,7 @@ export const GitJugglerPlugin = async (ctx) => {
358
382
  },
359
383
  }
360
384
  }
361
- '''
385
+ """
362
386
 
363
387
  RECORDER_SCRIPT = _inject(_RECORDER_TEMPLATE)
364
388
  OPENCODE_PLUGIN = _inject(_OPENCODE_PLUGIN_TEMPLATE)
@@ -395,7 +419,13 @@ def _has_claude_hook(settings: dict) -> bool:
395
419
  hooks = settings.get("hooks")
396
420
  if not isinstance(hooks, dict):
397
421
  return False
398
- for phase in ("SessionStart", "UserPromptSubmit", "PreToolUse", "PostToolUse", "SessionEnd"):
422
+ for phase in (
423
+ "SessionStart",
424
+ "UserPromptSubmit",
425
+ "PreToolUse",
426
+ "PostToolUse",
427
+ "SessionEnd",
428
+ ):
399
429
  entries = hooks.get(phase)
400
430
  if not isinstance(entries, list):
401
431
  return False
@@ -404,13 +434,61 @@ def _has_claude_hook(settings: dict) -> bool:
404
434
  if not isinstance(entry, dict):
405
435
  continue
406
436
  for hook in entry.get("hooks", []):
407
- if isinstance(hook, dict) and _is_git_juggler_command(str(hook.get("command", ""))):
437
+ if isinstance(hook, dict) and _is_git_juggler_command(
438
+ str(hook.get("command", ""))
439
+ ):
408
440
  found = True
409
441
  if not found:
410
442
  return False
411
443
  return True
412
444
 
413
445
 
446
+ def _remove_git_juggler_claude_hooks(settings: dict) -> bool:
447
+ hooks = settings.get("hooks")
448
+ if not isinstance(hooks, dict):
449
+ return False
450
+ changed = False
451
+ for phase, entries in list(hooks.items()):
452
+ if not isinstance(entries, list):
453
+ continue
454
+ new_entries = []
455
+ for entry in entries:
456
+ if not isinstance(entry, dict):
457
+ new_entries.append(entry)
458
+ continue
459
+ hook_list = entry.get("hooks")
460
+ if not isinstance(hook_list, list):
461
+ new_entries.append(entry)
462
+ continue
463
+ kept_hooks = [
464
+ hook
465
+ for hook in hook_list
466
+ if not (
467
+ isinstance(hook, dict)
468
+ and _is_git_juggler_command(str(hook.get("command", "")))
469
+ )
470
+ ]
471
+ if len(kept_hooks) == len(hook_list):
472
+ new_entries.append(entry)
473
+ continue
474
+ changed = True
475
+ if kept_hooks:
476
+ new_entry = dict(entry)
477
+ new_entry["hooks"] = kept_hooks
478
+ new_entries.append(new_entry)
479
+ elif set(entry.keys()).difference({"matcher", "hooks"}):
480
+ # Preserve unusual user-authored metadata rather than guessing it
481
+ # is safe to delete the whole entry.
482
+ new_entry = dict(entry)
483
+ new_entry["hooks"] = []
484
+ new_entries.append(new_entry)
485
+ if new_entries:
486
+ hooks[phase] = new_entries
487
+ else:
488
+ del hooks[phase]
489
+ return changed
490
+
491
+
414
492
  def claude_status() -> HookProviderStatus:
415
493
  settings, error = _load_json(CLAUDE_SETTINGS_PATH)
416
494
  installed = bool(settings is not None and _has_claude_hook(settings))
@@ -429,7 +507,11 @@ def opencode_status() -> HookProviderStatus:
429
507
  error = None
430
508
  installed = False
431
509
  try:
432
- text = OPENCODE_PLUGIN_PATH.read_text(encoding="utf-8") if OPENCODE_PLUGIN_PATH.exists() else ""
510
+ text = (
511
+ OPENCODE_PLUGIN_PATH.read_text(encoding="utf-8")
512
+ if OPENCODE_PLUGIN_PATH.exists()
513
+ else ""
514
+ )
433
515
  # The marker rules out plugins installed before session ids and prompt events were recorded.
434
516
  installed = "GitJugglerPlugin" in text and OPENCODE_PLUGIN_MARKER in text
435
517
  except OSError as exc:
@@ -488,7 +570,8 @@ def install_claude_hooks() -> HookProviderStatus:
488
570
  error=f"Cannot auto-install because Claude hooks.{phase} is not a list.",
489
571
  )
490
572
  if not any(
491
- isinstance(hook, dict) and _is_git_juggler_command(str(hook.get("command", "")))
573
+ isinstance(hook, dict)
574
+ and _is_git_juggler_command(str(hook.get("command", "")))
492
575
  for entry in existing
493
576
  if isinstance(entry, dict)
494
577
  for hook in entry.get("hooks", [])
@@ -497,7 +580,9 @@ def install_claude_hooks() -> HookProviderStatus:
497
580
  existing.extend(entries)
498
581
 
499
582
  CLAUDE_SETTINGS_PATH.parent.mkdir(parents=True, exist_ok=True)
500
- CLAUDE_SETTINGS_PATH.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
583
+ CLAUDE_SETTINGS_PATH.write_text(
584
+ json.dumps(settings, indent=2) + "\n", encoding="utf-8"
585
+ )
501
586
  return claude_status()
502
587
 
503
588
 
@@ -516,14 +601,56 @@ def upgrade_installed_hooks() -> None:
516
601
  on its next start. Never installs anything the user hasn't.
517
602
  """
518
603
  try:
519
- if RECORDER_PATH.exists() and RECORDER_PATH.read_text(encoding="utf-8") != RECORDER_SCRIPT:
604
+ if (
605
+ RECORDER_PATH.exists()
606
+ and RECORDER_PATH.read_text(encoding="utf-8") != RECORDER_SCRIPT
607
+ ):
520
608
  ensure_recorder_script()
521
609
  except OSError:
522
610
  pass
523
611
  try:
524
612
  if OPENCODE_PLUGIN_PATH.exists():
525
613
  text = OPENCODE_PLUGIN_PATH.read_text(encoding="utf-8")
526
- if "GitJugglerPlugin" in text and "Managed by git-juggler" in text and text != OPENCODE_PLUGIN:
614
+ if (
615
+ "GitJugglerPlugin" in text
616
+ and "Managed by git-juggler" in text
617
+ and text != OPENCODE_PLUGIN
618
+ ):
527
619
  OPENCODE_PLUGIN_PATH.write_text(OPENCODE_PLUGIN, encoding="utf-8")
528
620
  except OSError:
529
621
  pass
622
+
623
+
624
+ def uninstall_claude_hooks() -> HookProviderStatus:
625
+ settings, error = _load_json(CLAUDE_SETTINGS_PATH)
626
+ if settings is None:
627
+ return HookProviderStatus(
628
+ provider="claude",
629
+ installed=False,
630
+ config_path=str(CLAUDE_SETTINGS_PATH),
631
+ event_path=str(EVENT_PATH),
632
+ snippet=claude_snippet(),
633
+ description=claude_status().description,
634
+ error=f"Cannot auto-remove from invalid Claude settings: {error}",
635
+ )
636
+ if _remove_git_juggler_claude_hooks(settings):
637
+ CLAUDE_SETTINGS_PATH.parent.mkdir(parents=True, exist_ok=True)
638
+ CLAUDE_SETTINGS_PATH.write_text(
639
+ json.dumps(settings, indent=2) + "\n", encoding="utf-8"
640
+ )
641
+ return claude_status()
642
+
643
+
644
+ def uninstall_opencode_hooks() -> HookProviderStatus:
645
+ error = None
646
+ try:
647
+ if OPENCODE_PLUGIN_PATH.exists():
648
+ text = OPENCODE_PLUGIN_PATH.read_text(encoding="utf-8")
649
+ if "GitJugglerPlugin" in text and "git-juggler-plugin" in text:
650
+ OPENCODE_PLUGIN_PATH.unlink()
651
+ else:
652
+ error = "Not removing because the OpenCode plugin file does not look managed by git-juggler."
653
+ except OSError as exc:
654
+ error = str(exc)
655
+ status = opencode_status()
656
+ return HookProviderStatus(**{**status.__dict__, "error": error or status.error})
@@ -3,7 +3,15 @@ from __future__ import annotations
3
3
  from fastapi import APIRouter
4
4
 
5
5
  from ..agents.hook_events import AgentHookEventReader
6
- from ..agents.hooks import EVENT_PATH, SESSIONS_DIR, hooks_status, install_claude_hooks, install_opencode_hooks
6
+ from ..agents.hooks import (
7
+ EVENT_PATH,
8
+ SESSIONS_DIR,
9
+ hooks_status,
10
+ install_claude_hooks,
11
+ install_opencode_hooks,
12
+ uninstall_claude_hooks,
13
+ uninstall_opencode_hooks,
14
+ )
7
15
  from ..agents.tracking.activity_models import AgentRepositoryScan
8
16
  from ..schemas import (
9
17
  AgentActivityResponse,
@@ -14,7 +22,9 @@ from ..schemas import (
14
22
 
15
23
 
16
24
  router = APIRouter()
17
- hook_event_reader = AgentHookEventReader(sessions_dir=SESSIONS_DIR, legacy_event_path=EVENT_PATH)
25
+ hook_event_reader = AgentHookEventReader(
26
+ sessions_dir=SESSIONS_DIR, legacy_event_path=EVENT_PATH
27
+ )
18
28
 
19
29
 
20
30
  def _agent_scan_response(scan: AgentRepositoryScan) -> AgentRepositoryScanResponse:
@@ -100,3 +110,19 @@ def api_install_claude_hooks() -> AgentHookProviderStatusResponse:
100
110
  )
101
111
  def api_install_opencode_hooks() -> AgentHookProviderStatusResponse:
102
112
  return _hook_status_response(install_opencode_hooks())
113
+
114
+
115
+ @router.delete(
116
+ "/api/agents/hooks/claude",
117
+ response_model=AgentHookProviderStatusResponse,
118
+ )
119
+ def api_uninstall_claude_hooks() -> AgentHookProviderStatusResponse:
120
+ return _hook_status_response(uninstall_claude_hooks())
121
+
122
+
123
+ @router.delete(
124
+ "/api/agents/hooks/opencode",
125
+ response_model=AgentHookProviderStatusResponse,
126
+ )
127
+ def api_uninstall_opencode_hooks() -> AgentHookProviderStatusResponse:
128
+ return _hook_status_response(uninstall_opencode_hooks())
@@ -28,6 +28,7 @@ def create_config_router(root_path: Path) -> APIRouter:
28
28
  pinned_repo_paths=config.load_pinned_repo_paths(),
29
29
  repo_groups=config.load_repo_groups(),
30
30
  excluded_paths=config.load_excluded_paths(),
31
+ graph_page_size=config.load_graph_page_size(),
31
32
  github=config.load_github_config(),
32
33
  jenkins=config.load_jenkins_config(),
33
34
  terminal_shell=terminal_shell,
@@ -63,6 +64,9 @@ def create_config_router(root_path: Path) -> APIRouter:
63
64
  if body.excluded_paths is not None:
64
65
  config.save_excluded_paths(body.excluded_paths)
65
66
 
67
+ if body.graph_page_size is not None:
68
+ config.save_graph_page_size(body.graph_page_size)
69
+
66
70
  if body.github is not None:
67
71
  config.save_github_config(body.github.model_dump())
68
72
 
@@ -158,6 +158,19 @@ def save_excluded_paths(paths: list[str]) -> None:
158
158
  _save_raw(data)
159
159
 
160
160
 
161
+ def load_graph_page_size() -> int:
162
+ raw = _load_raw().get("graph_page_size")
163
+ if not isinstance(raw, int):
164
+ return 500
165
+ return min(5000, max(1, raw))
166
+
167
+
168
+ def save_graph_page_size(size: int) -> None:
169
+ data = _load_raw()
170
+ data["graph_page_size"] = min(5000, max(1, size))
171
+ _save_raw(data)
172
+
173
+
161
174
  def load_github_config() -> dict | None:
162
175
  raw = _load_raw().get("github")
163
176
  return raw if isinstance(raw, dict) else None
@@ -0,0 +1 @@
1
+ :root,:root[data-theme=dark]{--text:#c8ccd4;--text-dim:#7d8590;--text-h:#f0f2f5;--bg:#0d1117;--panel-bg:#12151b;--hover-bg:#1c2129;--active-bg:#202632;--border:#262b33;--accent:#4c9aff;--accent-fg:#0d1117;--danger:#ff5630;--danger-fg:#ffb4b4;--danger-bg:#4a1f24;--warning:#ffd666;--success:#36b37e;--menu-bg:#12151b;--input-bg:#0d1117;--shadow:#00000080;--shadow-soft:#00000073;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}:root[data-theme=light]{--text:#3a3f46;--text-dim:#6e7781;--text-h:#1f2328;--bg:#fff;--panel-bg:#f6f8fa;--hover-bg:#eef0f2;--active-bg:#e4e7eb;--border:#d0d7de;--accent:#0969da;--accent-fg:#fff;--danger:#cf222e;--danger-fg:#a40e26;--danger-bg:#ffebe9;--warning:#9a6700;--success:#1a7f37;--menu-bg:#fff;--input-bg:#fff;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root{color:var(--text);background:var(--bg);-webkit-font-smoothing:antialiased;font:13px/1.4 -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif}*{box-sizing:border-box}.sidebar-content,.graph-and-list{scrollbar-width:thin;scrollbar-color:color-mix(in srgb, var(--text-dim) 40%, transparent) transparent;scrollbar-width:thin;scrollbar-color:transparent transparent}.sidebar-content:hover,.graph-and-list:hover{scrollbar-color:color-mix(in srgb, var(--text-dim) 40%, transparent) transparent}body{margin:0}#root{width:100vw;height:100vh}.menu-overlay{background:var(--shadow);z-index:100;justify-content:center;align-items:flex-start;padding-top:12vh;transition:background-color .12s;display:flex;position:fixed;inset:0}.menu-overlay.previewing-theme{background:0 0}.menu-dialog{background:var(--menu-bg);border:1px solid var(--border);width:760px;max-width:calc(100vw - 32px);height:640px;max-height:64vh;box-shadow:0 16px 48px var(--shadow);border-radius:8px;display:flex;overflow:hidden}.menu-sidebar{background:var(--bg);border-right:1px solid var(--border);flex:none;width:140px;padding:8px 0}.command-palette-overlay{background:var(--shadow);z-index:110;justify-content:center;align-items:flex-start;padding-top:12vh;display:flex;position:fixed;inset:0}.command-palette-dialog{background:var(--menu-bg);border:1px solid var(--border);width:560px;max-width:calc(100vw - 32px);max-height:60vh;box-shadow:0 16px 48px var(--shadow);border-radius:8px;flex-direction:column;display:flex;overflow:hidden}.command-palette-input{background:var(--input-bg);border:0;border-bottom:1px solid var(--border);color:var(--text);font:inherit;border-radius:0;flex:none;padding:14px 18px;font-size:15px}.command-palette-input:focus{outline:none}.command-palette-results{flex:1;min-height:0;padding:6px;overflow-y:auto}.command-palette-item{color:var(--text);cursor:pointer;border-radius:5px;justify-content:space-between;align-items:center;gap:12px;padding:8px 10px;font-size:13px;display:flex}.command-palette-item.active{background:var(--active-bg);color:var(--text-h)}.command-palette-item-label{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.command-palette-item-meta{text-overflow:ellipsis;white-space:nowrap;max-width:55%;color:var(--text-dim);flex:none;font-size:11px;overflow:hidden}.menu-section-item{color:var(--text-dim);cursor:pointer;padding:8px 16px;font-size:13px}.menu-section-item:hover{background:var(--hover-bg)}.menu-section-item.active{background:var(--active-bg);color:var(--text-h)}.menu-content{flex:1;min-width:0;padding:20px 24px;overflow-y:auto}.menu-section{flex-direction:column;gap:16px;display:flex}.menu-section>*{margin:0}.menu-hint{color:var(--text-dim);margin:0 0 16px;font-size:12px}.menu-path-list{flex-direction:column;gap:4px;margin-bottom:12px;display:flex}.menu-path-row{background:var(--bg);border:1px solid var(--border);border-radius:4px;justify-content:space-between;align-items:center;gap:8px;padding:6px 10px;display:flex}.menu-path-text{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;overflow:hidden}.menu-path-remove{cursor:pointer;color:var(--text-dim);text-align:center;border-radius:3px;flex:none;width:18px;height:18px;line-height:18px}.menu-path-remove:hover{background:var(--hover-bg);color:var(--text-h)}.menu-empty{color:var(--text-dim);padding:6px 0;font-size:13px}.keybinding-value{color:var(--text-dim);white-space:nowrap;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}.keybinding-actions{flex:none;gap:6px;display:flex}.menu-add-path{gap:8px;display:flex}.menu-add-path input{background:var(--input-bg);border:1px solid var(--border);min-width:0;color:var(--text);font:inherit;border-radius:4px;flex:1;padding:6px 8px;font-size:12px}.menu-add-path input:focus{border-color:var(--accent);outline:none}.menu-add-path .menu-add-button{background:var(--accent);color:var(--accent-fg);cursor:pointer;border:none;border-radius:4px;padding:0 14px;font-size:12px;font-weight:600}.menu-add-path .menu-secondary-button{cursor:pointer;border-radius:4px;padding:0 14px;font-size:12px}.menu-error{color:var(--danger-fg);margin-top:10px;font-size:12px}.menu-notice{border:1px solid color-mix(in srgb, var(--accent) 45%, transparent);background:color-mix(in srgb, var(--accent) 12%, transparent);color:var(--text);border-radius:6px;margin:0 0 14px;padding:10px 12px;font-size:12px}.menu-test-result{border:1px solid color-mix(in srgb, var(--text-dim) 45%, transparent);background:color-mix(in srgb, var(--text-dim) 12%, transparent);color:var(--text);border-radius:6px;margin:10px 0 14px;padding:10px 12px;font-size:12px}.menu-test-result.success{border-color:color-mix(in srgb, var(--accent) 45%, transparent);background:color-mix(in srgb, var(--accent) 12%, transparent)}.menu-test-result.error{border-color:color-mix(in srgb, var(--danger-fg) 45%, transparent);background:color-mix(in srgb, var(--danger-fg) 12%, transparent)}.menu-field{color:var(--text-dim);flex-direction:column;gap:5px;margin-bottom:14px;font-size:12px;display:flex}.menu-field>span:first-child{color:var(--text-h);font-size:13px}.menu-field .menu-hint{margin:0 0 3px}.menu-field input,.menu-field textarea,.github-repo-row input,.jenkins-rule-card input{background:var(--input-bg);border:1px solid var(--border);color:var(--text);font:inherit;border-radius:4px;padding:6px 8px;font-size:12px}.menu-field textarea{resize:vertical;min-height:44px}.menu-field input:focus,.menu-field textarea:focus,.github-repo-row input:focus,.jenkins-rule-card input:focus{border-color:var(--accent);outline:none}.create-tag-dialog{background:var(--menu-bg);border:1px solid var(--border);width:420px;max-width:calc(100vw - 32px);max-height:80vh;box-shadow:0 16px 48px var(--shadow);border-radius:8px;flex-direction:column;padding:20px 24px;display:flex;overflow-y:auto}.create-tag-dialog h3{color:var(--text-h);margin:0 0 6px;font-size:15px}.directory-browser-path{color:var(--text-dim);word-break:break-all;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px}.directory-browser-list{border:1px solid var(--border);border-radius:4px;flex-direction:column;gap:2px;max-height:280px;margin:12px 0;padding:4px;display:flex;overflow-y:auto}.directory-browser-entry{cursor:pointer;color:var(--text);border-radius:4px;padding:6px 8px;font-size:13px}.directory-browser-entry:hover{background:var(--hover-bg);color:var(--text-h)}.branch-delete-modal{background:var(--menu-bg);border:1px solid var(--border);width:420px;max-width:calc(100vw - 32px);box-shadow:0 16px 48px var(--shadow);border-radius:8px;flex-direction:column;padding:20px 24px;display:flex}.branch-delete-modal h3{color:var(--text-h);margin:0 0 6px;font-size:15px}.modal-check-row{color:var(--text);align-items:center;gap:8px;margin-top:10px;font-size:12px;display:flex}.menu-primary-button.danger{border-color:var(--danger);color:var(--danger-fg)}.github-repo-mappings{flex-direction:column;gap:8px;display:flex}.github-repo-row{grid-template-columns:minmax(160px,1fr) minmax(80px,.45fr) minmax(80px,.45fr) auto;gap:8px;display:grid}.jenkins-rules{flex-direction:column;gap:10px;display:flex}.jenkins-rule-card{background:var(--panel-bg);border:1px solid var(--border);border-radius:6px;flex-direction:column;gap:10px;padding:10px;display:flex}.jenkins-rule-heading{grid-template-columns:minmax(220px,1fr) auto;align-items:center;gap:8px;display:grid}.jenkins-rule-repo-tests{flex-direction:column;gap:6px;display:flex}.jenkins-rule-repo-test{color:var(--text);grid-template-columns:auto minmax(120px,.35fr) minmax(0,1fr);align-items:center;gap:8px;font-size:12px;display:grid}.jenkins-rule-play{border:1px solid var(--border);background:var(--bg);width:24px;height:24px;color:var(--success);cursor:pointer;border-radius:999px;padding:0;font-size:11px;line-height:22px}.jenkins-rule-play:disabled{cursor:default;opacity:.45}.jenkins-rule-repo-name,.jenkins-rule-test-log{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.jenkins-rule-test-log{color:var(--text-dim)}.jenkins-rule-test-log.success{color:var(--success)}.jenkins-rule-test-log.error{color:var(--danger)}.menu-actions{justify-content:flex-end;gap:8px;margin-top:14px;display:flex}.menu-primary-button,.menu-secondary-button{font:inherit;cursor:pointer;border-radius:4px;padding:6px 10px;font-size:12px}.menu-primary-button{background:var(--accent);border:1px solid var(--accent);color:var(--accent-fg);font-weight:600}.menu-secondary-button{background:var(--bg);border:1px solid var(--border);color:var(--text)}.menu-secondary-button:hover{background:var(--hover-bg);color:var(--text-h)}.menu-secondary-button.danger,.menu-secondary-button.danger:hover{border-color:var(--danger);color:var(--danger-fg)}.menu-setting{justify-content:space-between;align-items:center;gap:16px;display:flex}.menu-setting-main{min-width:0}.menu-setting-label{color:var(--text-h);font-size:13px}.menu-setting .menu-hint{margin:2px 0 0}.agent-hooks-section{flex-direction:column;gap:10px;display:flex}.agent-hooks-section>.menu-actions{margin-top:0}.agent-hooks-section .menu-setting-main .menu-hint{margin:2px 0 0}.agent-hook-grid{gap:12px;margin-top:12px;display:grid}.agent-hook-card{border:1px solid var(--border);background:var(--panel-bg);border-radius:8px;padding:12px}.agent-hook-card-heading{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.agent-hook-status{border:1px solid var(--border);color:var(--text-muted);border-radius:999px;flex:none;padding:2px 8px;font-size:11px}.agent-hook-status.installed{border-color:color-mix(in srgb, var(--success) 45%, transparent);color:var(--success)}.agent-hook-meta{color:var(--text-muted);overflow-wrap:anywhere;margin-top:6px;font-size:12px}.agent-hook-snippet{border:1px solid var(--border);background:var(--bg);max-height:220px;color:var(--text);white-space:pre-wrap;border-radius:6px;margin:10px 0 0;padding:10px;font-size:11px;overflow:auto}.theme-options{gap:6px;display:flex}.theme-options button{background:var(--bg);border:1px solid var(--border);color:var(--text);font:inherit;cursor:pointer;border-radius:4px;padding:5px 12px;font-size:12px}.theme-options button.active{background:var(--accent);border-color:var(--accent);color:var(--accent-fg);font-weight:600}.menu-setting-stacked{flex-direction:column;align-items:stretch;gap:10px}.theme-picker{flex-direction:column;gap:8px;min-width:0;display:flex}.theme-picker-toolbar{gap:8px;display:flex}.theme-picker-toolbar input[type=search]{background:var(--input-bg);border:1px solid var(--border);min-width:0;color:var(--text);font:inherit;border-radius:4px;flex:1;padding:5px 8px;font-size:12px}.theme-picker-toolbar input[type=search]:focus{border-color:var(--accent);outline:none}.theme-list{border:1px solid var(--border);border-radius:6px;max-height:300px;padding:4px;overflow-y:auto}.theme-group-label{letter-spacing:.04em;text-transform:uppercase;color:var(--text-dim);padding:8px 8px 4px;font-size:11px}.theme-row-wrap{align-items:center;display:flex}.theme-row{min-width:0;color:var(--text);font:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:4px;flex:1;align-items:center;gap:10px;padding:5px 8px;font-size:12px;display:flex}.theme-row:hover,.theme-row:focus-visible{background:var(--hover-bg);color:var(--text-h);outline:none}.theme-row.active{background:var(--active-bg);border-color:var(--accent);color:var(--text-h)}.theme-row-name{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.theme-row-kind{color:var(--text-dim);font-size:11px}.theme-row-pin{color:var(--text-dim);cursor:pointer;background:0 0;border:none;padding:4px 6px;font-size:14px;line-height:1}.theme-row-pin:hover,.theme-row-pin.pinned{color:var(--warning)}.theme-row-remove{color:var(--text-dim);cursor:pointer;background:0 0;border:none;padding:4px 8px;font-size:16px;line-height:1}.theme-row-remove:hover{color:var(--danger)}.theme-swatch{border:1px solid;border-radius:4px;flex:none;align-items:center;gap:3px;height:20px;padding:0 5px 0 0;display:inline-flex;overflow:hidden}.theme-swatch-panel{align-self:stretch;width:8px}.theme-swatch-dot{border-radius:50%;width:7px;height:7px}.switch{cursor:pointer;flex:none;align-items:center;display:inline-flex}.switch input{display:none}.switch-track{background:var(--border);border-radius:999px;width:34px;height:18px;transition:background .15s;position:relative}.switch-thumb{background:#fff;border-radius:50%;width:14px;height:14px;transition:transform .15s;position:absolute;top:2px;left:2px}.switch input:checked+.switch-track{background:var(--accent)}.switch input:checked+.switch-track .switch-thumb{transform:translate(16px)}.context-menu{z-index:111;background:var(--menu-bg);border:1px solid var(--border);min-width:180px;box-shadow:0 8px 24px var(--shadow);border-radius:6px;padding:4px;position:fixed}.context-menu-item{color:var(--text);cursor:pointer;border-radius:4px;padding:6px 10px;font-size:13px}.context-menu-item-with-mark{grid-template-columns:18px 1fr;align-items:center;gap:6px;display:grid}.context-menu-item-with-trailing-mark,.context-menu-item-with-arrow{justify-content:space-between;align-items:center;gap:16px;display:flex}.context-menu-submenu{position:relative}.context-menu-submenu-panel{display:none;position:absolute;top:-4px;left:calc(100% + 4px)}.context-menu-submenu:hover>.context-menu-submenu-panel{display:block}.context-menu-separator{background:var(--border);height:1px;margin:4px}.context-menu-item:hover{background:var(--hover-bg);color:var(--text-h)}.welcome-dialog{flex-direction:column;width:560px;height:620px;max-height:85vh;position:relative}.welcome-close{text-align:center;width:22px;height:22px;color:var(--text-dim);cursor:pointer;border-radius:4px;line-height:22px;position:absolute;top:10px;right:12px}.welcome-close:hover{background:var(--hover-bg);color:var(--text-h)}.welcome-header{flex:none;padding:20px 24px 0}.welcome-header h2{color:var(--text-h);margin:0 0 4px;font-size:17px}.welcome-progress-bar{gap:6px;margin:14px 0 8px;display:flex}.welcome-progress-segment{background:var(--border);border-radius:999px;flex:1;height:3px}.welcome-progress-segment.filled{background:var(--accent)}.welcome-progress-caption{letter-spacing:.06em;text-transform:uppercase;color:var(--text-dim);font-size:11px}.welcome-body{flex:1;min-height:0;padding:16px 24px;overflow-y:auto}.welcome-body h3{color:var(--text-h);margin:0 0 6px;font-size:15px}.welcome-footer{border-top:1px solid var(--border);flex:none;justify-content:space-between;align-items:center;gap:12px;padding:14px 24px;display:flex}.welcome-footer-nav{gap:8px;display:flex}.welcome-skip{color:var(--text-dim);cursor:pointer;font-size:13px}.welcome-skip:hover{color:var(--text-h);text-decoration:underline}.theme-quickpicks{flex-direction:column;gap:14px;display:flex}.theme-quickpick-grid{grid-template-columns:repeat(3,1fr);gap:10px;display:grid}.theme-quickpick-card{background:var(--bg);border:1px solid var(--border);cursor:pointer;border-radius:6px;flex-direction:column;gap:8px;padding:8px;display:flex}.theme-quickpick-card:hover{border-color:color-mix(in srgb, var(--accent) 45%, var(--border))}.theme-quickpick-card.active{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.theme-quickpick-preview{border:1px solid;border-radius:4px;align-items:flex-end;gap:4px;height:44px;padding:6px;display:flex}.theme-quickpick-bar{border-radius:999px;flex:1;height:4px}.theme-quickpick-chip{opacity:.7;border-radius:999px;width:16px;height:4px}.theme-quickpick-label{color:var(--text-h);text-align:left;font-size:12px;font-weight:600}.theme-quickpick-editor-section{flex-direction:column;gap:8px;display:flex}.theme-quickpick-mini-grid{border:1px solid var(--border);border-radius:6px;grid-template-columns:repeat(4,1fr);align-content:start;gap:6px;max-height:180px;padding:6px;display:grid;overflow-y:auto}.theme-quickpick-mini-card{background:var(--bg);border:1px solid var(--border);cursor:pointer;border-radius:5px;flex-direction:column;gap:4px;min-width:0;padding:5px;display:flex}.theme-quickpick-mini-card:hover{border-color:color-mix(in srgb, var(--accent) 45%, var(--border))}.theme-quickpick-mini-card.active{border-color:var(--accent);box-shadow:0 0 0 1px var(--accent)}.theme-quickpick-mini-preview{border:1px solid;border-radius:3px;align-items:flex-end;height:20px;padding:3px;display:flex}.theme-quickpick-mini-bar{border-radius:999px;flex:1;height:3px}.theme-quickpick-mini-label{color:var(--text-dim);text-align:left;white-space:nowrap;text-overflow:ellipsis;font-size:10px;overflow:hidden}.theme-quickpick-mini-card.active .theme-quickpick-mini-label{color:var(--text-h)}.agent-quickpicks{flex-direction:column;gap:10px;display:flex}.agent-quickpick-grid{grid-template-columns:repeat(2,1fr);gap:10px;display:grid}.agent-quickpick-card{background:var(--bg);border:1px solid var(--border);border-radius:6px;flex-direction:column;gap:10px;padding:12px;display:flex}.agent-quickpick-heading{align-items:center;gap:8px;display:flex}.agent-quickpick-logo{border:1px solid var(--border);background:#fff;border-radius:6px;flex:none;justify-content:center;align-items:center;width:24px;height:24px;display:flex}.agent-quickpick-logo img{object-fit:contain;width:16px;height:16px;display:block}.agent-quickpick-actions{align-items:center;gap:8px;display:flex}.agent-quickpick-tracking{color:var(--success);font-size:12px;font-weight:600}.agent-quickpick-manual{border-top:1px solid var(--border);padding-top:10px}.theme-loading{color:var(--muted,inherit);align-items:center;gap:8px;padding:8px 4px;font-size:12px;display:flex}.theme-spinner{border:2px solid;border-right-color:#0000;border-radius:50%;width:12px;height:12px;animation:.7s linear infinite theme-spin}@keyframes theme-spin{to{transform:rotate(360deg)}}.sidebar{background:var(--panel-bg);border-right:1px solid var(--border);-webkit-user-select:none;user-select:none;flex-direction:column;flex:none;width:250px;min-width:200px;display:flex;overflow:hidden}.sidebar-content{flex:auto;min-height:0;overflow-y:auto}.sidebar-tabs{border:1px solid var(--border);background:var(--input-bg);border-radius:7px;flex:none;gap:2px;margin:8px;padding:2px;display:flex}.sidebar-tab{min-width:0;color:var(--text-dim);cursor:pointer;font:inherit;white-space:nowrap;background:0 0;border:0;border-radius:5px;flex:1;justify-content:center;align-items:center;gap:6px;padding:5px 0;font-size:12px;font-weight:500;transition:background-color .15s,color .15s;display:inline-flex}.sidebar-tab:hover{color:var(--text-h)}.sidebar-tab.active{background:var(--active-bg);color:var(--text-h)}.sidebar-tab-badge{color:var(--accent);flex:none;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px}.sidebar-tab-badge.idle{color:var(--text-dim)}.tab-agent-badge{background:var(--accent);min-width:15px;color:var(--accent-fg);text-align:center;border-radius:999px;flex:none;padding:0 5px;font-size:10px;line-height:15px}.tab-agent-badge.idle{background:var(--border);color:var(--text-dim)}.sidebar-resize-handle{cursor:col-resize;z-index:4;flex:0 0 5px;margin-left:-3px}.sidebar-resize-handle:hover,.sidebar-resize-handle:active{background:var(--accent);opacity:.5}.repo-search{align-items:stretch;gap:8px;padding:12px 12px 10px;display:flex}.agent-hover-target{pointer-events:all}.agent-hover-card{z-index:60;pointer-events:none;flex-direction:column;gap:10px;display:flex;position:fixed}.repo-search input{border:1px solid var(--border);background:var(--input-bg);min-width:0;color:var(--text);font:inherit;border-radius:6px;flex:1;padding:7px 10px;font-size:12px}.repo-search input:focus{border-color:var(--accent);outline:none}.repo-bulk-button{border:1px solid var(--border);color:var(--text-dim);cursor:pointer;background:0 0;border-radius:6px;flex:0 0 30px;justify-content:center;align-items:center;font-size:13px;line-height:1;display:flex}.repo-bulk-button:hover{background:var(--hover-bg);color:var(--text-h)}.repo-list h2{letter-spacing:.08em;text-transform:uppercase;color:var(--text-dim);justify-content:space-between;align-items:center;gap:8px;margin:0;padding:18px 16px 6px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;font-weight:400;display:flex}.repo-heading-count{letter-spacing:normal;color:var(--text-dim);opacity:.7;flex:none;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px}.repo-item{cursor:pointer;color:var(--text);border-radius:6px;align-items:center;gap:10px;margin:1px 8px 0;padding:6px 8px;display:flex;position:relative}.repo-item:hover{background:var(--hover-bg)}.repo-item.active{background:var(--active-bg);box-shadow:inset 2px 0 0 var(--accent);color:var(--text-h)}.repo-item.active .repo-name{font-weight:600}.repo-item[draggable=true]{cursor:grab}.repo-item[draggable=true]:active{cursor:grabbing}.repo-item.dragging{opacity:.45}.repo-status-dot{background:0 0;border-radius:3px;flex:0 0 6px;width:6px;height:6px}.repo-status-dot.known{background:var(--border)}.repo-status-dot.dirty{background:var(--warning)}.repo-names{flex-direction:column;flex:1;justify-content:center;gap:1px;min-width:0;display:flex}.repo-name{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13px;overflow:hidden}.repo-name-line{align-items:center;gap:6px;min-width:0;display:flex}.repo-agent-tag{border:1px solid var(--border);color:var(--text-dim);border-radius:4px;flex:none;padding:0 5px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;line-height:14px}.repo-agent-tag.active{border-color:color-mix(in srgb, var(--success) 50%, var(--border));color:var(--success)}.repo-branch{color:var(--text-dim);text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px;overflow:hidden}.repo-list .repo-group-heading{box-sizing:border-box;padding:18px 16px 6px;transition:background-color .12s;overflow:hidden}.repo-group-heading:hover{background:color-mix(in srgb, var(--hover-bg) 60%, transparent)}.repo-group-name{text-overflow:ellipsis;white-space:nowrap;cursor:grab;flex:auto;min-width:0;overflow:hidden}.repo-group-display-name{text-transform:uppercase}.repo-group-name:active{cursor:grabbing}.repo-group-name-input{min-width:0;font:inherit;letter-spacing:normal;text-transform:none;color:var(--text-h);background:var(--input-bg);border:1px solid var(--accent);border-radius:4px;outline:none;flex:auto;margin:-3px 0 -3px -5px;padding:2px 4px}.repo-group-collapse{color:var(--text-dim);cursor:pointer;background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:2px;display:flex}.repo-group-collapse:hover{color:var(--text-h)}.repo-group-collapse svg{transition:transform .12s}.repo-group-collapse.collapsed svg{transform:rotate(-90deg)}.repo-select,.repo-group-select{appearance:none;border:1px solid var(--text-dim);cursor:pointer;background:0 0;border-radius:4px;flex:0 0 14px;place-content:center;width:14px;height:14px;margin:0;display:grid}.repo-select:checked,.repo-group-select:checked,.repo-group-select:indeterminate{border-color:var(--accent);background:var(--accent)}.repo-select:checked:after,.repo-group-select:checked:after{content:"✓";color:var(--accent-fg);font-size:10px;line-height:1}.repo-group-select:indeterminate:after{content:"–";color:var(--accent-fg);font-size:10px;line-height:1}.repo-select{margin-left:auto}.repo-group-heading.dragging{opacity:.45}.repo-group-section{transition:background-color .15s}.repo-group-section.collapsed{padding-bottom:0}.repo-bookmark-overlay{z-index:80;position:fixed;inset:0}.repo-bookmark-menu{background:var(--menu-bg);border:1px solid var(--border);min-width:180px;max-width:260px;box-shadow:0 12px 32px var(--shadow-soft);border-radius:6px;padding:6px;position:fixed}.repo-bookmark-menu-item{width:100%;min-height:28px;color:var(--text);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:4px;grid-template-columns:18px 1fr;align-items:center;gap:6px;padding:4px 8px;font-size:12px;display:grid}.repo-bookmark-menu-item:hover{background:var(--hover-bg);color:var(--text-h)}.repo-bookmark-menu-item:disabled{cursor:default;opacity:.5}.repo-bookmark-menu-item:disabled:hover{color:var(--text);background:0 0}.repo-bookmark-menu-separator{background:var(--border);height:1px;margin:5px 4px}.repo-empty{color:var(--text-dim);padding:8px 16px;font-size:13px}.graph-and-list{flex:1;display:flex;overflow-y:auto}.graph-column{background:var(--panel-bg);flex:none}.list-column{flex:1;min-width:0}.graph-panel{transition:height .18s}.fetch-band-shift{transition:transform .18s}@keyframes push-edge-flash{0%,to{opacity:.45;stroke-width:2px}50%{opacity:1;stroke-width:4px}}.push-edge{animation:.9s ease-in-out infinite push-edge-flash}.push-spinner{filter:drop-shadow(0 0 3px color-mix(in srgb, var(--accent) 55%, transparent))}.error-banner{background:var(--danger-bg);color:var(--danger-fg);padding:8px 16px;font-size:13px}.loading-banner{color:var(--text-dim);padding:8px 16px;font-size:13px}.load-more-sentinel{color:var(--text-dim);align-items:center;min-height:1px;padding:0 16px;font-size:11px;font-style:italic;display:flex}.load-more-sentinel--visible{height:var(--load-more-height)}.ghost-row-spacer{color:var(--text-dim);opacity:0;align-items:center;height:0;padding:0 16px;font-size:11px;font-style:italic;transition:height .18s,opacity .18s;display:flex;overflow:hidden}.ghost-row-spacer--visible{height:var(--ghost-row-height);opacity:.7}.commit-row{border-bottom:1px solid var(--border);border-left:3px solid #0000}.commit-row.match{border-left-color:var(--warning)}.commit-row.match .commit-subject{color:var(--warning)}@keyframes commit-row-flash{0%{background-color:color-mix(in srgb, var(--accent) 35%, transparent)}to{background-color:#0000}}.commit-row.flash .commit-row-main{animation:.7s ease-out commit-row-flash}.commit-row-main{cursor:pointer;align-items:center;gap:10px;padding:0 16px;display:flex;overflow:visible}.commit-row-main:hover{background:var(--hover-bg)}.commit-row.expanded .commit-row-main{background:var(--active-bg)}.uncommitted-row .commit-row-main{color:var(--text-h)}.uncommitted-badge{background:color-mix(in srgb, var(--text-dim) 15%, transparent);border-color:var(--text-dim);color:var(--text-dim)}.commit-panel-container{container-type:inline-size}.commit-panel{grid-template-columns:minmax(0,1fr) minmax(240px,1.25fr);gap:24px;max-width:820px;display:grid}@container (width<=560px){.commit-panel{grid-template-columns:minmax(0,1fr);gap:16px}}.commit-panel-compose{flex-direction:column;gap:10px;min-width:0;display:flex}.commit-panel-heading{justify-content:space-between;align-items:center;gap:12px;display:flex}.commit-panel-label{letter-spacing:.08em;text-transform:uppercase;color:var(--text-dim);font-family:ui-monospace,Menlo,Consolas,monospace;font-size:10px}.commit-panel-subject-count{color:var(--text-dim);font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px}.commit-panel-subject-count.over{color:var(--danger)}.commit-panel-message{resize:vertical;background:var(--input-bg);border:1px solid var(--border);min-height:116px;color:var(--text-h);font:inherit;border-radius:8px;padding:10px 12px;font-size:13px;line-height:1.5}.commit-panel-message:focus{border-color:var(--accent);outline:none}.commit-panel-actions{gap:8px;display:flex}.commit-panel-actions button:disabled{cursor:default;opacity:.45;pointer-events:none}.commit-panel-commit{flex:1;justify-content:center;align-items:center;gap:8px;display:flex}.commit-panel-commit-count{opacity:.8;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;font-weight:500}.commit-panel-hint{color:var(--text-dim);font-size:12px;line-height:1.5}.commit-panel-changes{flex-direction:column;min-width:0;display:flex}.commit-panel-changes-header{align-items:center;gap:8px;height:20px;margin-bottom:4px;padding:0 4px;display:flex}.commit-panel-toggle-all{cursor:pointer;display:flex}.commit-panel-changes-title{flex:1}.commit-panel-totals{gap:8px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;display:flex}.lines-added{color:var(--success)}.lines-deleted{color:var(--danger)}.line-counts{gap:6px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;display:flex}.commit-file .line-counts{flex:none}.commit-panel-checkbox{border:1px solid var(--text-dim);width:14px;height:14px;color:var(--accent-fg);text-align:center;border-radius:3px;flex:none;font-size:10px;line-height:12px;transition:background-color .12s}.commit-panel-checkbox.on{border-color:var(--accent);background:var(--accent)}.commit-panel-files{flex-direction:column;font-size:13px;display:flex}.commit-panel-file{cursor:pointer;border-radius:3px;align-items:center;gap:8px;height:20px;padding:0 4px;display:flex}.commit-panel-file:hover{background:var(--hover-bg)}.commit-panel-toggle-all:focus-visible,.commit-panel-file:focus-visible{outline:1px solid var(--accent);outline-offset:-1px}.commit-panel-file .file-status{flex:none}.commit-panel-file.status-untracked .file-status,.commit-panel-file.status-added .file-status{color:var(--success)}.commit-panel-file.status-deleted .file-status{color:var(--danger)}.commit-panel-file.status-modified .file-status{color:var(--warning)}.commit-panel-file.status-renamed .file-status{color:var(--accent)}.commit-panel-file-path{white-space:nowrap;flex:1;align-items:baseline;gap:6px;min-width:0;display:flex;overflow:hidden}.commit-panel-file-name{min-width:0;color:var(--text-h);text-overflow:ellipsis;flex:0 auto;font-size:13px;overflow:hidden}.commit-panel-file-name:hover{text-decoration:underline}.commit-panel-file-dir{min-width:0;color:var(--text-dim);text-overflow:ellipsis;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;overflow:hidden}.commit-panel-file-stats{flex:none;align-items:center;gap:8px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:11px;display:flex}.commit-panel-file-stats .lines-added,.commit-panel-file-stats .lines-deleted{text-align:right;width:30px}.commit-panel-ratio{gap:2px;display:flex}.commit-panel-ratio>span{background:var(--active-bg);border-radius:1px;width:6px;height:6px}.commit-panel-ratio>.ratio-add{background:var(--success)}.commit-panel-ratio>.ratio-del{background:var(--danger)}.commit-hash{color:var(--text-dim);flex:none}.commit-subject{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--text-h);flex:auto;overflow:hidden}.commit-refs{flex:none;gap:4px;display:flex}.commit-author{text-overflow:ellipsis;min-width:0;color:var(--text-dim);white-space:nowrap;flex:0 0 120px;font-size:12px;overflow:hidden}.commit-date{color:var(--text-dim);white-space:nowrap;flex:none;font-size:12px}.badge{white-space:nowrap;border:1px solid;border-radius:999px;align-items:center;gap:3px;padding:1px 7px;font-size:11px;display:inline-flex}.branch-badge.remote{border-style:dashed}.badge-icon{flex:none}.commit-detail{background:var(--panel-bg);border-top:1px dashed var(--border);padding:12px 16px 16px 46px}.commit-detail-loading{color:var(--text-dim);height:20px;font-size:13px}.commit-meta{grid-template-columns:100px 1fr;gap:4px 12px;margin:0 0 12px;font-size:13px;display:grid}.commit-meta dt{color:var(--text-dim)}.commit-meta dd{color:var(--text);word-break:break-all;margin:0}.mono{font-family:ui-monospace,Menlo,Consolas,monospace}.commit-message{color:var(--text-h);white-space:pre-wrap;overflow-wrap:anywhere;margin:0 0 12px;font-size:13px;line-height:18px}.commit-files{font-size:13px}.commit-file{align-items:center;gap:8px;height:20px;display:flex}.file-status{text-align:center;width:14px;font-family:ui-monospace,Menlo,Consolas,monospace;font-weight:600}.commit-file.status-added .file-status{color:var(--success)}.commit-file.status-deleted .file-status{color:var(--danger)}.commit-file.status-modified .file-status{color:var(--warning)}.commit-file.status-renamed .file-status{color:var(--accent)}.commit-file.clickable,.file-path.clickable{cursor:pointer}.commit-file.clickable:hover .file-path,.file-path.clickable:hover{color:var(--text-h);text-decoration:underline}.file-diff-overlay{align-items:center;padding-top:0}.file-diff-dialog{background:var(--menu-bg);border:1px solid var(--border);width:min(1400px,100vw - 48px);height:calc(100vh - 64px);box-shadow:0 16px 48px var(--shadow);border-radius:8px;flex-direction:column;display:flex;overflow:hidden}.file-diff-header{border-bottom:1px solid var(--border);align-items:center;gap:10px;padding:10px 14px;display:flex}.file-diff-title{min-width:0;color:var(--text-h);overflow-wrap:anywhere;flex:1;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:13px}.file-diff-body{min-height:0;overflow:hidden}.file-diff-full-toggle{flex:none;gap:8px!important}.file-diff-full-toggle label{white-space:nowrap}.file-diff-message{color:var(--text-dim);text-align:center;padding:24px;font-size:13px}.file-diff-main{flex:1;min-height:0;display:flex}.file-diff-files{border-right:1px solid var(--border);flex:0 0 260px;padding:6px 0;font-size:12px;overflow:auto}.file-diff-file{cursor:pointer;color:var(--text);align-items:center;gap:8px;padding:3px 12px;display:flex}.file-diff-file:hover{background:var(--hover-bg)}.file-diff-file.active{background:var(--active-bg);color:var(--text-h)}.file-diff-file-name{white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}.file-diff-file.status-added .file-status{color:var(--success)}.file-diff-file.status-deleted .file-status{color:var(--danger)}.file-diff-file.status-modified .file-status{color:var(--warning)}.file-diff-file.status-renamed .file-status{color:var(--accent)}.file-diff-body{flex-direction:column;flex:1;min-width:0;display:flex}.file-diff-panes{flex:1;min-height:0;display:flex;position:relative}.file-diff-overview{z-index:0;border-left:1px solid var(--border);background:color-mix(in srgb, var(--text-dim) 6%, transparent);width:28px;position:absolute;top:0;bottom:0;right:0}.file-diff-pane{z-index:1;position:relative}.file-diff-pane::-webkit-scrollbar{width:10px;height:10px}.file-diff-pane-right::-webkit-scrollbar{width:28px}.file-diff-pane::-webkit-scrollbar-track{background:0 0}.file-diff-pane::-webkit-scrollbar-corner{background:0 0}.file-diff-pane::-webkit-scrollbar-thumb{background:color-mix(in srgb, var(--text-dim) 25%, transparent);border-radius:5px}.file-diff-pane::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb, var(--text-dim) 45%, transparent)}.overview-bar{width:45%;min-height:2px;position:absolute}.overview-delete{background:var(--danger);left:2px}.overview-insert{background:var(--success);right:2px}.file-diff-pane{flex:1 1 0;min-width:0;overflow:auto}.file-diff-pane-left{border-right:1px solid var(--border)}.file-diff-table{border-collapse:collapse;width:max-content;min-width:100%;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:18px}.file-diff-table tr{height:18px}.file-diff-table td{vertical-align:top;padding:0 6px}.diff-num{text-align:right;color:var(--text-dim);-webkit-user-select:none;user-select:none}.diff-code{white-space:pre;color:var(--text)}.diff-delete{background:color-mix(in srgb, var(--danger) 18%, transparent)}.diff-insert{background:color-mix(in srgb, var(--success) 18%, transparent)}.diff-empty{background:color-mix(in srgb, var(--text-dim) 8%, transparent)}.file-diff-table tr.diff-hunk{height:26px}.diff-hunk td{vertical-align:middle;color:var(--accent);background:color-mix(in srgb, var(--accent) 10%, transparent);border-top:1px solid var(--border);border-bottom:1px solid var(--border);padding:0 8px}.ci-active-dot{filter:drop-shadow(0 0 4px color-mix(in srgb, var(--accent) 80%, transparent))}.ci-run-badge{color:#24292f;white-space:nowrap;cursor:pointer;background:#fff;border:1px solid #fff;border-radius:999px;align-items:center;gap:4px;padding:1px 7px 1px 3px;font-size:11px;display:inline-flex;position:relative}.ci-run-icon{justify-content:center;align-items:center;width:14px;height:14px;display:inline-flex}.ci-provider-logo{object-fit:contain}.ci-run-status{font-weight:800;line-height:1}.ci-run-tooltip{z-index:95;white-space:normal;cursor:default;width:480px;position:fixed}.ci-run-tooltip-body{flex-direction:column;gap:8px;display:flex}.terminal-container{border-top:1px solid var(--border);background:var(--panel-bg);flex-direction:column;flex:none;height:260px;display:flex;position:relative}.terminal-container.collapsed{height:auto}.terminal-container.collapsed .terminal-body{display:none}.terminal-resize-handle{cursor:row-resize;z-index:5;height:6px;position:absolute;top:-3px;left:0;right:0}.terminal-resize-handle:hover,.terminal-resize-handle:active{background:var(--accent);opacity:.5}.terminal-header{height:26px;color:var(--text-dim);cursor:pointer;-webkit-user-select:none;user-select:none;justify-content:space-between;align-items:center;padding:0 16px;font-size:12px;display:flex}.terminal-body{flex:1;min-height:0;position:relative}.terminal-instance{height:100%}.terminal-instance.hidden{display:none}.terminal-panel{height:100%;padding:4px 8px}.app{width:100vw;height:100vh;display:flex;overflow:hidden}.main{flex-direction:column;flex:1;min-width:0;display:flex}.main-header{background:var(--panel-bg);border-bottom:1px solid var(--border);flex:none;align-items:stretch;display:flex}.workspace-panes{flex:auto;min-height:0;display:flex;position:relative;overflow:hidden}.workspace-panes:after{content:"Drop to open split";border:2px dashed var(--accent);background:color-mix(in srgb, var(--accent) 14%, transparent);width:28%;color:var(--text-h);letter-spacing:.02em;box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--accent) 24%, transparent), 0 16px 40px var(--shadow-soft);opacity:0;pointer-events:none;z-index:20;border-radius:0;justify-content:center;align-items:center;font-size:13px;font-weight:600;transition:opacity .12s,transform .12s;display:flex;position:absolute;top:0;bottom:0;right:0;transform:translate(12px)scale(.98)}body.tab-split-preview .workspace-panes:after{opacity:1;transform:translate(0)scale(1)}.workspace-pane{background:var(--bg);flex-direction:column;flex:1 1 0;min-width:0;display:flex}.workspace-panes.split .workspace-pane[data-tab-pane=right]{border-left:1px solid var(--border)}.split-resize-handle{cursor:col-resize;z-index:3;background:0 0;flex:0 0 5px;margin-left:-3px}.split-resize-handle:hover{background:color-mix(in srgb, var(--accent) 35%, transparent)}.tabs-bar{flex:auto;min-width:0;display:flex;overflow-x:auto}.main-header{min-height:44px}.tab{border-right:1px solid var(--border);max-width:240px;color:var(--text-dim);cursor:pointer;white-space:nowrap;-webkit-user-select:none;user-select:none;box-sizing:border-box;flex:none;align-items:center;gap:10px;padding:0 10px 0 14px;transition:width .12s,padding .12s,opacity .12s,background-color .12s;display:flex;position:relative;overflow:hidden}.tab:active{cursor:grabbing}body.tab-dragging,body.tab-dragging *{cursor:grabbing!important}.tab.dragging{opacity:.45}.tab-ghost{z-index:1000;pointer-events:none;opacity:.85;background:var(--active-bg);transition:none;position:fixed;top:0;left:0;box-shadow:0 4px 12px #00000059}.tab.preview{font-style:italic}.tab.unavailable{opacity:.65}.tab:before{content:"";background:0 0;height:2px;position:absolute;top:0;left:0;right:0}.tab:hover{background:var(--hover-bg)}.tab.active{background:var(--bg);color:var(--text-h)}.tab.active:before{background:var(--accent)}.tab-names{flex-direction:column;min-width:0;display:flex}.tab-label{text-overflow:ellipsis;line-height:17px;overflow:hidden}.tab.active .tab-label{font-weight:600}.tab-meta{align-items:center;gap:6px;min-width:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;line-height:13px;display:flex}.tab-branch{text-overflow:ellipsis;color:var(--text-dim);overflow:hidden}.tab-close{text-align:center;opacity:.35;border-radius:4px;flex:none;width:18px;height:18px;line-height:18px;transition:opacity .12s,background-color .12s}.tab.active .tab-close,.tab:hover .tab-close{opacity:1}.tab-close:hover{background:var(--active-bg);color:var(--text-h)}.search-box{border-left:1px solid var(--border);flex:none;align-items:center;gap:6px;padding:0 10px;display:flex;position:relative}.agent-active-marker{pointer-events:none}.agent-active-marker.idle{opacity:.45}.multiselect{min-width:0;position:relative}.multiselect-trigger{box-sizing:border-box;background:var(--bg);border:1px solid var(--border);width:100%;min-width:0;height:28px;color:var(--text);font:inherit;cursor:pointer;border-radius:4px;justify-content:space-between;align-items:center;gap:6px;padding:5px 7px;font-size:12px;display:flex}.multiselect-trigger:hover,.multiselect-trigger.active{border-color:var(--accent)}.multiselect-trigger-label{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.multiselect-caret{color:var(--text-dim);flex:none}.multiselect-overlay{z-index:130;position:fixed;inset:0}.multiselect-panel{z-index:131;background:var(--menu-bg);border:1px solid var(--border);max-height:180px;box-shadow:0 8px 24px var(--shadow-soft);border-radius:6px;padding:4px;position:fixed;overflow-y:auto}.multiselect-search{box-sizing:border-box;background:var(--input-bg);border:1px solid var(--border);width:100%;color:var(--text);font:inherit;border-radius:4px;outline:none;margin-bottom:4px;padding:5px 6px;font-size:12px}.multiselect-search:focus{border-color:var(--accent)}.multiselect-empty{color:var(--text-dim);padding:6px;font-size:12px}.multiselect-option{color:var(--text);cursor:pointer;border-radius:4px;align-items:center;gap:6px;padding:5px 6px;font-size:12px;display:flex;overflow:hidden}.multiselect-option:hover{background:var(--active-bg)}.multiselect-option span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.content{flex:1;min-height:0;display:flex;overflow:hidden}.empty-state{color:var(--text-dim);margin:auto}.unavailable-state{text-align:center;max-width:360px;line-height:1.4}.xterm{cursor:text;-webkit-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;inset:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;inset:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre;font-family:monospace}.xterm .xterm-accessibility-tree>div{transform-origin:0;width:fit-content}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:underline double;text-decoration:underline double}.xterm-underline-3{-webkit-text-decoration:underline wavy;text-decoration:underline wavy}.xterm-underline-4{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.xterm-underline-5{-webkit-text-decoration:underline dashed;text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;z-index:11;background:0 0;transition:opacity .1s linear}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{display:none;position:absolute}.xterm .xterm-scrollable-element>.shadow.top{width:100%;height:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;display:block;top:0;left:3px}.xterm .xterm-scrollable-element>.shadow.left{width:3px;height:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;display:block;top:3px;left:0}.xterm .xterm-scrollable-element>.shadow.top-left-corner{width:3px;height:3px;display:block;top:0;left:0}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset}