git-juggler 0.2.11__tar.gz → 0.2.14__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 (211) hide show
  1. {git_juggler-0.2.11 → git_juggler-0.2.14}/.github/workflows/build-package.yml +16 -2
  2. {git_juggler-0.2.11 → git_juggler-0.2.14}/PKG-INFO +1 -1
  3. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/hooks.py +141 -14
  4. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/agents.py +28 -2
  5. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/config.py +4 -0
  6. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/config.py +13 -0
  7. git_juggler-0.2.14/src/backend/git_juggler/frontend_dist/assets/index-BDHOc_fm.js +23 -0
  8. git_juggler-0.2.11/src/backend/git_juggler/frontend_dist/assets/index-C6HbHDsB.css → git_juggler-0.2.14/src/backend/git_juggler/frontend_dist/assets/index-D0W6hS5M.css +1 -1
  9. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/index.html +2 -2
  10. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/data.py +1 -1
  11. git_juggler-0.2.14/src/backend/git_juggler/git/detail.py +106 -0
  12. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/schemas.py +2 -0
  13. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/conftest.py +1 -0
  14. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_agent_hooks.py +58 -0
  15. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_ci_poll_route.py +7 -6
  16. git_juggler-0.2.14/src/backend/tests/test_commit_detail.py +120 -0
  17. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_opencode_sessions.py +8 -2
  18. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_preferences.py +13 -0
  19. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/App.css +70 -166
  20. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/api/client.ts +13 -2
  21. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/api/types.ts +2 -0
  22. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Commits/CommitList.tsx +6 -3
  23. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Graph/GraphPanel.tsx +6 -3
  24. git_juggler-0.2.14/src/frontend/src/components/GraphToolbar/GraphToolbar.tsx +284 -0
  25. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/AgentsSettings.tsx +12 -4
  26. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/RepoPathsSettings.tsx +17 -0
  27. git_juggler-0.2.14/src/frontend/src/components/Search/SearchBox.tsx +133 -0
  28. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Tabs/TabsBar.tsx +14 -6
  29. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/agents.ts +1 -0
  30. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/graph.ts +9 -1
  31. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/repos.ts +5 -0
  32. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/storeCore.ts +870 -208
  33. git_juggler-0.2.11/src/backend/git_juggler/frontend_dist/assets/index-C-Yz67tX.js +0 -23
  34. git_juggler-0.2.11/src/backend/git_juggler/git/detail.py +0 -70
  35. git_juggler-0.2.11/src/frontend/src/components/Search/SearchBox.tsx +0 -142
  36. git_juggler-0.2.11/src/frontend/src/components/Search/TriSwitch.tsx +0 -123
  37. {git_juggler-0.2.11 → git_juggler-0.2.14}/.gitignore +0 -0
  38. {git_juggler-0.2.11 → git_juggler-0.2.14}/CLAUDE.md +0 -0
  39. {git_juggler-0.2.11 → git_juggler-0.2.14}/LICENSE +0 -0
  40. {git_juggler-0.2.11 → git_juggler-0.2.14}/README.md +0 -0
  41. {git_juggler-0.2.11 → git_juggler-0.2.14}/notes.txt +0 -0
  42. {git_juggler-0.2.11 → git_juggler-0.2.14}/pyproject.toml +0 -0
  43. {git_juggler-0.2.11 → git_juggler-0.2.14}/resources/git-juggler-demo-1.gif +0 -0
  44. {git_juggler-0.2.11 → git_juggler-0.2.14}/resources/git-juggler-demo-2.png +0 -0
  45. {git_juggler-0.2.11 → git_juggler-0.2.14}/resources/git-juggler-demo-3.png +0 -0
  46. {git_juggler-0.2.11 → git_juggler-0.2.14}/resources/git-juggler-demo-4.png +0 -0
  47. {git_juggler-0.2.11 → git_juggler-0.2.14}/scripts/build-package.sh +0 -0
  48. {git_juggler-0.2.11 → git_juggler-0.2.14}/scripts/dev.sh +0 -0
  49. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/__init__.py +0 -0
  50. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/__init__.py +0 -0
  51. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/hook_events.py +0 -0
  52. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/__init__.py +0 -0
  53. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/activity_models.py +0 -0
  54. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/claude_sessions.py +0 -0
  55. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/claude_transcripts.py +0 -0
  56. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/git_resolver.py +0 -0
  57. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/opencode_sessions.py +0 -0
  58. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/paths.py +0 -0
  59. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/text.py +0 -0
  60. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/__init__.py +0 -0
  61. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/ci.py +0 -0
  62. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/git.py +0 -0
  63. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/repos.py +0 -0
  64. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/api/terminal.py +0 -0
  65. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/app.py +0 -0
  66. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ci/__init__.py +0 -0
  67. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ci/github_actions.py +0 -0
  68. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ci/http.py +0 -0
  69. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ci/jenkins.py +0 -0
  70. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ci/run_cache.py +0 -0
  71. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/dev_app.py +0 -0
  72. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/agent-logos/claude.png +0 -0
  73. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/agent-logos/opencode.webp +0 -0
  74. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/ci-logos/jenkins.webp +0 -0
  75. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/git-juggler.png +0 -0
  76. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/logo.png +0 -0
  77. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/__init__.py +0 -0
  78. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/command.py +0 -0
  79. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/diff.py +0 -0
  80. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/gitfiles.py +0 -0
  81. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/refs.py +0 -0
  82. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/status.py +0 -0
  83. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/git/utils.py +0 -0
  84. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/main.py +0 -0
  85. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/repo_discovery.py +0 -0
  86. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/terminal.py +0 -0
  87. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/themes.py +0 -0
  88. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ui/__init__.py +0 -0
  89. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/git_juggler/ui/folder_picker.py +0 -0
  90. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/hook_event_files.py +0 -0
  91. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_agent_tracking.py +0 -0
  92. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_ci.py +0 -0
  93. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_ci_poll.py +0 -0
  94. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_claude_sessions_data.py +0 -0
  95. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_file_diff.py +0 -0
  96. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_git_data.py +0 -0
  97. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_github_actions.py +0 -0
  98. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_jenkins.py +0 -0
  99. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_pick_folder.py +0 -0
  100. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_repos.py +0 -0
  101. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_run_cache.py +0 -0
  102. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/backend/tests/test_themes.py +0 -0
  103. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/.gitignore +0 -0
  104. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/README.md +0 -0
  105. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/index.html +0 -0
  106. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/package-lock.json +0 -0
  107. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/package.json +0 -0
  108. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/public/agent-logos/claude.png +0 -0
  109. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/public/agent-logos/opencode.webp +0 -0
  110. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/public/ci-logos/jenkins.webp +0 -0
  111. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/public/git-juggler.png +0 -0
  112. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/public/logo.png +0 -0
  113. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/App.tsx +0 -0
  114. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -0
  115. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Agents/AgentHoverCard.tsx +0 -0
  116. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Agents/AgentStatusPopover.tsx +0 -0
  117. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Agents/agentFormat.ts +0 -0
  118. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
  119. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
  120. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
  121. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
  122. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Badges/ciStatus.tsx +0 -0
  123. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
  124. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/CIRunPopover.tsx +0 -0
  125. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/CommandPalette/CommandPalette.tsx +0 -0
  126. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
  127. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Commits/CommitRow.tsx +0 -0
  128. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Commits/LineCounts.tsx +0 -0
  129. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Commits/UncommittedRow.tsx +0 -0
  130. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +0 -0
  131. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +0 -0
  132. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/CreateBranchModal.tsx +0 -0
  133. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -0
  134. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +0 -0
  135. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
  136. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Diff/FileDiffModal.tsx +0 -0
  137. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Graph/branchColor.ts +0 -0
  138. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
  139. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Graph/laneWidth.ts +0 -0
  140. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/AgentQuickPicks.tsx +0 -0
  141. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/CiPollField.tsx +0 -0
  142. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
  143. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/GitHubSettings.tsx +0 -0
  144. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/JenkinsSettings.tsx +0 -0
  145. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/MainMenu.tsx +0 -0
  146. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/ThemePicker.tsx +0 -0
  147. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Menu/ThemeQuickPicks.tsx +0 -0
  148. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +0 -0
  149. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Search/MultiSelect.tsx +0 -0
  150. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/NewGroupModal.tsx +0 -0
  151. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/RepoList.tsx +0 -0
  152. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/Sidebar.tsx +0 -0
  153. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/panelKit.tsx +0 -0
  154. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
  155. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Terminal/TerminalPanel.tsx +0 -0
  156. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/Welcome/WelcomeWizard.tsx +0 -0
  157. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/icons.tsx +0 -0
  158. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/inputs/NumberField.tsx +0 -0
  159. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/inputs/TextField.tsx +0 -0
  160. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/inputs/ToggleField.tsx +0 -0
  161. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/components/inputs/styles.ts +0 -0
  162. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/index.css +0 -0
  163. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/index.tsx +0 -0
  164. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/agentRings.ts +0 -0
  165. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/agentStatusPopoverData.ts +0 -0
  166. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/appTheme.ts +0 -0
  167. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/branchVisibility.ts +0 -0
  168. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/bundledThemes.ts +0 -0
  169. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/ciPoll.ts +0 -0
  170. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/ciRunPopoverData.ts +0 -0
  171. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/color.ts +0 -0
  172. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
  173. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/flip.ts +0 -0
  174. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/formatDate.ts +0 -0
  175. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/preferenceSender.ts +0 -0
  176. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/scrollToCommit.ts +0 -0
  177. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/sideBySide.ts +0 -0
  178. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/stageWindow.ts +0 -0
  179. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/stripAnsi.ts +0 -0
  180. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/swallowNextClick.ts +0 -0
  181. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/lib/vscodeTheme.ts +0 -0
  182. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/ci.ts +0 -0
  183. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/overlayStack.ts +0 -0
  184. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/overlays.ts +0 -0
  185. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/preferenceSync.ts +0 -0
  186. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/preferences.ts +0 -0
  187. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/settings.ts +0 -0
  188. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/store.ts +0 -0
  189. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/tabs.ts +0 -0
  190. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/terminal.ts +0 -0
  191. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/state/themes.ts +0 -0
  192. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/styles/ci.css +0 -0
  193. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/styles/commits.css +0 -0
  194. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/styles/graph.css +0 -0
  195. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/styles/menu.css +0 -0
  196. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/styles/sidebar.css +0 -0
  197. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/src/styles/terminal.css +0 -0
  198. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/agentFormat.test.ts +0 -0
  199. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/agentRings.test.ts +0 -0
  200. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/agentStatusPopoverData.test.ts +0 -0
  201. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/ciPoll.test.ts +0 -0
  202. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/ciRunPopoverData.test.ts +0 -0
  203. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/computeColumns.test.ts +0 -0
  204. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/preferenceSender.test.ts +0 -0
  205. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/sideBySide.test.ts +0 -0
  206. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/stageWindow.test.ts +0 -0
  207. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tests/vscodeTheme.test.ts +0 -0
  208. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tsconfig.app.json +0 -0
  209. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tsconfig.json +0 -0
  210. {git_juggler-0.2.11 → git_juggler-0.2.14}/src/frontend/tsconfig.node.json +0 -0
  211. {git_juggler-0.2.11 → git_juggler-0.2.14}/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:
@@ -41,7 +40,21 @@ jobs:
41
40
  python-version: "3.12"
42
41
 
43
42
  - name: Install Python build tooling
44
- run: python -m pip install --upgrade pip build
43
+ run: python -m pip install --upgrade pip build 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
45
58
 
46
59
  - name: Build frontend and Python distributions
47
60
  run: ./scripts/build-package.sh
@@ -54,6 +67,7 @@ jobs:
54
67
  if-no-files-found: error
55
68
 
56
69
  publish:
70
+ if: github.ref_type == 'tag'
57
71
  needs: build
58
72
  runs-on: ubuntu-latest
59
73
  environment:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: git-juggler
3
- Version: 0.2.11
3
+ Version: 0.2.14
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