@pennyfarthing/cyclist 9.3.0 → 10.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.
Files changed (101) hide show
  1. package/dist/api/hook-request.d.ts +11 -0
  2. package/dist/api/hook-request.d.ts.map +1 -1
  3. package/dist/api/hook-request.js +126 -28
  4. package/dist/api/hook-request.js.map +1 -1
  5. package/dist/api/hotspots.d.ts +3 -0
  6. package/dist/api/hotspots.d.ts.map +1 -0
  7. package/dist/api/hotspots.js +54 -0
  8. package/dist/api/hotspots.js.map +1 -0
  9. package/dist/api/index.d.ts +2 -0
  10. package/dist/api/index.d.ts.map +1 -1
  11. package/dist/api/index.js +3 -0
  12. package/dist/api/index.js.map +1 -1
  13. package/dist/api/permissions.d.ts +16 -0
  14. package/dist/api/permissions.d.ts.map +1 -0
  15. package/dist/api/permissions.js +67 -0
  16. package/dist/api/permissions.js.map +1 -0
  17. package/dist/api/settings.d.ts +1 -1
  18. package/dist/api/settings.d.ts.map +1 -1
  19. package/dist/api/settings.js +44 -17
  20. package/dist/api/settings.js.map +1 -1
  21. package/dist/api/theme-agents.d.ts +4 -0
  22. package/dist/api/theme-agents.d.ts.map +1 -1
  23. package/dist/api/theme-agents.js +3 -0
  24. package/dist/api/theme-agents.js.map +1 -1
  25. package/dist/approval-gate.d.ts +3 -75
  26. package/dist/approval-gate.d.ts.map +1 -1
  27. package/dist/approval-gate.js +4 -121
  28. package/dist/approval-gate.js.map +1 -1
  29. package/dist/hooks/cyclist-pretooluse-hook.d.ts +60 -0
  30. package/dist/hooks/cyclist-pretooluse-hook.d.ts.map +1 -0
  31. package/dist/hooks/cyclist-pretooluse-hook.js +57 -0
  32. package/dist/hooks/cyclist-pretooluse-hook.js.map +1 -0
  33. package/dist/hooks/pretooluse-hook.d.ts +89 -0
  34. package/dist/hooks/pretooluse-hook.d.ts.map +1 -0
  35. package/dist/hooks/pretooluse-hook.js +235 -0
  36. package/dist/hooks/pretooluse-hook.js.map +1 -0
  37. package/dist/main.d.ts +1 -134
  38. package/dist/main.d.ts.map +1 -1
  39. package/dist/main.js +42 -373
  40. package/dist/main.js.map +1 -1
  41. package/dist/menu-builder.d.ts +7 -1
  42. package/dist/menu-builder.d.ts.map +1 -1
  43. package/dist/menu-builder.js +36 -1
  44. package/dist/menu-builder.js.map +1 -1
  45. package/dist/otlp-receiver.d.ts.map +1 -1
  46. package/dist/otlp-receiver.js +6 -0
  47. package/dist/otlp-receiver.js.map +1 -1
  48. package/dist/public/css/react.css +1 -1
  49. package/dist/public/js/react/react.js +42 -42
  50. package/dist/server.d.ts.map +1 -1
  51. package/dist/server.js +16 -3
  52. package/dist/server.js.map +1 -1
  53. package/dist/settings-store.d.ts +3 -1
  54. package/dist/settings-store.d.ts.map +1 -1
  55. package/dist/settings-store.js +18 -9
  56. package/dist/settings-store.js.map +1 -1
  57. package/dist/story-parser.d.ts +17 -0
  58. package/dist/story-parser.d.ts.map +1 -1
  59. package/dist/story-parser.js +183 -13
  60. package/dist/story-parser.js.map +1 -1
  61. package/dist/websocket.d.ts +1 -0
  62. package/dist/websocket.d.ts.map +1 -1
  63. package/dist/websocket.js +48 -5
  64. package/dist/websocket.js.map +1 -1
  65. package/dist/workflow-presets.d.ts +72 -0
  66. package/dist/workflow-presets.d.ts.map +1 -0
  67. package/dist/workflow-presets.js +93 -0
  68. package/dist/workflow-presets.js.map +1 -0
  69. package/package.json +2 -2
  70. package/src/public/App.tsx +61 -1
  71. package/src/public/components/ApprovalModal/index.tsx +31 -1
  72. package/src/public/components/ControlBar.tsx +19 -20
  73. package/src/public/components/DockviewWorkspace.tsx +39 -5
  74. package/src/public/components/FontPicker/index.tsx +118 -33
  75. package/src/public/components/FullFileTree.tsx +223 -0
  76. package/src/public/components/Message.tsx +89 -11
  77. package/src/public/components/MessageView.tsx +206 -93
  78. package/src/public/components/PersonaHeader.tsx +47 -15
  79. package/src/public/components/SubagentSpan.tsx +15 -8
  80. package/src/public/components/panels/BackgroundPanel.tsx +1 -1
  81. package/src/public/components/panels/ChangedPanel.tsx +30 -44
  82. package/src/public/components/panels/HotspotsPanel.tsx +365 -0
  83. package/src/public/components/panels/MessagePanel.tsx +79 -5
  84. package/src/public/components/panels/SettingsPanel.tsx +3 -28
  85. package/src/public/components/panels/WorkflowPanel.tsx +108 -13
  86. package/src/public/components/panels/index.ts +1 -0
  87. package/src/public/contexts/ClaudeContext.tsx +16 -1
  88. package/src/public/css/theme-system.css +46 -38
  89. package/src/public/hooks/useColorScheme.ts +27 -0
  90. package/src/public/hooks/useFileBrowser.ts +71 -0
  91. package/src/public/hooks/useHotspots.ts +113 -0
  92. package/src/public/hooks/usePlanModeExit.ts +105 -0
  93. package/src/public/hooks/useStory.ts +12 -3
  94. package/src/public/images/cyclist-dark.png +0 -0
  95. package/src/public/images/cyclist-light.png +0 -0
  96. package/src/public/styles/dockview-theme.css +31 -33
  97. package/src/public/styles/tailwind.css +417 -58
  98. package/src/public/types/message.ts +6 -1
  99. package/src/public/utils/markdown.ts +2 -2
  100. package/src/public/utils/slash-commands.ts +1 -1
  101. package/src/public/utils/toolStackGrouper.ts +5 -6
