git-juggler 0.2.12__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.
- {git_juggler-0.2.12 → git_juggler-0.2.14}/.github/workflows/build-package.yml +16 -2
- {git_juggler-0.2.12 → git_juggler-0.2.14}/PKG-INFO +1 -1
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/hooks.py +141 -14
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/agents.py +28 -2
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/config.py +4 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/config.py +13 -0
- git_juggler-0.2.14/src/backend/git_juggler/frontend_dist/assets/index-BDHOc_fm.js +23 -0
- git_juggler-0.2.12/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
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/index.html +2 -2
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/data.py +1 -1
- git_juggler-0.2.14/src/backend/git_juggler/git/detail.py +106 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/schemas.py +2 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/conftest.py +1 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_agent_hooks.py +58 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_ci_poll_route.py +7 -6
- git_juggler-0.2.14/src/backend/tests/test_commit_detail.py +120 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_opencode_sessions.py +8 -2
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_preferences.py +13 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/App.css +70 -166
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/api/client.ts +13 -2
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/api/types.ts +2 -0
- git_juggler-0.2.14/src/frontend/src/components/GraphToolbar/GraphToolbar.tsx +284 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/AgentsSettings.tsx +12 -4
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/RepoPathsSettings.tsx +17 -0
- git_juggler-0.2.14/src/frontend/src/components/Search/SearchBox.tsx +133 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Tabs/TabsBar.tsx +14 -6
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/agents.ts +1 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/graph.ts +9 -1
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/repos.ts +5 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/storeCore.ts +870 -208
- git_juggler-0.2.12/src/backend/git_juggler/frontend_dist/assets/index-YHzN7wEh.js +0 -23
- git_juggler-0.2.12/src/backend/git_juggler/git/detail.py +0 -70
- git_juggler-0.2.12/src/frontend/src/components/Search/SearchBox.tsx +0 -142
- git_juggler-0.2.12/src/frontend/src/components/Search/TriSwitch.tsx +0 -123
- {git_juggler-0.2.12 → git_juggler-0.2.14}/.gitignore +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/CLAUDE.md +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/LICENSE +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/README.md +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/notes.txt +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/pyproject.toml +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/resources/git-juggler-demo-1.gif +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/resources/git-juggler-demo-2.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/resources/git-juggler-demo-3.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/resources/git-juggler-demo-4.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/scripts/build-package.sh +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/scripts/dev.sh +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/hook_events.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/activity_models.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/claude_sessions.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/claude_transcripts.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/git_resolver.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/opencode_sessions.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/paths.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/agents/tracking/text.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/ci.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/git.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/repos.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/api/terminal.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/app.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ci/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ci/github_actions.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ci/http.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ci/jenkins.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ci/run_cache.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/dev_app.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/agent-logos/claude.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/agent-logos/opencode.webp +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/ci-logos/jenkins.webp +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/git-juggler.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/frontend_dist/logo.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/command.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/diff.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/gitfiles.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/refs.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/status.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/git/utils.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/main.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/repo_discovery.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/terminal.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/themes.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ui/__init__.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/git_juggler/ui/folder_picker.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/hook_event_files.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_agent_tracking.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_ci.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_ci_poll.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_claude_sessions_data.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_file_diff.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_git_data.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_github_actions.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_jenkins.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_pick_folder.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_repos.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_run_cache.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/backend/tests/test_themes.py +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/.gitignore +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/README.md +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/index.html +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/package-lock.json +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/package.json +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/public/agent-logos/claude.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/public/agent-logos/opencode.webp +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/public/ci-logos/jenkins.webp +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/public/git-juggler.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/public/logo.png +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/App.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Agents/AgentActivityPanel.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Agents/AgentHoverCard.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Agents/AgentStatusPopover.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Agents/agentFormat.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Badges/BranchBadge.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Badges/CiRunBadge.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Badges/StashBadge.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Badges/TagBadge.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Badges/ciStatus.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Branches/DeleteBranchModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/CIRunPopover.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/CommandPalette/CommandPalette.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Commits/CommitDetail.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Commits/CommitList.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Commits/CommitRow.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Commits/LineCounts.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Commits/UncommittedRow.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/BranchContextMenu.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/CommitContextMenu.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/CreateBranchModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/CreateTagModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/RepoContextMenu.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/ContextMenu/TagContextMenu.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Diff/FileDiffModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Graph/GraphPanel.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Graph/branchColor.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Graph/computeColumns.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Graph/laneWidth.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/AgentQuickPicks.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/CiPollField.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/DirectoryBrowserModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/GitHubSettings.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/JenkinsSettings.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/MainMenu.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/ThemePicker.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Menu/ThemeQuickPicks.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Pipelines/PipelinesPanel.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Search/MultiSelect.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/NewGroupModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/RepoList.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/Sidebar.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Sidebar/panelKit.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Tags/DeleteTagModal.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Terminal/TerminalPanel.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/Welcome/WelcomeWizard.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/icons.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/inputs/NumberField.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/inputs/TextField.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/inputs/ToggleField.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/components/inputs/styles.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/index.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/index.tsx +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/agentRings.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/agentStatusPopoverData.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/appTheme.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/branchVisibility.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/bundledThemes.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/ciPoll.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/ciRunPopoverData.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/color.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/dismissOnOutsideClick.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/flip.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/formatDate.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/preferenceSender.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/scrollToCommit.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/sideBySide.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/stageWindow.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/stripAnsi.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/swallowNextClick.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/lib/vscodeTheme.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/ci.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/overlayStack.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/overlays.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/preferenceSync.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/preferences.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/settings.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/store.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/tabs.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/terminal.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/state/themes.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/styles/ci.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/styles/commits.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/styles/graph.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/styles/menu.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/styles/sidebar.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/src/styles/terminal.css +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/agentFormat.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/agentRings.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/agentStatusPopoverData.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/ciPoll.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/ciRunPopoverData.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/computeColumns.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/preferenceSender.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/sideBySide.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/stageWindow.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tests/vscodeTheme.test.ts +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tsconfig.app.json +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tsconfig.json +0 -0
- {git_juggler-0.2.12 → git_juggler-0.2.14}/src/frontend/tsconfig.node.json +0 -0
- {git_juggler-0.2.12 → 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:
|
|
@@ -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 =
|
|
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 = (
|
|
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 = {
|
|
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": [
|
|
235
|
-
|
|
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
|
|
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 (
|
|
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(
|
|
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 =
|
|
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)
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|