aidevops 3.29.26 → 3.29.27

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.
package/README.md CHANGED
@@ -58,7 +58,7 @@ The result: an AI operations platform that manages projects across every busines
58
58
  [![Copyright](https://img.shields.io/badge/Copyright-Marcus%20Quinn%202025--2026-blue.svg)](https://github.com/marcusquinn)
59
59
 
60
60
  <!-- Release & Version Info -->
61
- [![Version](https://img.shields.io/badge/Version-3.29.26-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
61
+ [![Version](https://img.shields.io/badge/Version-3.29.27-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
62
62
  [![npm version](https://img.shields.io/npm/v/aidevops)](https://www.npmjs.com/package/aidevops)
63
63
  [![Homebrew](https://img.shields.io/badge/homebrew-marcusquinn%2Ftap-orange)](https://github.com/marcusquinn/homebrew-tap)
64
64
  [![GitHub repository](https://img.shields.io/badge/github-repository-181717.svg?logo=github)](https://github.com/marcusquinn/aidevops)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.29.26
1
+ 3.29.27
package/aidevops.sh CHANGED
@@ -5,7 +5,7 @@
5
5
  # AI DevOps Framework CLI
6
6
  # Usage: aidevops <command> [options]
7
7
  #
8
- # Version: 3.29.26
8
+ # Version: 3.29.27
9
9
 
10
10
  set -euo pipefail
11
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "3.29.26",
3
+ "version": "3.29.27",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -35,7 +35,7 @@ import {
35
35
  } from "react-icons/fi";
36
36
  import type { ContrastPreference, ConversationMode, FontPreference, FontSizePreference, ShellMode, SidebarMode, SurfaceIconName, SurfaceId, SurfaceNavGroup, SurfaceNavItem, ThemePreference } from "./app-model";
37
37
  import { dashboardNavItem, navGroups, sidebarModeForSurface, surfaceRecordCounts, text } from "./app-model";
38
- import { SidebarFooter } from "./AppearanceControls";
38
+ import { SidebarFooter, wrappedOptionIndex } from "./AppearanceControls";
39
39
  import { VaultPadlock, vaultCollectionForSurface } from "./VaultBadges";
40
40
 
41
41
  export { hueFromInputValue } from "./AppearanceControls";
@@ -215,8 +215,14 @@ function ConversationSidebar({ conversationMode, selectedLocalRepoIndex, selecte
215
215
  setSelectedSessionId(activeSessionId);
216
216
  }
217
217
  }, [activeSessionId, selectedSessionId, setSelectedSessionId]);
218
- const canSelectPreviousRepo = selectedLocalRepoIndex > 0;
219
- const canSelectNextRepo = selectedLocalRepoIndex < repos.length - 1;
218
+ const selectRepoByIndex = (index: number) => {
219
+ if (repos.length === 0) {
220
+ return;
221
+ }
222
+
223
+ setSelectedLocalRepoIndex(index);
224
+ setSelectedSessionId(undefined);
225
+ };
220
226
 
221
227
  return (
222
228
  <section className="conversation-panel" aria-label={text.opencodeSessions}>
@@ -226,11 +232,11 @@ function ConversationSidebar({ conversationMode, selectedLocalRepoIndex, selecte
226
232
  <section className="repo-session-selector" aria-label={text.localRepoSelector}>
227
233
  <span className="repo-selector-heading" id="local-repo-selector-label">{text.localRepos}</span>
228
234
  <div className="selector-with-stepper repo-selector-row">
229
- <button aria-label="Previous local repo" className="selector-step-button" disabled={!canSelectPreviousRepo} onClick={() => { setSelectedLocalRepoIndex(Math.max(0, selectedLocalRepoIndex - 1)); setSelectedSessionId(undefined); }} type="button"><FiChevronLeft aria-hidden="true" /></button>
235
+ <button aria-label="Previous local repo" className="selector-step-button" disabled={repos.length === 0} onClick={() => selectRepoByIndex(wrappedOptionIndex(selectedLocalRepoIndex, repos.length, -1))} type="button"><FiChevronLeft aria-hidden="true" /></button>
230
236
  <select aria-labelledby="local-repo-selector-label" onChange={(event) => { setSelectedLocalRepoIndex(Number.parseInt(event.currentTarget.value, 10)); setSelectedSessionId(undefined); }} value={Math.min(selectedLocalRepoIndex, Math.max(0, repos.length - 1))}>
231
237
  {repos.length === 0 ? <option value={0}>No local repos</option> : repos.map((repo, index) => <option key={repo.path_ref} value={index}>{repo.name}</option>)}
232
238
  </select>
233
- <button aria-label="Next local repo" className="selector-step-button" disabled={!canSelectNextRepo} onClick={() => { setSelectedLocalRepoIndex(Math.min(repos.length - 1, selectedLocalRepoIndex + 1)); setSelectedSessionId(undefined); }} type="button"><FiChevronRight aria-hidden="true" /></button>
239
+ <button aria-label="Next local repo" className="selector-step-button" disabled={repos.length === 0} onClick={() => selectRepoByIndex(wrappedOptionIndex(selectedLocalRepoIndex, repos.length, 1))} type="button"><FiChevronRight aria-hidden="true" /></button>
234
240
  </div>
235
241
  </section>
236
242
  <section className="sidebar-group session-history-list">
@@ -19,6 +19,14 @@ export function hueFromInputValue(value: string): number | null {
19
19
  return Math.min(359, Math.max(0, hue));
20
20
  }
21
21
 
22
+ export function wrappedOptionIndex(currentIndex: number, optionCount: number, direction: -1 | 1): number {
23
+ if (optionCount <= 0) {
24
+ return 0;
25
+ }
26
+
27
+ return (currentIndex + direction + optionCount) % optionCount;
28
+ }
29
+
22
30
  export function SidebarFooter(props: SidebarFooterProps): ReactElement {
23
31
  const [appearanceOpen, setAppearanceOpen] = useState(true);
24
32
  const AppearanceChevron = appearanceOpen ? FiChevronDown : FiChevronUp;
@@ -73,7 +81,7 @@ function AppearancePanelBody({ accentHue, contrastPreference, fontPreference, fo
73
81
 
74
82
  function ThemeControl({ setThemePreference, themePreference }: { setThemePreference: (theme: ThemePreference) => void; themePreference: ThemePreference }): ReactElement {
75
83
  return (
76
- <fieldset className="theme-control compact" aria-label={text.theme}>
84
+ <fieldset className="theme-control compact appearance-segmented-control" aria-label={text.theme}>
77
85
  {(["system", "light", "dark"] as const).map((theme) => (
78
86
  <button
79
87
  aria-pressed={themePreference === theme}
@@ -141,7 +149,7 @@ function ContrastControl({ contrastPreference, setContrastPreference }: { contra
141
149
  return (
142
150
  <fieldset className="contrast-control" aria-label={text.contrast}>
143
151
  <legend>{text.contrast}</legend>
144
- <div className="contrast-options">
152
+ <div className="contrast-options appearance-segmented-control">
145
153
  {contrastOptions.map((option) => (
146
154
  <button
147
155
  aria-pressed={contrastPreference === option.value}
@@ -211,8 +219,6 @@ function FontSizeControl({ fontSizePreference, setFontSizePreference }: { fontSi
211
219
  function FontControl({ fontPreference, setFontPreference }: { fontPreference: FontPreference; setFontPreference: (font: FontPreference) => void }): ReactElement {
212
220
  const selectedFontFamily = fontFamilyForPreference(fontPreference);
213
221
  const fontIndex = Math.max(0, fontOptions.findIndex((option) => option.value === fontPreference));
214
- const canSelectPreviousFont = fontIndex > 0;
215
- const canSelectNextFont = fontIndex < fontOptions.length - 1;
216
222
  const setFontByIndex = (index: number) => {
217
223
  const nextFont = fontOptions[index]?.value;
218
224
  if (nextFont !== undefined) {
@@ -224,7 +230,7 @@ function FontControl({ fontPreference, setFontPreference }: { fontPreference: Fo
224
230
  <div className="font-control">
225
231
  <span id="appearance-font-selector-label">{text.font}</span>
226
232
  <div className="selector-with-stepper font-selector-row">
227
- <button aria-label="Previous font" className="selector-step-button" disabled={!canSelectPreviousFont} onClick={() => setFontByIndex(fontIndex - 1)} type="button"><FiChevronLeft aria-hidden="true" /></button>
233
+ <button aria-label="Previous font" className="selector-step-button" onClick={() => setFontByIndex(wrappedOptionIndex(fontIndex, fontOptions.length, -1))} type="button"><FiChevronLeft aria-hidden="true" /></button>
228
234
  <select
229
235
  aria-labelledby="appearance-font-selector-label"
230
236
  onChange={(event) => setFontPreference(event.currentTarget.value as FontPreference)}
@@ -237,7 +243,7 @@ function FontControl({ fontPreference, setFontPreference }: { fontPreference: Fo
237
243
  </option>
238
244
  ))}
239
245
  </select>
240
- <button aria-label="Next font" className="selector-step-button" disabled={!canSelectNextFont} onClick={() => setFontByIndex(fontIndex + 1)} type="button"><FiChevronRight aria-hidden="true" /></button>
246
+ <button aria-label="Next font" className="selector-step-button" onClick={() => setFontByIndex(wrappedOptionIndex(fontIndex, fontOptions.length, 1))} type="button"><FiChevronRight aria-hidden="true" /></button>
241
247
  </div>
242
248
  </div>
243
249
  );
@@ -7,7 +7,10 @@
7
7
  --bg-secondary: #f8f8f8;
8
8
  --bg-tertiary: #f0f0f0;
9
9
  --border: color-mix(in srgb, rgb(0 0 0 / 13%) 62%, rgb(255 255 255 / 54%));
10
+ --border-accent: color-mix(in srgb, var(--accent) 38%, var(--border));
11
+ --border-danger: color-mix(in srgb, #ef4444 44%, var(--border));
10
12
  --border-hover: color-mix(in srgb, rgb(0 0 0 / 24%) 64%, rgb(255 255 255 / 48%));
13
+ --border-warning: color-mix(in srgb, #d97706 38%, var(--border));
11
14
  --code-bg: hsl(var(--accent-hue) 55% 91%);
12
15
  --desktop-titlebar-height: 0px;
13
16
  --font-family-app: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
@@ -127,7 +130,10 @@
127
130
 
128
131
  :root[data-borders="hidden"] {
129
132
  --border: transparent;
133
+ --border-accent: transparent;
134
+ --border-danger: transparent;
130
135
  --border-hover: transparent;
136
+ --border-warning: transparent;
131
137
  --glass-panel-shadow: var(--shadow-soft);
132
138
  }
133
139
 
@@ -304,7 +310,7 @@ code {
304
310
 
305
311
  .machine-orb.active {
306
312
  background: color-mix(in srgb, var(--accent) 18%, transparent);
307
- border-color: color-mix(in srgb, var(--accent) 42%, transparent);
313
+ border-color: var(--border-accent);
308
314
  color: var(--accent);
309
315
  }
310
316
 
@@ -380,7 +386,7 @@ code {
380
386
  .terminal-mark {
381
387
  align-items: center;
382
388
  background: color-mix(in srgb, var(--accent) 15%, #0a0a0a);
383
- border: 1px solid color-mix(in srgb, var(--accent) 38%, transparent);
389
+ border: 1px solid var(--border-accent);
384
390
  border-radius: 11px;
385
391
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 28%, transparent);
386
392
  color: var(--accent);
@@ -504,7 +510,7 @@ code {
504
510
 
505
511
  .new-session-button {
506
512
  background: color-mix(in srgb, var(--accent) 15%, transparent);
507
- border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
513
+ border: 1px solid var(--border-accent);
508
514
  border-radius: 12px;
509
515
  color: var(--accent);
510
516
  font-weight: 900;
@@ -542,7 +548,7 @@ code {
542
548
 
543
549
  .selector-step-button:hover:not(:disabled),
544
550
  .selector-step-button:focus-visible {
545
- border-color: color-mix(in srgb, var(--accent) 40%, transparent);
551
+ border-color: var(--border-accent);
546
552
  color: var(--accent);
547
553
  outline: none;
548
554
  }
@@ -692,7 +698,7 @@ code {
692
698
  .vault-padlock {
693
699
  align-items: center;
694
700
  background: color-mix(in srgb, #d97706 13%, transparent);
695
- border: 1px solid color-mix(in srgb, #d97706 38%, transparent);
701
+ border: 1px solid var(--border-warning);
696
702
  border-radius: 999px;
697
703
  color: #b45309;
698
704
  display: inline-flex;
@@ -718,7 +724,7 @@ code {
718
724
 
719
725
  .vault-padlock[data-vault-state="unlocked"] {
720
726
  background: color-mix(in srgb, var(--accent) 14%, transparent);
721
- border-color: color-mix(in srgb, var(--accent) 38%, transparent);
727
+ border-color: var(--border-accent);
722
728
  color: var(--accent);
723
729
  }
724
730
 
@@ -742,43 +748,27 @@ code {
742
748
 
743
749
  .sidebar-footer {
744
750
  border-top: 1px solid var(--border);
745
- padding-top: 12px;
751
+ padding: 0;
746
752
  }
747
753
 
748
- .theme-control.compact {
754
+ .appearance-segmented-control {
749
755
  background: var(--surface-muted);
750
756
  border: 0;
751
757
  border-radius: 12px;
752
758
  display: grid;
753
759
  gap: 4px;
754
- grid-template-columns: repeat(3, 1fr);
760
+ grid-template-columns: repeat(3, minmax(0, 1fr));
755
761
  margin: 0;
756
762
  min-inline-size: 0;
757
763
  padding: 4px;
758
764
  }
759
765
 
760
- .theme-option {
766
+ .appearance-panel {
761
767
  background: transparent;
762
768
  border: 0;
763
- border-radius: 9px;
764
- color: var(--text-muted);
765
- font-size: 0.72rem;
766
- font-weight: 900;
767
- padding: 7px 6px;
768
- text-transform: uppercase;
769
- }
770
-
771
- .theme-option:hover,
772
- .theme-option.active {
773
- background: var(--surface-elevated);
774
- color: var(--accent);
775
- }
776
-
777
- .appearance-panel {
778
- background: var(--surface-muted);
779
- border: 1px solid var(--border);
780
- border-radius: 14px;
769
+ border-radius: 0;
781
770
  display: grid;
771
+ font-size: 1em;
782
772
  overflow: hidden;
783
773
  }
784
774
 
@@ -786,9 +776,10 @@ code {
786
776
  background: transparent;
787
777
  border: 0;
788
778
  color: var(--text-secondary);
779
+ font-size: 1em;
789
780
  gap: 8px;
790
781
  justify-content: center;
791
- padding: 8px 10px;
782
+ padding: 0.8em 1em;
792
783
  text-transform: uppercase;
793
784
  width: 100%;
794
785
  }
@@ -809,12 +800,12 @@ code {
809
800
  .appearance-panel-body {
810
801
  border-top: 1px solid var(--border);
811
802
  display: grid;
812
- gap: 12px;
813
- padding: 12px;
803
+ gap: 0.9em;
804
+ padding: 1em;
814
805
  }
815
806
 
816
807
  .appearance-panel.collapsed .appearance-panel-tab {
817
- border-radius: 14px;
808
+ border-radius: 0;
818
809
  }
819
810
 
820
811
  .theme-hue-control,
@@ -827,7 +818,7 @@ code {
827
818
 
828
819
  .theme-control-heading {
829
820
  gap: 8px;
830
- justify-content: space-between;
821
+ justify-content: flex-start;
831
822
  }
832
823
 
833
824
  .hue-label-row {
@@ -842,7 +833,7 @@ code {
842
833
  .font-size-control label,
843
834
  .font-control span {
844
835
  color: var(--text-muted);
845
- font-size: 0.74rem;
836
+ font-size: 0.86em;
846
837
  font-weight: 900;
847
838
  letter-spacing: 0.08em;
848
839
  text-transform: uppercase;
@@ -854,28 +845,27 @@ code {
854
845
  padding: 0;
855
846
  }
856
847
 
857
- .contrast-options {
858
- display: grid;
859
- gap: 6px;
860
- grid-template-columns: repeat(3, minmax(0, 1fr));
861
- }
862
-
848
+ .theme-option,
863
849
  .contrast-options button {
864
850
  background: var(--surface-elevated);
865
851
  border: 1px solid var(--border);
866
852
  border-radius: 10px;
867
853
  color: var(--text-secondary);
868
- font-size: 0.72rem;
854
+ font-size: 0.86em;
869
855
  font-weight: 900;
870
- padding: 7px 6px;
856
+ min-height: 2.75em;
857
+ padding: 0.55em 0.5em;
871
858
  text-transform: uppercase;
872
859
  }
873
860
 
861
+ .theme-option:hover,
862
+ .theme-option:focus-visible,
863
+ .theme-option.active,
874
864
  .contrast-options button:hover,
875
865
  .contrast-options button:focus-visible,
876
866
  .contrast-options button.active {
877
867
  background: color-mix(in srgb, var(--accent) 18%, var(--surface-elevated));
878
- border-color: color-mix(in srgb, var(--accent) 48%, var(--border));
868
+ border-color: var(--border-accent);
879
869
  color: var(--accent);
880
870
  outline: none;
881
871
  }
@@ -885,7 +875,7 @@ code {
885
875
  border: 1px solid var(--border);
886
876
  border-radius: 9px;
887
877
  color: var(--text-primary);
888
- font-size: 0.78rem;
878
+ font-size: 0.95em;
889
879
  font-weight: 800;
890
880
  min-width: 0;
891
881
  padding: 5px 6px;
@@ -920,7 +910,7 @@ code {
920
910
  border: 0;
921
911
  color: var(--text-muted);
922
912
  display: grid;
923
- font-size: 0.7rem;
913
+ font-size: 0.82em;
924
914
  font-weight: 900;
925
915
  grid-template-columns: repeat(5, 1fr);
926
916
  margin: 0;
@@ -1128,7 +1118,7 @@ code {
1128
1118
  .profile-avatar-button:focus-visible,
1129
1119
  .command-trigger:hover,
1130
1120
  .command-trigger:focus-visible {
1131
- border-color: color-mix(in srgb, var(--accent) 40%, transparent);
1121
+ border-color: var(--border-accent);
1132
1122
  color: var(--accent);
1133
1123
  outline: none;
1134
1124
  }
@@ -2162,7 +2152,7 @@ code {
2162
2152
  }
2163
2153
 
2164
2154
  .managed-app-card.expanded {
2165
- border-color: color-mix(in srgb, var(--accent) 32%, var(--border));
2155
+ border-color: var(--border-accent);
2166
2156
  }
2167
2157
 
2168
2158
  .managed-app-card:hover,
@@ -2376,13 +2366,13 @@ button.managed-toggle:disabled {
2376
2366
 
2377
2367
  button.managed-toggle:hover,
2378
2368
  button.managed-toggle:focus-visible {
2379
- border-color: color-mix(in srgb, var(--accent) 42%, var(--border));
2369
+ border-color: var(--border-accent);
2380
2370
  outline: none;
2381
2371
  }
2382
2372
 
2383
2373
  .switch-track {
2384
2374
  background: color-mix(in srgb, #ef4444 18%, var(--surface-muted));
2385
- border: 1px solid color-mix(in srgb, #ef4444 44%, var(--border));
2375
+ border: 1px solid var(--border-danger);
2386
2376
  border-radius: 999px;
2387
2377
  display: inline-flex;
2388
2378
  flex: 0 0 auto;
@@ -2431,14 +2421,14 @@ button.managed-toggle:focus-visible {
2431
2421
 
2432
2422
  .app-log-link:hover,
2433
2423
  .app-log-link:focus-visible {
2434
- border-color: color-mix(in srgb, var(--accent) 42%, var(--border));
2424
+ border-color: var(--border-accent);
2435
2425
  outline: none;
2436
2426
  }
2437
2427
 
2438
2428
  .app-action-button {
2439
2429
  align-items: center;
2440
2430
  background: color-mix(in srgb, var(--accent) 16%, var(--surface-elevated));
2441
- border: 1px solid color-mix(in srgb, var(--accent) 48%, var(--border));
2431
+ border: 1px solid var(--border-accent);
2442
2432
  border-radius: 999px;
2443
2433
  color: var(--accent);
2444
2434
  display: inline-flex;
@@ -2542,7 +2532,7 @@ button.managed-toggle:focus-visible {
2542
2532
 
2543
2533
  .terminal-close-button:hover,
2544
2534
  .terminal-close-button:focus-visible {
2545
- border-color: color-mix(in srgb, var(--accent) 42%, var(--border));
2535
+ border-color: var(--border-accent);
2546
2536
  color: var(--accent);
2547
2537
  outline: none;
2548
2538
  }
@@ -2746,7 +2736,7 @@ button.managed-toggle:focus-visible {
2746
2736
 
2747
2737
  .toggle-pill.checked {
2748
2738
  background: color-mix(in srgb, var(--accent) 14%, transparent);
2749
- border-color: color-mix(in srgb, var(--accent) 50%, transparent);
2739
+ border-color: var(--border-accent);
2750
2740
  color: var(--accent);
2751
2741
  }
2752
2742
 
@@ -2796,7 +2786,7 @@ button.managed-toggle:focus-visible {
2796
2786
  background:
2797
2787
  linear-gradient(135deg, color-mix(in srgb, var(--accent) 16%, transparent), transparent 46%),
2798
2788
  color-mix(in srgb, var(--accent) 7%, var(--surface-muted));
2799
- border-color: color-mix(in srgb, var(--accent) 38%, var(--border));
2789
+ border-color: var(--border-accent);
2800
2790
  box-shadow: 0 18px 46px color-mix(in srgb, var(--accent) 10%, transparent);
2801
2791
  transform: translateY(-1px);
2802
2792
  }
@@ -3114,7 +3104,7 @@ button.managed-toggle:focus-visible {
3114
3104
 
3115
3105
  .appearance-switch strong {
3116
3106
  color: var(--text-muted);
3117
- font-size: 0.74rem;
3107
+ font-size: 0.86em;
3118
3108
  font-weight: 900;
3119
3109
  letter-spacing: 0.08em;
3120
3110
  margin-right: auto;
@@ -3130,7 +3120,7 @@ button.managed-toggle:focus-visible {
3130
3120
  align-items: center;
3131
3121
  backdrop-filter: blur(18px);
3132
3122
  background: color-mix(in srgb, var(--surface) 78%, transparent);
3133
- border: 1px solid color-mix(in srgb, var(--accent) 34%, var(--border));
3123
+ border: 1px solid var(--border-accent);
3134
3124
  border-radius: 24px;
3135
3125
  box-shadow: 0 24px 90px color-mix(in srgb, var(--accent) 14%, transparent), var(--shadow-soft);
3136
3126
  display: grid;
@@ -3422,7 +3412,7 @@ button.managed-toggle:focus-visible {
3422
3412
 
3423
3413
  .switch-control input:checked + span {
3424
3414
  background: color-mix(in srgb, var(--accent) 22%, transparent);
3425
- border-color: color-mix(in srgb, var(--accent) 52%, transparent);
3415
+ border-color: var(--border-accent);
3426
3416
  }
3427
3417
 
3428
3418
  .switch-control input:checked + span::after {
package/setup.sh CHANGED
@@ -12,7 +12,7 @@ shopt -s inherit_errexit 2>/dev/null || true
12
12
  # AI Assistant Server Access Framework Setup Script
13
13
  # Helps developers set up the framework for their infrastructure
14
14
  #
15
- # Version: 3.29.26
15
+ # Version: 3.29.27
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)