@@ -0,0 +1,105 @@
1
+ /**
2
+ * usePlanModeExit - Smooth plan mode exit with tirepump choice
3
+ *
4
+ * Story: MSSCI-14327
5
+ *
6
+ * Handles the transition from plan mode to accept mode after plan approval,
7
+ * and offers the user a choice to tirepump (commit/push) changes.
8
+ */
9
+
10
+ import { useState, useCallback } from 'react';
11
+ import type { Mode } from '../components/ModeSwitch/index';
12
+
13
+ /** The Claude CLI mode to transition to after plan exit */
14
+ export const PLAN_EXIT_MODE = 'acceptEdits';
15
+
16
+ export interface PlanModeExitOptions {
17
+ currentMode: Mode;
18
+ setMode: (mode: Mode) => void;
19
+ approved: boolean;
20
+ wsConnected?: boolean;
21
+ }
22
+
23
+ export interface TirepumpOption {
24
+ action: 'tirepump' | 'continue';
25
+ label: string;
26
+ }
27
+
28
+ export interface PlanModeExitResult {
29
+ showTirepumpChoice: boolean;
30
+ options?: TirepumpOption[];
31
+ localOnly?: boolean;
32
+ }
33
+
34
+ export interface TirepumpChoiceOptions {
35
+ choice: 'tirepump' | 'continue';
36
+ currentAgent?: string;
37
+ setMode?: (mode: Mode) => void;
38
+ onContextClear?: (data: { agent: string }) => void;
39
+ }
40
+
41
+ export interface TirepumpChoiceResult {
42
+ contextCleared: boolean;
43
+ action: 'tirepump' | 'continue';
44
+ }
45
+
46
+ const TIREPUMP_OPTIONS: TirepumpOption[] = [
47
+ { action: 'tirepump', label: 'Commit & push changes' },
48
+ { action: 'continue', label: 'Continue without committing' },
49
+ ];
50
+
51
+ export async function handlePlanModeExit(
52
+ options: PlanModeExitOptions
53
+ ): Promise<PlanModeExitResult> {
54
+ const { currentMode, setMode, approved, wsConnected } = options;
55
+
56
+ if (!approved) {
57
+ return { showTirepumpChoice: false };
58
+ }
59
+
60
+ // Transition from plan to accept if not already there
61
+ if (currentMode !== 'accept') {
62
+ setMode('accept');
63
+ }
64
+
65
+ const localOnly = wsConnected === false;
66
+
67
+ return {
68
+ showTirepumpChoice: true,
69
+ options: TIREPUMP_OPTIONS,
70
+ ...(localOnly && { localOnly: true }),
71
+ };
72
+ }
73
+
74
+ export async function handleTirepumpChoice(
75
+ options: TirepumpChoiceOptions
76
+ ): Promise<TirepumpChoiceResult> {
77
+ const { choice, currentAgent, onContextClear } = options;
78
+
79
+ if (choice === 'tirepump') {
80
+ onContextClear?.({ agent: currentAgent ?? '' });
81
+ return { contextCleared: true, action: 'tirepump' };
82
+ }
83
+
84
+ return { contextCleared: false, action: 'continue' };
85
+ }
86
+
87
+ export function usePlanModeExit() {
88
+ const [showChoice, setShowChoice] = useState(false);
89
+ const [exitResult, setExitResult] = useState<PlanModeExitResult | null>(null);
90
+
91
+ const exitPlanMode = useCallback(async (options: PlanModeExitOptions) => {
92
+ const result = await handlePlanModeExit(options);
93
+ setExitResult(result);
94
+ setShowChoice(result.showTirepumpChoice);
95
+ return result;
96
+ }, []);
97
+
98
+ const chooseTirepump = useCallback(async (options: TirepumpChoiceOptions) => {
99
+ const result = await handleTirepumpChoice(options);
100
+ setShowChoice(false);
101
+ return result;
102
+ }, []);
103
+
104
+ return { showChoice, exitResult, exitPlanMode, chooseTirepump };
105
+ }
@@ -11,7 +11,7 @@
11
11
  import { useState, useEffect, useRef } from 'react';
