git-juggler 0.2.4__tar.gz → 0.2.6__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 (206) hide show
  1. {git_juggler-0.2.4 → git_juggler-0.2.6}/PKG-INFO +7 -2
  2. {git_juggler-0.2.4 → git_juggler-0.2.6}/README.md +6 -1
  3. git_juggler-0.2.6/resources/git-juggler-demo-1.gif +0 -0
  4. git_juggler-0.2.6/resources/git-juggler-demo-2.png +0 -0
  5. git_juggler-0.2.6/resources/git-juggler-demo-3.png +0 -0
  6. git_juggler-0.2.6/resources/git-juggler-demo-4.png +0 -0
  7. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/ci.py +2 -1
  8. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/ci/__init__.py +8 -7
  9. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/ci/jenkins.py +121 -26
  10. git_juggler-0.2.4/src/backend/git_juggler/frontend_dist/assets/index-D_IslQuv.js → git_juggler-0.2.6/src/backend/git_juggler/frontend_dist/assets/index-DC_wK6SO.js +15 -15
  11. git_juggler-0.2.6/src/backend/git_juggler/frontend_dist/assets/index-KUdo_LCw.css +1 -0
  12. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/frontend_dist/index.html +2 -2
  13. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/schemas.py +7 -4
  14. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_ci_poll.py +9 -9
  15. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_jenkins.py +123 -3
  16. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/App.css +31 -9
  17. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/api/client.ts +2 -1
  18. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/api/types.ts +6 -3
  19. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +1 -1
  20. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/GitHubSettings.tsx +81 -53
  21. git_juggler-0.2.6/src/frontend/src/components/Menu/JenkinsSettings.tsx +281 -0
  22. git_juggler-0.2.6/src/frontend/src/components/Search/MultiSelect.tsx +99 -0
  23. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/storeCore.ts +40 -4
  24. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/styles/menu.css +28 -3
  25. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/styles/sidebar.css +4 -1
  26. git_juggler-0.2.4/src/backend/git_juggler/frontend_dist/assets/index-Bq4BDE-5.css +0 -1
  27. git_juggler-0.2.4/src/backend/git_juggler/frontend_dist/favicon.svg +0 -1
  28. git_juggler-0.2.4/src/backend/git_juggler/frontend_dist/icons.svg +0 -24
  29. git_juggler-0.2.4/src/frontend/public/favicon.svg +0 -1
  30. git_juggler-0.2.4/src/frontend/public/icons.svg +0 -24
  31. git_juggler-0.2.4/src/frontend/src/components/Menu/JenkinsSettings.tsx +0 -184
  32. git_juggler-0.2.4/src/frontend/src/components/Search/MultiSelect.tsx +0 -47
  33. {git_juggler-0.2.4 → git_juggler-0.2.6}/.github/workflows/build-package.yml +0 -0
  34. {git_juggler-0.2.4 → git_juggler-0.2.6}/.gitignore +0 -0
  35. {git_juggler-0.2.4 → git_juggler-0.2.6}/CLAUDE.md +0 -0
  36. {git_juggler-0.2.4 → git_juggler-0.2.6}/LICENSE +0 -0
  37. {git_juggler-0.2.4 → git_juggler-0.2.6}/notes.txt +0 -0
  38. {git_juggler-0.2.4 → git_juggler-0.2.6}/pyproject.toml +0 -0
  39. {git_juggler-0.2.4 → git_juggler-0.2.6}/scripts/build-package.sh +0 -0
  40. {git_juggler-0.2.4 → git_juggler-0.2.6}/scripts/dev.sh +0 -0
  41. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/__init__.py +0 -0
  42. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/__init__.py +0 -0
  43. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/hook_events.py +0 -0
  44. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/hooks.py +0 -0
  45. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/__init__.py +0 -0
  46. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/activity_models.py +0 -0
  47. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/claude_sessions.py +0 -0
  48. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/claude_transcripts.py +0 -0
  49. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/git_resolver.py +0 -0
  50. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/opencode_sessions.py +0 -0
  51. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/paths.py +0 -0
  52. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/agents/tracking/text.py +0 -0
  53. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/__init__.py +0 -0
  54. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/agents.py +0 -0
  55. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/config.py +0 -0
  56. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/git.py +0 -0
  57. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/repos.py +0 -0
  58. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/api/terminal.py +0 -0
  59. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/app.py +0 -0
  60. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/ci/github_actions.py +0 -0
  61. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/ci/http.py +0 -0
  62. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/config.py +0 -0
  63. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/dev_app.py +0 -0
  64. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/frontend_dist/agent-logos/claude.png +0 -0
  65. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/frontend_dist/agent-logos/opencode.webp +0 -0
  66. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/frontend_dist/ci-logos/jenkins.webp +0 -0
  67. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/frontend_dist/git-juggler.png +0 -0
  68. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/frontend_dist/logo.png +0 -0
  69. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/__init__.py +0 -0
  70. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/command.py +0 -0
  71. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/data.py +0 -0
  72. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/detail.py +0 -0
  73. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/diff.py +0 -0
  74. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/refs.py +0 -0
  75. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/status.py +0 -0
  76. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/git/utils.py +0 -0
  77. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/main.py +0 -0
  78. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/repo_discovery.py +0 -0
  79. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/terminal.py +0 -0
  80. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/themes.py +0 -0
  81. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/ui/__init__.py +0 -0
  82. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/git_juggler/ui/folder_picker.py +0 -0
  83. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_agent_hooks.py +0 -0
  84. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_agent_tracking.py +0 -0
  85. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_ci.py +0 -0
  86. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_ci_poll_route.py +0 -0
  87. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_claude_sessions_data.py +0 -0
  88. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_file_diff.py +0 -0
  89. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_git_data.py +0 -0
  90. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_github_actions.py +0 -0
  91. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_opencode_sessions.py +0 -0
  92. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_pick_folder.py +0 -0
  93. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_preferences.py +0 -0
  94. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_repos.py +0 -0
  95. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/backend/tests/test_themes.py +0 -0
  96. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/.gitignore +0 -0
  97. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/README.md +0 -0
  98. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/index.html +0 -0
  99. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/package-lock.json +0 -0
  100. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/package.json +0 -0
  101. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/public/agent-logos/claude.png +0 -0
  102. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/public/agent-logos/opencode.webp +0 -0
  103. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/public/ci-logos/jenkins.webp +0 -0
  104. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/public/git-juggler.png +0 -0
  105. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/public/logo.png +0 -0
  106. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/App.tsx +0 -0
  107. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -0
  108. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Agents/AgentHoverCard.tsx +0 -0
  109. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Agents/AgentStatusPopover.tsx +0 -0
  110. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Agents/agentFormat.ts +0 -0
  111. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
  112. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
  113. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
  114. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
  115. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Badges/ciStatus.tsx +0 -0
  116. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
  117. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/CIRunPopover.tsx +0 -0
  118. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/CommandPalette/CommandPalette.tsx +0 -0
  119. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
  120. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Commits/CommitList.tsx +0 -0
  121. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Commits/CommitRow.tsx +0 -0
  122. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Commits/LineCounts.tsx +0 -0
  123. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Commits/UncommittedRow.tsx +0 -0
  124. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +0 -0
  125. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/ContextMenu/CreateBranchModal.tsx +0 -0
  126. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -0
  127. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +0 -0
  128. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
  129. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Diff/FileDiffModal.tsx +0 -0
  130. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Graph/GraphPanel.tsx +0 -0
  131. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Graph/branchColor.ts +0 -0
  132. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
  133. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Graph/laneWidth.ts +0 -0
  134. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/AgentQuickPicks.tsx +0 -0
  135. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/AgentsSettings.tsx +0 -0
  136. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/CiPollField.tsx +0 -0
  137. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
  138. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/MainMenu.tsx +0 -0
  139. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/RepoPathsSettings.tsx +0 -0
  140. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/ThemePicker.tsx +0 -0
  141. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Menu/ThemeQuickPicks.tsx +0 -0
  142. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +0 -0
  143. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Pipelines/StageStrip.tsx +0 -0
  144. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Search/SearchBox.tsx +0 -0
  145. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Search/TriSwitch.tsx +0 -0
  146. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Sidebar/NewGroupModal.tsx +0 -0
  147. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Sidebar/RepoList.tsx +0 -0
  148. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Sidebar/Sidebar.tsx +0 -0
  149. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Tabs/TabsBar.tsx +0 -0
  150. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
  151. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Terminal/TerminalPanel.tsx +0 -0
  152. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/Welcome/WelcomeWizard.tsx +0 -0
  153. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/icons.tsx +0 -0
  154. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/inputs/NumberField.tsx +0 -0
  155. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/inputs/TextField.tsx +0 -0
  156. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/inputs/ToggleField.tsx +0 -0
  157. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/components/inputs/styles.ts +0 -0
  158. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/index.css +0 -0
  159. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/index.tsx +0 -0
  160. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/agentStatusPopoverData.ts +0 -0
  161. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/appTheme.ts +0 -0
  162. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/branchVisibility.ts +0 -0
  163. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/bundledThemes.ts +0 -0
  164. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/ciPoll.ts +0 -0
  165. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/ciRunPopoverData.ts +0 -0
  166. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/color.ts +0 -0
  167. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
  168. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/flip.ts +0 -0
  169. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/formatDate.ts +0 -0
  170. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/preferenceSender.ts +0 -0
  171. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/scrollToCommit.ts +0 -0
  172. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/sideBySide.ts +0 -0
  173. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/stageWindow.ts +0 -0
  174. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/stripAnsi.ts +0 -0
  175. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/swallowNextClick.ts +0 -0
  176. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/lib/vscodeTheme.ts +0 -0
  177. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/agents.ts +0 -0
  178. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/ci.ts +0 -0
  179. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/graph.ts +0 -0
  180. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/overlayStack.ts +0 -0
  181. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/overlays.ts +0 -0
  182. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/preferenceSync.ts +0 -0
  183. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/preferences.ts +0 -0
  184. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/repos.ts +0 -0
  185. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/settings.ts +0 -0
  186. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/store.ts +0 -0
  187. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/tabs.ts +0 -0
  188. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/terminal.ts +0 -0
  189. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/state/themes.ts +0 -0
  190. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/styles/ci.css +0 -0
  191. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/styles/commits.css +0 -0
  192. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/styles/graph.css +0 -0
  193. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/src/styles/terminal.css +0 -0
  194. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/agentFormat.test.ts +0 -0
  195. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/agentStatusPopoverData.test.ts +0 -0
  196. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/ciPoll.test.ts +0 -0
  197. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/ciRunPopoverData.test.ts +0 -0
  198. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/computeColumns.test.ts +0 -0
  199. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/preferenceSender.test.ts +0 -0
  200. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/sideBySide.test.ts +0 -0
  201. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/stageWindow.test.ts +0 -0
  202. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tests/vscodeTheme.test.ts +0 -0
  203. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tsconfig.app.json +0 -0
  204. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tsconfig.json +0 -0
  205. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/tsconfig.node.json +0 -0
  206. {git_juggler-0.2.4 → git_juggler-0.2.6}/src/frontend/vite.config.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: git-juggler
