bosun 0.36.0 β†’ 0.36.2

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 (98) hide show
  1. package/.env.example +98 -16
  2. package/README.md +27 -0
  3. package/agent-event-bus.mjs +5 -5
  4. package/agent-pool.mjs +129 -12
  5. package/agent-prompts.mjs +7 -1
  6. package/agent-sdk.mjs +13 -2
  7. package/agent-supervisor.mjs +2 -2
  8. package/agent-work-report.mjs +1 -1
  9. package/anomaly-detector.mjs +6 -6
  10. package/autofix.mjs +15 -15
  11. package/bosun-skills.mjs +4 -4
  12. package/bosun.schema.json +160 -4
  13. package/claude-shell.mjs +11 -11
  14. package/cli.mjs +21 -21
  15. package/codex-config.mjs +19 -19
  16. package/codex-shell.mjs +180 -29
  17. package/config-doctor.mjs +27 -2
  18. package/config.mjs +60 -7
  19. package/copilot-shell.mjs +4 -4
  20. package/error-detector.mjs +1 -1
  21. package/fleet-coordinator.mjs +2 -2
  22. package/gemini-shell.mjs +692 -0
  23. package/github-oauth-portal.mjs +1 -1
  24. package/github-reconciler.mjs +2 -2
  25. package/kanban-adapter.mjs +741 -168
  26. package/merge-strategy.mjs +25 -25
  27. package/monitor.mjs +123 -105
  28. package/opencode-shell.mjs +22 -22
  29. package/package.json +7 -1
  30. package/postinstall.mjs +22 -22
  31. package/pr-cleanup-daemon.mjs +6 -6
  32. package/prepublish-check.mjs +4 -4
  33. package/presence.mjs +2 -2
  34. package/primary-agent.mjs +85 -7
  35. package/publish.mjs +1 -1
  36. package/review-agent.mjs +1 -1
  37. package/session-tracker.mjs +11 -0
  38. package/setup-web-server.mjs +429 -21
  39. package/setup.mjs +367 -12
  40. package/shared-knowledge.mjs +1 -1
  41. package/startup-service.mjs +9 -9
  42. package/stream-resilience.mjs +58 -4
  43. package/sync-engine.mjs +2 -2
  44. package/task-assessment.mjs +9 -9
  45. package/task-cli.mjs +1 -1
  46. package/task-complexity.mjs +71 -2
  47. package/task-context.mjs +1 -2
  48. package/task-executor.mjs +104 -41
  49. package/telegram-bot.mjs +825 -494
  50. package/telegram-sentinel.mjs +28 -28
  51. package/ui/app.js +256 -23
  52. package/ui/app.monolith.js +1 -1
  53. package/ui/components/agent-selector.js +4 -3
  54. package/ui/components/chat-view.js +101 -28
  55. package/ui/components/diff-viewer.js +3 -3
  56. package/ui/components/kanban-board.js +3 -3
  57. package/ui/components/session-list.js +255 -35
  58. package/ui/components/workspace-switcher.js +3 -3
  59. package/ui/demo.html +209 -194
  60. package/ui/index.html +3 -3
  61. package/ui/modules/icon-utils.js +206 -142
  62. package/ui/modules/icons.js +2 -27
  63. package/ui/modules/settings-schema.js +29 -5
  64. package/ui/modules/streaming.js +30 -2
  65. package/ui/modules/vision-stream.js +275 -0
  66. package/ui/modules/voice-client.js +102 -9
  67. package/ui/modules/voice-fallback.js +62 -6
  68. package/ui/modules/voice-overlay.js +594 -59
  69. package/ui/modules/voice.js +31 -38
  70. package/ui/setup.html +284 -34
  71. package/ui/styles/components.css +47 -0
  72. package/ui/styles/sessions.css +75 -0
  73. package/ui/tabs/agents.js +73 -43
  74. package/ui/tabs/chat.js +37 -40
  75. package/ui/tabs/control.js +2 -2
  76. package/ui/tabs/dashboard.js +1 -1
  77. package/ui/tabs/infra.js +10 -10
  78. package/ui/tabs/library.js +8 -8
  79. package/ui/tabs/logs.js +10 -10
  80. package/ui/tabs/settings.js +20 -20
  81. package/ui/tabs/tasks.js +76 -47
  82. package/ui-server.mjs +1761 -124
  83. package/update-check.mjs +13 -13
  84. package/ve-kanban.mjs +1 -1
  85. package/whatsapp-channel.mjs +5 -5
  86. package/workflow-engine.mjs +20 -1
  87. package/workflow-nodes.mjs +904 -4
  88. package/workflow-templates/agents.mjs +321 -7
  89. package/workflow-templates/ci-cd.mjs +6 -6
  90. package/workflow-templates/github.mjs +156 -84
  91. package/workflow-templates/planning.mjs +8 -8
  92. package/workflow-templates/reliability.mjs +8 -8
  93. package/workflow-templates/security.mjs +3 -3
  94. package/workflow-templates.mjs +15 -9
  95. package/workspace-manager.mjs +85 -1
  96. package/workspace-monitor.mjs +2 -2
  97. package/workspace-registry.mjs +2 -2
  98. package/worktree-manager.mjs +1 -1