12
12
 
13
13
  // Import types from story-parser for criteria and workflow
14
- import type { CriteriaItem, WorkflowPhase } from '../../../story-parser.js';
14
+ import type { CriteriaItem, WorkflowPhase, AvailableWorkflow } from '../../../story-parser.js';
15
15
 
16
16
  export interface StoryData {
17
17
  id: string;
@@ -24,15 +24,19 @@ export interface StoryData {
24
24
  // MSSCI-12849: AC and BikeLane panel data
25
25
  criteria?: CriteriaItem[] | null;
26
26
  workflowPhases?: WorkflowPhase[] | null;
27
+ // MSSCI-14300: Distinguish phased vs stepped workflow rendering
28
+ workflowType?: string;
27
29
  }
28
30
 
29
31
  // Re-export types for panel components
30
- export type { CriteriaItem, WorkflowPhase };
32
+ export type { CriteriaItem, WorkflowPhase, AvailableWorkflow };
31
33
 
32
34
  interface UseStoryResult {
33
35
  story: StoryData | null;
34
36
  isLoading: boolean;
35
37
  error: Error | null;
38
+ // MSSCI-14301: Available workflows for discovery panel
39
+ availableWorkflows: AvailableWorkflow[] | null;
36
40
  }
37
41
 
38
42
  /** WebSocket message format from /ws/story */
@@ -44,7 +48,9 @@ interface StoryMessage {
44
48
  status?: string | null;
45
49
  points?: number | null;
46
50
  workflow?: WorkflowPhase[] | null;
51
+ workflowType?: string | null;
47
52
  criteria?: CriteriaItem[] | null;
53
+ availableWorkflows?: AvailableWorkflow[] | null;
48
54
  [key: string]: unknown;
49
55
  }
50
56
 
@@ -59,6 +65,7 @@ function transformMessage(msg: StoryMessage): StoryData | null {
59
65
  points: msg.points ?? undefined,
60
66
  criteria: msg.criteria,
61
67
  workflowPhases: msg.workflow,
68
+ workflowType: msg.workflowType ?? undefined,
62
69
  };
63
70
  }
