@pennyfarthing/cyclist 9.3.0 → 9.4.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 (46) hide show
  1. package/dist/api/hotspots.d.ts +3 -0
  2. package/dist/api/hotspots.d.ts.map +1 -0
  3. package/dist/api/hotspots.js +54 -0
  4. package/dist/api/hotspots.js.map +1 -0
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api/index.js +1 -0
  8. package/dist/api/index.js.map +1 -1
  9. package/dist/api/settings.d.ts +1 -1
  10. package/dist/api/settings.d.ts.map +1 -1
  11. package/dist/api/settings.js +44 -17
  12. package/dist/api/settings.js.map +1 -1
  13. package/dist/public/css/react.css +1 -1
  14. package/dist/public/js/react/react.js +31 -31
  15. package/dist/server.d.ts.map +1 -1
  16. package/dist/server.js +3 -1
  17. package/dist/server.js.map +1 -1
  18. package/dist/story-parser.d.ts +17 -0
  19. package/dist/story-parser.d.ts.map +1 -1
  20. package/dist/story-parser.js +183 -13
  21. package/dist/story-parser.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/public/App.tsx +2 -0
  24. package/src/public/components/ControlBar.tsx +1 -1
  25. package/src/public/components/DockviewWorkspace.tsx +4 -0
  26. package/src/public/components/FontPicker/index.tsx +118 -33
  27. package/src/public/components/FullFileTree.tsx +223 -0
  28. package/src/public/components/Message.tsx +32 -10
  29. package/src/public/components/MessageView.tsx +177 -92
  30. package/src/public/components/PersonaHeader.tsx +45 -15
  31. package/src/public/components/SubagentSpan.tsx +15 -8
  32. package/src/public/components/panels/ChangedPanel.tsx +30 -44
  33. package/src/public/components/panels/HotspotsPanel.tsx +365 -0
  34. package/src/public/components/panels/MessagePanel.tsx +14 -2
  35. package/src/public/components/panels/WorkflowPanel.tsx +85 -12
  36. package/src/public/components/panels/index.ts +1 -0
  37. package/src/public/css/theme-system.css +46 -38
  38. package/src/public/hooks/useFileBrowser.ts +71 -0
  39. package/src/public/hooks/useHotspots.ts +113 -0
  40. package/src/public/hooks/useStory.ts +12 -3
  41. package/src/public/images/cyclist-dark.png +0 -0
  42. package/src/public/images/cyclist-light.png +0 -0
  43. package/src/public/styles/tailwind.css +236 -58
  44. package/src/public/types/message.ts +4 -0
  45. package/src/public/utils/slash-commands.ts +1 -1
  46. package/src/public/utils/toolStackGrouper.ts +5 -6
