git-juggler 0.2.14__tar.gz → 0.2.16__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.
- {git_juggler-0.2.14 → git_juggler-0.2.16}/.github/workflows/build-package.yml +28 -3
- {git_juggler-0.2.14 → git_juggler-0.2.16}/PKG-INFO +1 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/hook_events.py +45 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/ci.py +2 -2
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/config.py +22 -12
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/git.py +24 -2
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/repos.py +1 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/terminal.py +1 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/config.py +30 -2
- git_juggler-0.2.14/src/backend/git_juggler/frontend_dist/assets/index-BDHOc_fm.js → git_juggler-0.2.16/src/backend/git_juggler/frontend_dist/assets/index-BOaJc15H.js +19 -16
- git_juggler-0.2.14/src/backend/git_juggler/frontend_dist/assets/index-D0W6hS5M.css → git_juggler-0.2.16/src/backend/git_juggler/frontend_dist/assets/index-DRsp7rcC.css +1 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/frontend_dist/index.html +2 -2
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/command.py +13 -2
- git_juggler-0.2.16/src/backend/git_juggler/git/conflicts.py +175 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/data.py +30 -3
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/refs.py +6 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/status.py +5 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/main.py +17 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/repo_discovery.py +39 -20
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/schemas.py +49 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_ci_poll_route.py +1 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_claude_sessions_data.py +80 -0
- git_juggler-0.2.16/src/backend/tests/test_git_conflicts.py +77 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_git_data.py +29 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_preferences.py +33 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_repos.py +31 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/App.css +93 -5
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/App.tsx +43 -11
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/api/client.ts +10 -1
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/api/types.ts +43 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/CommandPalette/CommandPalette.tsx +1 -0
- git_juggler-0.2.16/src/frontend/src/components/Conflicts/ConflictsPanel.tsx +267 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +16 -1
- git_juggler-0.2.16/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +67 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +4 -6
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/JenkinsSettings.tsx +33 -20
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/RepoPathsSettings.tsx +69 -44
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Sidebar/RepoList.tsx +2 -7
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Tabs/TabsBar.tsx +0 -6
- git_juggler-0.2.16/src/frontend/src/components/Tags/CreateTagDialog.tsx +178 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Terminal/TerminalPanel.tsx +3 -2
- git_juggler-0.2.16/src/frontend/src/components/inputs/TemplateInput.tsx +236 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/agentRings.ts +4 -3
- git_juggler-0.2.16/src/frontend/src/lib/jenkinsUrlTemplate.ts +80 -0
- git_juggler-0.2.16/src/frontend/src/lib/templateTokens.ts +62 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/graph.ts +5 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/repos.ts +5 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/storeCore.ts +122 -9
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/terminal.ts +1 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/styles/sidebar.css +0 -7
- git_juggler-0.2.16/src/frontend/src/styles/terminal.css +265 -0
- git_juggler-0.2.16/src/frontend/tests/templateTokens.test.ts +80 -0
- git_juggler-0.2.14/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -129
- git_juggler-0.2.14/src/frontend/src/styles/terminal.css +0 -64
- {git_juggler-0.2.14 → git_juggler-0.2.16}/.gitignore +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/CLAUDE.md +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/LICENSE +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/README.md +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/notes.txt +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/pyproject.toml +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/resources/git-juggler-demo-1.gif +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/resources/git-juggler-demo-2.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/resources/git-juggler-demo-3.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/resources/git-juggler-demo-4.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/scripts/build-package.sh +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/scripts/dev.sh +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/hooks.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/activity_models.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/claude_sessions.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/claude_transcripts.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/git_resolver.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/opencode_sessions.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/paths.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/agents/tracking/text.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/api/agents.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/app.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ci/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ci/github_actions.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ci/http.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ci/jenkins.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ci/run_cache.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/dev_app.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/frontend_dist/agent-logos/claude.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/frontend_dist/agent-logos/opencode.webp +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/frontend_dist/ci-logos/jenkins.webp +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/frontend_dist/git-juggler.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/frontend_dist/logo.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/detail.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/diff.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/gitfiles.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/git/utils.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/terminal.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/themes.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ui/__init__.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/git_juggler/ui/folder_picker.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/conftest.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/hook_event_files.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_agent_hooks.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_agent_tracking.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_ci.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_ci_poll.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_commit_detail.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_file_diff.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_github_actions.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_jenkins.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_opencode_sessions.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_pick_folder.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_run_cache.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/backend/tests/test_themes.py +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/.gitignore +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/README.md +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/index.html +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/package-lock.json +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/package.json +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/public/agent-logos/claude.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/public/agent-logos/opencode.webp +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/public/ci-logos/jenkins.webp +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/public/git-juggler.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/public/logo.png +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Agents/AgentHoverCard.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Agents/AgentStatusPopover.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Agents/agentFormat.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Badges/ciStatus.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/CIRunPopover.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Commits/CommitList.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Commits/CommitRow.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Commits/LineCounts.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Commits/UncommittedRow.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/ContextMenu/CreateBranchModal.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Diff/FileDiffModal.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Graph/GraphPanel.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Graph/branchColor.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Graph/laneWidth.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/GraphToolbar/GraphToolbar.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/AgentQuickPicks.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/AgentsSettings.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/CiPollField.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/GitHubSettings.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/MainMenu.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/ThemePicker.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Menu/ThemeQuickPicks.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Search/MultiSelect.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Search/SearchBox.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Sidebar/NewGroupModal.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Sidebar/Sidebar.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Sidebar/panelKit.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/Welcome/WelcomeWizard.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/icons.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/inputs/NumberField.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/inputs/TextField.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/inputs/ToggleField.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/components/inputs/styles.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/index.css +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/index.tsx +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/agentStatusPopoverData.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/appTheme.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/branchVisibility.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/bundledThemes.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/ciPoll.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/ciRunPopoverData.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/color.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/flip.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/formatDate.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/preferenceSender.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/scrollToCommit.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/sideBySide.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/stageWindow.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/stripAnsi.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/swallowNextClick.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/lib/vscodeTheme.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/agents.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/ci.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/overlayStack.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/overlays.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/preferenceSync.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/preferences.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/settings.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/store.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/tabs.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/state/themes.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/styles/ci.css +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/styles/commits.css +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/styles/graph.css +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/src/styles/menu.css +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/agentFormat.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/agentRings.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/agentStatusPopoverData.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/ciPoll.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/ciRunPopoverData.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/computeColumns.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/preferenceSender.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/sideBySide.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/stageWindow.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tests/vscodeTheme.test.ts +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tsconfig.app.json +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tsconfig.json +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/tsconfig.node.json +0 -0
- {git_juggler-0.2.14 → git_juggler-0.2.16}/src/frontend/vite.config.ts +0 -0
|
@@ -17,7 +17,7 @@ jobs:
|
|
|
17
17
|
fi
|
|
18
18
|
fi
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
test:
|
|
21
21
|
needs: validate-tag
|
|
22
22
|
runs-on: ubuntu-latest
|
|
23
23
|
|
|
@@ -39,8 +39,8 @@ jobs:
|
|
|
39
39
|
with:
|
|
40
40
|
python-version: "3.12"
|
|
41
41
|
|
|
42
|
-
- name: Install Python
|
|
43
|
-
run: python -m pip install --upgrade pip
|
|
42
|
+
- name: Install Python test tooling
|
|
43
|
+
run: python -m pip install --upgrade pip pytest
|
|
44
44
|
|
|
45
45
|
- name: Install backend test dependencies
|
|
46
46
|
run: python -m pip install -e .
|
|
@@ -56,6 +56,31 @@ jobs:
|
|
|
56
56
|
working-directory: src/frontend
|
|
57
57
|
run: npm test
|
|
58
58
|
|
|
59
|
+
build:
|
|
60
|
+
needs: test
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
|
|
63
|
+
steps:
|
|
64
|
+
- name: Check out repository
|
|
65
|
+
uses: actions/checkout@v4
|
|
66
|
+
with:
|
|
67
|
+
fetch-depth: 0 # full history + tags, needed by hatch-vcs to derive the version
|
|
68
|
+
|
|
69
|
+
- name: Set up Node.js
|
|
70
|
+
uses: actions/setup-node@v4
|
|
71
|
+
with:
|
|
72
|
+
node-version: 22
|
|
73
|
+
cache: npm
|
|
74
|
+
cache-dependency-path: src/frontend/package-lock.json
|
|
75
|
+
|
|
76
|
+
- name: Set up Python
|
|
77
|
+
uses: actions/setup-python@v5
|
|
78
|
+
with:
|
|
79
|
+
python-version: "3.12"
|
|
80
|
+
|
|
81
|
+
- name: Install Python build tooling
|
|
82
|
+
run: python -m pip install --upgrade pip build
|
|
83
|
+
|
|
59
84
|
- name: Build frontend and Python distributions
|
|
60
85
|
run: ./scripts/build-package.sh
|
|
61
86
|
|
|
@@ -230,6 +230,8 @@ class AgentHookEventReader:
|
|
|
230
230
|
self._cached_signature: tuple | None = None
|
|
231
231
|
self._cached_scans: list[AgentRepositoryScan] = []
|
|
232
232
|
self._cached_valid_until = 0
|
|
233
|
+
self._last_active_heads: dict[tuple[tuple[str, str], str], GitWorktreeInfo] = {}
|
|
234
|
+
self._idle_heads: dict[tuple[tuple[str, str], str], GitWorktreeInfo] = {}
|
|
233
235
|
|
|
234
236
|
def recent_scans(self, now: int | None = None) -> list[AgentRepositoryScan]:
|
|
235
237
|
"""One scan per open agent session.
|
|
@@ -294,6 +296,7 @@ class AgentHookEventReader:
|
|
|
294
296
|
opencode_signature = db_signature(self.opencode_db_path)
|
|
295
297
|
scans: list[AgentRepositoryScan] = []
|
|
296
298
|
next_change: int | None = None
|
|
299
|
+
seen_head_keys: set[tuple[tuple[str, str], str]] = set()
|
|
297
300
|
for session_key, session_events in sessions.items():
|
|
298
301
|
# A session is open until its end hook fires (or its process dies),
|
|
299
302
|
# however long it has been quiet.
|
|
@@ -374,6 +377,8 @@ class AgentHookEventReader:
|
|
|
374
377
|
elif busy is True:
|
|
375
378
|
# Working right now: the most recently touched worktree is where.
|
|
376
379
|
max(activities.values(), key=lambda item: item.last_activity).state = "active"
|
|
380
|
+
for activity in activities.values():
|
|
381
|
+
self._pin_idle_head(session_key, activity, seen_head_keys)
|
|
377
382
|
details: SessionDetails | None = None
|
|
378
383
|
if card is not None and session_id:
|
|
379
384
|
details = self._session_details(card, session_id)
|
|
@@ -404,8 +409,48 @@ class AgentHookEventReader:
|
|
|
404
409
|
parent_provider=provider if parent_opencode_session is not None else None,
|
|
405
410
|
)
|
|
406
411
|
)
|
|
412
|
+
self._prune_head_state(seen_head_keys)
|
|
407
413
|
return scans, next_change
|
|
408
414
|
|
|
415
|
+
def _pin_idle_head(
|
|
416
|
+
self,
|
|
417
|
+
session_key: tuple[str, str],
|
|
418
|
+
activity: AgentWorktreeActivity,
|
|
419
|
+
seen: set[tuple[tuple[str, str], str]],
|
|
420
|
+
) -> None:
|
|
421
|
+
key = (session_key, activity.worktree_path)
|
|
422
|
+
seen.add(key)
|
|
423
|
+
current = self._worktree_info(activity)
|
|
424
|
+
if activity.state == "active":
|
|
425
|
+
self._idle_heads.pop(key, None)
|
|
426
|
+
self._last_active_heads[key] = current
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
anchor = self._idle_heads.get(key)
|
|
430
|
+
if anchor is None or anchor.repository_id != activity.repository_id:
|
|
431
|
+
anchor = self._last_active_heads.get(key)
|
|
432
|
+
if anchor is None or anchor.repository_id != activity.repository_id:
|
|
433
|
+
anchor = current
|
|
434
|
+
self._idle_heads[key] = anchor
|
|
435
|
+
activity.branch = anchor.branch
|
|
436
|
+
activity.commit = anchor.commit
|
|
437
|
+
|
|
438
|
+
def _prune_head_state(self, seen: set[tuple[tuple[str, str], str]]) -> None:
|
|
439
|
+
for cache in (self._last_active_heads, self._idle_heads):
|
|
440
|
+
for key in list(cache):
|
|
441
|
+
if key not in seen:
|
|
442
|
+
del cache[key]
|
|
443
|
+
|
|
444
|
+
@staticmethod
|
|
445
|
+
def _worktree_info(activity: AgentWorktreeActivity) -> GitWorktreeInfo:
|
|
446
|
+
return GitWorktreeInfo(
|
|
447
|
+
repository_id=activity.repository_id,
|
|
448
|
+
worktree_path=activity.worktree_path,
|
|
449
|
+
common_git_dir=activity.repository_id,
|
|
450
|
+
branch=activity.branch,
|
|
451
|
+
commit=activity.commit,
|
|
452
|
+
)
|
|
453
|
+
|
|
409
454
|
@staticmethod
|
|
410
455
|
def _since_last_prompt(events: list[HookEvent]) -> list[HookEvent]:
|
|
411
456
|
"""The session's events from its latest user prompt onward.
|
|
@@ -25,7 +25,7 @@ router = APIRouter()
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def _resolve_repo_path(repo_id: str) -> Path:
|
|
28
|
-
path = resolve_repo_path(config.load_repo_paths(), repo_id)
|
|
28
|
+
path = resolve_repo_path(config.load_repo_paths(), repo_id, config.load_individual_repo_paths())
|
|
29
29
|
if path is None:
|
|
30
30
|
raise HTTPException(status_code=404, detail="repo not found")
|
|
31
31
|
return path
|
|
@@ -82,7 +82,7 @@ def api_ci_stages(repo_id: str, provider: str, run_id: str) -> list[CiStage]:
|
|
|
82
82
|
@router.get("/api/ci/recent", response_model=list[ActivePipeline])
|
|
83
83
|
def api_ci_recent(limit: Annotated[int, Query(ge=1, le=50)] = 10) -> list[ActivePipeline]:
|
|
84
84
|
return get_recent_pipelines(
|
|
85
|
-
list_repos(config.load_repo_paths()),
|
|
85
|
+
list_repos(config.load_repo_paths(), config.load_individual_repo_paths()),
|
|
86
86
|
config.load_github_config(),
|
|
87
87
|
config.load_jenkins_config(),
|
|
88
88
|
limit,
|
|
@@ -25,6 +25,7 @@ def create_config_router(root_path: Path) -> APIRouter:
|
|
|
25
25
|
def _current_config() -> ConfigResponse:
|
|
26
26
|
return ConfigResponse(
|
|
27
27
|
repo_paths=[str(p) for p in config.load_repo_paths()],
|
|
28
|
+
individual_repo_paths=[str(p) for p in config.load_individual_repo_paths()],
|
|
28
29
|
pinned_repo_paths=config.load_pinned_repo_paths(),
|
|
29
30
|
repo_groups=config.load_repo_groups(),
|
|
30
31
|
excluded_paths=config.load_excluded_paths(),
|
|
@@ -32,27 +33,36 @@ def create_config_router(root_path: Path) -> APIRouter:
|
|
|
32
33
|
github=config.load_github_config(),
|
|
33
34
|
jenkins=config.load_jenkins_config(),
|
|
34
35
|
terminal_shell=terminal_shell,
|
|
36
|
+
terminal_helper_command=[sys.executable, "-m", "git_juggler.main"],
|
|
35
37
|
)
|
|
36
38
|
|
|
37
39
|
@router.get("/api/config", response_model=ConfigResponse)
|
|
38
40
|
def api_get_config() -> ConfigResponse:
|
|
39
41
|
return _current_config()
|
|
40
42
|
|
|
43
|
+
def _resolve_dirs(raw_paths: list[str], *, require_git: bool) -> list[Path]:
|
|
44
|
+
resolved: list[Path] = []
|
|
45
|
+
seen: set[str] = set()
|
|
46
|
+
for raw in raw_paths:
|
|
47
|
+
path = Path(raw).expanduser().resolve()
|
|
48
|
+
if not path.is_dir():
|
|
49
|
+
raise HTTPException(status_code=400, detail=f"not a directory: {raw}")
|
|
50
|
+
if require_git and not (path / ".git").exists():
|
|
51
|
+
raise HTTPException(status_code=400, detail=f"not a git repository: {raw}")
|
|
52
|
+
key = str(path)
|
|
53
|
+
if key in seen:
|
|
54
|
+
continue
|
|
55
|
+
seen.add(key)
|
|
56
|
+
resolved.append(path)
|
|
57
|
+
return resolved
|
|
58
|
+
|
|
41
59
|
@router.put("/api/config", response_model=ConfigResponse)
|
|
42
60
|
def api_update_config(body: ConfigUpdateRequest) -> ConfigResponse:
|
|
43
61
|
if body.repo_paths is not None:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if not path.is_dir():
|
|
49
|
-
raise HTTPException(status_code=400, detail=f"not a directory: {raw}")
|
|
50
|
-
key = str(path)
|
|
51
|
-
if key in seen:
|
|
52
|
-
continue
|
|
53
|
-
seen.add(key)
|
|
54
|
-
resolved.append(path)
|
|
55
|
-
config.save_repo_paths(resolved)
|
|
62
|
+
config.save_repo_paths(_resolve_dirs(body.repo_paths, require_git=False))
|
|
63
|
+
|
|
64
|
+
if body.individual_repo_paths is not None:
|
|
65
|
+
config.save_individual_repo_paths(_resolve_dirs(body.individual_repo_paths, require_git=True))
|
|
56
66
|
|
|
57
67
|
if body.pinned_repo_paths is not None:
|
|
58
68
|
deduped = list(dict.fromkeys(body.pinned_repo_paths))
|
|
@@ -5,18 +5,19 @@ from pathlib import Path
|
|
|
5
5
|
from fastapi import APIRouter, HTTPException, Query
|
|
6
6
|
|
|
7
7
|
from .. import config
|
|
8
|
+
from ..git.conflicts import create_resolution_token, get_conflict_file_content
|
|
8
9
|
from ..git.data import GRAPH_PAGE_SIZE, HistoryChangedError, get_graph, get_repo_status
|
|
9
10
|
from ..git.detail import get_commit_detail
|
|
10
11
|
from ..git.diff import get_commit_file_diff, get_working_file_diff
|
|
11
12
|
from ..repo_discovery import resolve_repo_path
|
|
12
|
-
from ..schemas import CommitDetail, FileDiff, GraphResponse, RepoStatusResponse
|
|
13
|
+
from ..schemas import CommitDetail, ConflictFileContent, ConflictResolutionTokenRequest, ConflictResolutionTokenResponse, FileDiff, GraphResponse, RepoStatusResponse
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
router = APIRouter()
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def _resolve_repo_path(repo_id: str) -> Path:
|
|
19
|
-
path = resolve_repo_path(config.load_repo_paths(), repo_id)
|
|
20
|
+
path = resolve_repo_path(config.load_repo_paths(), repo_id, config.load_individual_repo_paths())
|
|
20
21
|
if path is None:
|
|
21
22
|
raise HTTPException(status_code=404, detail="repo not found")
|
|
22
23
|
return path
|
|
@@ -75,3 +76,24 @@ def api_working_file_diff(
|
|
|
75
76
|
return get_working_file_diff(repo_path, path, old_path, full)
|
|
76
77
|
except Exception as exc: # noqa: BLE001 - surfaced as a 404 either way
|
|
77
78
|
raise HTTPException(status_code=404, detail="diff not found") from exc
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@router.get("/api/repos/{repo_id}/conflicts/content", response_model=ConflictFileContent)
|
|
82
|
+
def api_conflict_file_content(repo_id: str, path: str) -> ConflictFileContent:
|
|
83
|
+
repo_path = _resolve_repo_path(repo_id)
|
|
84
|
+
try:
|
|
85
|
+
return get_conflict_file_content(repo_path, path)
|
|
86
|
+
except Exception as exc: # noqa: BLE001 - surfaced as a 404 either way
|
|
87
|
+
raise HTTPException(status_code=404, detail="conflict file not found") from exc
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@router.post("/api/repos/{repo_id}/conflicts/resolution-token", response_model=ConflictResolutionTokenResponse)
|
|
91
|
+
def api_create_conflict_resolution_token(
|
|
92
|
+
repo_id: str, request: ConflictResolutionTokenRequest
|
|
93
|
+
) -> ConflictResolutionTokenResponse:
|
|
94
|
+
if request.repo_id != repo_id:
|
|
95
|
+
raise HTTPException(status_code=400, detail="repo mismatch")
|
|
96
|
+
_resolve_repo_path(repo_id)
|
|
97
|
+
return ConflictResolutionTokenResponse(
|
|
98
|
+
token=create_resolution_token(repo_id, request.path, request.content)
|
|
99
|
+
)
|
|
@@ -13,7 +13,7 @@ router = APIRouter()
|
|
|
13
13
|
|
|
14
14
|
@router.get("/api/repos", response_model=list[RepoSummary])
|
|
15
15
|
def api_list_repos() -> list[RepoSummary]:
|
|
16
|
-
return list_repos(config.load_repo_paths())
|
|
16
|
+
return list_repos(config.load_repo_paths(), config.load_individual_repo_paths())
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@router.get("/api/repos/scan-progress", response_model=RepoScanProgress)
|
|
@@ -17,7 +17,7 @@ def create_terminal_router(root_path: Path) -> APIRouter:
|
|
|
17
17
|
repo_id = websocket.query_params.get("repo")
|
|
18
18
|
cwd = root_path
|
|
19
19
|
if repo_id:
|
|
20
|
-
resolved = resolve_repo_path(config.load_repo_paths(), repo_id)
|
|
20
|
+
resolved = resolve_repo_path(config.load_repo_paths(), repo_id, config.load_individual_repo_paths())
|
|
21
21
|
if resolved is None:
|
|
22
22
|
await websocket.close(code=1008)
|
|
23
23
|
return
|
|
@@ -5,11 +5,22 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from pydantic import ValidationError
|
|
7
7
|
|
|
8
|
-
from .schemas import Preferences
|
|
8
|
+
from .schemas import JenkinsConfig, JenkinsRuleConfig, Preferences
|
|
9
9
|
|
|
10
10
|
CONFIG_DIR = Path.home() / ".config" / "git-juggler"
|
|
11
11
|
CONFIG_PATH = CONFIG_DIR / "config.json"
|
|
12
12
|
|
|
13
|
+
# A fresh install ships one example Jenkins rule so the settings show how
|
|
14
|
+
# pipeline URL placeholders work. It has no repos, so nothing is ever requested
|
|
15
|
+
# from the placeholder host until the user assigns some. The settings' "Add
|
|
16
|
+
# rule" starts from the same rule (lib/jenkinsUrlTemplate.ts in the frontend).
|
|
17
|
+
EXAMPLE_JENKINS_BASE_URL = "https://jenkins.example.com"
|
|
18
|
+
EXAMPLE_JENKINS_RULE = JenkinsRuleConfig(
|
|
19
|
+
id="example",
|
|
20
|
+
name="Example rule",
|
|
21
|
+
job_url=EXAMPLE_JENKINS_BASE_URL + "/job/{repo_name_url}/job/{branch_name_url}",
|
|
22
|
+
)
|
|
23
|
+
|
|
13
24
|
|
|
14
25
|
def _load_raw() -> dict:
|
|
15
26
|
if not CONFIG_PATH.exists():
|
|
@@ -41,6 +52,21 @@ def save_repo_paths(paths: list[Path]) -> None:
|
|
|
41
52
|
_save_raw(data)
|
|
42
53
|
|
|
43
54
|
|
|
55
|
+
def load_individual_repo_paths() -> list[Path]:
|
|
56
|
+
"""Repos the user added one by one, on top of whatever the search paths
|
|
57
|
+
(``repo_paths``) turn up."""
|
|
58
|
+
raw = _load_raw().get("individual_repo_paths", [])
|
|
59
|
+
if not isinstance(raw, list):
|
|
60
|
+
return []
|
|
61
|
+
return [Path(p) for p in raw if isinstance(p, str)]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def save_individual_repo_paths(paths: list[Path]) -> None:
|
|
65
|
+
data = _load_raw()
|
|
66
|
+
data["individual_repo_paths"] = [str(p) for p in paths]
|
|
67
|
+
_save_raw(data)
|
|
68
|
+
|
|
69
|
+
|
|
44
70
|
def load_pinned_repo_paths() -> list[str]:
|
|
45
71
|
raw = _load_raw().get("pinned_repo_paths", [])
|
|
46
72
|
if not isinstance(raw, list):
|
|
@@ -201,10 +227,12 @@ def save_jenkins_config(jenkins: dict | None) -> None:
|
|
|
201
227
|
|
|
202
228
|
def ensure_seeded(default_path: Path) -> None:
|
|
203
229
|
"""On first run (no config file yet), seed it with the CLI-provided path
|
|
204
|
-
so existing single-path usage keeps working without extra setup
|
|
230
|
+
so existing single-path usage keeps working without extra setup, plus the
|
|
231
|
+
example Jenkins rule."""
|
|
205
232
|
if CONFIG_PATH.exists():
|
|
206
233
|
return
|
|
207
234
|
save_repo_paths([default_path])
|
|
235
|
+
save_jenkins_config(JenkinsConfig(base_url=EXAMPLE_JENKINS_BASE_URL, rules=[EXAMPLE_JENKINS_RULE]).model_dump())
|
|
208
236
|
|
|
209
237
|
|
|
210
238
|
def reset_to_factory(default_path: Path) -> None:
|