64
71
 
@@ -66,6 +73,7 @@ export function useStory(): UseStoryResult {
66
73
  const [story, setStory] = useState<StoryData | null>(null);
67
74
  const [isLoading, setIsLoading] = useState(true);
68
75
  const [error, setError] = useState<Error | null>(null);
76
+ const [availableWorkflows, setAvailableWorkflows] = useState<AvailableWorkflow[] | null>(null);
69
77
  const wsRef = useRef<WebSocket | null>(null);
70
78
  const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
71
79
 
@@ -86,6 +94,7 @@ export function useStory(): UseStoryResult {
86
94
  const msg = JSON.parse(event.data) as StoryMessage;
87
95
  if (msg.type === 'init' || msg.type === 'update') {
88
96
  setStory(transformMessage(msg));
97
+ setAvailableWorkflows(msg.availableWorkflows ?? null);
89
98
  setIsLoading(false);
90
99
  setError(null);
91
100
  }
@@ -122,5 +131,5 @@ export function useStory(): UseStoryResult {
122
131
  };
123
132
  }, []);
124
133
 
125
- return { story, isLoading, error };
134
+ return { story, isLoading, error, availableWorkflows };
126
135
  }
@@ -21,7 +21,7 @@
21
21
  /* Dockview CSS variable mappings to Cyclist theme */
