@pennyfarthing/cyclist 10.0.3 → 10.2.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.
Files changed (154) hide show
  1. package/dist/api/agent-load.d.ts +3 -0
  2. package/dist/api/agent-load.d.ts.map +1 -0
  3. package/dist/api/agent-load.js +124 -0
  4. package/dist/api/agent-load.js.map +1 -0
  5. package/dist/api/code-markers.d.ts +9 -0
  6. package/dist/api/code-markers.d.ts.map +1 -0
  7. package/dist/api/code-markers.js +62 -0
  8. package/dist/api/code-markers.js.map +1 -0
  9. package/dist/api/complexity.d.ts +3 -0
  10. package/dist/api/complexity.d.ts.map +1 -0
  11. package/dist/api/complexity.js +47 -0
  12. package/dist/api/complexity.js.map +1 -0
  13. package/dist/api/dead-code.d.ts +3 -0
  14. package/dist/api/dead-code.d.ts.map +1 -0
  15. package/dist/api/dead-code.js +70 -0
  16. package/dist/api/dead-code.js.map +1 -0
  17. package/dist/api/dependencies.d.ts +3 -0
  18. package/dist/api/dependencies.d.ts.map +1 -0
  19. package/dist/api/dependencies.js +43 -0
  20. package/dist/api/dependencies.js.map +1 -0
  21. package/dist/api/git.d.ts +3 -2
  22. package/dist/api/git.d.ts.map +1 -1
  23. package/dist/api/git.js +11 -6
  24. package/dist/api/git.js.map +1 -1
  25. package/dist/api/health-score.d.ts +3 -0
  26. package/dist/api/health-score.d.ts.map +1 -0
  27. package/dist/api/health-score.js +47 -0
  28. package/dist/api/health-score.js.map +1 -0
  29. package/dist/api/hotspots.d.ts.map +1 -1
  30. package/dist/api/hotspots.js +9 -1
  31. package/dist/api/hotspots.js.map +1 -1
  32. package/dist/api/index.d.ts +7 -1
  33. package/dist/api/index.d.ts.map +1 -1
  34. package/dist/api/index.js +12 -2
  35. package/dist/api/index.js.map +1 -1
  36. package/dist/api/persona.d.ts +2 -0
  37. package/dist/api/persona.d.ts.map +1 -1
  38. package/dist/api/persona.js +19 -1
  39. package/dist/api/persona.js.map +1 -1
  40. package/dist/api/settings.js +1 -1
  41. package/dist/api/settings.js.map +1 -1
  42. package/dist/claude-service.d.ts +8 -2
  43. package/dist/claude-service.d.ts.map +1 -1
  44. package/dist/claude-service.js +21 -2
  45. package/dist/claude-service.js.map +1 -1
  46. package/dist/git-diff.d.ts.map +1 -1
  47. package/dist/git-diff.js +6 -5
  48. package/dist/git-diff.js.map +1 -1
  49. package/dist/main.d.ts.map +1 -1
  50. package/dist/main.js +11 -2
  51. package/dist/main.js.map +1 -1
  52. package/dist/plugin-loader.d.ts +49 -0
  53. package/dist/plugin-loader.d.ts.map +1 -0
  54. package/dist/plugin-loader.js +92 -0
  55. package/dist/plugin-loader.js.map +1 -0
  56. package/dist/preload.js +12 -1
  57. package/dist/preload.js.map +1 -1
  58. package/dist/prime.d.ts +3 -2
  59. package/dist/prime.d.ts.map +1 -1
  60. package/dist/prime.js +25 -8
  61. package/dist/prime.js.map +1 -1
  62. package/dist/public/css/react.css +1 -1
  63. package/dist/public/js/react/react.js +50 -39
  64. package/dist/server.d.ts.map +1 -1
  65. package/dist/server.js +19 -16
  66. package/dist/server.js.map +1 -1
  67. package/dist/sprint-data.d.ts +6 -0
  68. package/dist/sprint-data.d.ts.map +1 -1
  69. package/dist/sprint-data.js +118 -67
  70. package/dist/sprint-data.js.map +1 -1
  71. package/dist/story-parser.js +1 -1
  72. package/dist/story-parser.js.map +1 -1
  73. package/dist/theme-metadata.js +2 -2
  74. package/dist/theme-metadata.js.map +1 -1
  75. package/dist/websocket.d.ts +0 -6
  76. package/dist/websocket.d.ts.map +1 -1
  77. package/dist/websocket.js +36 -40
  78. package/dist/websocket.js.map +1 -1
  79. package/package.json +2 -1
  80. package/portraits/fifth-element/large/cornelius-54343.png +0 -0
  81. package/portraits/fifth-element/large/diva-53453.png +0 -0
  82. package/portraits/fifth-element/large/korben-34232.png +0 -0
  83. package/portraits/fifth-element/large/leeloo-54333.png +0 -0
  84. package/portraits/fifth-element/large/lindberg-34432.png +0 -0
  85. package/portraits/fifth-element/large/mondoshawan-55131.png +0 -0
  86. package/portraits/fifth-element/large/munro-25321.png +0 -0
  87. package/portraits/fifth-element/large/pacoli-45232.png +0 -0
  88. package/portraits/fifth-element/large/ruby-53544.png +0 -0
  89. package/portraits/fifth-element/large/zorg-45312.png +0 -0
  90. package/portraits/fifth-element/medium/cornelius-54343.png +0 -0
  91. package/portraits/fifth-element/medium/diva-53453.png +0 -0
  92. package/portraits/fifth-element/medium/korben-34232.png +0 -0
  93. package/portraits/fifth-element/medium/leeloo-54333.png +0 -0
  94. package/portraits/fifth-element/medium/lindberg-34432.png +0 -0
  95. package/portraits/fifth-element/medium/mondoshawan-55131.png +0 -0
  96. package/portraits/fifth-element/medium/munro-25321.png +0 -0
  97. package/portraits/fifth-element/medium/pacoli-45232.png +0 -0
  98. package/portraits/fifth-element/medium/ruby-53544.png +0 -0
  99. package/portraits/fifth-element/medium/zorg-45312.png +0 -0
  100. package/src/public/App.tsx +0 -2
  101. package/src/public/components/AgentLoadDialog.tsx +202 -0
  102. package/src/public/components/AgentPopup.tsx +3 -5
  103. package/src/public/components/ContextSparkline.tsx +56 -0
  104. package/src/public/components/ControlBar.tsx +140 -6
  105. package/src/public/components/DeadCodeDialog.tsx +169 -0
  106. package/src/public/components/DockviewWorkspace.tsx +0 -3
  107. package/src/public/components/FullFileTree.tsx +18 -4
  108. package/src/public/components/HealthGauge.tsx +181 -0
  109. package/src/public/components/MessageView.tsx +23 -6
  110. package/src/public/components/PersonaHeader.tsx +46 -3
  111. package/src/public/components/TandemPortrait.tsx +71 -0
  112. package/src/public/components/ToolCallBlock.tsx +21 -6
  113. package/src/public/components/dialogs/CodeMarkersDialog.tsx +169 -0
  114. package/src/public/components/dialogs/ComplexityDialog.tsx +163 -0
  115. package/src/public/components/dialogs/DependenciesDialog.tsx +120 -0
  116. package/src/public/components/dialogs/HotspotsDialog.tsx +451 -0
  117. package/src/public/components/dialogs/ToolDialog.tsx +43 -0
  118. package/src/public/components/panels/ACPanel.tsx +1 -1
  119. package/src/public/components/panels/AcceptanceCriteriaPanel.tsx +15 -30
  120. package/src/public/components/panels/DebugPanel.tsx +79 -3
  121. package/src/public/components/panels/GitPanel.tsx +25 -30
  122. package/src/public/components/panels/MessagePanel.tsx +44 -2
  123. package/src/public/components/panels/SettingsPanel.tsx +4 -4
  124. package/src/public/components/panels/SprintPanel.tsx +247 -123
  125. package/src/public/components/panels/index.ts +0 -1
  126. package/src/public/components/ui/dialog.tsx +3 -3
  127. package/src/public/css/theme-system.css +98 -11
  128. package/src/public/hooks/index.ts +4 -0
  129. package/src/public/hooks/useAgentLoad.ts +105 -0
  130. package/src/public/hooks/useCodeMarkers.ts +101 -0
  131. package/src/public/hooks/useColorScheme.ts +25 -10
  132. package/src/public/hooks/useComplexity.ts +80 -0
  133. package/src/public/hooks/useDeadCode.ts +99 -0
  134. package/src/public/hooks/useDependencies.ts +82 -0
  135. package/src/public/hooks/useHealthScore.ts +69 -0
  136. package/src/public/hooks/useHotspots.ts +11 -1
  137. package/src/public/hooks/usePersona.ts +26 -3
  138. package/src/public/hooks/useSprint.ts +7 -1
  139. package/src/public/styles/tailwind.css +389 -83
  140. package/src/public/utils/messageFilters.ts +77 -6
  141. package/src/public/utils/slash-commands.ts +3 -35
  142. package/dist/hooks/cyclist-pretooluse-hook.d.ts +0 -60
  143. package/dist/hooks/cyclist-pretooluse-hook.d.ts.map +0 -1
  144. package/dist/hooks/cyclist-pretooluse-hook.js +0 -57
  145. package/dist/hooks/cyclist-pretooluse-hook.js.map +0 -1
  146. package/dist/hooks/pretooluse-hook.d.ts +0 -89
  147. package/dist/hooks/pretooluse-hook.d.ts.map +0 -1
  148. package/dist/hooks/pretooluse-hook.js +0 -235
  149. package/dist/hooks/pretooluse-hook.js.map +0 -1
  150. package/dist/notification-sound.d.ts +0 -59
  151. package/dist/notification-sound.d.ts.map +0 -1
  152. package/dist/notification-sound.js +0 -219
  153. package/dist/notification-sound.js.map +0 -1
  154. package/src/public/types/electron.d.ts +0 -18