3
- Version: 0.2.4
3
+ Version: 0.2.6
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
@@ -15,7 +15,12 @@ Description-Content-Type: text/markdown
15
15
 
16
16
  A local Git dashboard for people juggling agents and too many repos.
17
17
 
18
- git-juggler is a local dashboard for agent-heavy workflows and multi-repo projects. Watch agents work live, follow every repo’s git graph, and see what changed across your workspace without opening a dozen editors.
18
+ Watch agents work live, follow every repo’s git graph, and catch what changed across your workspace — all from one place, without opening a dozen editors.
19
+
20
+ <img src="https://github.com/franz38/git-juggler/blob/main/resources/git-juggler-demo-1.gif?raw=true" alt="git-juggler" width="100%">
21
+
22
+ <img src="https://github.com/franz38/git-juggler/blob/main/resources/git-juggler-demo-4.png?raw=true" alt="git-juggler" width="100%">
23
+
19
24
 
20
25
  ## How to install
21
26
 
@@ -2,7 +2,12 @@
2
2
 
3
3
  A local Git dashboard for people juggling agents and too many repos.
4
4
 
5
- git-juggler is a local dashboard for agent-heavy workflows and multi-repo projects. Watch agents work live, follow every repo’s git graph, and see what changed across your workspace without opening a dozen editors.
5
+ Watch agents work live, follow every repo’s git graph, and catch what changed across your workspace — all from one place, without opening a dozen editors.
6
+
7
+ <img src="https://github.com/franz38/git-juggler/blob/main/resources/git-juggler-demo-1.gif?raw=true" alt="git-juggler" width="100%">
8
+
9
+ <img src="https://github.com/franz38/git-juggler/blob/main/resources/git-juggler-demo-4.png?raw=true" alt="git-juggler" width="100%">
10
+
6
11
 