@@ -265,6 +265,7 @@
265
265
  background: var(--bg-secondary, #252526);
266
266
  border-bottom: 1px solid var(--border-color, #3c3c3c);
267
267
  flex-shrink: 0;
268
+ position: relative;
268
269
  }
269
270
 
270
271
  .persona-header.empty {
@@ -293,8 +294,8 @@
293
294
  }
294
295
 
295
296
  .persona-portrait {
296
- width: 40px;
297
- height: 40px;
297
+ width: 100px;
298
+ height: 100px;
298
299
  border-radius: 50%;
299
300
  overflow: hidden;
300
301
  flex-shrink: 0;
@@ -302,6 +303,7 @@
302
303
  display: flex;
303
304
  align-items: center;
304
305
  justify-content: center;
306
+ transition: width 0.2s ease, height 0.2s ease;
305
307
  }
306
308
 
307
309
  .persona-portrait .portrait-image {
@@ -314,12 +316,8 @@
314
316
  font-size: 1.5rem;
315
317
  }
316
318
 
317
- /* Role badge positioned at bottom of portrait */
318
- .persona-portrait-group .persona-role.badge {
319
- position: absolute;
320
- bottom: -4px;
321
- left: 50%;
322
- transform: translateX(-50%);
319
+ /* Role badge inline in name row */
320
+ .persona-name-row .persona-role.badge {
323
321
  padding: 1px 6px;
324
322
  color: #fff;
325
323
  font-size: 0.6rem;
@@ -328,6 +326,7 @@
328
326
  font-weight: 600;
329
327
  white-space: nowrap;
330
328
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
329
+ flex-shrink: 0;
331
330
  }
332
331
 
333
332
  .persona-info {
@@ -338,9 +337,59 @@
338
337
  flex: 1;
339
338
  }
340
339
 
340
+ .persona-branding {
341
+ height: 80px;
342
+ width: auto;
343
+ flex-shrink: 0;
344
+ opacity: 0.5;
345
+ pointer-events: none;
346
+ transition: height 0.2s ease;
347
+ }
348
+
349
+ /* Compact mode: collapsed header */
350
+ .persona-header.compact .persona-portrait {
351
+ width: 40px;
352
+ height: 40px;
353
+ }
354
+
355
+ .persona-header.compact .persona-portrait .portrait-fallback {
356
+ font-size: 1.2rem;
357
+ }
358
+
359
+ .persona-header.compact .persona-branding {
360
+ height: 32px;
361
+ }
362
+
363
+ .persona-header.compact .persona-catchphrase {
364
+ display: none;
365
+ }
366
+
367
+ /* Collapse toggle button */
368
+ .persona-collapse-toggle {
369
+ position: absolute;
370
+ bottom: 2px;
371
+ right: 8px;
372
+ background: none;
373
+ border: none;
374
+ color: var(--text-secondary, #8b8b8b);
375
+ font-size: 0.6rem;
376
+ cursor: pointer;
377
+ padding: 0 4px;
378
+ opacity: 0;
379
+ transition: opacity 0.15s;
380
+ }
381
+
382
+ .persona-header:hover .persona-collapse-toggle {
383
+ opacity: 1;
384
+ }
385
+
386
+ .persona-collapse-toggle:hover {
387
+ color: var(--text-primary, #d4d4d4);
388
+ }
389
+
341
390
  .persona-name-row {
342
391
  display: flex;
343
- align-items: baseline;
392
+ align-items: center;
344
393
  gap: 8px;
345
394
  flex-wrap: wrap;
346
395
  }
@@ -403,16 +452,15 @@
403
452
  overflow-x: hidden;
404
453
  display: flex;
405
454
  flex-direction: column;
406
- gap: 0.75rem;
407
- padding: 1rem;
455
+ gap: 0;
456
+ padding: 1rem 1.5rem;
408
457
  }
409
458
 
410
- /* Individual message bubbles */
459
+ /* Individual messages — Tufte: no bubbles, no backgrounds */
411
460
  .message {
412
461
  display: flex;
413
462
  gap: 0.75rem;
414
- padding: 0.75rem 1rem;
415
- border-radius: 8px;
463
+ padding: 0.25rem 0;
416
464
  line-height: 1.5;
417
465
  }
418
466
 
@@ -464,17 +512,17 @@
464
512
  border-radius: 3px;
465
513
  }
466
514
 
515
+ /* User messages — blockquote right border (avatar is on the right) */
467
516
  .message-user {
468
- background: var(--accent-color, #007acc);
469
- color: #fff;
470
- margin-left: auto;
471
- max-width: 85%;
472
- border-bottom-right-radius: 4px;
517
+ border-right: 2px solid var(--text-secondary, #8b8b8b);
518
+ border-left: none;
519
+ padding-right: 0.75rem;
520
+ color: var(--text-primary, #d4d4d4);
473
521
  }
474
522
 
475
523
  .message-user .message-content pre,
476
524
  .message-user .message-content :not(pre) > code {
477
- background: rgba(0, 0, 0, 0.2);
525
+ background: var(--bg-tertiary, #2d2d2d);
478
526
  }
479
527
 
480
528
  .message-attachment-indicator {
@@ -489,20 +537,99 @@
489
537
  opacity: 0.8;
490
538
  }
491
539
 
540
+ /* Agent messages — no background, no constraints */
492
541
  .message-agent {
493
- background: var(--bg-tertiary, #2d2d2d);
494
542
  color: var(--text-primary, #d4d4d4);
495
- max-width: 95%;
496
- border-bottom-left-radius: 4px;
543
+ }
544
+
545
+ /* Turn-based grouping — Tufte spacing */
546
+ .turn-group {
547
+ padding-top: 1rem;
548
+ }
549
+
550
+ .turn-group:first-child {
551
+ padding-top: 0;
552
+ }
553
+
554
+ .turn-group + .turn-group {
555
+ border-top: 1px solid var(--border-color, #3c3c3c);
556
+ }
557
+
558
+ /* Turn label — speaker + timestamp + optional role badge */
559
+ .turn-label {
560
+ display: flex;
561
+ align-items: center;
562
+ flex-wrap: wrap;
563
+ gap: 0.5rem;
564
+ padding: 0.25rem 0;
565
+ margin-bottom: 0.25rem;
566
+ }
567
+
568
+ .turn-speaker {
569
+ font-size: 0.75rem;
570
+ font-weight: 600;
571
+ color: var(--text-secondary, #8b8b8b);
572
+ text-transform: uppercase;
573
+ letter-spacing: 0.05em;
574
+ }
575
+
576
+ .turn-timestamp {
577
+ font-size: 0.6875rem;
578
+ color: var(--text-muted, #666);
579
+ margin-left: auto;
580
+ }
581
+
582
+ /* Role badge in turn label — flush right under timestamp */
583
+ .turn-role-badge {
584
+ font-size: 0.5625rem;
585
+ padding: 0px 5px;
586
+ color: #fff;
587
+ border-radius: 3px;
588
+ font-weight: 700;
589
+ letter-spacing: 0.05em;
590
+ line-height: 1.4;
591
+ }
592
+
593
+ /* User turn: avatar + label on the right */
594
+ .turn-user .turn-label {
595
+ flex-direction: row-reverse;
596
+ }
597
+
598
+ .turn-user .turn-timestamp {
599
+ margin-left: 0;
600
+ margin-right: auto;
601
+ }
602
+
603
+ .turn-user .message {
604
+ flex-direction: row-reverse;
605
+ }
606
+
607
+ .turn-user .message-avatar {
608
+ margin-left: 0.75rem;
609
+ margin-right: 0;
610
+ }
611
+
612
+ .turn-user .message-content {
613
+ text-align: right;
614
+ }
615
+
616
+ /* Hide avatar on continuation messages (same speaker, not first in turn) */
617
+ .message.continuation .message-avatar {
618
+ visibility: hidden;
619
+ }
620
+
621
+ /* Same-turn messages: tight spacing */
622
+ .message.continuation {
623
+ padding-top: 0;
497
624
  }
498
625
 
499
626
  /* Hook/System message styling (AC3: Story 75-5) */
500
627
  .message-hook,
501
628
  .message-system {
502
- background: var(--bg-secondary, #252526);
503
- border: 1px dashed var(--border-color, #3c3c3c);
629
+ background: none;
630
+ border-left: 2px dashed var(--border-color, #3c3c3c);
631
+ padding-left: 0.75rem;
504
632
  color: var(--text-secondary, #8b8b8b);
505
- max-width: 100%;
506
633
  font-size: 0.85rem;
507
634
  }
508
635
 
@@ -519,12 +646,11 @@
519
646
 
520
647
  .message-tool_use,
521
648
  .message-tool_result {
522
- background: var(--bg-secondary, #252526);
523
- border: 1px solid var(--border-color, #3c3c3c);
649
+ background: none;
650
+ border: none;
524
651
  color: var(--text-secondary, #8b8b8b);
525
652
  font-family: var(--font-mono, 'SF Mono', Monaco, monospace);
526
653
  font-size: var(--font-size-code, 0.85rem);
527
- max-width: 100%;
528
654
  }
529
655
 
530
656
  .tool-name {
@@ -570,67 +696,99 @@
570
696
  }
571
697
 
572
698
  /* =============================================================================
573
- Subagent Span Styles
699
+ Subagent Span Styles — Tufte: indented left-border, no box
574
700
  ============================================================================= */
575
701
 
576
702
  .subagent-span {
577
703
  margin: 0.5rem 0;
578
- border: 1px solid var(--border-color, #3c3c3c);
579
- border-radius: 6px;
580
- background: var(--bg-secondary, #252526);
704
+ margin-left: 2.75rem; /* align with message content */
705
+ border: none;
706
+ border-left: 2px solid var(--accent-color, #007acc);
707
+ border-radius: 0;
708
+ background: none;
709
+ padding-left: 0.5rem;
581
710
  }
582
711
 
583
712
  .subagent-header {
584
713
  display: flex;
585
714
  align-items: center;
586
715
  gap: 0.5rem;
587
- padding: 0.5rem 0.75rem;
716
+ padding: 0.25rem 0;
588
717
  cursor: pointer;
589
- background: var(--bg-tertiary, #2d2d2d);
590
- border-radius: 5px 5px 0 0;
718
+ background: none;
719
+ border-radius: 0;
591
720
  font-size: 0.8rem;
721
+ transition: background-color 0.15s ease;
592
722
  }
593
723
 
594
- .subagent-span.collapsed .subagent-header {
595
- border-radius: 5px;
724
+ .subagent-header:hover {
725
+ background-color: var(--bg-secondary, #252526);
596
726
  }
597
727
 
598
728
  .subagent-toggle {
599
729
  color: var(--text-secondary, #8b8b8b);
600
- font-size: 0.7rem;
730
+ font-size: 0.625rem;
731
+ width: 0.875rem;
732
+ display: flex;
733
+ align-items: center;
734
+ justify-content: center;
601
735
  }
602
736
 
603
- .subagent-type {
604
- color: var(--accent-color, #007acc);
737
+ .subagent-helper-name {
605
738
  font-weight: 600;
606
- text-transform: capitalize;
739
+ color: var(--text-primary, #d4d4d4);
607
740
  }
608
741
 
609
- .subagent-name {
610
- color: var(--text-primary, #d4d4d4);
742
+ .subagent-friendly-message {
743
+ color: var(--text-secondary, #8b8b8b);
744
+ overflow: hidden;
745
+ text-overflow: ellipsis;
746
+ white-space: nowrap;
747
+ flex: 1;
748
+ min-width: 0;
749
+ }
750
+
751
+ .subagent-type-badge {
752
+ font-size: 0.625rem;
753
+ padding: 0 4px;
754
+ flex-shrink: 0;
611
755
  }
612
756
 
613
757
  .subagent-count {
614
- color: var(--text-secondary, #8b8b8b);
615
- font-size: 0.75rem;
758
+ color: var(--text-muted, #666);
759
+ font-size: 0.6875rem;
760
+ font-family: var(--font-mono, monospace);
616
761
  margin-left: auto;
762
+ flex-shrink: 0;
617
763
  }
618
764
 
619
765
  .subagent-content {
620
- padding: 0.5rem;
766
+ padding: 0.25rem 0;
621
767
  }
622
768
 
623
- /* Subagent prompt - the task/instruction given to subagent */
769
+ /* Subagent prompt truncated single line, no avatar */
624
770
  .message-subagent-prompt {
625
- background: var(--bg-tertiary, #2d2d2d);
626
- border-left: 3px solid var(--accent-color, #007acc);
627
- color: var(--text-secondary, #8b8b8b);
628
- font-size: 0.85rem;
629
- max-width: 100%;
771
+ background: none;
772
+ border-left: none;
773
+ color: var(--text-muted, #666);
774
+ font-size: 0.75rem;
775
+ padding: 0.125rem 0;
630
776
  }
631
777
 
632
778
  .message-subagent-prompt .message-content {
633
779
  font-style: italic;
780
+ overflow: hidden;
781
+ text-overflow: ellipsis;
782
+ white-space: nowrap;
783
+ }
784
+
785
+ /* Tool calls inside subagent: no extra indent (already indented by parent) */
786
+ .subagent-content .tool-call-block {
787
+ margin-left: 0;
788
+ }
789
+
790
+ .subagent-content .message {
791
+ padding: 0.125rem 0;
634
792
  }
635
793
 
636
794
  /* =============================================================================
@@ -2577,6 +2735,29 @@
2577
2735
  font-style: italic;
2578
2736
  }
2579
2737
 
2738
+ /* Full file tree enhancements */
2739
+ .full-filetree .filetree-scroll {
2740
+ flex: 1;
2741
+ min-height: 0;
2742
+ }
2743
+
2744
+ .full-filetree .tree-loading {
2745
+ color: var(--text-secondary, #8b8b8b);
2746
+ font-size: 0.75rem;
2747
+ font-style: italic;
2748
+ padding: 4px 8px;
2749
+ }
2750
+
2751
+ .full-filetree .tree-error {
2752
+ color: var(--color-danger, #ef4444);
2753
+ font-size: 0.75rem;
2754
+ padding: 4px 8px;
2755
+ }
2756
+
2757
+ .full-filetree .directory-header.has-changes .directory-name {
2758
+ color: var(--color-warning, #f59e0b);
2759
+ }
2760
+
2580
2761
  /* Directory sections */
2581
2762
  .directory-section {
2582
2763
  border-bottom: 1px solid var(--border-color, #3c3c3c);
@@ -3027,11 +3208,8 @@
3027
3208
  ============================================================================= */
3028
3209
 
3029
3210
  .message-bell-injected {
3030
- background: linear-gradient(
3031
- 90deg,
3032
- rgba(255, 193, 7, 0.1) 0%,
3033
- transparent 20%
3034
- );
3211
+ border-left: 2px solid var(--warning-color, #cca700);
3212
+ padding-left: 0.75rem;
3035
3213
  }
3036
3214
 
3037
3215
  .message-bell-injected .bell-indicator {
@@ -36,6 +36,10 @@ export interface MessageData {
36
36
  durationMs?: number;
37
37
  /** Number of images attached to user message */
38
38
  imageCount?: number;
39
+ /** Agent identity captured at message creation time */
40
+ agentSlug?: string;
41
+ agentTheme?: string;
42
+ agentCharacter?: string;
39
43
  }
40
44
 
41
45
  /**
@@ -186,7 +186,7 @@ export const SLASH_COMMANDS: SlashCommand[] = [
186
186
  },
187
187
  {
188
188
  "name": "/release",
189
- "description": "Merge develop to main and push (optional version bump)"
189
+ "description": "Interactive stepped release with verification gates"
190
190
  },
191
191
  {
192
192
  "name": "/repo-status",
@@ -38,8 +38,8 @@ interface Message {
38
38
  * Rules:
39
39
  * - tool_result messages do NOT break the stack (they're paired with tool_use elsewhere)
40
40
  * - assistant/user messages DO break the stack
41
- * - Single tool_use messages are NOT grouped (returns empty array for that sequence)
42
- * - Returns array of ToolStackData, each representing 2+ consecutive tools
41
+ * - Single tool_use messages ARE grouped (consistent rendering with multi-tool stacks)
42
+ * - Returns array of ToolStackData, each representing 1+ consecutive tools
43
43
  *
44
44
  * @param messages - Array of messages to process
45
45
  * @returns Array of tool stacks (only stacks with 2+ tools)
@@ -75,8 +75,8 @@ export function groupToolsIntoStacks(messages: Message[]): ToolStackData[] {
75
75
  continue;
76
76
  } else {
77
77
  // assistant, user, or other message types break the stack
78
- // Only create stack if 2+ tools (single tools in middle render normally)
79
- if (currentTools.length >= 2) {
78
+ // Create stack for any tools (single tools also get stacked for consistent rendering)
79
+ if (currentTools.length >= 1) {
80
80
  const lastTool = currentTools[currentTools.length - 1];
81
81
  stacks.push({
82
82
  stackId: generateStableStackId(currentTools),
@@ -91,8 +91,7 @@ export function groupToolsIntoStacks(messages: Message[]): ToolStackData[] {
91
91
  }
92
92
 
93
93
  // Handle remaining tools at end of messages
94
- // Same threshold as mid-stream: only stack 2+ tools (single tools render normally)
95
- if (currentTools.length >= 2) {
94
+ if (currentTools.length >= 1) {
96
95
  const lastTool = currentTools[currentTools.length - 1];
97
96
  stacks.push({
98
97
  stackId: generateStableStackId(currentTools),