@@ -8,10 +8,19 @@
8
8
  * Note: Tool call display moved to AuditLogPanel for comprehensive view.
9
9
  */
10
10
 
11
- import React, { useState, useEffect } from 'react';
11
+ import React, { useState, useEffect, useRef, useCallback } from 'react';
12
12
  import { Button } from '@/components/ui/button';
13
13
  import { Badge } from '@/components/ui/badge';
14
14
  import { Separator } from '@/components/ui/separator';
15
+ import { HotspotsDialog } from '../dialogs/HotspotsDialog';
16
+ import { CodeMarkersDialog } from '../dialogs/CodeMarkersDialog';
17
+ import { ComplexityDialog } from '../dialogs/ComplexityDialog';
18
+ import { DependenciesDialog } from '../dialogs/DependenciesDialog';
19
+ import { AgentLoadDialog } from '../AgentLoadDialog';
20
+ import { DeadCodeDialog } from '../DeadCodeDialog';
21
+ import { HealthGauge } from '../HealthGauge';
22
+ import { ContextSparkline, SparklinePoint } from '../ContextSparkline';
23
+ import { useHealthScore } from '../../hooks/useHealthScore';
15
24
 
16
25
  /** Context tier type */
17
26
  type ContextTier = 'FULL' | 'REFRESH' | 'HANDOFF' | 'MINIMAL';