7
12
  ## How to install
8
13
 
@@ -51,10 +51,11 @@ def api_ci_poll(
51
51
  repo_id: str,
52
52
  run: Annotated[list[str] | None, Query()] = None,
53
53
  head_sha: str | None = None,
54
+ branch_name: str | None = None,
54
55
  ) -> list[CiRunInfo]:
55
56
  path = _resolve_repo_path(repo_id)
56
57
  return poll_ci_runs(
57
- path, run, head_sha, config.load_github_config(), config.load_jenkins_config()
58
+ path, run, head_sha, branch_name, config.load_github_config(), config.load_jenkins_config()
58
59
  )
59
60
 
60
61
 
@@ -19,7 +19,7 @@ class CiProvider(Protocol):
19
19
  graph and refreshed on its own, slow, interval — never by the run poll."""
20
20
  ...
21
21
 
22
- def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None) -> list[CiRunInfo]:
22
+ def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None, branch_name: str | None) -> list[CiRunInfo]:
23
23
  """`run_ids=None`: every active (queued/running) run of the repo, to
24
24
  discover a pipeline that just started (`head_sha` narrows it to one
25
25
  commit where the provider can filter). Otherwise exactly those runs,
@@ -36,7 +36,7 @@ class GitHubActionsProvider:
36
36
  def fetch_completed_runs(self, repo_path: Path, commit_hashes: set[str]) -> dict[str, list[CiRunInfo]]:
37
37
  return github_actions.get_github_actions_runs(repo_path, commit_hashes, self.config or {})
38
38
 
39
- def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None) -> list[CiRunInfo]:
39
+ def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None, branch_name: str | None) -> list[CiRunInfo]:
40
40
  return github_actions.poll_runs(repo_path, self.config, run_ids, head_sha)
41
41
 
42
42
 
@@ -48,8 +48,8 @@ class JenkinsProvider:
48
48
  def fetch_completed_runs(self, repo_path: Path, commit_hashes: set[str]) -> dict[str, list[CiRunInfo]]:
49
49
  return jenkins.get_jenkins_builds(repo_path, commit_hashes, self.config)
50
50
 
51
- def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None) -> list[CiRunInfo]:
52
- return jenkins.poll_runs(repo_path, self.config, run_ids, head_sha)
51
+ def poll_runs(self, repo_path: Path, run_ids: list[str] | None, head_sha: str | None, branch_name: str | None) -> list[CiRunInfo]:
52
+ return jenkins.poll_runs(repo_path, self.config, run_ids, head_sha, branch_name)
53
53
 
54
54
 
55
55
  def enabled_providers(github_config: dict | None, jenkins_config: dict | None) -> list[CiProvider]:
@@ -78,6 +78,7 @@ def poll_ci_runs(
78
78
  repo_path: Path,
79
79
  run_refs: list[str] | None,
80
80
  head_sha: str | None,
81
+ branch_name: str | None,
81
82
  github_config: dict | None,
82
83
  jenkins_config: dict | None,
83
84
  ) -> list[CiRunInfo]:
@@ -86,7 +87,7 @@ def poll_ci_runs(
86
87
  providers named there are asked, about just those runs."""
87
88
  providers = enabled_providers(github_config, jenkins_config)
88
89
  if run_refs is None:
89
- return [run for provider in providers for run in provider.poll_runs(repo_path, None, head_sha)]
90
+ return [run for provider in providers for run in provider.poll_runs(repo_path, None, head_sha, branch_name)]
90
91
 
91
92
  ids_by_provider: dict[str, list[str]] = {}
92
93
  for ref in run_refs:
@@ -97,7 +98,7 @@ def poll_ci_runs(
97
98
  run
98
99
  for provider in providers
99
100
  if provider.name in ids_by_provider
100
- for run in provider.poll_runs(repo_path, ids_by_provider[provider.name], head_sha)
101
+ for run in provider.poll_runs(repo_path, ids_by_provider[provider.name], head_sha, branch_name)
101
102
  ]
102
103
 
103
104
 
@@ -116,7 +117,7 @@ def get_ci_run_stages(
116
117
 
117
118
 
118
119
  def _active_runs_for_repo(repo: RepoSummary, github_config: dict | None, jenkins_config: dict | None) -> list[ActivePipeline]:
119
- runs = poll_ci_runs(Path(repo.path), None, None, github_config, jenkins_config)
120
+ runs = poll_ci_runs(Path(repo.path), None, None, None, github_config, jenkins_config)
120
121
  return [ActivePipeline(repo_id=repo.id, repo_name=repo.name, run=run) for run in runs]
121
122
 
122
123
 
@@ -2,31 +2,125 @@ from __future__ import annotations
2
2
 
3
3
  import base64
4
4
  import os
5
+ import re
5
6
  from datetime import datetime, timezone
6
7
  from pathlib import Path
7
- from urllib.parse import unquote, urlencode, urlparse
8
+ from urllib.parse import quote, unquote, urlencode, urlparse
8
9
  from urllib.request import urlopen
9
10
 
10
11
  from .http import fetch_json
11
12
  from ..schemas import CiRunInfo, CiStage
12
13
 
13
14
 
14
- def _matching_job_configs(jenkins_config: dict, repo_path: Path) -> list[dict]:
15
- matches: list[dict] = []
16
- for item in jenkins_config.get("jobs", []):
17
- if not isinstance(item, dict):
15
+ _TEMPLATE_RE = re.compile(r"\{([A-Za-z0-9_]+)\}")
16
+ _BRANCH_TEMPLATE_RE = re.compile(r"\{branch_[A-Za-z0-9_]+\}")
17
+
18
+
19
+ def _slug(value: str) -> str:
20
+ return re.sub(r"[^A-Za-z0-9._-]+", "-", value).strip("-") or value
21
+
22
+
23
+ def _repo_template_values(repo_path: Path) -> dict[str, str]:
24
+ resolved = repo_path.expanduser().resolve()
25
+ repo_name = resolved.name
26
+ repo_slug = _slug(repo_name)
27
+ return {
28
+ "repo_path": str(resolved),
29
+ "repo_name": repo_name,
30
+ "repo_name_url": quote(repo_name, safe=""),
31
+ "repo_slug": repo_slug,
32
+ "repo_slug_url": quote(repo_slug, safe=""),
33
+ }
34
+
35
+
36
+ def _branch_template_values(branch_name: str | None) -> dict[str, str]:
37
+ if not branch_name:
38
+ return {}
39
+ branch_slug = _slug(branch_name)
40
+ return {
41
+ "branch_name": branch_name,
42
+ "branch_name_url": quote(branch_name, safe=""),
43
+ "branch_slug": branch_slug,
44
+ "branch_slug_url": quote(branch_slug, safe=""),
45
+ }
46
+
47
+
48
+ def _render_template(value: str, values: dict[str, str]) -> str:
49
+ return _TEMPLATE_RE.sub(lambda match: values.get(match.group(1), match.group(0)), value)
50
+
51
+
52
+ def _has_branch_template(value: str) -> bool:
53
+ return _BRANCH_TEMPLATE_RE.search(value) is not None
54
+
55
+
56
+ def _matches_repo_path(raw_path: str, repo_path: Path, values: dict[str, str]) -> bool:
57
+ rendered_path = _render_template(raw_path, values).strip()
58
+ if rendered_path == "*":
59
+ return True
60
+ try:
61
+ return Path(rendered_path).expanduser().resolve() == repo_path.resolve()
62
+ except Exception:
63
+ return False
64
+
65
+
66
+ def _render_job_config(item: dict, repo_path: Path, values: dict[str, str], branch_name: str | None) -> dict | None:
67
+ job_url = item.get("job_url")
68
+ if not isinstance(job_url, str) or not job_url:
69
+ return None
70
+ if _has_branch_template(job_url) and not branch_name:
71
+ return None
72
+ rendered = _render_template(job_url, {**values, **_branch_template_values(branch_name)})
73
+ return {**item, "repo_path": str(repo_path.resolve()), "job_url": rendered}
74
+
75
+
76
+ def _matching_rule_job_configs(jenkins_config: dict, repo_path: Path, values: dict[str, str], branch_name: str | None) -> list[dict]:
77
+ for rule in jenkins_config.get("rules", []):
78
+ if not isinstance(rule, dict):
18
79
  continue
19
- raw_path = item.get("repo_path")
20
- job_url = item.get("job_url")
21
- if not isinstance(raw_path, str) or not isinstance(job_url, str) or not job_url:
80
+ repo_paths = rule.get("repo_paths")
81
+ if not isinstance(repo_paths, list) or not any(
82
+ isinstance(raw_path, str) and _matches_repo_path(raw_path, repo_path, values) for raw_path in repo_paths
83
+ ):
22
84
  continue
23
- try:
24
- configured_path = Path(raw_path).expanduser().resolve()
25
- except Exception:
85
+ rendered = _render_job_config({"repo_path": str(repo_path), "job_url": rule.get("job_url")}, repo_path, values, branch_name)
86
+ if rendered is not None:
87
+ return [rendered]
88
+ return []
89
+ return []
90
+
91
+
92
+ def _matching_job_configs(jenkins_config: dict, repo_path: Path, branch_name: str | None = None) -> list[dict]:
93
+ values = _repo_template_values(repo_path)
94
+ return _matching_rule_job_configs(jenkins_config, repo_path, values, branch_name)
95
+
96
+
97
+ def _job_url_prefixes(jenkins_config: dict, repo_path: Path) -> list[str]:
98
+ values = _repo_template_values(repo_path)
99
+ prefixes: list[str] = []
100
+ for job in _matching_job_configs(jenkins_config, repo_path):
101
+ prefixes.append(str(job["job_url"]).rstrip("/"))
102
+
103
+ items: list[dict] = []
104
+ for rule in jenkins_config.get("rules", []):
105
+ if not isinstance(rule, dict):
26
106
  continue
27
- if configured_path == repo_path.resolve():
28
- matches.append(item)
29
- return matches
107
+ repo_paths = rule.get("repo_paths")
108
+ if not isinstance(repo_paths, list) or not any(
109
+ isinstance(raw_path, str) and _matches_repo_path(raw_path, repo_path, values) for raw_path in repo_paths
110
+ ):
111
+ continue
112
+ items.append({"job_url": rule.get("job_url")})
113
+ break
114
+
115
+ for item in items:
116
+ raw_url = item.get("job_url")
117
+ if not isinstance(raw_url, str) or not _has_branch_template(raw_url):
118
+ continue
119
+ prefix_template = _BRANCH_TEMPLATE_RE.split(raw_url, maxsplit=1)[0]
120
+ prefix = _render_template(prefix_template, values).rstrip("/")
121
+ if prefix:
122
+ prefixes.append(prefix)
123
+ return list(dict.fromkeys(prefixes))
30
124
 
31
125
 
32
126
  def _headers(jenkins_config: dict) -> dict[str, str]:
@@ -228,14 +322,13 @@ def get_build_stages(repo_path: Path, jenkins_config: dict | None, build_url: st
228
322
  followed when it lives under a job configured for this repo."""
229
323
  if not jenkins_config:
230
324
  return None
231
- wanted = build_url.rstrip("/") + "/"
232
- for job in _matching_job_configs(jenkins_config, repo_path):
233
- if wanted.startswith(str(job["job_url"]).rstrip("/") + "/"):
234
- return _fetch_build_stages(build_url, _headers(jenkins_config))
325
+ wanted = build_url.rstrip("/")
326
+ if any(wanted.startswith(prefix + "/") for prefix in _job_url_prefixes(jenkins_config, repo_path)):
327
+ return _fetch_build_stages(build_url, _headers(jenkins_config))
235
328
  return None
236
329
 
237
330
 
238
- def get_active_builds(repo_path: Path, jenkins_config: dict | None) -> list[CiRunInfo]:
331
+ def get_active_builds(repo_path: Path, jenkins_config: dict | None, branch_name: str | None = None) -> list[CiRunInfo]:
239
332
  """Builds running right now, with their stages."""
240
333
  if not jenkins_config:
241
334
  return []
@@ -245,7 +338,7 @@ def get_active_builds(repo_path: Path, jenkins_config: dict | None) -> list[CiRu
245
338
  "actions[lastBuiltRevision[SHA1,branch[name]],parameters[name,value]]]{0,10}"
246
339
  )
247
340
  result: list[CiRunInfo] = []
248
- for job in _matching_job_configs(jenkins_config, repo_path):
341
+ for job in _matching_job_configs(jenkins_config, repo_path, branch_name):
249
342
  job_url = str(job["job_url"])
250
343
  data = _fetch_json(f"{job_url.rstrip('/')}/api/json?{urlencode({'tree': tree})}", headers)
251
344
  builds = data.get("builds") if isinstance(data, dict) else None
@@ -266,7 +359,7 @@ def get_builds_by_url(repo_path: Path, jenkins_config: dict | None, build_urls:
266
359
  if not jenkins_config:
267
360
  return []
268
361
  headers = _headers(jenkins_config)
269
- job_urls = [str(job["job_url"]).rstrip("/") for job in _matching_job_configs(jenkins_config, repo_path)]
362
+ job_urls = _job_url_prefixes(jenkins_config, repo_path)
270
363
 
271
364
  result: list[CiRunInfo] = []
272
365
  for build_url in build_urls:
@@ -289,12 +382,13 @@ def poll_runs(
289
382
  jenkins_config: dict | None,
290
383
  run_ids: list[str] | None,
291
384
  head_sha: str | None = None,
385
+ branch_name: str | None = None,
292
386
  ) -> list[CiRunInfo]:
293
387
  """`run_ids=None`: every running build of the repo's jobs (discovery).
294
388
  Otherwise exactly those builds, whatever their state. Jenkins can't filter
295
389
  builds by commit, so `head_sha` is accepted for interface parity only."""
296
390
  if run_ids is None:
297
- return get_active_builds(repo_path, jenkins_config)
391
+ return get_active_builds(repo_path, jenkins_config, branch_name)
298
392
  return get_builds_by_url(repo_path, jenkins_config, run_ids)
299
393
 
300
394
 
@@ -308,10 +402,11 @@ def test_connection(jenkins_config: dict | None) -> tuple[bool, str]:
308
402
  if isinstance(base_url, str) and base_url.strip():
309
403
  urls.append(f"{base_url.strip().rstrip('/')}/api/json")
310
404
 
311
- for job in jenkins_config.get("jobs", []):
312
- job_url = job.get("job_url") if isinstance(job, dict) else None
313
- if isinstance(job_url, str) and job_url.strip():
314
- urls.append(f"{job_url.strip().rstrip('/')}/api/json")
405
+ for rule in jenkins_config.get("rules", []):
406
+ if isinstance(rule, dict):
407
+ job_url = rule.get("job_url")
408
+ if isinstance(job_url, str) and job_url.strip() and not _has_branch_template(job_url):
409
+ urls.append(f"{job_url.strip().rstrip('/')}/api/json")
315
410
 
316
411
  if not urls:
317
412
  return False, "Set a Jenkins base URL or at least one job URL first."