cli-jaw 2.17.59 → 2.17.60

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 (36) hide show
  1. package/dist/src/agent/agy-models.js +64 -0
  2. package/dist/src/agent/agy-models.js.map +1 -0
  3. package/dist/src/agent/copilot-models.js +127 -0
  4. package/dist/src/agent/copilot-models.js.map +1 -0
  5. package/dist/src/agent/opencode-models.js +67 -0
  6. package/dist/src/agent/opencode-models.js.map +1 -0
  7. package/dist/src/cli/registry-live.js +52 -1
  8. package/dist/src/cli/registry-live.js.map +1 -1
  9. package/dist/src/telegram/bot.js +5 -4
  10. package/dist/src/telegram/bot.js.map +1 -1
  11. package/dist/src/telegram/callback-query.js +10 -0
  12. package/dist/src/telegram/callback-query.js.map +1 -0
  13. package/package.json +1 -1
  14. package/public/dist/.vite/manifest.json +25 -25
  15. package/public/dist/assets/{Activity-7WobRx8M.js → Activity-BUrp73Re.js} +1 -1
  16. package/public/dist/assets/{Developer-B08i7aVh.js → Developer-BdKPcw8Z.js} +1 -1
  17. package/public/dist/assets/{Display-NrqY_IaN.js → Display-BHGFX3O0.js} +1 -1
  18. package/public/dist/assets/{Embedding-YNyhSHUj.js → Embedding-DvdTj1BF.js} +1 -1
  19. package/public/dist/assets/{manager-B7QobNzw.js → manager-BpPiQqP8.js} +2 -2
  20. package/public/dist/assets/{manager-shortcuts-jsiX-zV6.js → manager-shortcuts-BDOzd8zm.js} +1 -1
  21. package/public/dist/assets/{settings-Bd6HveOX.js → settings-CUxM2lH7.js} +1 -1
  22. package/public/dist/assets/{settings-embedding-DeOQX-nw.js → settings-embedding-abQOXHyq.js} +2 -2
  23. package/public/dist/assets/shared-CiNP20HB.js +1 -0
  24. package/public/dist/manager/index.html +1 -1
  25. package/public/dist/settings/index.html +1 -1
  26. package/public/manager/src/App.tsx +2 -3
  27. package/public/manager/src/manager-shortcut-runner.ts +9 -2
  28. package/public/manager/src/manager-shortcuts.ts +32 -0
  29. package/public/manager/src/settings/pages/manager/shared.tsx +444 -12
  30. package/public/manager/src/usePreviewShortcutMessages.ts +5 -1
  31. package/scripts/check-private-boundary.mjs +4 -1
  32. package/scripts/ci/aggregate-check.sh +20 -3
  33. package/scripts/ci/macos-unit-manifest.mjs +27 -0
  34. package/scripts/ci/macos-unit-manifest.txt +29 -0
  35. package/scripts/require-release-evidence.mjs +4 -5
  36. package/public/dist/assets/shared-BI-LEt7g.js +0 -1