@@ -94,6 +103,49 @@ export function DebugPanel(): React.ReactElement {
94
103
  const [context, setContext] = useState<ContextData | null>(null);
95
104
  const [tokenStats, setTokenStats] = useState<Record<string, unknown> | null>(null);
96
105
  const [breakdownExpanded, setBreakdownExpanded] = useState(false);
106
+ const [hotspotsOpen, setHotspotsOpen] = useState(false);
107
+ const [codeMarkersOpen, setCodeMarkersOpen] = useState(false);
108
+ const [complexityOpen, setComplexityOpen] = useState(false);
109
+ const [dependenciesOpen, setDependenciesOpen] = useState(false);
110
+ const [agentLoadOpen, setAgentLoadOpen] = useState(false);
111
+ const [deadCodeOpen, setDeadCodeOpen] = useState(false);
112
+ const healthScore = useHealthScore();
113
+ const sparklineRef = useRef<SparklinePoint[]>([]);
114
+ const [sparklineVersion, setSparklineVersion] = useState(0);
115
+
116
+ const pushSparklinePoint = useCallback((percent: number, tokens: number) => {
117
+ const buf = sparklineRef.current;
118
+ buf.push({ percent, tokens, timestamp: Date.now() });
119
+ if (buf.length > 50) buf.shift();
120
+ setSparklineVersion(v => v + 1);
121
+ }, []);
122
+
123
+ const handleDimensionClick = (dimensionName: string) => {
124
+ switch (dimensionName) {
125
+ case 'churn':
126
+ setHotspotsOpen(true);
127
+ break;
128
+ case 'test_gaps':
129
+ // TODO: TestGapsDialog — for now no drill-down
130
+ break;
131
+ case 'todo_density':
132
+ case 'deprecation_debt':
133
+ setCodeMarkersOpen(true);
134
+ break;
135
+ case 'complexity':
136
+ setComplexityOpen(true);
137
+ break;
138
+ case 'dead_code':
139
+ setDeadCodeOpen(true);
140
+ break;
141
+ case 'dependency_freshness':
142
+ setDependenciesOpen(true);
143
+ break;
144
+ case 'agent_context_efficiency':
145
+ setAgentLoadOpen(true);
146
+ break;
147
+ }
148
+ };
97
149
 
98
150
  useEffect(() => {
99
151
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -104,7 +156,11 @@ export function DebugPanel(): React.ReactElement {
104
156
  try {
105
157
  const data = JSON.parse(event.data);
106
158
  if (data.type === 'init' || data.type === 'update') {
107
- setContext(data.context as ContextData);
159
+ const ctx = data.context as ContextData;
160
+ setContext(ctx);
161
+ if (ctx.percent != null) {
162
+ pushSparklinePoint(ctx.percent, ctx.tokens ?? 0);
163
+ }
108
164
  }
109
165
  } catch {
110
166
  // Ignore parse errors
@@ -126,7 +182,7 @@ export function DebugPanel(): React.ReactElement {
126
182
  contextWs.close();
127
183
  tokenWs.close();
128
184
  };
129
- }, []);
185
+ }, [pushSparklinePoint]);
130
186
 
131
187
  // Compute tier-specific CSS class
132
188
  const tierClass = context?.tier ? `tier-${context.tier.toLowerCase()}` : '';
@@ -134,6 +190,19 @@ export function DebugPanel(): React.ReactElement {
134
190
 
135
191
  return (
136
192
  <div className="debug-panel" data-testid="debug-panel">
193
+ <HealthGauge
194
+ score={healthScore.data?.composite_score ?? null}
195
+ dimensions={healthScore.data?.dimensions ?? []}
196
+ totalDimensions={8}
197
+ onDimensionClick={handleDimensionClick}
198
+ isLoading={healthScore.isLoading}
199
+ lastFetchedAt={healthScore.lastFetchedAt}
200
+ onRefresh={healthScore.refresh}
201
+ error={healthScore.error}
202
+ />
203
+
204
+ <Separator className="my-3" />
205
+
137
206
  <h4>Context Usage</h4>
138
207
  {context ? (
139
208
  <div className="context-info">
@@ -200,6 +269,7 @@ export function DebugPanel(): React.ReactElement {
200
269
  style={{ width: `${context.percent || 0}%` }}
201
270
  />
202
271
  </div>
272
+ <ContextSparkline history={sparklineRef.current} key={sparklineVersion} />
203
273
  <span className="context-text">
204
274
  {(context.tokens ?? 0).toLocaleString()} / {context.baseline != null && context.available != null
205
275
  ? (context.baseline + context.available).toLocaleString()
@@ -261,6 +331,12 @@ export function DebugPanel(): React.ReactElement {
261
331
  <div className="placeholder">No token stats</div>
262
332
  )}
263
333
 
334
+ <HotspotsDialog open={hotspotsOpen} onOpenChange={setHotspotsOpen} />
335
+ <CodeMarkersDialog open={codeMarkersOpen} onOpenChange={setCodeMarkersOpen} />
336
+ <ComplexityDialog open={complexityOpen} onOpenChange={setComplexityOpen} />
337
+ <DependenciesDialog open={dependenciesOpen} onOpenChange={setDependenciesOpen} />
338
+ <AgentLoadDialog isOpen={agentLoadOpen} onClose={() => setAgentLoadOpen(false)} />
339
+ <DeadCodeDialog isOpen={deadCodeOpen} onClose={() => setDeadCodeOpen(false)} />
264
340
  </div>
265
341
  );
266
342
  }
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import React, { useState } from 'react';
9
+ import { RefreshCw } from 'lucide-react';
9
10
  import { Button } from '@/components/ui/button';
10
11
  import { Badge } from '@/components/ui/badge';
11
12
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
@@ -61,14 +62,12 @@ function FileList({ files }: FileListProps): React.ReactElement {
61
62
 
62
63
  interface RepoStatusProps {
63
64
  repo: RepoStatusData;
64
- onPullDevelop?: (repoName: string, repoPath: string) => void;
65
65
  }
66
66
 
67
- function RepoStatus({ repo, onPullDevelop }: RepoStatusProps): React.ReactElement {
67
+ function RepoStatus({ repo }: RepoStatusProps): React.ReactElement {
68
68
  const [isExpanded, setIsExpanded] = useState(false);
69
- const { name, path, branch, ahead, behind, developBehind, staged, modified, untracked, isDirty, files } = repo;
69
+ const { name, branch, ahead, behind, developBehind, staged, modified, untracked, isDirty, files } = repo;
70
70
  const hasFiles = files.length > 0;
71
- const hasDevelopUpdates = developBehind !== undefined && developBehind > 0;
72
71
 
73
72
  return (
74
73
  <TooltipProvider delayDuration={300}>
@@ -90,14 +89,14 @@ function RepoStatus({ repo, onPullDevelop }: RepoStatusProps): React.ReactElemen
90
89
  <span className="branch-name">{branch}</span>
91
90
  </div>
92
91
 
93
- {(ahead !== undefined && ahead > 0) || (behind !== undefined && behind > 0) ? (
92
+ {((ahead !== undefined && ahead > 0) || (behind !== undefined && behind > 0) || (developBehind !== undefined && developBehind > 0)) && (
94
93
  <div className="sync-status">
95
94
  {ahead !== undefined && ahead > 0 && (
96
95
  <Tooltip>
97
96
  <TooltipTrigger asChild>
98
97
  <span className="ahead">↑{ahead}</span>
99
98
  </TooltipTrigger>
100
- <TooltipContent>Commits ahead</TooltipContent>
99
+ <TooltipContent>Commits ahead of remote</TooltipContent>
101
100
  </Tooltip>
102
101
  )}
103
102
  {behind !== undefined && behind > 0 && (
@@ -105,29 +104,15 @@ function RepoStatus({ repo, onPullDevelop }: RepoStatusProps): React.ReactElemen
105
104
  <TooltipTrigger asChild>
106
105
  <span className="behind">↓{behind}</span>
107
106
  </TooltipTrigger>
108
- <TooltipContent>Commits behind</TooltipContent>
107
+ <TooltipContent>Commits behind remote</TooltipContent>
109
108
  </Tooltip>
110
109
  )}
111
- </div>
112
- ) : null}
113
-
114
- {hasDevelopUpdates && (
115
- <div className="develop-behind-warning">
116
- <span className="warning-icon">⚠️</span>
117
- <span className="warning-text">develop is {developBehind} commit{developBehind > 1 ? 's' : ''} ahead</span>
118
- {onPullDevelop && (
110
+ {developBehind !== undefined && developBehind > 0 && (
119
111
  <Tooltip>
120
112
  <TooltipTrigger asChild>
121
- <Button
122
- variant="outline"
123
- size="sm"
124
- className="pull-develop-btn"
125
- onClick={() => onPullDevelop(name, path)}
126
- >
127
- Pull
128
- </Button>
113
+ <span className="develop-behind">⟳{developBehind}</span>
129
114
  </TooltipTrigger>
130
- <TooltipContent>Pull latest from develop</TooltipContent>
115
+ <TooltipContent>develop is {developBehind} commit{developBehind > 1 ? 's' : ''} ahead</TooltipContent>
131
116
  </Tooltip>
132
117
  )}
133
118
  </div>
@@ -169,11 +154,8 @@ export function GitPanel(): React.ReactElement {
169
154
  const { repos, isLoading, error } = useGitStatus();
170
155
  const { send } = useClaudeContext();
171
156
 
172
- const handlePullDevelop = (repoName: string, repoPath: string) => {
173
- const prompt = repoPath === '.'
174
- ? `pull develop`
175
- : `cd ${repoPath} && git pull origin develop`;
176
- send(prompt);
157
+ const handleSyncAll = () => {
158
+ send('Sync all repos');
177
159
  };
178
160
 
179
161
  if (isLoading) {
@@ -211,8 +193,21 @@ export function GitPanel(): React.ReactElement {
211
193
 
212
194
  return (
213
195
  <div className="git-panel stacked" data-testid="git-panel">
196
+ <div className="git-panel-actions">
197
+ <TooltipProvider delayDuration={300}>
198
+ <Tooltip>
199
+ <TooltipTrigger asChild>
200
+ <button className="sync-all-btn" onClick={handleSyncAll} aria-label="Sync all repos">
201
+ <RefreshCw size={14} />
202
+ <span>Sync all repos</span>
203
+ </button>
204
+ </TooltipTrigger>
205
+ <TooltipContent>Pull latest changes for all repos</TooltipContent>
206
+ </Tooltip>
207
+ </TooltipProvider>
208
+ </div>
214
209
  {repos.map(repo => (
215
- <RepoStatus key={repo.name} repo={repo} onPullDevelop={handlePullDevelop} />
210
+ <RepoStatus key={repo.name} repo={repo} />
216
211
  ))}
217
212
  </div>
218
213
  );
@@ -248,6 +248,7 @@ export function MessagePanel(): React.ReactElement {
248
248
  handleBellModeChange,
249
249
  handleRelayModeChange,
250
250
  handleTirePump,
251
+ handleAgentSwitch,
251
252
  } = useControlBar();
252
253
 
253
254
  // Claude context for WebSocket communication
@@ -415,13 +416,53 @@ export function MessagePanel(): React.ReactElement {
415
416
  handleStop();
416
417
  }, [pauseQueue, handleStop]);
417
418
 
419
+ // Resizable editor panel
420
+ const [editorHeight, setEditorHeight] = useState<number | null>(null);
421
+ const panelRef = useRef<HTMLDivElement>(null);
422
+ const isDragging = useRef(false);
423
+
424
+ const handleResizeStart = useCallback((e: React.MouseEvent) => {
425
+ e.preventDefault();
426
+ isDragging.current = true;
427
+ const startY = e.clientY;
428
+ const panelEl = panelRef.current;
429
+ if (!panelEl) return;
430
+ const panelRect = panelEl.getBoundingClientRect();
431
+ const startEditorHeight = editorHeight ?? panelEl.querySelector('.message-panel-editor')?.getBoundingClientRect().height ?? 150;
432
+
433
+ const onMouseMove = (ev: MouseEvent) => {
434
+ if (!isDragging.current) return;
435
+ const delta = startY - ev.clientY;
436
+ const newHeight = Math.max(80, Math.min(startEditorHeight + delta, panelRect.height * 0.7));
437
+ setEditorHeight(newHeight);
438
+ };
439
+ const onMouseUp = () => {
440
+ isDragging.current = false;
441
+ document.removeEventListener('mousemove', onMouseMove);
442
+ document.removeEventListener('mouseup', onMouseUp);
443
+ document.body.style.cursor = '';
444
+ document.body.style.userSelect = '';
445
+ };
446
+ document.addEventListener('mousemove', onMouseMove);
447
+ document.addEventListener('mouseup', onMouseUp);
448
+ document.body.style.cursor = 'row-resize';
449
+ document.body.style.userSelect = 'none';
450
+ }, [editorHeight]);
451
+
418
452
  return (
419
- <div className="message-panel" data-testid="message-panel">
453
+ <div className="message-panel" data-testid="message-panel" ref={panelRef}>
420
454
  <PersonaHeader />
421
455
  <div className="message-panel-content">
422
456
  <MessageView messages={messages} />
423
457
  </div>
424
- <div className="message-panel-editor">
458
+ <div
459
+ className="message-panel-resize-handle"
460
+ onMouseDown={handleResizeStart}
461
+ />
462
+ <div
463
+ className="message-panel-editor"
464
+ style={editorHeight != null ? { height: editorHeight, flexShrink: 0 } : undefined}
465
+ >
425
466
  <div className="editor-with-controls">
426
467
  <div className="editor-area">
427
468
  <Editor
@@ -444,6 +485,7 @@ export function MessagePanel(): React.ReactElement {
444
485
  contextPercent={contextPercent}
445
486
  currentAgent={currentAgent}
446
487
  onTirePump={handleTirePump}
488
+ onAgentSwitch={handleAgentSwitch}
447
489
  />
448
490
  </div>
449
491
  </div>
@@ -53,11 +53,11 @@ interface Settings {
53
53
  interface ThemeMetadata {
54
54
  id: string;
55
55
  name: string;
56
- tier: 'S' | 'A' | 'B' | 'U';
56
+ tier: string;
57
57
  }
58
58
 
59
- // Tier sort order: S=0, A=1, B=2, U=3
60
- const TIER_ORDER: Record<string, number> = { S: 0, A: 1, B: 2, U: 3 };
59
+ // Tier sort order: S=0, A=1, B=2, unranked=3
60
+ const TIER_ORDER: Record<string, number> = { S: 0, A: 1, B: 2 };
61
61
 
62
62
  // Panel display names for the visibility toggles
63
63
  const PANEL_DISPLAY_NAMES: Record<string, string> = {
@@ -348,7 +348,7 @@ export function SettingsPanel(): React.ReactElement {
348
348
  >
349
349
  {sortedThemes.map(theme => (
350
350
  <option key={theme.id} value={theme.id}>
351
- [{theme.tier}] {theme.name}
351
+ [{theme.tier || 'Unranked'}] {theme.name}
352
352
  </option>
353
353
  ))}
354
354
  </select>