package/ui/tabs/infra.js CHANGED
@@ -419,10 +419,10 @@ export function InfraTab() {
419
419
  onKeyDown=${(e) => e.key === "Enter" && handleCreateWorkspace()}
420
420
  />
421
421
  <button class="btn btn-primary btn-sm" onClick=${handleCreateWorkspace}>
422
- ${iconText("βž• Create")}
422
+ ${iconText(":plus: Create")}
423
423
  </button>
424
424
  <button class="btn btn-secondary btn-sm" onClick=${handleScanDisk}>
425
- ${iconText("πŸ”„ Scan")}
425
+ ${iconText(":refresh: Scan")}
426
426
  </button>
427
427
  </div>
428
428
 
@@ -448,13 +448,13 @@ export function InfraTab() {
448
448
  class="btn btn-secondary btn-sm"
449
449
  onClick=${() => handlePullWorkspace(ws.id)}
450
450
  >
451
- ⬇️ Pull
451
+ :download: Pull
452
452
  </button>
453
453
  <button
454
454
  class="btn btn-danger btn-sm"
455
455
  onClick=${() => handleDeleteWorkspace(ws.id)}
456
456
  >
457
- ${resolveIcon("πŸ—‘")}
457
+ ${resolveIcon(":trash:")}
458
458
  </button>
459
459
  </div>
460
460
  </div>
@@ -472,7 +472,7 @@ export function InfraTab() {
472
472
  class="flex-between"
473
473
  style="padding:4px 0;border-bottom:1px solid rgba(255,255,255,0.05)"
474
474
  >
475
- <span class="meta-text">${iconText(`πŸ“ ${repoName}`)}</span>
475
+ <span class="meta-text">${iconText(`:folder: ${repoName}`)}</span>
476
476
  <button
477
477
  class="btn btn-ghost btn-sm"
478
478
  onClick=${() => handleRemoveRepo(ws.id, repoName)}
@@ -501,7 +501,7 @@ export function InfraTab() {
501
501
  class="btn btn-secondary btn-sm"
502
502
  onClick=${() => handleAddRepo(ws.id, addRepoWs === ws.id ? addRepoUrl : "")}
503
503
  >
504
- ${iconText("πŸ“₯ Clone")}
504
+ ${iconText(":download: Clone")}
505
505
  </button>
506
506
  </div>
507
507
  </div>
@@ -544,7 +544,7 @@ export function InfraTab() {
544
544
  Release
545
545
  </button>
546
546
  <button class="btn btn-danger btn-sm" onClick=${handlePrune}>
547
- ${iconText("πŸ—‘ Prune")}
547
+ ${iconText(":trash: Prune")}
548
548
  </button>
549
549
  </div>
550
550
 
@@ -721,7 +721,7 @@ export function InfraTab() {
721
721
  class="btn btn-primary btn-sm"
722
722
  onClick=${() => handleClaim(ws.id)}
723
723
  >
724
- ${iconText("πŸ”’ Claim")}
724
+ ${iconText(":lock: Claim")}
725
725
  </button>
726
726
  <button
727
727
  class="btn btn-secondary btn-sm"
@@ -733,7 +733,7 @@ export function InfraTab() {
733
733
  class="btn btn-ghost btn-sm"
734
734
  onClick=${() => handleSharedRelease(ws.id)}
735
735
  >
736
- ${iconText("πŸ”“ Release")}
736
+ ${iconText(":unlock: Release")}
737
737
  </button>
738
738
  </div>
739
739
  </div>
@@ -749,7 +749,7 @@ export function InfraTab() {
749
749
  <${Card}>
750
750
  <!-- Coordinator info -->
751
751
  <div class="task-card mb-md">
752
- <div class="task-card-title">${iconText("🎯 Coordinator")}</div>
752
+ <div class="task-card-title">${iconText(":target: Coordinator")}</div>
753
753
  <div class="meta-text">
754
754
  ${coordinator?.instance_label || coordinator?.instance_id || "none"}
755
755
  Β· Priority ${coordinator?.coordinator_priority ?? "β€”"}
@@ -217,7 +217,7 @@ async function testProfileMatch(title) {
217
217
  * Icons per type
218
218
  * ═══════════════════════════════════════════════════════════════ */
219
219
 
220
- const TYPE_ICONS = { prompt: "πŸ“", agent: "πŸ€–", skill: "🧠" };
220
+ const TYPE_ICONS = { prompt: ":edit:", agent: ":bot:", skill: ":cpu:" };
221
221
  const TYPE_LABELS = { prompt: "Prompt", agent: "Agent Profile", skill: "Skill" };
222
222
  const TYPE_COLORS = { prompt: "#58a6ff", agent: "#af7bff", skill: "#3fb950" };
223
223
 
@@ -272,7 +272,7 @@ function TypePills() {
272
272
  }
273
273
 
274
274
  function LibraryCard({ entry, onSelect }) {
275
- const icon = TYPE_ICONS[entry.type] || "πŸ“„";
275
+ const icon = TYPE_ICONS[entry.type] || ":file:";
276
276
  const typeLabel = TYPE_LABELS[entry.type] || entry.type;
277
277
  const typeColor = TYPE_COLORS[entry.type] || "#aaa";
278
278
  return html`
@@ -296,7 +296,7 @@ function LibraryCard({ entry, onSelect }) {
296
296
  <span class="library-card-tag" key=${tag}>${tag}</span>
297
297
  `)}
298
298
  ${entry.scope && entry.scope !== "global" && html`
299
- <span class="library-card-scope">${iconText(`πŸ“Œ ${entry.scope}`)}</span>
299
+ <span class="library-card-scope">${iconText(`:pin: ${entry.scope}`)}</span>
300
300
  `}
301
301
  </div>
302
302
  </div>
@@ -497,7 +497,7 @@ function ScopeDetector() {
497
497
  return html`
498
498
  <div>
499
499
  <button class="btn-ghost library-type-pill" onClick=${loadScopes} style="font-size:0.82em;">
500
- ${loading ? html`<${Spinner} size=${12} />` : iconText("πŸ” Detect Scopes")}
500
+ ${loading ? html`<${Spinner} size=${12} />` : iconText(":search: Detect Scopes")}
501
501
  </button>
502
502
  ${showing && scopes.value.length > 0 && html`
503
503
  <div class="library-scopes">
@@ -547,7 +547,7 @@ function ProfileMatcher() {
547
547
  style="flex:1;padding:6px 10px;border-radius:8px;border:1px solid var(--border,#333);
548
548
  background:var(--bg-input,#0d1117);color:var(--text-primary,#eee);font-size:0.85em;" />
549
549
  <button class="library-type-pill active" onClick=${doMatch} style="font-size:0.82em;" disabled=${loading}>
550
- ${loading ? html`<${Spinner} size=${12} />` : iconText("🎯 Match")}
550
+ ${loading ? html`<${Spinner} size=${12} />` : iconText(":target: Match")}
551
551
  </button>
552
552
  </div>
553
553
  ${match && html`
@@ -658,10 +658,10 @@ export function LibraryTab() {
658
658
  return html`
659
659
  <div class="library-root">
660
660
  <div class="library-header">
661
- <h2>${iconText("πŸ“š Library")}</h2>
661
+ <h2>${iconText(":u1f4da: Library")}</h2>
662
662
  <button class="library-type-pill" onClick=${handleRebuild}
663
663
  title="Rescan directories and rebuild manifest">
664
- ${iconText("πŸ”„ Rebuild")}
664
+ ${iconText(":refresh: Rebuild")}
665
665
  </button>
666
666
  <button class="library-type-pill active" onClick=${() => setEditing({})}>
667
667
  οΌ‹ New
@@ -672,7 +672,7 @@ export function LibraryTab() {
672
672
  <div class="library-init-banner">
673
673
  <p><b>Welcome to the Library!</b></p>
674
674
  <p>Initialize to scaffold built-in agent profiles and index existing prompts and skills.</p>
675
- <button onClick=${handleInit}>${iconText("πŸš€ Initialize Library")}</button>
675
+ <button onClick=${handleInit}>${iconText(":rocket: Initialize Library")}</button>
676
676
  </div>
677
677
  `}
678
678
 
package/ui/tabs/logs.js CHANGED
@@ -568,13 +568,13 @@ export function LogsTab() {
568
568
  class="btn btn-ghost btn-sm"
569
569
  onClick=${() => copyToClipboard(filteredLogText, "Logs")}
570
570
  >
571
- ${iconText("πŸ“‹ Copy")}
571
+ ${iconText(":clipboard: Copy")}
572
572
  </button>
573
573
  <button
574
574
  class="btn btn-ghost btn-sm"
575
575
  onClick=${downloadLogs}
576
576
  >
577
- ${iconText("πŸ’Ύ Download")}
577
+ ${iconText(":save: Download")}
578
578
  </button>
579
579
  </div>
580
580
  <//>
@@ -591,7 +591,7 @@ export function LogsTab() {
591
591
  }}
592
592
  />
593
593
  <button class="btn btn-secondary btn-sm" onClick=${handleAgentSearch}>
594
- ${iconText("πŸ” Search")}
594
+ ${iconText(":search: Search")}
595
595
  </button>
596
596
  </div>
597
597
  <div class="range-row mb-md">
@@ -647,7 +647,7 @@ export function LogsTab() {
647
647
  class="btn btn-ghost btn-sm"
648
648
  onClick=${() => copyToClipboard(rawTailText, "Log tail")}
649
649
  >
650
- ${iconText("πŸ“‹ Copy")}
650
+ ${iconText(":clipboard: Copy")}
651
651
  </button>
652
652
  </div>
653
653
  <//>
@@ -665,7 +665,7 @@ export function LogsTab() {
665
665
  }}
666
666
  />
667
667
  <button class="btn btn-secondary btn-sm" onClick=${handleContextLoad}>
668
- ${iconText("πŸ“‚ Load")}
668
+ ${iconText(":folder: Load")}
669
669
  </button>
670
670
  </div>
671
671
  <div class="log-box">
@@ -698,7 +698,7 @@ export function LogsTab() {
698
698
  "Context",
699
699
  )}
700
700
  >
701
- ${iconText("πŸ“‹ Copy")}
701
+ ${iconText(":clipboard: Copy")}
702
702
  </button>
703
703
  </div>
704
704
  `}
@@ -720,7 +720,7 @@ export function LogsTab() {
720
720
  class="btn btn-ghost btn-sm"
721
721
  onClick=${() => copyToClipboard(gitDiff?.value || "", "Diff")}
722
722
  >
723
- ${iconText("πŸ“‹ Copy")}
723
+ ${iconText(":clipboard: Copy")}
724
724
  </button>
725
725
  </div>
726
726
  <div class="log-box mb-md">
@@ -766,19 +766,19 @@ export function LogsTab() {
766
766
  <div class="btn-row mb-sm">
767
767
  ${(branchDetail.workspaceTarget || branchDetail.activeSlot || branchDetail.worktree) &&
768
768
  html`<button class="btn btn-primary btn-sm" onClick=${() => openWorkspace(branchDetail)}>
769
- ${iconText("πŸ” Open Workspace Viewer")}
769
+ ${iconText(":search: Open Workspace Viewer")}
770
770
  </button>`}
771
771
  ${branchDetail.workspaceLink?.url &&
772
772
  html`<button
773
773
  class="btn btn-secondary btn-sm"
774
774
  onClick=${() => openLink(branchDetail.workspaceLink.url)}
775
775
  >
776
- ${iconText("πŸ”— Open Workspace Link")}
776
+ ${iconText(":link: Open Workspace Link")}
777
777
  </button>`}
778
778
  <button
779
779
  class="btn btn-ghost btn-sm"
780
780
  onClick=${() => copyToClipboard(branchDetail.diffStat || "", "Diff")}
781
- >${iconText("πŸ“‹ Copy Diff")}</button>
781
+ >${iconText(":clipboard: Copy Diff")}</button>
782
782
  </div>
783
783
  ${workspaceLink &&
784
784
  html`
@@ -972,7 +972,7 @@ function ServerConfigMode() {
972
972
  type="button"
973
973
  title=${secretVisible ? "Hide" : "Show"}
974
974
  >
975
- ${resolveIcon(secretVisible ? "πŸ™ˆ" : "πŸ‘")}
975
+ ${resolveIcon(secretVisible ? ":eyeOff:" : ":eye:")}
976
976
  </button>
977
977
  </div>
978
978
  `;
@@ -1048,7 +1048,7 @@ function ServerConfigMode() {
1048
1048
  </div>
1049
1049
  <div class="setting-row-key">${def.key}</div>
1050
1050
  ${control}
1051
- ${error && html`<div class="setting-validation-error">${iconText(`⚠ ${error}`)}</div>`}
1051
+ ${error && html`<div class="setting-validation-error">${iconText(`:alert: ${error}`)}</div>`}
1052
1052
  </div>
1053
1053
  `;
1054
1054
  },
@@ -1067,7 +1067,7 @@ function ServerConfigMode() {
1067
1067
  ${loadError &&
1068
1068
  html`
1069
1069
  <div class="settings-banner settings-banner-error">
1070
- <span>${resolveIcon("⚠️")}</span>
1070
+ <span>${resolveIcon(":alert:")}</span>
1071
1071
  <span class="settings-banner-text">
1072
1072
  <strong>Backend Unreachable</strong> β€” ${loadError}
1073
1073
  </span>
@@ -1079,7 +1079,7 @@ function ServerConfigMode() {
1079
1079
  !loadError &&
1080
1080
  html`
1081
1081
  <div class="settings-banner settings-banner-warn">
1082
- <span>${resolveIcon("🧠")}</span>
1082
+ <span>${resolveIcon(":cpu:")}</span>
1083
1083
  <span class="settings-banner-text">Connection lost β€” reconnecting…</span>
1084
1084
  </div>
1085
1085
  `}
@@ -1087,7 +1087,7 @@ function ServerConfigMode() {
1087
1087
  ${configSync &&
1088
1088
  html`
1089
1089
  <div class="settings-banner ${configSync.skipped?.length ? "settings-banner-warn" : "settings-banner-info"}">
1090
- <span>${resolveIcon("πŸ’Ύ")}</span>
1090
+ <span>${resolveIcon(":save:")}</span>
1091
1091
  <span class="settings-banner-text">
1092
1092
  ${configSync.skipped?.length
1093
1093
  ? `Saved ${configSync.total} settings; synced ${configSync.updated} to config file.`
@@ -1108,7 +1108,7 @@ function ServerConfigMode() {
1108
1108
  !loadError &&
1109
1109
  html`
1110
1110
  <div class="settings-banner settings-banner-info">
1111
- <span>${resolveIcon("🧭")}</span>
1111
+ <span>${resolveIcon(":compass:")}</span>
1112
1112
  <span class="settings-banner-text">
1113
1113
  Settings are saved to <code>${serverMeta.envPath}</code> and synced to <code>${serverMeta.configPath}</code> for supported keys.
1114
1114
  </span>
@@ -1151,7 +1151,7 @@ function ServerConfigMode() {
1151
1151
  if (filteredSettings.length === 0) {
1152
1152
  return html`
1153
1153
  <div class="settings-empty-search">
1154
- <div class="settings-empty-search-icon">${resolveIcon("πŸ”")}</div>
1154
+ <div class="settings-empty-search-icon">${resolveIcon(":search:")}</div>
1155
1155
  <div>No settings match "<strong>${searchQuery}</strong>"</div>
1156
1156
  <div class="meta-text mt-sm">Try a different search term</div>
1157
1157
  </div>
@@ -1271,7 +1271,7 @@ function ServerConfigMode() {
1271
1271
  ${hasRestartSetting &&
1272
1272
  html`
1273
1273
  <div class="settings-banner settings-banner-warn" style="margin-top:8px">
1274
- <span>${resolveIcon("πŸ”„")}</span>
1274
+ <span>${resolveIcon(":refresh:")}</span>
1275
1275
  <span class="settings-banner-text">
1276
1276
  Some changes require a restart. The server will auto-reload (~2 seconds).
1277
1277
  </span>
@@ -1553,7 +1553,7 @@ function AppPreferencesMode() {
1553
1553
 
1554
1554
 
1555
1555
  <!-- ─── Account ─── -->
1556
- <${Collapsible} title=${iconText("πŸ‘€ Account")} defaultOpen=${true}>
1556
+ <${Collapsible} title=${iconText(":user: Account")} defaultOpen=${true}>
1557
1557
  <${Card}>
1558
1558
  <div class="settings-row">
1559
1559
  ${user?.photo_url &&
@@ -1578,7 +1578,7 @@ function AppPreferencesMode() {
1578
1578
  <//>
1579
1579
 
1580
1580
  <!-- ─── Appearance ─── -->
1581
- <${Collapsible} title=${iconText("🎨 Appearance")} defaultOpen=${false}>
1581
+ <${Collapsible} title=${iconText(":palette: Appearance")} defaultOpen=${false}>
1582
1582
  <${Card}>
1583
1583
  <div class="card-subtitle mb-sm">Color Theme</div>
1584
1584
  <div class="theme-picker-grid">
@@ -1628,7 +1628,7 @@ function AppPreferencesMode() {
1628
1628
  <//>
1629
1629
 
1630
1630
  <!-- ─── Notifications ─── -->
1631
- <${Collapsible} title=${iconText("πŸ”” Notifications")} defaultOpen=${false}>
1631
+ <${Collapsible} title=${iconText(":bell: Notifications")} defaultOpen=${false}>
1632
1632
  <${Card}>
1633
1633
  <${ListItem}
1634
1634
  title="Real-time Updates"
@@ -1667,7 +1667,7 @@ function AppPreferencesMode() {
1667
1667
  <//>
1668
1668
 
1669
1669
  <!-- ─── Data & Storage ─── -->
1670
- <${Collapsible} title=${iconText("πŸ’Ύ Data & Storage")} defaultOpen=${false}>
1670
+ <${Collapsible} title=${iconText(":save: Data & Storage")} defaultOpen=${false}>
1671
1671
  <${Card}>
1672
1672
  <${ListItem}
1673
1673
  title="WebSocket"
@@ -1688,7 +1688,7 @@ function AppPreferencesMode() {
1688
1688
  subtitle="Remove all stored preferences"
1689
1689
  trailing=${html`
1690
1690
  <button class="btn btn-ghost btn-sm" onClick=${handleClearCache}>
1691
- ${iconText("πŸ—‘ Clear")}
1691
+ ${iconText(":trash: Clear")}
1692
1692
  </button>
1693
1693
  `}
1694
1694
  />
@@ -1696,7 +1696,7 @@ function AppPreferencesMode() {
1696
1696
  <//>
1697
1697
 
1698
1698
  <!-- ─── Executor Defaults ─── -->
1699
- <${Collapsible} title=${iconText("βš™οΈ Executor Defaults")} defaultOpen=${false}>
1699
+ <${Collapsible} title=${iconText(":settings: Executor Defaults")} defaultOpen=${false}>
1700
1700
  <${Card}>
1701
1701
  <div class="card-subtitle mb-sm">Default Max Parallel</div>
1702
1702
  <div class="range-row mb-md">
@@ -1743,7 +1743,7 @@ function AppPreferencesMode() {
1743
1743
  <//>
1744
1744
 
1745
1745
  <!-- ─── Advanced ─── -->
1746
- <${Collapsible} title=${iconText("πŸ”§ Advanced")} defaultOpen=${false}>
1746
+ <${Collapsible} title=${iconText(":settings: Advanced")} defaultOpen=${false}>
1747
1747
  <${Card}>
1748
1748
  <${ListItem}
1749
1749
  title="Debug Mode"
@@ -1794,7 +1794,7 @@ function AppPreferencesMode() {
1794
1794
  <//>
1795
1795
 
1796
1796
  <!-- ─── About ─── -->
1797
- <${Collapsible} title="ℹ️ About" defaultOpen=${false}>
1797
+ <${Collapsible} title=":help: About" defaultOpen=${false}>
1798
1798
  <${Card}>
1799
1799
  <div style="text-align:center;padding:12px 0">
1800
1800
  <div style="font-size:18px;font-weight:700;margin-bottom:4px">
@@ -1958,7 +1958,7 @@ function GitHubDeviceFlowCard({ config }) {
1958
1958
  return html`
1959
1959
  <${Card}>
1960
1960
  <div style="display:flex;align-items:center;gap:10px;padding:4px 0">
1961
- <span style="font-size:20px">${resolveIcon("πŸ™")}</span>
1961
+ <span style="font-size:20px">${resolveIcon(":git:")}</span>
1962
1962
  <div style="flex:1;min-width:0">
1963
1963
  <div style="font-size:13px;font-weight:600;color:var(--text-primary)">GitHub Connected</div>
1964
1964
  <div style="font-size:12px;color:var(--text-secondary)">Token is configured. Re-authenticate below if needed.</div>
@@ -1976,7 +1976,7 @@ function GitHubDeviceFlowCard({ config }) {
1976
1976
  return html`
1977
1977
  <${Card}>
1978
1978
  <div style="text-align:center;padding:12px 0">
1979
- <div style="font-size:32px;margin-bottom:8px">${resolveIcon("βœ…")}</div>
1979
+ <div style="font-size:32px;margin-bottom:8px">${resolveIcon(":check:")}</div>
1980
1980
  <div style="font-size:15px;font-weight:600;color:var(--text-primary)">Signed in as ${ghUser}</div>
1981
1981
  <div style="font-size:12px;color:var(--text-secondary);margin-top:4px">GitHub token saved to .env</div>
1982
1982
  </div>
@@ -2016,7 +2016,7 @@ function GitHubDeviceFlowCard({ config }) {
2016
2016
  return html`
2017
2017
  <${Card}>
2018
2018
  <div style="text-align:center;padding:12px 0">
2019
- <div style="font-size:24px;margin-bottom:8px">${resolveIcon("⚠️")}</div>
2019
+ <div style="font-size:24px;margin-bottom:8px">${resolveIcon(":alert:")}</div>
2020
2020
  <div style="font-size:13px;color:var(--color-error);margin-bottom:12px">${error}</div>
2021
2021
  <button class="btn btn-sm btn-primary" onClick=${startFlow}>Try Again</button>
2022
2022
  </div>
@@ -2028,7 +2028,7 @@ function GitHubDeviceFlowCard({ config }) {
2028
2028
  return html`
2029
2029
  <${Card}>
2030
2030
  <div style="text-align:center;padding:16px 0">
2031
- <div style="font-size:32px;margin-bottom:8px">${resolveIcon("πŸ™")}</div>
2031
+ <div style="font-size:32px;margin-bottom:8px">${resolveIcon(":git:")}</div>
2032
2032
  <div style="font-size:15px;font-weight:600;margin-bottom:4px;color:var(--text-primary)">
2033
2033
  Sign in with GitHub
2034
2034
  </div>