aidevops 3.25.0 → 3.26.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.
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.25.0-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
61
+ [![Version](https://img.shields.io/badge/Version-3.26.0-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.25.0
1
+ 3.26.0
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.25.0
8
+ # Version: 3.26.0
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.25.0",
3
+ "version": "3.26.0",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -221,14 +221,14 @@ function ConversationSidebar({ conversationMode, selectedLocalRepoIndex, setConv
221
221
  {conversationMode === "people" ? <PeopleChannelList /> : <>
222
222
  <button className="new-session-button" disabled title="Creating sessions needs an audited OpenCode write route." type="button">{text.newSession}</button>
223
223
  <section className="repo-session-selector" aria-label={text.localRepoSelector}>
224
- <button aria-label="Previous local repo" disabled={!canSelectPreviousRepo} onClick={() => setSelectedLocalRepoIndex(Math.max(0, selectedLocalRepoIndex - 1))} type="button"><FiChevronLeft /></button>
225
- <label>
226
- <span>{text.localRepos}</span>
227
- <select onChange={(event) => setSelectedLocalRepoIndex(Number.parseInt(event.currentTarget.value, 10))} value={Math.min(selectedLocalRepoIndex, Math.max(0, repos.length - 1))}>
224
+ <span className="repo-selector-heading" id="local-repo-selector-label">{text.localRepos}</span>
225
+ <div className="selector-with-stepper repo-selector-row">
226
+ <button aria-label="Previous local repo" className="selector-step-button" disabled={!canSelectPreviousRepo} onClick={() => setSelectedLocalRepoIndex(Math.max(0, selectedLocalRepoIndex - 1))} type="button"><FiChevronLeft aria-hidden="true" /></button>
227
+ <select aria-labelledby="local-repo-selector-label" onChange={(event) => setSelectedLocalRepoIndex(Number.parseInt(event.currentTarget.value, 10))} value={Math.min(selectedLocalRepoIndex, Math.max(0, repos.length - 1))}>
228
228
  {repos.length === 0 ? <option value={0}>No local repos</option> : repos.map((repo, index) => <option key={repo.path_ref} value={index}>{repo.name}</option>)}
229
229
  </select>
230
- </label>
231
- <button aria-label="Next local repo" disabled={!canSelectNextRepo} onClick={() => setSelectedLocalRepoIndex(Math.min(repos.length - 1, selectedLocalRepoIndex + 1))} type="button"><FiChevronRight /></button>
230
+ <button aria-label="Next local repo" className="selector-step-button" disabled={!canSelectNextRepo} onClick={() => setSelectedLocalRepoIndex(Math.min(repos.length - 1, selectedLocalRepoIndex + 1))} type="button"><FiChevronRight aria-hidden="true" /></button>
231
+ </div>
232
232
  </section>
233
233
  <section className="sidebar-group session-history-list">
234
234
  <h2>{text.sessionHistory}</h2>
@@ -386,8 +386,20 @@ function SidebarFooter({ accentHue, fontPreference, fontSizePreference, setAccen
386
386
  const [appearanceOpen, setAppearanceOpen] = useState(true);
387
387
  const AppearanceChevron = appearanceOpen ? FiChevronDown : FiChevronUp;
388
388
  const selectedFontFamily = fontFamilyForPreference(fontPreference);
389
+ const fontIndex = Math.max(0, fontOptions.findIndex((option) => option.value === fontPreference));
389
390
  const fontSizeIndex = Math.max(0, fontSizeOptions.findIndex((option) => option.value === fontSizePreference));
391
+ const canSelectPreviousFont = fontIndex > 0;
392
+ const canSelectNextFont = fontIndex < fontOptions.length - 1;
390
393
  const [hueInput, setHueInput] = useState(() => String(accentHue));
394
+ const setFontByIndex = (index: number) => {
395
+ const nextFont = fontOptions[index]?.value;
396
+
397
+ if (nextFont === undefined) {
398
+ return;
399
+ }
400
+
401
+ setFontPreference(nextFont);
402
+ };
391
403
  const updateAccentHue = (value: number) => {
392
404
  if (!Number.isFinite(value)) {
393
405
  return;
@@ -486,20 +498,25 @@ function SidebarFooter({ accentHue, fontPreference, fontSizePreference, setAccen
486
498
  {fontSizeOptions.map((option) => <span className={option.value === fontSizePreference ? "active" : ""} key={option.value}>{option.label}</span>)}
487
499
  </div>
488
500
  </div>
489
- <label className="font-control">
490
- <span>{text.font}</span>
491
- <select
492
- onChange={(event) => setFontPreference(event.currentTarget.value as FontPreference)}
493
- style={{ fontFamily: selectedFontFamily }}
494
- value={fontPreference}
495
- >
496
- {fontOptions.map((option) => (
497
- <option key={option.value} style={{ fontFamily: option.fontFamily }} value={option.value}>
498
- {option.label}
499
- </option>
500
- ))}
501
- </select>
502
- </label>
501
+ <div className="font-control">
502
+ <span id="appearance-font-selector-label">{text.font}</span>
503
+ <div className="selector-with-stepper font-selector-row">
504
+ <button aria-label="Previous font" className="selector-step-button" disabled={!canSelectPreviousFont} onClick={() => setFontByIndex(fontIndex - 1)} type="button"><FiChevronLeft aria-hidden="true" /></button>
505
+ <select
506
+ aria-labelledby="appearance-font-selector-label"
507
+ onChange={(event) => setFontPreference(event.currentTarget.value as FontPreference)}
508
+ style={{ fontFamily: selectedFontFamily }}
509
+ value={fontPreference}
510
+ >
511
+ {fontOptions.map((option) => (
512
+ <option key={option.value} style={{ fontFamily: option.fontFamily }} value={option.value}>
513
+ {option.label}
514
+ </option>
515
+ ))}
516
+ </select>
517
+ <button aria-label="Next font" className="selector-step-button" disabled={!canSelectNextFont} onClick={() => setFontByIndex(fontIndex + 1)} type="button"><FiChevronRight aria-hidden="true" /></button>
518
+ </div>
519
+ </div>
503
520
  </div>
504
521
  : null}
505
522
  </section>
@@ -1,5 +1,5 @@
1
1
  /* jshint esversion: 11 */
2
- import { type ReactElement, type ReactNode, useEffect, useMemo, useState } from "react";
2
+ import { type ReactElement, type ReactNode, useEffect, useMemo, useRef, useState } from "react";
3
3
  import { FiBell, FiChevronLeft, FiChevronRight, FiCommand, FiCpu, FiGlobe, FiHash, FiLogOut, FiMessageSquare, FiSearch, FiSettings, FiShield, FiUser } from "react-icons/fi";
4
4
  import type { GuiFileRootId, GuiStatusData } from "../../gui-shared/src";
5
5
  import { SurfaceGlyph } from "./AppNavigation";
@@ -54,6 +54,7 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
54
54
  status: GuiStatusData;
55
55
  }): ReactElement {
56
56
  const [assistantOpen, setAssistantOpen] = useState(false);
57
+ const [commandInitialQuery, setCommandInitialQuery] = useState("");
57
58
  const [commandOpen, setCommandOpen] = useState(false);
58
59
  const [notificationsOpen, setNotificationsOpen] = useState(false);
59
60
  const [profileOpen, setProfileOpen] = useState(false);
@@ -64,6 +65,14 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
64
65
  const openCommandPalette = (event: KeyboardEvent) => {
65
66
  if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
66
67
  event.preventDefault();
68
+ setCommandInitialQuery("");
69
+ setCommandOpen(true);
70
+ return;
71
+ }
72
+
73
+ if (event.key === "/" && !isEditableShortcutTarget(event.target)) {
74
+ event.preventDefault();
75
+ setCommandInitialQuery("/");
67
76
  setCommandOpen(true);
68
77
  }
69
78
  };
@@ -92,16 +101,18 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
92
101
  <h1>{activeItem.label}</h1>
93
102
  </div>
94
103
  </div>
95
- <div className="header-actions">
104
+ <div className="header-center-controls">
96
105
  <div className="sidebar-history-controls workspace-history-controls">
97
106
  <button aria-label="Previous surface" disabled={!canGoBack} onClick={goBack} type="button"><FiChevronLeft /></button>
98
107
  <button aria-label="Next surface" disabled={!canGoForward} onClick={goForward} type="button"><FiChevronRight /></button>
99
108
  </div>
100
- <button className="workspace-search command-trigger" onClick={() => setCommandOpen(true)} type="button">
109
+ <button className="workspace-search command-trigger" onClick={() => { setCommandInitialQuery(""); setCommandOpen(true); }} type="button">
101
110
  <FiSearch aria-hidden="true" />
102
111
  <span>⌘K</span>
103
112
  <strong>{text.searchPlaceholder}</strong>
104
113
  </button>
114
+ </div>
115
+ <div className="header-actions">
105
116
  <div className="header-action-menu">
106
117
  <button aria-expanded={notificationsOpen} aria-label="Open notifications" className="header-icon-button" onClick={() => { setNotificationsOpen((current) => !current); setProfileOpen(false); }} type="button">
107
118
  <FiBell aria-hidden="true" />
@@ -120,11 +131,19 @@ function WorkspaceHeader({ activeItem, activeSectionLabel, activeSurface, canGoB
120
131
  </div>
121
132
  </div>
122
133
  {assistantOpen ? <AssistantPanel activeSurface={activeSurface} userName={userName} /> : null}
123
- {commandOpen ? <CommandPalette close={() => setCommandOpen(false)} openSurface={openSurface} /> : null}
134
+ {commandOpen ? <CommandPalette close={() => setCommandOpen(false)} initialQuery={commandInitialQuery} openSurface={openSurface} status={status} /> : null}
124
135
  </header>
125
136
  );
126
137
  }
127
138
 
139
+ function isEditableShortcutTarget(target: EventTarget | null): boolean {
140
+ if (!(target instanceof HTMLElement)) {
141
+ return false;
142
+ }
143
+
144
+ return target.isContentEditable || ["INPUT", "SELECT", "TEXTAREA"].includes(target.tagName);
145
+ }
146
+
128
147
  function NotificationsMenu({ openSurface }: { openSurface: (surface: SurfaceId) => void }): ReactElement {
129
148
  return (
130
149
  <div className="popover-menu notifications-menu" role="menu">
@@ -216,14 +235,79 @@ function AssistantPanel({ activeSurface, userName }: { activeSurface: SurfaceId;
216
235
  );
217
236
  }
218
237
 
219
- function CommandPalette({ close, openSurface }: { close: () => void; openSurface: (surface: SurfaceId) => void }): ReactElement {
220
- const [query, setQuery] = useState("");
238
+ interface CommandPaletteItem {
239
+ description: string;
240
+ icon: SurfaceNavItem["icon"];
241
+ id: string;
242
+ label: string;
243
+ searchText: string;
244
+ surface: SurfaceId;
245
+ tag: string;
246
+ }
247
+
248
+ const slashCommandItems: CommandPaletteItem[] = [
249
+ { id: "slash-add-device", label: "/add-device", description: "Start device pairing setup", icon: "device", searchText: "/add-device add device pair machine", surface: "devices", tag: "Command" },
250
+ { id: "slash-add-local-repo", label: "/add-local-repo", description: "Add a local repository path", icon: "folder", searchText: "/add-local-repo add local repo git", surface: "git", tag: "Command" },
251
+ { id: "slash-add-remote-repo", label: "/add-remote-repo", description: "Register a remote repository", icon: "git", searchText: "/add-remote-repo add remote repo", surface: "projects", tag: "Command" },
252
+ { id: "slash-add-secret", label: "/add-secret", description: "Add a protected secret reference", icon: "shield", searchText: "/add-secret add secret vault", surface: "security", tag: "Command" },
253
+ { id: "slash-add-ai-provider", label: "/add-ai-provider", description: "Connect an AI provider account", icon: "users", searchText: "/add-ai-provider add ai provider oauth", surface: "aiProviders", tag: "Command" },
254
+ ];
255
+
256
+ const plannedChannelItems: CommandPaletteItem[] = [
257
+ { id: "channel-devops", label: "#devops", description: "Chat channel for aidevops operations", icon: "message", searchText: "#devops devops channel chat", surface: "messagingAccounts", tag: "Channel" },
258
+ { id: "channel-comms", label: "#comms", description: "Chat channel for communications", icon: "message", searchText: "#comms comms channel chat", surface: "messagingAccounts", tag: "Channel" },
259
+ ];
260
+
261
+ function CommandPalette({ close, initialQuery, openSurface, status }: { close: () => void; initialQuery: string; openSurface: (surface: SurfaceId) => void; status: GuiStatusData }): ReactElement {
262
+ const [query, setQuery] = useState(initialQuery);
263
+ const inputRef = useRef<HTMLInputElement>(null);
221
264
  const matches = useMemo(() => {
222
265
  const normalizedQuery = query.trim().toLowerCase();
223
- return orderedNavItems
224
- .filter((item) => normalizedQuery.length === 0 || `${item.label} ${item.description}`.toLowerCase().includes(normalizedQuery))
266
+ const surfaceItems: CommandPaletteItem[] = orderedNavItems.map((item) => ({
267
+ description: item.description,
268
+ icon: item.icon,
269
+ id: `surface-${item.id}`,
270
+ label: item.label,
271
+ searchText: `${item.label} ${item.description}`,
272
+ surface: item.id,
273
+ tag: "Surface",
274
+ }));
275
+ const sessionItems: CommandPaletteItem[] = status.opencode_sessions.sessions.map((session) => ({
276
+ description: `AI session in ${session.repo_path_ref}`,
277
+ icon: "terminal",
278
+ id: `session-${session.id_ref}`,
279
+ label: `#${session.title}`,
280
+ searchText: `#${session.title} ${session.title} ${session.repo_path_ref} ${session.agent} ${session.model}`,
281
+ surface: "git",
282
+ tag: "AI session",
283
+ }));
284
+ const directMessageItems: CommandPaletteItem[] = [{
285
+ description: "Direct message thread placeholder",
286
+ icon: "users",
287
+ id: "dm-local-user",
288
+ label: `@${status.machine.username || "local-user"}`,
289
+ searchText: `@${status.machine.username || "local-user"} direct message dm person local user`,
290
+ surface: "messagingAccounts",
291
+ tag: "Direct message",
292
+ }];
293
+ const items = [...surfaceItems, ...sessionItems, ...plannedChannelItems, ...directMessageItems, ...slashCommandItems];
294
+ const prefix = normalizedQuery.charAt(0);
295
+ const scopedItems = prefix === "/"
296
+ ? slashCommandItems
297
+ : prefix === "@"
298
+ ? directMessageItems
299
+ : prefix === "#"
300
+ ? [...sessionItems, ...plannedChannelItems]
301
+ : items;
302
+
303
+ return scopedItems
304
+ .filter((item) => normalizedQuery.length === 0 || item.searchText.toLowerCase().includes(normalizedQuery))
225
305
  .slice(0, 8);
226
- }, [query]);
306
+ }, [query, status.machine.username, status.opencode_sessions.sessions]);
307
+
308
+ useEffect(() => {
309
+ inputRef.current?.focus();
310
+ }, []);
227
311
 
228
312
  useEffect(() => {
229
313
  const closeOnEscape = (event: KeyboardEvent) => {
@@ -242,14 +326,14 @@ function CommandPalette({ close, openSurface }: { close: () => void; openSurface
242
326
  <section aria-label="Command palette" className="command-palette">
243
327
  <label className="command-input-row">
244
328
  <FiSearch aria-hidden="true" />
245
- <input onChange={(event) => setQuery(event.currentTarget.value)} placeholder="Search commands and surfaces" value={query} />
329
+ <input ref={inputRef} onChange={(event) => setQuery(event.currentTarget.value)} placeholder="Search commands, #sessions, #channels, @people, or /actions" value={query} />
246
330
  </label>
247
331
  <ul>
248
332
  {matches.map((item) => (
249
333
  <li key={item.id}>
250
- <button onClick={() => openSurface(item.id)} type="button">
334
+ <button onClick={() => openSurface(item.surface)} type="button">
251
335
  <span className="surface-icon" aria-hidden="true"><SurfaceGlyph icon={item.icon} /></span>
252
- <span><strong>{item.label}</strong><small>{item.description}</small></span>
336
+ <span><strong>{item.label}</strong><small>{item.tag} · {item.description}</small></span>
253
337
  </button>
254
338
  </li>
255
339
  ))}
@@ -211,6 +211,7 @@ code {
211
211
  .theme-control-heading,
212
212
  .appearance-panel-tab,
213
213
  .header-title,
214
+ .header-center-controls,
214
215
  .header-actions,
215
216
  .workspace-search,
216
217
  .version-pill,
@@ -377,42 +378,65 @@ code {
377
378
  }
378
379
 
379
380
  .repo-session-selector {
380
- align-items: end;
381
+ display: grid;
382
+ gap: 7px;
383
+ }
384
+
385
+ .selector-with-stepper {
386
+ align-items: center;
381
387
  display: grid;
382
388
  gap: 8px;
383
389
  grid-template-columns: auto minmax(0, 1fr) auto;
384
390
  }
385
391
 
386
- .repo-session-selector button {
392
+ .repo-session-selector .selector-step-button,
393
+ .selector-step-button {
387
394
  align-items: center;
388
395
  background: var(--surface-muted);
389
396
  border: 1px solid var(--border);
390
397
  border-radius: 999px;
391
398
  color: var(--text-secondary);
392
399
  display: inline-flex;
393
- height: 34px;
400
+ height: 40px;
394
401
  justify-content: center;
395
- width: 34px;
396
402
  }
397
403
 
398
- .repo-session-selector label {
399
- display: grid;
400
- gap: 5px;
401
- min-width: 0;
404
+ .selector-step-button {
405
+ padding: 0;
406
+ width: 40px;
402
407
  }
403
408
 
404
- .repo-session-selector label span {
409
+ .selector-step-button:hover:not(:disabled),
410
+ .selector-step-button:focus-visible {
411
+ border-color: color-mix(in srgb, var(--accent) 40%, transparent);
412
+ color: var(--accent);
413
+ outline: none;
414
+ }
415
+
416
+ .selector-step-button svg {
417
+ height: 18px;
418
+ stroke-width: 2.5;
419
+ width: 18px;
420
+ }
421
+
422
+ .repo-selector-heading {
405
423
  color: var(--text-muted);
406
424
  font-size: 0.72rem;
407
425
  font-weight: 900;
426
+ text-align: center;
408
427
  text-transform: uppercase;
409
428
  }
410
429
 
430
+ .repo-selector-row {
431
+ align-items: center;
432
+ }
433
+
411
434
  .repo-session-selector select {
412
435
  background: var(--surface-muted);
413
436
  border: 1px solid var(--border);
414
437
  border-radius: 10px;
415
438
  color: var(--text-primary);
439
+ min-height: 40px;
416
440
  min-width: 0;
417
441
  padding: 8px;
418
442
  }
@@ -754,11 +778,16 @@ code {
754
778
  border: 1px solid var(--border);
755
779
  border-radius: 12px;
756
780
  color: var(--text-primary);
781
+ min-height: 40px;
757
782
  min-width: 0;
758
783
  padding: 9px 10px;
759
784
  width: 100%;
760
785
  }
761
786
 
787
+ .font-selector-row {
788
+ align-items: center;
789
+ }
790
+
762
791
  .app-inset {
763
792
  display: grid;
764
793
  grid-template-rows: auto minmax(0, 1fr);
@@ -770,9 +799,9 @@ code {
770
799
  background: color-mix(in srgb, var(--surface-elevated) 94%, transparent);
771
800
  border-bottom: 1px solid var(--border);
772
801
  display: flex;
773
- gap: 16px;
774
- min-height: 58px;
775
- padding: 10px 14px;
802
+ gap: 12px;
803
+ min-height: 54px;
804
+ padding: 8px 14px;
776
805
  position: sticky;
777
806
  top: 0;
778
807
  z-index: 2;
@@ -780,7 +809,7 @@ code {
780
809
 
781
810
  .header-title {
782
811
  flex: 0 0 auto;
783
- gap: 10px;
812
+ gap: 8px;
784
813
  min-width: 0;
785
814
  }
786
815
 
@@ -788,12 +817,14 @@ code {
788
817
  color: var(--text-muted);
789
818
  font-size: 0.76rem;
790
819
  font-weight: 900;
820
+ line-height: 1.15;
791
821
  margin: 0;
792
822
  text-transform: uppercase;
793
823
  }
794
824
 
795
825
  .header-title h1 {
796
826
  font-size: 1rem;
827
+ line-height: 1.15;
797
828
  margin: 0;
798
829
  }
799
830
 
@@ -809,15 +840,25 @@ code {
809
840
  align-items: center;
810
841
  color: var(--accent);
811
842
  display: inline-flex;
812
- height: 34px;
843
+ height: 32px;
813
844
  justify-content: center;
814
- width: 34px;
845
+ width: 32px;
815
846
  }
816
847
 
817
848
  .workspace-surface-icon svg {
818
- height: 24px;
849
+ height: 23px;
819
850
  stroke-width: 2.2;
820
- width: 24px;
851
+ width: 23px;
852
+ }
853
+
854
+ .header-center-controls {
855
+ gap: 10px;
856
+ left: 50%;
857
+ max-width: min(560px, 48vw);
858
+ min-width: 0;
859
+ position: absolute;
860
+ transform: translateX(-50%);
861
+ width: min(560px, 48vw);
821
862
  }
822
863
 
823
864
  .header-actions {
@@ -840,7 +881,7 @@ code {
840
881
 
841
882
  .command-trigger {
842
883
  justify-content: flex-start;
843
- max-width: 340px;
884
+ max-width: none;
844
885
  }
845
886
 
846
887
  .command-trigger strong {
@@ -1024,12 +1065,11 @@ code {
1024
1065
  }
1025
1066
 
1026
1067
  .command-palette-backdrop {
1027
- align-items: flex-start;
1068
+ align-items: center;
1028
1069
  background: rgb(0 0 0 / 28%);
1029
1070
  display: flex;
1030
1071
  inset: 0;
1031
1072
  justify-content: center;
1032
- padding-top: 12vh;
1033
1073
  position: fixed;
1034
1074
  z-index: 20;
1035
1075
  }
@@ -1052,6 +1092,7 @@ code {
1052
1092
  max-width: min(680px, calc(100vw - 32px));
1053
1093
  padding: 14px;
1054
1094
  position: relative;
1095
+ transform: translateY(-2vh);
1055
1096
  width: 100%;
1056
1097
  }
1057
1098
 
@@ -2071,12 +2112,20 @@ code {
2071
2112
  }
2072
2113
 
2073
2114
  .workspace-header,
2115
+ .header-center-controls,
2074
2116
  .header-actions,
2075
2117
  .split-heading {
2076
2118
  align-items: stretch;
2077
2119
  flex-direction: column;
2078
2120
  }
2079
2121
 
2122
+ .header-center-controls {
2123
+ max-width: none;
2124
+ position: static;
2125
+ transform: none;
2126
+ width: 100%;
2127
+ }
2128
+
2080
2129
  .workspace-search {
2081
2130
  min-width: 0;
2082
2131
  width: 100%;
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.25.0
15
+ # Version: 3.26.0
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)