@@ -93,7 +93,107 @@ const COPY = {
93
93
  shortcutJumpInstance: {
94
94
  label: '인스턴스 점프',
95
95
  scope: '단축키',
96
- description: '렌더된 사이드바 행 1–9로 이동합니다. Meta+1–4는 탭 전환에 남아 있어 Alt를 씁니다.',
96
+ description: '렌더된 사이드바 행 1–9로 이동합니다. Meta+1–3는 탭 전환에 남아 있어 Alt를 씁니다.',
97
+ },
98
+ shortcutToggleBottomPanel: {
99
+ label: '하단 패널',
100
+ scope: '단축키',
101
+ description: '하단 패널(터미널·활동)을 열거나 닫습니다.',
102
+ },
103
+ shortcutToggleRightPanel: {
104
+ label: '오른쪽 사이드바',
105
+ scope: '단축키',
106
+ description: '오른쪽 패널을 열거나 닫고, 비어 있으면 파일 패널을 만듭니다.',
107
+ },
108
+ shortcutToggleLeftSidebar: {
109
+ label: '왼쪽 사이드바',
110
+ scope: '단축키',
111
+ description: '왼쪽 인스턴스 사이드바를 접거나 펼칩니다.',
112
+ },
113
+ shortcutFocusTerminal: {
114
+ label: '터미널 표시',
115
+ scope: '단축키',
116
+ description: '하단 터미널 탭을 열고 활성 터미널에 포커스합니다.',
117
+ },
118
+ shortcutNewTerminalSession: {
119
+ label: '새 터미널 세션',
120
+ scope: '단축키',
121
+ description: '터미널 탭을 열고 새 셸 세션을 시작합니다.',
122
+ },
123
+ shortcutTerminalNewTab: {
124
+ label: '새 터미널 탭',
125
+ scope: '단축키',
126
+ description: '새 터미널 세션을 엽니다.',
127
+ },
128
+ shortcutTerminalClear: {
129
+ label: '터미널 지우기',
130
+ scope: '단축키',
131
+ description: '포커스된 터미널 버퍼를 지웁니다.',
132
+ },
133
+ shortcutOpenDiff: {
134
+ label: 'Diff 패널',
135
+ scope: '단축키',
136
+ description: '오른쪽 diff 패널을 열거나 포커스합니다.',
137
+ },
138
+ shortcutOpenFolderTree: {
139
+ label: '폴더 패널',
140
+ scope: '단축키',
141
+ description: '오른쪽 파일 트리 패널을 열거나 포커스합니다.',
142
+ },
143
+ shortcutCloseFocusedTab: {
144
+ label: '포커스된 탭 닫기',
145
+ scope: '단축키',
146
+ description: '브라우저, 터미널, 패널 중 포커스된 탭을 닫습니다.',
147
+ },
148
+ shortcutSwitchTab1: {
149
+ label: 'Overview 탭',
150
+ scope: '단축키',
151
+ description: '인스턴스 Overview 탭으로 전환합니다.',
152
+ },
153
+ shortcutSwitchTab2: {
154
+ label: 'Preview 탭',
155
+ scope: '단축키',
156
+ description: '인스턴스 Preview 탭으로 전환합니다.',
157
+ },
158
+ shortcutSwitchTab3: {
159
+ label: 'Logs 탭',
160
+ scope: '단축키',
161
+ description: '인스턴스 Logs 탭으로 전환합니다.',
162
+ },
163
+ shortcutPreviousTab: {
164
+ label: '이전 탭',
165
+ scope: '단축키',
166
+ description: '이전 인스턴스 상세 탭으로 전환합니다.',
167
+ },
168
+ shortcutNextTab: {
169
+ label: '다음 탭',
170
+ scope: '단축키',
171
+ description: '다음 인스턴스 상세 탭으로 전환합니다.',
172
+ },
173
+ shortcutBrowserReload: {
174
+ label: '새로고침',
175
+ scope: '단축키',
176
+ description: '포커스 위치에 따라 브라우저, 프리뷰, 앱 창을 새로고침합니다.',
177
+ },
178
+ shortcutBrowserHardReload: {
179
+ label: '강력 새로고침',
180
+ scope: '단축키',
181
+ description: '캐시를 무시하고 다시 로드합니다.',
182
+ },
183
+ shortcutBrowserFocusUrl: {
184
+ label: 'URL 바 포커스',
185
+ scope: '단축키',
186
+ description: '브라우저 패널의 주소창에 포커스합니다.',
187
+ },
188
+ shortcutBrowserBack: {
189
+ label: '브라우저 뒤로',
190
+ scope: '단축키',
191
+ description: '브라우저 패널에서 이전 페이지로 이동합니다.',
192
+ },
193
+ shortcutBrowserForward: {
194
+ label: '브라우저 앞으로',
195
+ scope: '단축키',
196
+ description: '브라우저 패널에서 다음 페이지로 이동합니다.',
97
197
  },
98
198
  },