22
22
  --dv-group-view-background-color: var(--bg-secondary, #0f0f1a);
23
23
  --dv-tabs-and-actions-container-background-color: var(--bg-tertiary, #1a1a2e);
24
- --dv-activegroup-visiblepanel-tab-background-color: var(--accent-color, #6366f1);
24
+ --dv-activegroup-visiblepanel-tab-background-color: var(--accent, #6366f1);
25
25
  --dv-activegroup-visiblepanel-tab-color: var(--text-primary, #ffffff);
26
26
  --dv-activegroup-hiddenpanel-tab-background-color: var(--bg-tertiary, #1a1a2e);
27
27
  --dv-activegroup-hiddenpanel-tab-color: var(--text-secondary, #a1a1aa);
@@ -29,11 +29,11 @@
29
29
  --dv-inactivegroup-visiblepanel-tab-color: var(--text-secondary, #a1a1aa);
30
30
  --dv-inactivegroup-hiddenpanel-tab-background-color: var(--bg-tertiary, #1a1a2e);
31
31
  --dv-inactivegroup-hiddenpanel-tab-color: var(--text-muted, #71717a);
32
- --dv-separator-border: var(--border-color, #27272a);
33
- --dv-paneview-header-border-color: var(--border-color, #27272a);
32
+ --dv-separator-border: var(--border, #27272a);
33
+ --dv-paneview-header-border-color: var(--border, #27272a);
34
34
  --dv-tab-close-icon: var(--text-muted, #71717a);
35
35
  --dv-drag-over-background-color: rgba(99, 102, 241, 0.2);
36
- --dv-drag-over-border-color: var(--accent-color, #6366f1);
36
+ --dv-drag-over-border-color: var(--accent, #6366f1);
37
37
  }
38
38
 
39
39
  .dockview-container {
@@ -70,12 +70,12 @@
70
70
  }
71
71
 
72
72
  .cyclist-dockview .dv-tab:hover {
73
- background-color: var(--bg-hover, #27272a);
73
+ background-color: var(--bg-tertiary, #27272a);
74
74
  }
75
75
 
76
76
  /* Active tab indicator */
77
77
  .cyclist-dockview .dv-tab.dv-active-tab {
78
- border-bottom: 2px solid var(--accent-color, #6366f1);
78
+ border-bottom: 2px solid var(--accent, #6366f1);
79
79
  }
80
80
 
81
81
  /* Tab close button */
@@ -93,13 +93,11 @@
93
93
  ============================================================================= */
94
94
 
95
95
  .cyclist-dockview .dv-group-panel {
96
- border: 1px solid var(--border-color, #27272a);
96
+ border: 1px solid var(--border, #27272a);
97
97
  }
98
98
 
99
- /* Message panel - hide close button (sacred, cannot be closed) */
100
- .cyclist-dockview .dv-tab[data-panel-id="message"] .dv-close-action {
101
- display: none !important;
102
- }
99
+ /* Message panel tab bar is hidden via Dockview API (group.headerVisible = false)
100
+ in DockviewWorkspace.tsx - no CSS selector needed */
103
101
 
104
102
  /* =============================================================================
105
103
  Resize Sash Styles
@@ -111,11 +109,11 @@
111
109
  }
112
110
 
113
111
  .cyclist-dockview .dv-resize-container .dv-sash:hover {
114
- background-color: var(--accent-color, #6366f1);
112
+ background-color: var(--accent, #6366f1);
115
113
  }
116
114
 
117
115
  .cyclist-dockview .dv-resize-container .dv-sash:active {
118
- background-color: var(--accent-color-bright, #818cf8);
116
+ background-color: var(--accent-hover, #818cf8);
119
117
  }
120
118
 
121
119
  /* =============================================================================
@@ -151,12 +149,12 @@
151
149
 
152
150
  /* Focus indicators */
153
151
  .cyclist-dockview .dv-tab:focus-visible {
154
- outline: 2px solid var(--accent-color, #6366f1);
152
+ outline: 2px solid var(--accent, #6366f1);
155
153
  outline-offset: -2px;
156
154
  }
157
155
 
158
156
  .cyclist-dockview .dv-sash:focus-visible {
159
- outline: 2px solid var(--accent-color, #6366f1);
157
+ outline: 2px solid var(--accent, #6366f1);
160
158
  }
161
159
 
162
160
  /* Reduced motion */
@@ -206,7 +204,7 @@
206
204
  padding: 4px 8px;
207
205
  margin: 4px;
208
206
  background-color: var(--bg-tertiary, #1a1a2e);
209
- border: 1px solid var(--border-color, #27272a);
207
+ border: 1px solid var(--border, #27272a);
210
208
  border-radius: 4px;
211
209
  color: var(--text-secondary, #a1a1aa);
212
210
  font-size: 12px;
@@ -215,9 +213,9 @@
215
213
  }
216
214
 
217
215
  .cyclist-dockview .dv-tabs-overflow-dropdown-default:hover {
218
- background-color: var(--bg-hover, #27272a);
216
+ background-color: var(--bg-tertiary, #27272a);
219
217
  color: var(--text-primary, #ffffff);
220
- border-color: var(--accent-color, #6366f1);
218
+ border-color: var(--accent, #6366f1);
221
219
  }
222
220
 
223
221
  /* Overflow dropdown container (list of hidden tabs) */
@@ -229,7 +227,7 @@
229
227
  max-height: 300px;
230
228
  overflow-y: auto;
231
229
  background-color: var(--bg-tertiary, #1a1a2e);
232
- border: 1px solid var(--border-color, #27272a);
230
+ border: 1px solid var(--border, #27272a);
233
231
  border-radius: 6px;
234
232
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
235
233
  z-index: 1000;
@@ -249,12 +247,12 @@
249
247
  }
250
248
 
251
249
  .cyclist-dockview .dv-tabs-overflow-container .dv-tab:hover {
252
- background-color: var(--accent-color, #6366f1);
250
+ background-color: var(--accent, #6366f1);
253
251
  color: var(--text-primary, #ffffff);
254
252
  }
255
253
 
256
254
  .cyclist-dockview .dv-tabs-overflow-container .dv-tab:not(:last-child) {
257
- border-bottom: 1px solid var(--border-color, #27272a);
255
+ border-bottom: 1px solid var(--border, #27272a);
258
256
  }
259
257
 
260
258
  /* =============================================================================
@@ -293,7 +291,7 @@
293
291
  gap: 4px;
294
292
  padding: 6px 10px;
295
293
  background-color: var(--bg-tertiary, #1a1a2e);
296
- border: 1px solid var(--border-color, #27272a);
294
+ border: 1px solid var(--border, #27272a);
297
295
  border-radius: 6px;
298
296
  color: var(--text-secondary, #a1a1aa);
299
297
  cursor: pointer;
@@ -302,9 +300,9 @@
302
300
  }
303
301
 
304
302
  .panel-restore-button:hover {
305
- background-color: var(--bg-hover, #27272a);
303
+ background-color: var(--bg-tertiary, #27272a);
306
304
  color: var(--text-primary, #ffffff);
307
- border-color: var(--accent-color, #6366f1);
305
+ border-color: var(--accent, #6366f1);
308
306
  }
309
307
 
310
308
  .panel-restore-icon {
@@ -314,7 +312,7 @@
314
312
  }
315
313
 
316
314
  .panel-restore-count {
317
- background-color: var(--accent-color, #6366f1);
315
+ background-color: var(--accent, #6366f1);
318
316
  color: var(--text-primary, #ffffff);
319
317
  padding: 2px 6px;
320
318
  border-radius: 10px;
@@ -329,7 +327,7 @@
329
327
  margin-top: 4px;
330
328
  min-width: 160px;
331
329
  background-color: var(--bg-tertiary, #1a1a2e);
332
- border: 1px solid var(--border-color, #27272a);
330
+ border: 1px solid var(--border, #27272a);
333
331
  border-radius: 6px;
334
332
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
335
333
  overflow: hidden;
@@ -342,7 +340,7 @@
342
340
  color: var(--text-muted, #71717a);
343
341
  text-transform: uppercase;
344
342
  letter-spacing: 0.5px;
345
- border-bottom: 1px solid var(--border-color, #27272a);
343
+ border-bottom: 1px solid var(--border, #27272a);
346
344
  }
347
345
 
348
346
  .panel-restore-item {
@@ -359,12 +357,12 @@
359
357
  }
360
358
 
361
359
  .panel-restore-item:hover {
362
- background-color: var(--accent-color, #6366f1);
360
+ background-color: var(--accent, #6366f1);
363
361
  color: var(--text-primary, #ffffff);
364
362
  }
365
363
 
366
364
  .panel-restore-item:focus-visible {
367
- outline: 2px solid var(--accent-color, #6366f1);
365
+ outline: 2px solid var(--accent, #6366f1);
368
366
  outline-offset: -2px;
369
367
  }
370
368
 
@@ -396,7 +394,7 @@
396
394
  display: flex;
397
395
  align-items: center;
398
396
  justify-content: center;
399
- background-color: rgba(10, 10, 15, 0.9);
397
+ background-color: color-mix(in srgb, var(--bg-primary, #0a0a0f) 90%, transparent);
400
398
  z-index: 10;
401
399
  }
402
400
 
@@ -407,7 +405,7 @@
407
405
  gap: 12px;
408
406
  padding: 24px;
409
407
  background-color: var(--bg-tertiary, #1a1a2e);
410
- border: 1px solid var(--border-color, #27272a);
408
+ border: 1px solid var(--border, #27272a);
411
409
  border-radius: 8px;
412
410
  text-align: center;
413
411
  }
@@ -424,7 +422,7 @@
424
422
 
425
423
  .tty-restart-button {
426
424
  padding: 8px 16px;
427
- background-color: var(--accent-color, #6366f1);
425
+ background-color: var(--accent, #6366f1);
428
426
  border: none;
429
427
  border-radius: 6px;
430
428
  color: var(--text-primary, #ffffff);
@@ -439,7 +437,7 @@
439
437
  }
440
438
 
441
439
  .tty-restart-button:focus-visible {
442
- outline: 2px solid var(--accent-color, #6366f1);
440
+ outline: 2px solid var(--accent, #6366f1);
443
441
  outline-offset: 2px;
444
442
  }
445
443