@pennyfarthing/cyclist 10.4.0 → 11.0.0
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.
- package/dist/api/agent-load.d.ts +1 -2
- package/dist/api/agent-load.d.ts.map +1 -1
- package/dist/api/agent-load.js +2 -123
- package/dist/api/agent-load.js.map +1 -1
- package/dist/api/audit-log.d.ts +1 -17
- package/dist/api/audit-log.d.ts.map +1 -1
- package/dist/api/audit-log.js +2 -162
- package/dist/api/audit-log.js.map +1 -1
- package/dist/api/background-tasks.d.ts +1 -26
- package/dist/api/background-tasks.d.ts.map +1 -1
- package/dist/api/background-tasks.js +2 -55
- package/dist/api/background-tasks.js.map +1 -1
- package/dist/api/bell.d.ts +1 -18
- package/dist/api/bell.d.ts.map +1 -1
- package/dist/api/bell.js +2 -33
- package/dist/api/bell.js.map +1 -1
- package/dist/api/code-markers.d.ts +1 -8
- package/dist/api/code-markers.d.ts.map +1 -1
- package/dist/api/code-markers.js +2 -61
- package/dist/api/code-markers.js.map +1 -1
- package/dist/api/complexity.d.ts +1 -2
- package/dist/api/complexity.d.ts.map +1 -1
- package/dist/api/complexity.js +2 -46
- package/dist/api/complexity.js.map +1 -1
- package/dist/api/context.d.ts +1 -37
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +2 -143
- package/dist/api/context.js.map +1 -1
- package/dist/api/dead-code.d.ts +1 -2
- package/dist/api/dead-code.d.ts.map +1 -1
- package/dist/api/dead-code.js +2 -69
- package/dist/api/dead-code.js.map +1 -1
- package/dist/api/dependencies.d.ts +1 -2
- package/dist/api/dependencies.d.ts.map +1 -1
- package/dist/api/dependencies.js +2 -42
- package/dist/api/dependencies.js.map +1 -1
- package/dist/api/evaluation.d.ts +1 -19
- package/dist/api/evaluation.d.ts.map +1 -1
- package/dist/api/evaluation.js +2 -127
- package/dist/api/evaluation.js.map +1 -1
- package/dist/api/file-browser.d.ts +1 -8
- package/dist/api/file-browser.d.ts.map +1 -1
- package/dist/api/file-browser.js +2 -114
- package/dist/api/file-browser.js.map +1 -1
- package/dist/api/git.d.ts +1 -46
- package/dist/api/git.d.ts.map +1 -1
- package/dist/api/git.js +2 -354
- package/dist/api/git.js.map +1 -1
- package/dist/api/health-score.d.ts +1 -2
- package/dist/api/health-score.d.ts.map +1 -1
- package/dist/api/health-score.js +2 -46
- package/dist/api/health-score.js.map +1 -1
- package/dist/api/hook-request.d.ts +1 -40
- package/dist/api/hook-request.d.ts.map +1 -1
- package/dist/api/hook-request.js +2 -277
- package/dist/api/hook-request.js.map +1 -1
- package/dist/api/hotspots.d.ts +1 -2
- package/dist/api/hotspots.d.ts.map +1 -1
- package/dist/api/hotspots.js +2 -61
- package/dist/api/hotspots.js.map +1 -1
- package/dist/api/identity.d.ts +1 -16
- package/dist/api/identity.d.ts.map +1 -1
- package/dist/api/identity.js +2 -78
- package/dist/api/identity.js.map +1 -1
- package/dist/api/index.d.ts +1 -34
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +2 -44
- package/dist/api/index.js.map +1 -1
- package/dist/api/mode.d.ts +1 -22
- package/dist/api/mode.d.ts.map +1 -1
- package/dist/api/mode.js +2 -37
- package/dist/api/mode.js.map +1 -1
- package/dist/api/otlp.d.ts +1 -2
- package/dist/api/otlp.d.ts.map +1 -1
- package/dist/api/otlp.js +2 -46
- package/dist/api/otlp.js.map +1 -1
- package/dist/api/permissions.d.ts +1 -15
- package/dist/api/permissions.d.ts.map +1 -1
- package/dist/api/permissions.js +2 -66
- package/dist/api/permissions.js.map +1 -1
- package/dist/api/persona.d.ts +1 -8
- package/dist/api/persona.d.ts.map +1 -1
- package/dist/api/persona.js +2 -67
- package/dist/api/persona.js.map +1 -1
- package/dist/api/portrait.d.ts +1 -5
- package/dist/api/portrait.d.ts.map +1 -1
- package/dist/api/portrait.js +2 -27
- package/dist/api/portrait.js.map +1 -1
- package/dist/api/settings.d.ts +1 -53
- package/dist/api/settings.d.ts.map +1 -1
- package/dist/api/settings.js +2 -464
- package/dist/api/settings.js.map +1 -1
- package/dist/api/spans.d.ts +1 -16
- package/dist/api/spans.d.ts.map +1 -1
- package/dist/api/spans.js +2 -244
- package/dist/api/spans.js.map +1 -1
- package/dist/api/stats.d.ts +1 -12
- package/dist/api/stats.d.ts.map +1 -1
- package/dist/api/stats.js +2 -84
- package/dist/api/stats.js.map +1 -1
- package/dist/api/story.d.ts +1 -2
- package/dist/api/story.d.ts.map +1 -1
- package/dist/api/story.js +2 -14
- package/dist/api/story.js.map +1 -1
- package/dist/api/telemetry.d.ts +1 -18
- package/dist/api/telemetry.d.ts.map +1 -1
- package/dist/api/telemetry.js +2 -164
- package/dist/api/telemetry.js.map +1 -1
- package/dist/api/theme-agents.d.ts +1 -60
- package/dist/api/theme-agents.d.ts.map +1 -1
- package/dist/api/theme-agents.js +2 -213
- package/dist/api/theme-agents.js.map +1 -1
- package/dist/api/todos.d.ts +1 -32
- package/dist/api/todos.d.ts.map +1 -1
- package/dist/api/todos.js +2 -43
- package/dist/api/todos.js.map +1 -1
- package/dist/api/token-stats.d.ts +1 -7
- package/dist/api/token-stats.d.ts.map +1 -1
- package/dist/api/token-stats.js +2 -35
- package/dist/api/token-stats.js.map +1 -1
- package/dist/api/welcome.d.ts +1 -21
- package/dist/api/welcome.d.ts.map +1 -1
- package/dist/api/welcome.js +2 -34
- package/dist/api/welcome.js.map +1 -1
- package/dist/bikerack.js +2 -2
- package/dist/bikerack.js.map +1 -1
- package/dist/env.d.ts +6 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/focus.d.ts +53 -0
- package/dist/focus.d.ts.map +1 -0
- package/dist/focus.js +122 -0
- package/dist/focus.js.map +1 -0
- package/dist/git-cache.d.ts +1 -0
- package/dist/git-cache.d.ts.map +1 -1
- package/dist/git-cache.js +3 -1
- package/dist/git-cache.js.map +1 -1
- package/dist/menu-builder.d.ts.map +1 -1
- package/dist/menu-builder.js +0 -1
- package/dist/menu-builder.js.map +1 -1
- package/dist/prime.d.ts +3 -3
- package/dist/prime.d.ts.map +1 -1
- package/dist/prime.js +38 -14
- package/dist/prime.js.map +1 -1
- package/dist/public/css/react.css +1 -1
- package/dist/public/js/react/react.js +53 -61
- package/dist/server.d.ts +18 -85
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +105 -405
- package/dist/server.js.map +1 -1
- package/dist/sprint-data.d.ts +1 -1
- package/dist/sprint-data.d.ts.map +1 -1
- package/dist/sprint-data.js +2 -2
- package/dist/sprint-data.js.map +1 -1
- package/dist/theme-metadata.d.ts +3 -3
- package/dist/theme-metadata.d.ts.map +1 -1
- package/dist/theme-metadata.js +4 -4
- package/dist/theme-metadata.js.map +1 -1
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +53 -75
- package/dist/websocket.js.map +1 -1
- package/package.json +2 -6
- package/portraits/hogans-heroes/large/burkhalter-35312.png +0 -0
- package/portraits/hogans-heroes/large/carter-34352.png +0 -0
- package/portraits/hogans-heroes/large/hochstetter-45314.png +0 -0
- package/portraits/hogans-heroes/large/hogan-44541.png +0 -0
- package/portraits/hogans-heroes/large/kinch-35241.png +0 -0
- package/portraits/hogans-heroes/large/klink-23434.png +0 -0
- package/portraits/hogans-heroes/large/lebeau-45443.png +0 -0
- package/portraits/hogans-heroes/large/marya-53543.png +0 -0
- package/portraits/hogans-heroes/large/newkirk-54432.png +0 -0
- package/portraits/hogans-heroes/large/schultz-42453.png +0 -0
- package/portraits/hogans-heroes/large/underground-55131.png +0 -0
- package/portraits/hogans-heroes/medium/burkhalter-35312.png +0 -0
- package/portraits/hogans-heroes/medium/carter-34352.png +0 -0
- package/portraits/hogans-heroes/medium/hochstetter-45314.png +0 -0
- package/portraits/hogans-heroes/medium/hogan-44541.png +0 -0
- package/portraits/hogans-heroes/medium/kinch-35241.png +0 -0
- package/portraits/hogans-heroes/medium/klink-23434.png +0 -0
- package/portraits/hogans-heroes/medium/lebeau-45443.png +0 -0
- package/portraits/hogans-heroes/medium/marya-53543.png +0 -0
- package/portraits/hogans-heroes/medium/newkirk-54432.png +0 -0
- package/portraits/hogans-heroes/medium/schultz-42453.png +0 -0
- package/portraits/hogans-heroes/medium/underground-55131.png +0 -0
- package/portraits/monty-python/large/announcer-44441.png +0 -0
- package/portraits/monty-python/large/arguer-35412.png +0 -0
- package/portraits/monty-python/large/bicycle-repair-man-35241.png +0 -0
- package/portraits/monty-python/large/colonel-35423.png +0 -0
- package/portraits/monty-python/large/counsellor-45341.png +0 -0
- package/portraits/monty-python/large/gumbys-23524.png +0 -0
- package/portraits/monty-python/large/nudge-43533.png +0 -0
- package/portraits/monty-python/large/praline-45413.png +0 -0
- package/portraits/monty-python/large/silly-walks-55322.png +0 -0
- package/portraits/monty-python/large/wensleydale-54451.png +0 -0
- package/portraits/monty-python/large/xim-nez-43534.png +0 -0
- package/portraits/monty-python/medium/announcer-44441.png +0 -0
- package/portraits/monty-python/medium/arguer-35412.png +0 -0
- package/portraits/monty-python/medium/bicycle-repair-man-35241.png +0 -0
- package/portraits/monty-python/medium/colonel-35423.png +0 -0
- package/portraits/monty-python/medium/counsellor-45341.png +0 -0
- package/portraits/monty-python/medium/gumbys-23524.png +0 -0
- package/portraits/monty-python/medium/nudge-43533.png +0 -0
- package/portraits/monty-python/medium/praline-45413.png +0 -0
- package/portraits/monty-python/medium/silly-walks-55322.png +0 -0
- package/portraits/monty-python/medium/wensleydale-54451.png +0 -0
- package/portraits/monty-python/medium/xim-nez-43534.png +0 -0
- package/portraits/stephen-king/large/andy-55231.png +0 -0
- package/portraits/stephen-king/large/christine-25112.png +0 -0
- package/portraits/stephen-king/large/danny-53243.png +0 -0
- package/portraits/stephen-king/large/flagg-55311.png +0 -0
- package/portraits/stephen-king/large/gaunt-54421.png +0 -0
- package/portraits/stephen-king/large/jack-44224.png +0 -0
- package/portraits/stephen-king/large/johnny-44353.png +0 -0
- package/portraits/stephen-king/large/margaret-15415.png +0 -0
- package/portraits/stephen-king/large/paul-45233.png +0 -0
- package/portraits/stephen-king/large/pennywise-54411.png +0 -0
- package/portraits/stephen-king/large/roland-35121.png +0 -0
- package/portraits/stephen-king/medium/andy-55231.png +0 -0
- package/portraits/stephen-king/medium/christine-25112.png +0 -0
- package/portraits/stephen-king/medium/danny-53243.png +0 -0
- package/portraits/stephen-king/medium/flagg-55311.png +0 -0
- package/portraits/stephen-king/medium/gaunt-54421.png +0 -0
- package/portraits/stephen-king/medium/jack-44224.png +0 -0
- package/portraits/stephen-king/medium/johnny-44353.png +0 -0
- package/portraits/stephen-king/medium/margaret-15415.png +0 -0
- package/portraits/stephen-king/medium/paul-45233.png +0 -0
- package/portraits/stephen-king/medium/pennywise-54411.png +0 -0
- package/portraits/stephen-king/medium/roland-35121.png +0 -0
- package/portraits/star-trek-tng/large/beverly-44352.png +0 -0
- package/portraits/star-trek-tng/large/data-55241.png +0 -0
- package/portraits/star-trek-tng/large/deanna-43353.png +0 -0
- package/portraits/star-trek-tng/large/geordi-54342.png +0 -0
- package/portraits/star-trek-tng/large/jean-luc-45342.png +0 -0
- package/portraits/star-trek-tng/large/kathryn-45332.png +0 -0
- package/portraits/star-trek-tng/large/miles-35342.png +0 -0
- package/portraits/star-trek-tng/large/q-53521.png +0 -0
- package/portraits/star-trek-tng/large/spock-45231.png +0 -0
- package/portraits/star-trek-tng/large/troi-44352.png +0 -0
- package/portraits/star-trek-tng/medium/beverly-44352.png +0 -0
- package/portraits/star-trek-tng/medium/data-55241.png +0 -0
- package/portraits/star-trek-tng/medium/deanna-43353.png +0 -0
- package/portraits/star-trek-tng/medium/geordi-54342.png +0 -0
- package/portraits/star-trek-tng/medium/jean-luc-45342.png +0 -0
- package/portraits/star-trek-tng/medium/kathryn-45332.png +0 -0
- package/portraits/star-trek-tng/medium/miles-35342.png +0 -0
- package/portraits/star-trek-tng/medium/q-53521.png +0 -0
- package/portraits/star-trek-tng/medium/spock-45231.png +0 -0
- package/portraits/star-trek-tng/medium/troi-44352.png +0 -0
- package/src/public/App.tsx +0 -340
- package/src/public/components/AgentLoadDialog.tsx +0 -202
- package/src/public/components/AgentPopup.tsx +0 -308
- package/src/public/components/ApprovalModal/ApprovalModal.css +0 -35
- package/src/public/components/ApprovalModal/index.tsx +0 -632
- package/src/public/components/BikeRackIndex.tsx +0 -54
- package/src/public/components/BikeRackWorkspace.tsx +0 -142
- package/src/public/components/CommandPalette.tsx +0 -555
- package/src/public/components/ConfirmDialog.tsx +0 -168
- package/src/public/components/ContextIndicator/ContextIndicator.css +0 -85
- package/src/public/components/ContextIndicator/index.tsx +0 -330
- package/src/public/components/ContextSparkline.tsx +0 -56
- package/src/public/components/ControlBar.tsx +0 -636
- package/src/public/components/DeadCodeDialog.tsx +0 -169
- package/src/public/components/DiffViewer.tsx +0 -585
- package/src/public/components/DockviewWorkspace.tsx +0 -737
- package/src/public/components/Editor.tsx +0 -630
- package/src/public/components/ErrorBoundary.tsx +0 -67
- package/src/public/components/FileTree.tsx +0 -379
- package/src/public/components/FontPicker/FontPicker.css +0 -276
- package/src/public/components/FontPicker/index.tsx +0 -430
- package/src/public/components/FullFileTree.tsx +0 -237
- package/src/public/components/HealthGauge.tsx +0 -181
- package/src/public/components/Message.tsx +0 -225
- package/src/public/components/MessageList.tsx +0 -98
- package/src/public/components/MessageView.tsx +0 -400
- package/src/public/components/ModeSwitch/ModeSwitch.css +0 -165
- package/src/public/components/ModeSwitch/index.tsx +0 -372
- package/src/public/components/PersonaHeader.tsx +0 -240
- package/src/public/components/QuickActions.tsx +0 -267
- package/src/public/components/SpanTimeline.tsx +0 -352
- package/src/public/components/StandalonePanel.tsx +0 -84
- package/src/public/components/StatsStrip.tsx +0 -162
- package/src/public/components/StreamingContent.tsx +0 -77
- package/src/public/components/SubagentSpan.tsx +0 -180
- package/src/public/components/TandemPortrait.tsx +0 -72
- package/src/public/components/ThemePalette/ThemePalette.css +0 -179
- package/src/public/components/ThemePalette/index.tsx +0 -326
- package/src/public/components/ToolCallBlock.tsx +0 -252
- package/src/public/components/ToolStack.tsx +0 -209
- package/src/public/components/ToolStatus.tsx +0 -57
- package/src/public/components/dialogs/CodeMarkersDialog.tsx +0 -169
- package/src/public/components/dialogs/ComplexityDialog.tsx +0 -163
- package/src/public/components/dialogs/DependenciesDialog.tsx +0 -120
- package/src/public/components/dialogs/HotspotsDialog.tsx +0 -451
- package/src/public/components/dialogs/ToolDialog.tsx +0 -43
- package/src/public/components/panel-registry.ts +0 -11
- package/src/public/components/panels/ACPanel.tsx +0 -93
- package/src/public/components/panels/AcceptanceCriteriaPanel.tsx +0 -104
- package/src/public/components/panels/AuditLogPanel.tsx +0 -465
- package/src/public/components/panels/BackgroundPanel.tsx +0 -115
- package/src/public/components/panels/BikeLanePanel.tsx +0 -214
- package/src/public/components/panels/ChangedPanel.tsx +0 -65
- package/src/public/components/panels/DebugPanel.tsx +0 -344
- package/src/public/components/panels/DiffsPanel.tsx +0 -155
- package/src/public/components/panels/GitPanel.tsx +0 -216
- package/src/public/components/panels/HotspotsPanel.tsx +0 -365
- package/src/public/components/panels/MessagePanel.tsx +0 -497
- package/src/public/components/panels/SettingsPanel.tsx +0 -453
- package/src/public/components/panels/SprintPanel.tsx +0 -670
- package/src/public/components/panels/TTYPanel.tsx +0 -299
- package/src/public/components/panels/TodoPanel.tsx +0 -142
- package/src/public/components/panels/WorkflowPanel.tsx +0 -224
- package/src/public/components/panels/index.ts +0 -24
- package/src/public/components/ui/alert-dialog.tsx +0 -139
- package/src/public/components/ui/badge.tsx +0 -36
- package/src/public/components/ui/button.tsx +0 -57
- package/src/public/components/ui/checkbox.tsx +0 -28
- package/src/public/components/ui/collapsible.tsx +0 -9
- package/src/public/components/ui/command.tsx +0 -151
- package/src/public/components/ui/dialog.tsx +0 -120
- package/src/public/components/ui/popover.tsx +0 -31
- package/src/public/components/ui/progress.tsx +0 -28
- package/src/public/components/ui/scroll-area.tsx +0 -46
- package/src/public/components/ui/select.tsx +0 -157
- package/src/public/components/ui/separator.tsx +0 -29
- package/src/public/components/ui/skeleton.tsx +0 -15
- package/src/public/components/ui/switch.tsx +0 -27
- package/src/public/components/ui/toggle-group.tsx +0 -59
- package/src/public/components/ui/toggle.tsx +0 -43
- package/src/public/components/ui/tooltip.tsx +0 -30
- package/src/public/contexts/ClaudeContext.tsx +0 -311
- package/src/public/contexts/MessageQueueContext.tsx +0 -143
- package/src/public/css/theme-browser.css +0 -550
- package/src/public/css/theme-system.css +0 -630
- package/src/public/hooks/index.ts +0 -49
- package/src/public/hooks/useAgentLoad.ts +0 -105
- package/src/public/hooks/useBackgroundTasks.ts +0 -131
- package/src/public/hooks/useClaude.ts +0 -234
- package/src/public/hooks/useCodeMarkers.ts +0 -101
- package/src/public/hooks/useColorScheme.ts +0 -42
- package/src/public/hooks/useCommandHistory.ts +0 -99
- package/src/public/hooks/useComplexity.ts +0 -80
- package/src/public/hooks/useDeadCode.ts +0 -99
- package/src/public/hooks/useDependencies.ts +0 -82
- package/src/public/hooks/useDiffs.ts +0 -143
- package/src/public/hooks/useFileBrowser.ts +0 -71
- package/src/public/hooks/useGitStatus.ts +0 -233
- package/src/public/hooks/useHealthScore.ts +0 -69
- package/src/public/hooks/useHotspots.ts +0 -123
- package/src/public/hooks/useLayoutPersistence.ts +0 -138
- package/src/public/hooks/useMarkdownParser.ts +0 -36
- package/src/public/hooks/useMarkerActions.ts +0 -234
- package/src/public/hooks/useMessageQueue.ts +0 -380
- package/src/public/hooks/useMessageStream.ts +0 -131
- package/src/public/hooks/usePersona.ts +0 -112
- package/src/public/hooks/usePlanModeExit.ts +0 -105
- package/src/public/hooks/useResponsiveLayout.ts +0 -173
- package/src/public/hooks/useSprint.ts +0 -147
- package/src/public/hooks/useStatsStrip.ts +0 -204
- package/src/public/hooks/useStory.ts +0 -135
- package/src/public/hooks/useSubagentHelper.ts +0 -64
- package/src/public/hooks/useSyntaxHighlighter.ts +0 -52
- package/src/public/hooks/useTabCompletion.ts +0 -124
- package/src/public/hooks/useTodos.ts +0 -93
- package/src/public/hooks/useUserAvatar.ts +0 -54
- package/src/public/index.tsx +0 -10
- package/src/public/lib/utils.ts +0 -6
- package/src/public/styles/dockview-theme.css +0 -459
- package/src/public/styles/tailwind.css +0 -4396
- package/src/public/types/electron.d.ts +0 -18
- package/src/public/types/message.ts +0 -51
- package/src/public/utils/avatar-service.ts +0 -73
- package/src/public/utils/color-presets.ts +0 -940
- package/src/public/utils/font-presets.ts +0 -362
- package/src/public/utils/formatDuration.ts +0 -14
- package/src/public/utils/markdown.ts +0 -249
- package/src/public/utils/messageFilters.ts +0 -128
- package/src/public/utils/slash-commands.ts +0 -353
- package/src/public/utils/subagent-display.ts +0 -146
- package/src/public/utils/syntax.ts +0 -219
- package/src/public/utils/toolIntentSummarizer.ts +0 -199
- package/src/public/utils/toolStackGrouper.ts +0 -106
- package/src/public/utils/toolTypeColors.ts +0 -45
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
export interface FileComplexity {
|
|
4
|
-
path: string;
|
|
5
|
-
total_lines: number;
|
|
6
|
-
longest_function: number;
|
|
7
|
-
avg_cyclomatic_complexity: number;
|
|
8
|
-
max_nesting_depth: number;
|
|
9
|
-
function_count: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface ComplexityData {
|
|
13
|
-
success: boolean;
|
|
14
|
-
target_path: string;
|
|
15
|
-
file_count: number;
|
|
16
|
-
files: FileComplexity[];
|
|
17
|
-
error?: string | null;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface UseComplexityOptions {
|
|
21
|
-
path?: string;
|
|
22
|
-
top?: number;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface UseComplexityReturn {
|
|
26
|
-
data: ComplexityData | null;
|
|
27
|
-
isLoading: boolean;
|
|
28
|
-
error: Error | null;
|
|
29
|
-
refresh: () => void;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function useComplexity(options: UseComplexityOptions): UseComplexityReturn {
|
|
33
|
-
const [data, setData] = useState<ComplexityData | null>(null);
|
|
34
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
35
|
-
const [error, setError] = useState<Error | null>(null);
|
|
36
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
37
|
-
|
|
38
|
-
const fetchComplexity = useCallback(() => {
|
|
39
|
-
if (abortRef.current) {
|
|
40
|
-
abortRef.current.abort();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const controller = new AbortController();
|
|
44
|
-
abortRef.current = controller;
|
|
45
|
-
|
|
46
|
-
setIsLoading(true);
|
|
47
|
-
setError(null);
|
|
48
|
-
|
|
49
|
-
const params = new URLSearchParams();
|
|
50
|
-
if (options.path) params.set('path', options.path);
|
|
51
|
-
if (options.top) params.set('top', String(options.top));
|
|
52
|
-
|
|
53
|
-
fetch(`/api/complexity?${params}`, { signal: controller.signal })
|
|
54
|
-
.then((res) => {
|
|
55
|
-
if (!res.ok) {
|
|
56
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
57
|
-
}
|
|
58
|
-
return res.json();
|
|
59
|
-
})
|
|
60
|
-
.then((json: ComplexityData) => {
|
|
61
|
-
setData(json);
|
|
62
|
-
setIsLoading(false);
|
|
63
|
-
})
|
|
64
|
-
.catch((err) => {
|
|
65
|
-
if (err.name === 'AbortError') return;
|
|
66
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
67
|
-
setIsLoading(false);
|
|
68
|
-
});
|
|
69
|
-
}, [options.path, options.top]);
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
return () => {
|
|
73
|
-
if (abortRef.current) {
|
|
74
|
-
abortRef.current.abort();
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
}, []);
|
|
78
|
-
|
|
79
|
-
return { data, isLoading, error, refresh: fetchComplexity };
|
|
80
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
export interface StaleFile {
|
|
4
|
-
path: string;
|
|
5
|
-
last_commit_date: string;
|
|
6
|
-
days_since_last_commit: number;
|
|
7
|
-
size_bytes: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface UnusedExport {
|
|
11
|
-
symbol: string;
|
|
12
|
-
file: string;
|
|
13
|
-
line: number;
|
|
14
|
-
export_type: string;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface DeadCodeData {
|
|
18
|
-
success: boolean;
|
|
19
|
-
repo_name?: string;
|
|
20
|
-
repo_path?: string;
|
|
21
|
-
time_window_days?: number;
|
|
22
|
-
stale_files?: StaleFile[];
|
|
23
|
-
unused_exports?: UnusedExport[];
|
|
24
|
-
stale_file_count?: number;
|
|
25
|
-
unused_export_count?: number;
|
|
26
|
-
total_files?: number;
|
|
27
|
-
total_exports_scanned?: number;
|
|
28
|
-
repo_results?: DeadCodeData[];
|
|
29
|
-
error?: string | null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface UseDeadCodeOptions {
|
|
33
|
-
days: number;
|
|
34
|
-
repo?: string;
|
|
35
|
-
layer?: 'stale' | 'exports' | 'all';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface UseDeadCodeReturn {
|
|
39
|
-
data: DeadCodeData | null;
|
|
40
|
-
isLoading: boolean;
|
|
41
|
-
error: Error | null;
|
|
42
|
-
refresh: () => void;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function useDeadCode(options: UseDeadCodeOptions): UseDeadCodeReturn {
|
|
46
|
-
const [data, setData] = useState<DeadCodeData | null>(null);
|
|
47
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
48
|
-
const [error, setError] = useState<Error | null>(null);
|
|
49
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
50
|
-
|
|
51
|
-
const fetchDeadCode = useCallback(() => {
|
|
52
|
-
// Cancel any in-flight request
|
|
53
|
-
if (abortRef.current) {
|
|
54
|
-
abortRef.current.abort();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const controller = new AbortController();
|
|
58
|
-
abortRef.current = controller;
|
|
59
|
-
|
|
60
|
-
setIsLoading(true);
|
|
61
|
-
setError(null);
|
|
62
|
-
|
|
63
|
-
const params = new URLSearchParams({
|
|
64
|
-
days: String(options.days),
|
|
65
|
-
layer: options.layer || 'all',
|
|
66
|
-
});
|
|
67
|
-
if (options.repo) {
|
|
68
|
-
params.set('repo', options.repo);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
fetch(`/api/dead-code?${params}`, { signal: controller.signal })
|
|
72
|
-
.then((res) => {
|
|
73
|
-
if (!res.ok) {
|
|
74
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
75
|
-
}
|
|
76
|
-
return res.json();
|
|
77
|
-
})
|
|
78
|
-
.then((json: DeadCodeData) => {
|
|
79
|
-
setData(json);
|
|
80
|
-
setIsLoading(false);
|
|
81
|
-
})
|
|
82
|
-
.catch((err) => {
|
|
83
|
-
if (err.name === 'AbortError') return;
|
|
84
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
85
|
-
setIsLoading(false);
|
|
86
|
-
});
|
|
87
|
-
}, [options.days, options.repo, options.layer]);
|
|
88
|
-
|
|
89
|
-
// Cleanup abort controller on unmount
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
return () => {
|
|
92
|
-
if (abortRef.current) {
|
|
93
|
-
abortRef.current.abort();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}, []);
|
|
97
|
-
|
|
98
|
-
return { data, isLoading, error, refresh: fetchDeadCode };
|
|
99
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
2
|
-
|
|
3
|
-
export interface OutdatedPackage {
|
|
4
|
-
name: string;
|
|
5
|
-
current: string;
|
|
6
|
-
wanted: string;
|
|
7
|
-
latest: string;
|
|
8
|
-
type: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface SecurityAdvisory {
|
|
12
|
-
severity: string;
|
|
13
|
-
count: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface DependenciesData {
|
|
17
|
-
success: boolean;
|
|
18
|
-
target_path: string;
|
|
19
|
-
outdated: OutdatedPackage[];
|
|
20
|
-
advisories: SecurityAdvisory[];
|
|
21
|
-
error?: string | null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface UseDependenciesOptions {
|
|
25
|
-
path?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface UseDependenciesReturn {
|
|
29
|
-
data: DependenciesData | null;
|
|
30
|
-
isLoading: boolean;
|
|
31
|
-
error: Error | null;
|
|
32
|
-
refresh: () => void;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function useDependencies(options: UseDependenciesOptions): UseDependenciesReturn {
|
|
36
|
-
const [data, setData] = useState<DependenciesData | null>(null);
|
|
37
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
38
|
-
const [error, setError] = useState<Error | null>(null);
|
|
39
|
-
const abortRef = useRef<AbortController | null>(null);
|
|
40
|
-
|
|
41
|
-
const fetchDependencies = useCallback(() => {
|
|
42
|
-
if (abortRef.current) {
|
|
43
|
-
abortRef.current.abort();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const controller = new AbortController();
|
|
47
|
-
abortRef.current = controller;
|
|
48
|
-
|
|
49
|
-
setIsLoading(true);
|
|
50
|
-
setError(null);
|
|
51
|
-
|
|
52
|
-
const params = new URLSearchParams();
|
|
53
|
-
if (options.path) params.set('path', options.path);
|
|
54
|
-
|
|
55
|
-
fetch(`/api/dependencies?${params}`, { signal: controller.signal })
|
|
56
|
-
.then((res) => {
|
|
57
|
-
if (!res.ok) {
|
|
58
|
-
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
59
|
-
}
|
|
60
|
-
return res.json();
|
|
61
|
-
})
|
|
62
|
-
.then((json: DependenciesData) => {
|
|
63
|
-
setData(json);
|
|
64
|
-
setIsLoading(false);
|
|
65
|
-
})
|
|
66
|
-
.catch((err) => {
|
|
67
|
-
if (err.name === 'AbortError') return;
|
|
68
|
-
setError(err instanceof Error ? err : new Error(String(err)));
|
|
69
|
-
setIsLoading(false);
|
|
70
|
-
});
|
|
71
|
-
}, [options.path]);
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
return () => {
|
|
75
|
-
if (abortRef.current) {
|
|
76
|
-
abortRef.current.abort();
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
}, []);
|
|
80
|
-
|
|
81
|
-
return { data, isLoading, error, refresh: fetchDependencies };
|
|
82
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useDiffs Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for subscribing to diff data via WebSocket.
|
|
5
|
-
* Story MSSCI-12717 - React Migration
|
|
6
|
-
* Story MSSCI-14238 - Git-based diffs (replaces OTEL-based extraction)
|
|
7
|
-
*
|
|
8
|
-
* IPC DEPRECATED - Now uses WebSocket /ws/diffs
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
12
|
-
|
|
13
|
-
export interface DiffData {
|
|
14
|
-
id?: string;
|
|
15
|
-
path: string;
|
|
16
|
-
/** @deprecated Use `diff` field for raw git diff content */
|
|
17
|
-
original: string;
|
|
18
|
-
/** @deprecated Use `diff` field for raw git diff content */
|
|
19
|
-
modified: string;
|
|
20
|
-
/** Raw git diff output (MSSCI-14238) */
|
|
21
|
-
diff?: string;
|
|
22
|
-
toolName: string;
|
|
23
|
-
timestamp: number;
|
|
24
|
-
/** File status from git (MSSCI-14238) */
|
|
25
|
-
status?: 'modified' | 'added' | 'deleted' | 'renamed';
|
|
26
|
-
/** Line additions count (MSSCI-14238) */
|
|
27
|
-
additions?: number;
|
|
28
|
-
/** Line deletions count (MSSCI-14238) */
|
|
29
|
-
deletions?: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface UseDiffsResult {
|
|
33
|
-
diffs: DiffData[];
|
|
34
|
-
selectedDiff: DiffData | null;
|
|
35
|
-
selectDiff: (path: string) => void;
|
|
36
|
-
clearDiffs: () => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const WS_RECONNECT_DELAY = 2000;
|
|
40
|
-
|
|
41
|
-
export function useDiffs(): UseDiffsResult {
|
|
42
|
-
const [diffs, setDiffs] = useState<DiffData[]>([]);
|
|
43
|
-
const [selectedDiff, setSelectedDiff] = useState<DiffData | null>(null);
|
|
44
|
-
const wsRef = useRef<WebSocket | null>(null);
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
48
|
-
let mounted = true;
|
|
49
|
-
|
|
50
|
-
const connect = () => {
|
|
51
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
52
|
-
const ws = new WebSocket(`${protocol}//${window.location.host}/ws/diffs`);
|
|
53
|
-
wsRef.current = ws;
|
|
54
|
-
|
|
55
|
-
ws.onopen = () => {
|
|
56
|
-
console.log('[useDiffs] WebSocket connected');
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
ws.onmessage = (event) => {
|
|
60
|
-
try {
|
|
61
|
-
const data = JSON.parse(event.data);
|
|
62
|
-
|
|
63
|
-
if (data.type === 'init') {
|
|
64
|
-
// Initial load of existing diffs
|
|
65
|
-
const initialDiffs = (data.diffs || []) as DiffData[];
|
|
66
|
-
setDiffs(initialDiffs);
|
|
67
|
-
if (initialDiffs.length > 0) {
|
|
68
|
-
setSelectedDiff(initialDiffs[initialDiffs.length - 1]);
|
|
69
|
-
}
|
|
70
|
-
} else if (data.type === 'diff') {
|
|
71
|
-
// New diff arrived
|
|
72
|
-
const diffData = data.diff as DiffData;
|
|
73
|
-
setDiffs(prev => {
|
|
74
|
-
// Update or add diff
|
|
75
|
-
const existing = prev.findIndex(d => d.path === diffData.path);
|
|
76
|
-
if (existing >= 0) {
|
|
77
|
-
const updated = [...prev];
|
|
78
|
-
updated[existing] = diffData;
|
|
79
|
-
return updated;
|
|
80
|
-
}
|
|
81
|
-
return [...prev, diffData];
|
|
82
|
-
});
|
|
83
|
-
// Auto-select new diff
|
|
84
|
-
setSelectedDiff(diffData);
|
|
85
|
-
} else if (data.type === 'refresh') {
|
|
86
|
-
// Full refresh of diffs (MSSCI-14238: git cache refresh)
|
|
87
|
-
const refreshedDiffs = (data.diffs || []) as DiffData[];
|
|
88
|
-
setDiffs(refreshedDiffs);
|
|
89
|
-
// Keep current selection if it still exists
|
|
90
|
-
if (refreshedDiffs.length > 0) {
|
|
91
|
-
setSelectedDiff(prev => {
|
|
92
|
-
if (prev) {
|
|
93
|
-
const stillExists = refreshedDiffs.find(d => d.path === prev.path);
|
|
94
|
-
if (stillExists) return stillExists;
|
|
95
|
-
}
|
|
96
|
-
return refreshedDiffs[refreshedDiffs.length - 1];
|
|
97
|
-
});
|
|
98
|
-
} else {
|
|
99
|
-
setSelectedDiff(null);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
} catch (err) {
|
|
103
|
-
console.error('[useDiffs] Failed to parse message:', err);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
ws.onclose = () => {
|
|
108
|
-
if (mounted) {
|
|
109
|
-
reconnectTimer = setTimeout(connect, WS_RECONNECT_DELAY);
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
ws.onerror = (err) => {
|
|
114
|
-
console.error('[useDiffs] WebSocket error:', err);
|
|
115
|
-
ws.close();
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
connect();
|
|
120
|
-
|
|
121
|
-
return () => {
|
|
122
|
-
mounted = false;
|
|
123
|
-
if (reconnectTimer) clearTimeout(reconnectTimer);
|
|
124
|
-
wsRef.current?.close();
|
|
125
|
-
};
|
|
126
|
-
}, []);
|
|
127
|
-
|
|
128
|
-
const selectDiff = useCallback((path: string) => {
|
|
129
|
-
const diff = diffs.find(d => d.path === path);
|
|
130
|
-
setSelectedDiff(diff || null);
|
|
131
|
-
}, [diffs]);
|
|
132
|
-
|
|
133
|
-
const clearDiffs = useCallback(() => {
|
|
134
|
-
setDiffs([]);
|
|
135
|
-
setSelectedDiff(null);
|
|
136
|
-
// Send clear message to server
|
|
137
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
138
|
-
wsRef.current.send(JSON.stringify({ type: 'clear' }));
|
|
139
|
-
}
|
|
140
|
-
}, []);
|
|
141
|
-
|
|
142
|
-
return { diffs, selectedDiff, selectDiff, clearDiffs };
|
|
143
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useFileBrowser Hook
|
|
3
|
-
*
|
|
4
|
-
* Fetches directory listings from /api/files for the full file tree.
|
|
5
|
-
* Lazy-loads subdirectories on demand.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useState, useCallback } from 'react';
|
|
9
|
-
|
|
10
|
-
export interface DirectoryEntry {
|
|
11
|
-
name: string;
|
|
12
|
-
path: string;
|
|
13
|
-
type: 'file' | 'directory';
|
|
14
|
-
isModified?: boolean;
|
|
15
|
-
size?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface DirectoryListing {
|
|
19
|
-
path: string;
|
|
20
|
-
entries: DirectoryEntry[];
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
interface DirectoryCache {
|
|
24
|
-
[path: string]: DirectoryEntry[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface UseFileBrowserResult {
|
|
28
|
-
cache: DirectoryCache;
|
|
29
|
-
loading: Set<string>;
|
|
30
|
-
error: string | null;
|
|
31
|
-
fetchDirectory: (dirPath: string) => Promise<void>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function useFileBrowser(): UseFileBrowserResult {
|
|
35
|
-
const [cache, setCache] = useState<DirectoryCache>({});
|
|
36
|
-
const [loading, setLoading] = useState<Set<string>>(new Set());
|
|
37
|
-
const [error, setError] = useState<string | null>(null);
|
|
38
|
-
|
|
39
|
-
const fetchDirectory = useCallback(async (dirPath: string) => {
|
|
40
|
-
// Already cached or loading
|
|
41
|
-
if (cache[dirPath] || loading.has(dirPath)) return;
|
|
42
|
-
|
|
43
|
-
setLoading(prev => new Set(prev).add(dirPath));
|
|
44
|
-
setError(null);
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
const params = dirPath ? `?path=${encodeURIComponent(dirPath)}` : '';
|
|
48
|
-
const res = await fetch(`/api/files${params}`);
|
|
49
|
-
if (!res.ok) throw new Error(`Failed to list directory: ${res.statusText}`);
|
|
50
|
-
const listing: DirectoryListing = await res.json();
|
|
51
|
-
|
|
52
|
-
// Sort: directories first, then files, alphabetical within each
|
|
53
|
-
const sorted = listing.entries.sort((a, b) => {
|
|
54
|
-
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
55
|
-
return a.name.localeCompare(b.name);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
setCache(prev => ({ ...prev, [dirPath || '__root__']: sorted }));
|
|
59
|
-
} catch (err) {
|
|
60
|
-
setError(err instanceof Error ? err.message : 'Failed to load directory');
|
|
61
|
-
} finally {
|
|
62
|
-
setLoading(prev => {
|
|
63
|
-
const next = new Set(prev);
|
|
64
|
-
next.delete(dirPath);
|
|
65
|
-
return next;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}, [cache, loading]);
|
|
69
|
-
|
|
70
|
-
return { cache, loading, error, fetchDirectory };
|
|
71
|
-
}
|
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useGitStatus Hook
|
|
3
|
-
*
|
|
4
|
-
* React hook for subscribing to git status data.
|
|
5
|
-
* Story MSSCI-12717 - React Migration
|
|
6
|
-
* Story MSSCI-12781 - Fixed to handle multi-repo response format
|
|
7
|
-
* Story MSSCI-12798 - Expose full repo array for stacked display
|
|
8
|
-
* Story MSSCI-12860 - IPC to WebSocket Migration (Phase 1)
|
|
9
|
-
*
|
|
10
|
-
* Uses WebSocket /ws/git for real-time updates (no polling).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { useState, useEffect, useRef } from 'react';
|
|
14
|
-
|
|
15
|
-
export interface GitStatusData {
|
|
16
|
-
branch: string;
|
|
17
|
-
ahead?: number;
|
|
18
|
-
behind?: number;
|
|
19
|
-
staged?: number;
|
|
20
|
-
modified?: number;
|
|
21
|
-
untracked?: number;
|
|
22
|
-
isDirty?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/** Per-repo status data for stacked display */
|
|
26
|
-
export interface RepoStatusData {
|
|
27
|
-
name: string;
|
|
28
|
-
path: string;
|
|
29
|
-
branch: string;
|
|
30
|
-
ahead?: number;
|
|
31
|
-
behind?: number;
|
|
32
|
-
/** Commits origin/develop has that this branch doesn't (needs pull/rebase) */
|
|
33
|
-
developBehind?: number;
|
|
34
|
-
staged: number;
|
|
35
|
-
modified: number;
|
|
36
|
-
untracked: number;
|
|
37
|
-
isDirty: boolean;
|
|
38
|
-
files: DirtyFile[];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Dirty file from git status --porcelain */
|
|
42
|
-
export interface DirtyFile {
|
|
43
|
-
status: string; // M, A, D, ?, etc.
|
|
44
|
-
path: string;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Raw repo git info from server */
|
|
48
|
-
interface RepoGitInfo {
|
|
49
|
-
name: string;
|
|
50
|
-
path: string;
|
|
51
|
-
branch: string;
|
|
52
|
-
clean: boolean;
|
|
53
|
-
ahead: number | null;
|
|
54
|
-
behind: number | null;
|
|
55
|
-
developBehind: number | null;
|
|
56
|
-
dirtyFiles: DirtyFile[];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** WebSocket message format from /ws/git */
|
|
60
|
-
interface GitMessage {
|
|
61
|
-
type: 'init' | 'update';
|
|
62
|
-
repos: RepoGitInfo[];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Count files by type from dirty files array
|
|
67
|
-
*/
|
|
68
|
-
function countFilesByType(dirtyFiles: DirtyFile[]): { staged: number; modified: number; untracked: number } {
|
|
69
|
-
let staged = 0;
|
|
70
|
-
let modified = 0;
|
|
71
|
-
let untracked = 0;
|
|
72
|
-
|
|
73
|
-
for (const file of dirtyFiles) {
|
|
74
|
-
// Git status codes:
|
|
75
|
-
// First char = staging area, Second char = working tree
|
|
76
|
-
// M = modified, A = added (staged), D = deleted, ? = untracked
|
|
77
|
-
const indexStatus = file.status[0] || ' ';
|
|
78
|
-
const workTreeStatus = file.status[1] || ' ';
|
|
79
|
-
|
|
80
|
-
// Staged files (anything in the index that's not space or ?)
|
|
81
|
-
if (indexStatus !== ' ' && indexStatus !== '?') {
|
|
82
|
-
staged++;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Modified in working tree (not staged)
|
|
86
|
-
if (workTreeStatus === 'M' || workTreeStatus === 'D') {
|
|
87
|
-
modified++;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Untracked files
|
|
91
|
-
if (indexStatus === '?' && workTreeStatus === '?') {
|
|
92
|
-
untracked++;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { staged, modified, untracked };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Transform raw git response to per-repo status array for stacked display
|
|
101
|
-
*/
|
|
102
|
-
function transformToRepoArray(repos: RepoGitInfo[]): RepoStatusData[] {
|
|
103
|
-
if (!repos?.length) {
|
|
104
|
-
return [];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
return repos.map(repo => {
|
|
108
|
-
const counts = countFilesByType(repo.dirtyFiles);
|
|
109
|
-
return {
|
|
110
|
-
name: repo.name,
|
|
111
|
-
path: repo.path,
|
|
112
|
-
branch: repo.branch,
|
|
113
|
-
ahead: repo.ahead ?? undefined,
|
|
114
|
-
behind: repo.behind ?? undefined,
|
|
115
|
-
developBehind: repo.developBehind ?? undefined,
|
|
116
|
-
staged: counts.staged,
|
|
117
|
-
modified: counts.modified,
|
|
118
|
-
untracked: counts.untracked,
|
|
119
|
-
isDirty: !repo.clean,
|
|
120
|
-
files: repo.dirtyFiles,
|
|
121
|
-
};
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Transform raw git response to GitStatusData for display (legacy aggregated view)
|
|
127
|
-
* Uses first repo for branch info, aggregates file counts across all repos
|
|
128
|
-
*/
|
|
129
|
-
function transformGitResponse(repos: RepoGitInfo[]): GitStatusData | null {
|
|
130
|
-
if (!repos?.length) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Use first repo as primary (usually the orchestrator or main repo)
|
|
135
|
-
const primaryRepo = repos[0];
|
|
136
|
-
|
|
137
|
-
// Count file types across all repos
|
|
138
|
-
let staged = 0;
|
|
139
|
-
let modified = 0;
|
|
140
|
-
let untracked = 0;
|
|
141
|
-
|
|
142
|
-
for (const repo of repos) {
|
|
143
|
-
const counts = countFilesByType(repo.dirtyFiles);
|
|
144
|
-
staged += counts.staged;
|
|
145
|
-
modified += counts.modified;
|
|
146
|
-
untracked += counts.untracked;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// Check if any repo is dirty
|
|
150
|
-
const isDirty = repos.some(repo => !repo.clean);
|
|
151
|
-
|
|
152
|
-
return {
|
|
153
|
-
branch: primaryRepo.branch,
|
|
154
|
-
ahead: primaryRepo.ahead ?? undefined,
|
|
155
|
-
behind: primaryRepo.behind ?? undefined,
|
|
156
|
-
staged: staged > 0 ? staged : undefined,
|
|
157
|
-
modified: modified > 0 ? modified : undefined,
|
|
158
|
-
untracked: untracked > 0 ? untracked : undefined,
|
|
159
|
-
isDirty,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
interface UseGitStatusResult {
|
|
164
|
-
gitStatus: GitStatusData | null;
|
|
165
|
-
repos: RepoStatusData[];
|
|
166
|
-
isLoading: boolean;
|
|
167
|
-
error: Error | null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export function useGitStatus(): UseGitStatusResult {
|
|
171
|
-
const [gitStatus, setGitStatus] = useState<GitStatusData | null>(null);
|
|
172
|
-
const [repos, setRepos] = useState<RepoStatusData[]>([]);
|
|
173
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
174
|
-
const [error, setError] = useState<Error | null>(null);
|
|
175
|
-
const wsRef = useRef<WebSocket | null>(null);
|
|
176
|
-
const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
|
|
177
|
-
|
|
178
|
-
useEffect(() => {
|
|
179
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
180
|
-
const wsUrl = `${protocol}//${window.location.host}/ws/git`;
|
|
181
|
-
|
|
182
|
-
const connect = () => {
|
|
183
|
-
try {
|
|
184
|
-
wsRef.current = new WebSocket(wsUrl);
|
|
185
|
-
|
|
186
|
-
wsRef.current.onopen = () => {
|
|
187
|
-
console.debug('[useGitStatus] WebSocket connected');
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
wsRef.current.onmessage = (event) => {
|
|
191
|
-
try {
|
|
192
|
-
const msg = JSON.parse(event.data) as GitMessage;
|
|
193
|
-
if (msg.type === 'init' || msg.type === 'update') {
|
|
194
|
-
setGitStatus(transformGitResponse(msg.repos));
|
|
195
|
-
setRepos(transformToRepoArray(msg.repos));
|
|
196
|
-
setIsLoading(false);
|
|
197
|
-
setError(null);
|
|
198
|
-
}
|
|
199
|
-
} catch (err) {
|
|
200
|
-
console.error('[useGitStatus] Failed to parse message:', err);
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
wsRef.current.onclose = () => {
|
|
205
|
-
console.debug('[useGitStatus] WebSocket closed, reconnecting...');
|
|
206
|
-
reconnectTimeoutRef.current = setTimeout(connect, 2000);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
wsRef.current.onerror = (err) => {
|
|
210
|
-
console.error('[useGitStatus] WebSocket error:', err);
|
|
211
|
-
setError(new Error('WebSocket connection failed'));
|
|
212
|
-
};
|
|
213
|
-
} catch (err) {
|
|
214
|
-
console.error('[useGitStatus] WebSocket init failed:', err);
|
|
215
|
-
setError(err instanceof Error ? err : new Error('Failed to connect'));
|
|
216
|
-
setIsLoading(false);
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
connect();
|
|
221
|
-
|
|
222
|
-
return () => {
|
|
223
|
-
if (reconnectTimeoutRef.current) {
|
|
224
|
-
clearTimeout(reconnectTimeoutRef.current);
|
|
225
|
-
}
|
|
226
|
-
if (wsRef.current) {
|
|
227
|
-
wsRef.current.close();
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
}, []);
|
|
231
|
-
|
|
232
|
-
return { gitStatus, repos, isLoading, error };
|
|
233
|
-
}
|