99
199
  support: {
@@ -184,7 +284,107 @@ const COPY = {
184
284
  shortcutJumpInstance: {
185
285
  label: 'Jump to instance',
186
286
  scope: 'Shortcut',
187
- description: 'Jump to rendered sidebar rows 1–9. Meta+1–4 stay on tab switching, so these use Alt.',
287
+ description: 'Jump to rendered sidebar rows 1–9. Meta+1–3 stay on tab switching, so these use Alt.',
288
+ },
289
+ shortcutToggleBottomPanel: {
290
+ label: 'Bottom panel',
291
+ scope: 'Shortcut',
292
+ description: 'Open or close the bottom panel (terminal and activity).',
293
+ },
294
+ shortcutToggleRightPanel: {
295
+ label: 'Right sidebar',
296
+ scope: 'Shortcut',
297
+ description: 'Open or close the right panel; creates a files panel when empty.',
298
+ },
299
+ shortcutToggleLeftSidebar: {
300
+ label: 'Left sidebar',
301
+ scope: 'Shortcut',
302
+ description: 'Collapse or expand the left instance sidebar.',
303
+ },
304
+ shortcutFocusTerminal: {
305
+ label: 'Reveal terminal',
306
+ scope: 'Shortcut',
307
+ description: 'Open the bottom terminal tab and focus the active terminal.',
308
+ },
309
+ shortcutNewTerminalSession: {
310
+ label: 'New terminal session',
311
+ scope: 'Shortcut',
312
+ description: 'Open the terminal tab and start a new shell session.',
313
+ },
314
+ shortcutTerminalNewTab: {
315
+ label: 'New terminal tab',
316
+ scope: 'Shortcut',
317
+ description: 'Open a new terminal session.',
318
+ },
319
+ shortcutTerminalClear: {
320
+ label: 'Clear terminal',
321
+ scope: 'Shortcut',
322
+ description: 'Clear the focused terminal buffer.',
323
+ },
324
+ shortcutOpenDiff: {
325
+ label: 'Diff panel',
326
+ scope: 'Shortcut',
327
+ description: 'Open or focus the right diff panel.',
328
+ },
329
+ shortcutOpenFolderTree: {
330
+ label: 'Folder panel',
331
+ scope: 'Shortcut',
332
+ description: 'Open or focus the right file tree panel.',
333
+ },
334
+ shortcutCloseFocusedTab: {
335
+ label: 'Close focused tab',
336
+ scope: 'Shortcut',
337
+ description: 'Close the focused browser, terminal, or panel tab.',
338
+ },
339
+ shortcutSwitchTab1: {
340
+ label: 'Overview tab',
341
+ scope: 'Shortcut',
342
+ description: 'Switch to the instance Overview tab.',
343
+ },
344
+ shortcutSwitchTab2: {
345
+ label: 'Preview tab',
346
+ scope: 'Shortcut',
347
+ description: 'Switch to the instance Preview tab.',
348
+ },
349
+ shortcutSwitchTab3: {
350
+ label: 'Logs tab',
351
+ scope: 'Shortcut',
352
+ description: 'Switch to the instance Logs tab.',
353
+ },
354
+ shortcutPreviousTab: {
355
+ label: 'Previous tab',
356
+ scope: 'Shortcut',
357
+ description: 'Switch to the previous instance detail tab.',
358
+ },
359
+ shortcutNextTab: {
360
+ label: 'Next tab',
361
+ scope: 'Shortcut',
362
+ description: 'Switch to the next instance detail tab.',
363
+ },
364
+ shortcutBrowserReload: {
365
+ label: 'Reload',
366
+ scope: 'Shortcut',
367
+ description: 'Reload the browser, preview, or app window depending on focus.',
368
+ },
369
+ shortcutBrowserHardReload: {
370
+ label: 'Hard reload',
371
+ scope: 'Shortcut',
372
+ description: 'Reload bypassing the cache.',
373
+ },
374
+ shortcutBrowserFocusUrl: {
375
+ label: 'Focus URL bar',
376
+ scope: 'Shortcut',
377
+ description: 'Focus the address field in the browser panel.',
378
+ },
379
+ shortcutBrowserBack: {
380
+ label: 'Browser back',
381
+ scope: 'Shortcut',
382
+ description: 'Navigate back in the browser panel.',
383
+ },
384
+ shortcutBrowserForward: {
385
+ label: 'Browser forward',
386
+ scope: 'Shortcut',
387
+ description: 'Navigate forward in the browser panel.',
188
388
  },
189
389
  },
190
390
  support: {
@@ -275,7 +475,107 @@ const COPY = {
275
475
  shortcutJumpInstance: {
276
476
  label: '跳转到实例',
277
477
  scope: '快捷键',
278
- description: '跳转到已渲染的侧边栏第 1–9 行。Meta+1–4 仍用于切换标签,因此这里使用 Alt。',
478
+ description: '跳转到已渲染的侧边栏第 1–9 行。Meta+1–3 仍用于切换标签,因此这里使用 Alt。',
479
+ },
480
+ shortcutToggleBottomPanel: {
481
+ label: '底部面板',
482
+ scope: '快捷键',
483
+ description: '打开或关闭底部面板(终端与活动)。',
484
+ },
485
+ shortcutToggleRightPanel: {
486
+ label: '右侧栏',
487
+ scope: '快捷键',
488
+ description: '打开或关闭右侧面板;为空时创建文件面板。',
489
+ },
490
+ shortcutToggleLeftSidebar: {
491
+ label: '左侧栏',
492
+ scope: '快捷键',
493
+ description: '折叠或展开左侧实例侧栏。',
494
+ },
495
+ shortcutFocusTerminal: {
496
+ label: '显示终端',
497
+ scope: '快捷键',
498
+ description: '打开底部终端标签并聚焦活动终端。',
499
+ },
500
+ shortcutNewTerminalSession: {
501
+ label: '新终端会话',
502
+ scope: '快捷键',
503
+ description: '打开终端标签并启动新的 Shell 会话。',
504
+ },
505
+ shortcutTerminalNewTab: {
506
+ label: '新终端标签',
507
+ scope: '快捷键',
508
+ description: '打开新的终端会话。',
509
+ },
510
+ shortcutTerminalClear: {
511
+ label: '清空终端',
512
+ scope: '快捷键',
513
+ description: '清空当前聚焦的终端缓冲区。',
514
+ },
515
+ shortcutOpenDiff: {
516
+ label: 'Diff 面板',
517
+ scope: '快捷键',
518
+ description: '打开或聚焦右侧 diff 面板。',
519
+ },
520
+ shortcutOpenFolderTree: {
521
+ label: '文件夹面板',
522
+ scope: '快捷键',
523
+ description: '打开或聚焦右侧文件树面板。',
524
+ },
525
+ shortcutCloseFocusedTab: {
526
+ label: '关闭焦点标签',
527
+ scope: '快捷键',
528
+ description: '关闭当前聚焦的浏览器、终端或面板标签。',
529
+ },
530
+ shortcutSwitchTab1: {
531
+ label: 'Overview 标签',
532
+ scope: '快捷键',
533
+ description: '切换到实例 Overview 标签。',
534
+ },
535
+ shortcutSwitchTab2: {
536
+ label: 'Preview 标签',
537
+ scope: '快捷键',
538
+ description: '切换到实例 Preview 标签。',
539
+ },
540
+ shortcutSwitchTab3: {
541
+ label: 'Logs 标签',
542
+ scope: '快捷键',
543
+ description: '切换到实例 Logs 标签。',
544
+ },
545
+ shortcutPreviousTab: {
546
+ label: '上一个标签',
547
+ scope: '快捷键',
548
+ description: '切换到上一个实例详情标签。',
549
+ },
550
+ shortcutNextTab: {
551
+ label: '下一个标签',
552
+ scope: '快捷键',
553
+ description: '切换到下一个实例详情标签。',
554
+ },
555
+ shortcutBrowserReload: {
556
+ label: '重新加载',
557
+ scope: '快捷键',
558
+ description: '根据焦点位置重新加载浏览器、预览或应用窗口。',
559
+ },
560
+ shortcutBrowserHardReload: {
561
+ label: '强制重新加载',
562
+ scope: '快捷键',
563
+ description: '忽略缓存重新加载。',
564
+ },
565
+ shortcutBrowserFocusUrl: {
566
+ label: '聚焦 URL 栏',
567
+ scope: '快捷键',
568
+ description: '聚焦浏览器面板的地址栏。',
569
+ },
570
+ shortcutBrowserBack: {
571
+ label: '浏览器后退',
572
+ scope: '快捷键',
573
+ description: '在浏览器面板中后退一页。',
574
+ },
575
+ shortcutBrowserForward: {
576
+ label: '浏览器前进',
577
+ scope: '快捷键',
578
+ description: '在浏览器面板中前进一页。',
279
579
  },
280
580
  },
281
581
  support: {
@@ -366,7 +666,107 @@ const COPY = {
366
666
  shortcutJumpInstance: {
367
667
  label: 'インスタンスへジャンプ',
368
668
  scope: 'ショートカット',
369
- description: '描画済みサイドバー行 1–9 へ移動します。Meta+1–4 はタブ切替のままなので Alt を使います。',
669
+ description: '描画済みサイドバー行 1–9 へ移動します。Meta+1–3 はタブ切替のままなので Alt を使います。',
670
+ },
671
+ shortcutToggleBottomPanel: {
672
+ label: 'ボトムパネル',
673
+ scope: 'ショートカット',
674
+ description: 'ボトムパネル(ターミナル・アクティビティ)を開閉します。',
675
+ },
676
+ shortcutToggleRightPanel: {
677
+ label: '右サイドバー',
678
+ scope: 'ショートカット',
679
+ description: '右パネルを開閉し、空の場合はファイルパネルを作成します。',
680
+ },
681
+ shortcutToggleLeftSidebar: {
682
+ label: '左サイドバー',
683
+ scope: 'ショートカット',
684
+ description: '左のインスタンスサイドバーを折りたたみ/展開します。',
685
+ },
686
+ shortcutFocusTerminal: {
687
+ label: 'ターミナルを表示',
688
+ scope: 'ショートカット',
689
+ description: 'ボトムのターミナルタブを開き、アクティブなターミナルにフォーカスします。',
690
+ },
691
+ shortcutNewTerminalSession: {
692
+ label: '新しいターミナルセッション',
693
+ scope: 'ショートカット',
694
+ description: 'ターミナルタブを開き、新しいシェルセッションを開始します。',
695
+ },
696
+ shortcutTerminalNewTab: {
697
+ label: '新しいターミナルタブ',
698
+ scope: 'ショートカット',
699
+ description: '新しいターミナルセッションを開きます。',
700
+ },
701
+ shortcutTerminalClear: {
702
+ label: 'ターミナルをクリア',
703
+ scope: 'ショートカット',
704
+ description: 'フォーカス中のターミナルバッファをクリアします。',
705
+ },
706
+ shortcutOpenDiff: {
707
+ label: 'Diff パネル',
708
+ scope: 'ショートカット',
709
+ description: '右の diff パネルを開くかフォーカスします。',
710
+ },
711
+ shortcutOpenFolderTree: {
712
+ label: 'フォルダーパネル',
713
+ scope: 'ショートカット',
714
+ description: '右のファイルツリーパネルを開くかフォーカスします。',
715
+ },
716
+ shortcutCloseFocusedTab: {
717
+ label: 'フォーカス中のタブを閉じる',
718
+ scope: 'ショートカット',
719
+ description: 'フォーカスされているブラウザ、ターミナル、パネルのタブを閉じます。',
720
+ },
721
+ shortcutSwitchTab1: {
722
+ label: 'Overview タブ',
723
+ scope: 'ショートカット',
724
+ description: 'インスタンスの Overview タブに切り替えます。',
725
+ },
726
+ shortcutSwitchTab2: {
727
+ label: 'Preview タブ',
728
+ scope: 'ショートカット',
729
+ description: 'インスタンスの Preview タブに切り替えます。',
730
+ },
731
+ shortcutSwitchTab3: {
732
+ label: 'Logs タブ',
733
+ scope: 'ショートカット',
734
+ description: 'インスタンスの Logs タブに切り替えます。',
735
+ },
736
+ shortcutPreviousTab: {
737
+ label: '前のタブ',
738
+ scope: 'ショートカット',
739
+ description: '前のインスタンス詳細タブに切り替えます。',
740
+ },
741
+ shortcutNextTab: {
742
+ label: '次のタブ',
743
+ scope: 'ショートカット',
744
+ description: '次のインスタンス詳細タブに切り替えます。',
745
+ },
746
+ shortcutBrowserReload: {
747
+ label: '再読み込み',
748
+ scope: 'ショートカット',
749
+ description: 'フォーカス位置に応じてブラウザ、プレビュー、アプリウィンドウを再読み込みします。',
750
+ },
751
+ shortcutBrowserHardReload: {
752
+ label: '強制再読み込み',
753
+ scope: 'ショートカット',
754
+ description: 'キャッシュを無視して再読み込みします。',
755
+ },
756
+ shortcutBrowserFocusUrl: {
757
+ label: 'URL バーにフォーカス',
758
+ scope: 'ショートカット',
759
+ description: 'ブラウザパネルのアドレスバーにフォーカスします。',
760
+ },
761
+ shortcutBrowserBack: {
762
+ label: 'ブラウザの戻る',
763
+ scope: 'ショートカット',
764
+ description: 'ブラウザパネルで前のページに戻ります。',
765
+ },
766
+ shortcutBrowserForward: {
767
+ label: 'ブラウザの進む',
768
+ scope: 'ショートカット',
769
+ description: 'ブラウザパネルで次のページに進みます。',
370
770
  },
371
771
  },
372
772
  support: {
@@ -480,15 +880,47 @@ function DashboardShortcutInput(props: DashboardShortcutInputProps) {
480
880
  );
481
881
  }
482
882
 
883
+ const SHORTCUT_COPY_KEYS: Record<DashboardShortcutAction, keyof typeof COPY.ko.fields> = {
884
+ toggleInstanceSettings: 'shortcutToggleInstanceSettings',
885
+ focusInstances: 'shortcutFocusInstances',
886
+ focusActiveSession: 'shortcutFocusActiveSession',
887
+ focusNotes: 'shortcutFocusNotes',
888
+ previousInstance: 'shortcutPreviousInstance',
889
+ nextInstance: 'shortcutNextInstance',
890
+ toggleBottomPanel: 'shortcutToggleBottomPanel',
891
+ toggleRightPanel: 'shortcutToggleRightPanel',
892
+ toggleLeftSidebar: 'shortcutToggleLeftSidebar',
893
+ focusTerminal: 'shortcutFocusTerminal',
894
+ newTerminalSession: 'shortcutNewTerminalSession',
895
+ terminalNewTab: 'shortcutTerminalNewTab',
896
+ terminalClear: 'shortcutTerminalClear',
897
+ openDiff: 'shortcutOpenDiff',
898
+ openFolderTree: 'shortcutOpenFolderTree',
899
+ closeFocusedTab: 'shortcutCloseFocusedTab',
900
+ switchTab1: 'shortcutSwitchTab1',
901
+ switchTab2: 'shortcutSwitchTab2',
902
+ switchTab3: 'shortcutSwitchTab3',
903
+ previousTab: 'shortcutPreviousTab',
904
+ nextTab: 'shortcutNextTab',
905
+ browserReload: 'shortcutBrowserReload',
906
+ browserHardReload: 'shortcutBrowserHardReload',
907
+ browserFocusUrl: 'shortcutBrowserFocusUrl',
908
+ browserBack: 'shortcutBrowserBack',
909
+ browserForward: 'shortcutBrowserForward',
910
+ resetSidebarWidth: 'shortcutResetSidebarWidth',
911
+ jumpInstance1: 'shortcutJumpInstance',
912
+ jumpInstance2: 'shortcutJumpInstance',
913
+ jumpInstance3: 'shortcutJumpInstance',
914
+ jumpInstance4: 'shortcutJumpInstance',
915
+ jumpInstance5: 'shortcutJumpInstance',
916
+ jumpInstance6: 'shortcutJumpInstance',
917
+ jumpInstance7: 'shortcutJumpInstance',
918
+ jumpInstance8: 'shortcutJumpInstance',
919
+ jumpInstance9: 'shortcutJumpInstance',
920
+ };
921
+
483
922
  function shortcutCopyKey(action: DashboardShortcutAction): keyof typeof COPY.ko.fields {
484
- if (action === 'toggleInstanceSettings') return 'shortcutToggleInstanceSettings';
485
- if (action === 'focusInstances') return 'shortcutFocusInstances';
486
- if (action === 'focusActiveSession') return 'shortcutFocusActiveSession';
487
- if (action === 'focusNotes') return 'shortcutFocusNotes';
488
- if (action === 'previousInstance') return 'shortcutPreviousInstance';
489
- if (action === 'resetSidebarWidth') return 'shortcutResetSidebarWidth';
490
- if (action.startsWith('jumpInstance')) return 'shortcutJumpInstance';
491
- return 'shortcutNextInstance';
923
+ return SHORTCUT_COPY_KEYS[action];
492
924
  }
493
925
 
494
926
  function TitleSupportSummary({ support, locale }: { support: DashboardActivityTitleSupport; locale: DashboardLocale }) {
@@ -5,6 +5,10 @@ import type { DashboardShortcutAction, DashboardShortcutKeymap } from './types';
5
5
  type PreviewShortcutMessageArgs = {
6
6
  enabled: boolean;
7
7
  keymap: DashboardShortcutKeymap;
8
+ /** True in the Electron desktop shell: chords the application menu owns are
9
+ * already dispatched through `manager:shortcut`, so a forwarded duplicate
10
+ * must not run the action a second time. */
11
+ menuOwned?: boolean;
8
12
  onAction: (action: DashboardShortcutAction) => void;
9
13
  };
10
14
 
@@ -30,7 +34,7 @@ export function usePreviewShortcutMessages(args: PreviewShortcutMessageArgs): vo
30
34
  metaKey: !!data.metaKey,
31
35
  shiftKey: !!data.shiftKey,
32
36
  } as unknown as KeyboardEvent;
33
- const action = actionForShortcutEvent(synth, args.keymap);
37
+ const action = actionForShortcutEvent(synth, args.keymap, { menuOwned: args.menuOwned === true });
34
38
  if (action) args.onAction(action);
35
39
  }
36
40
  window.addEventListener('message', onPreviewShortcut);
@@ -7,8 +7,11 @@ import { resolve } from 'node:path';
7
7
  import { fileURLToPath } from 'node:url';
8
8
 
9
9
  export function isPrivatePath(file) {
10
+ // `pr-assets` is not a private name but shares this guard: PR screenshot
11
+ // evidence lives only on the orphan `pr-assets` branch, so a same-named
12
+ // directory (e.g. `docs/pr-assets/`) must never land on a code branch.
10
13
  return file.replaceAll('\\', '/').split('/').some(segment =>
11
- /^(?:devlog(?:[._-].*)?|cli-jaw-internal|_plan|_fin|\.jwc)$/i.test(segment));
14
+ /^(?:devlog(?:[._-].*)?|cli-jaw-internal|_plan|_fin|\.jwc|pr-assets)$/i.test(segment));
12
15
  }
13
16
 
14
17
  function git(cwd, args) {
@@ -1,11 +1,16 @@
1
1
  #!/usr/bin/env bash
2
2
  # The ci-aggregate decision for .github/workflows/test.yml, kept in a script so
3
3
  # tests/unit/ci-aggregate-rules.test.ts can drive its truth table. Inputs are the
4
- # producer job results and the change classifier's output, all through env.
4
+ # producer job results, the change classifier's output and the event name, all
5
+ # through env.
5
6
  #
6
7
  # changes must be success and CODE_CHANGED exactly true|false
7
8
  # code=true : every producer must be success (a skip is a dropped job, not a pass)
8
9
  # code=false: every producer may be success or skipped (docs-only change)
10
+ # windows-unit and macos-unit are never scheduled on pull_request — the PR
11
+ # contract is the minimal Linux set and their evidence lands on the dev push
12
+ # after merge — so on that event alone a skip is the expected state, not a
13
+ # dropped job. On push and workflow_dispatch the normal rule applies.
9
14
  # failure, cancelled, empty, or anything unrecognised always fails
10
15
  set -uo pipefail
11
16
 
@@ -40,14 +45,26 @@ check() {
40
45
  esac
41
46
  }
42
47
 
48
+ # The cross-platform lanes are push/dispatch-only by design, so a skipped lane
49
+ # on pull_request is the expected shape of a green PR — everywhere else it is
50
+ # the same dropped-job signal as any other producer.
51
+ check_xplat() {
52
+ local job="$1" result="$2"
53
+ if [ "$result" = "skipped" ] && [ "${EVENT_NAME:-}" = "pull_request" ]; then
54
+ echo "PASS $job: skipped (cross-platform lanes do not run on pull_request)"
55
+ return
56
+ fi
57
+ check "$job" "$result"
58
+ }
59
+
43
60
  check test "${TEST_RESULT:-}"
44
61
  check integration "${INTEGRATION_RESULT:-}"
45
62
  check gates "${GATES_RESULT:-}"
46
- check windows-unit "${WINDOWS_UNIT_RESULT:-}"
63
+ check_xplat windows-unit "${WINDOWS_UNIT_RESULT:-}"
64
+ check_xplat macos-unit "${MACOS_UNIT_RESULT:-}"
47
65
 
48
66
  if [ "$failed" -ne 0 ]; then
49
67
  echo "::error::ci-aggregate failed — see the per-job results above"
50
68
  exit 1
51
69
  fi
52
70
  echo "ci-aggregate: all required jobs succeeded or were legitimately skipped"
53
-
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env node
2
+ // Validates scripts/ci/macos-unit-manifest.txt — the list of unit test files
3
+ // the macos-unit job runs on macos-latest. Same line format and validation
4
+ // contract as scripts/ci/windows-unit-manifest.mjs, whose readManifest this
5
+ // reuses: one repo-relative POSIX path per line, blank lines and full-line
6
+ // '#' comments ignored, no globs, duplicates, traversal, or paths outside
7
+ // tests/unit/. Consumed by the macOS job (--print), the gates job (plain
8
+ // validation), and tests/unit/macos-unit-manifest.test.ts.
9
+ import { resolve } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+ import process from 'node:process';
12
+ import { readManifest } from './windows-unit-manifest.mjs';
13
+
14
+ export const MANIFEST_PATH = 'scripts/ci/macos-unit-manifest.txt';
15
+ export { readManifest };
16
+
17
+ if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
18
+ const print = process.argv.slice(2).includes('--print');
19
+ try {
20
+ const entries = readManifest(MANIFEST_PATH);
21
+ if (print) process.stdout.write(entries.join('\n') + '\n');
22
+ else console.log(`macos-unit manifest ok: ${entries.length} files`);
23
+ } catch (error) {
24
+ console.error(`[macos-unit-manifest] ${error instanceof Error ? error.message : String(error)}`);
25
+ process.exit(1);
26
+ }
27
+ }
@@ -0,0 +1,29 @@
1
+ # Unit test files the macos-unit job runs on macos-latest (.github/workflows/test.yml).
2
+ # One repo-relative path per line, tests/unit/<name>.test.ts only; no globs.
3
+ # Validated by scripts/ci/macos-unit-manifest.mjs and tests/unit/macos-unit-manifest.test.ts.
4
+ #
5
+ # Picked for what only a real darwin host can prove: darwin-gated suites that
6
+ # skip everywhere else, real-runtime classification, and suites where the
7
+ # darwin-arm64 native build (better-sqlite3) or filesystem semantics differ.
8
+ tests/unit/tcc.test.ts
9
+ tests/unit/launchd-plist.test.ts
10
+ tests/unit/launchd-multi.test.ts
11
+ tests/unit/launchd-cleanup.test.ts
12
+ tests/unit/service.test.ts
13
+ tests/unit/service-artifact.test.ts
14
+ tests/unit/service-lifecycle-cli.test.ts
15
+ tests/unit/platform-kind-runtime.test.ts
16
+ tests/unit/sidecar-bundle-ownership.test.ts
17
+ tests/unit/install-sh-exec.test.ts
18
+ tests/unit/jaw-reminders-bridge.test.ts
19
+ tests/unit/manager-git-status-service.test.ts
20
+ tests/unit/manager-git-worktree-service.test.ts
21
+ tests/unit/electron-jaw-spawn.test.ts
22
+ tests/unit/electron-jaw-spawn-orphan-kill.test.ts
23
+
24
+ # Same reason the Windows lane carries them: schema migration needs a home that
25
+ # already exists, so every fresh-CLI_JAW_HOME CI run skips it elsewhere, and
26
+ # these two build an old home and walk the source tree by path — the place the
27
+ # darwin-arm64 better-sqlite3 prebuild actually differs (#691).
28
+ tests/unit/db-schema-migration.test.ts
29
+ tests/unit/db-legacy-mention-watch-surface.test.ts
@@ -148,12 +148,11 @@ function deriveInstallerSensitivePaths() {
148
148
  throw new Error(`cannot read ${PLATFORM_WORKFLOW_PATH}: ${error.message}`);
149
149
  }
150
150
  // `push` is what actually determines whether platform evidence can exist:
151
- // publish.yml looks for a successful `--event push` run of this workflow. The
152
- // union of both trigger lists is used so a divergence errs toward requiring
153
- // evidence rather than skipping it; a unit test asserts they stay identical.
151
+ // publish.yml looks for a successful `--event push` run of this workflow.
152
+ // The pull_request trigger was removed when cross-platform evidence moved
153
+ // to the dev push after merge, so on.push.paths is the whole contract now.
154
154
  const pushPaths = parseWorkflowTriggerPaths(source, 'push');
155
- const pullRequestPaths = parseWorkflowTriggerPaths(source, 'pull_request');
156
- return [...new Set([...pushPaths, ...pullRequestPaths, ...EXTRA_SENSITIVE_PATHS])];
155
+ return [...new Set([...pushPaths, ...EXTRA_SENSITIVE_PATHS])];
157
156
  }
158
157
 
159
158
  let installerSensitivePaths;