aidevops 3.31.29 → 3.31.31

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.31.29-blue.svg)](https://github.com/marcusquinn/aidevops/releases)
61
+ [![Version](https://img.shields.io/badge/Version-3.31.31-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.31.29
1
+ 3.31.31
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.31.29
8
+ # Version: 3.31.31
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.31.29",
3
+ "version": "3.31.31",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -94,10 +94,19 @@ export function PulseWorkersSurface({ status }: { status: GuiStatusData }): Reac
94
94
  </div>
95
95
  <button disabled={!actionById(pulse.actions, "create_systemic_fix")?.enabled} onClick={() => void runPulseWorkerAction(actionById(pulse.actions, "create_systemic_fix"), setJobs, setSelectedJobId, setDismissedJobIds)} title="Create a worker-ready systemic-fix task from selected evidence" type="button">Create systemic fix task</button>
96
96
  </article>
97
+ </section>
97
98
 
98
- <section className="pulse-chart-grid" aria-label="Pulse trend charts">
99
+ <section className="planned-card pulse-trends-panel" aria-label="Pulse trend charts">
100
+ <div className="split-heading">
101
+ <div>
102
+ <p className="eyebrow">Operational trends</p>
103
+ <h3>Key indicators</h3>
104
+ </div>
105
+ <span className="count-pill">single-column chart stack</span>
106
+ </div>
107
+ <div className="pulse-chart-grid pulse-chart-stack">
99
108
  {chartSeries.map((chart) => <PulseChartPanel chart={chart} key={chart.id} />)}
100
- </section>
109
+ </div>
101
110
  </section>
102
111
 
103
112
  <section className="planned-card pulse-filter-panel" aria-label="Pulse filters">
@@ -220,19 +229,70 @@ function actionById(actions: GuiPulseWorkerActionSummary[], id: GuiPulseWorkerAc
220
229
 
221
230
  function PulseChartPanel({ chart }: { chart: GuiPulseWorkerChartSeries }): ReactElement {
222
231
  const maxValue = Math.max(...chart.points.map((point) => point.value), 1);
232
+ const values = chart.points.map((point) => point.value);
233
+ const latestValue = values.at(-1) ?? 0;
234
+ const firstValue = values[0] ?? latestValue;
235
+ const trend = latestValue - firstValue;
236
+ const svgPaths = chartSvgPaths(values);
223
237
 
224
238
  return (
225
- <article className="planned-card pulse-chart-panel">
226
- <p className="eyebrow">Trends · day/week/month/year</p>
227
- <h3>{chart.label}</h3>
239
+ <article className="pulse-chart-panel">
240
+ <p className="eyebrow">Trends · compact bars · day/week/month/year</p>
241
+ <div className="pulse-chart-heading">
242
+ <h4>{chart.label}</h4>
243
+ <div className="pulse-chart-meta">
244
+ <span><strong>{formatChartValue(latestValue, chart.unit)}</strong><small>Latest</small></span>
245
+ <span><strong>{trendSummary(trend, chart.unit)}</strong><small>Δ period</small></span>
246
+ </div>
247
+ </div>
228
248
  <div className="pulse-chart-placeholder" aria-label={`${chart.label} chart for day week month year buckets`} role="img">
229
- {chart.points.map((point) => <span key={`${chart.id}-${point.period}`} style={{ "--bar-height": `${Math.max(18, Math.round((point.value / maxValue) * 100))}%` } as CSSProperties}>{point.period}</span>)}
249
+ <svg aria-hidden="true" className="pulse-chart-sparkline" focusable="false" preserveAspectRatio="none" viewBox="0 0 100 48">
250
+ <path className="pulse-chart-area" d={svgPaths.area} />
251
+ <path className="pulse-chart-line" d={svgPaths.line} />
252
+ {svgPaths.points.map((point) => <circle className="pulse-chart-dot" cx={point.x} cy={point.y} key={`${chart.id}-dot-${point.x}`} r="1.6" />)}
253
+ </svg>
254
+ {chart.points.map((point) => <span data-value={formatChartValue(point.value, chart.unit)} key={`${chart.id}-${point.period}`} style={{ "--bar-height": `${Math.max(18, Math.round((point.value / maxValue) * 100))}%` } as CSSProperties} title={`${point.period_label}: ${formatChartValue(point.value, chart.unit)}`}>{point.period}</span>)}
230
255
  </div>
231
256
  <p>{chart.unit} · {chart.points.map((point) => `${point.period_label}: ${point.value}`).join(" · ")}</p>
232
257
  </article>
233
258
  );
234
259
  }
235
260
 
261
+ function chartSvgPaths(values: number[]): { line: string; area: string; points: Array<{ x: number; y: number }> } {
262
+ if (values.length === 0) {
263
+ return { line: "M0 24 L100 24", area: "M0 24 L100 24 L100 48 L0 48 Z", points: [] };
264
+ }
265
+
266
+ const max = Math.max(...values, 1);
267
+ const min = Math.min(...values, 0);
268
+ const height = 48;
269
+ const padding = 5;
270
+ const span = Math.max(max - min, 1);
271
+ const points = values.map((value, index) => {
272
+ const x = values.length <= 1 ? 50 : (index / (values.length - 1)) * 100;
273
+ const y = max === min ? height / 2 : padding + ((max - value) / span) * (height - padding * 2);
274
+ return { x: Math.round(x * 10) / 10, y: Math.round(y * 10) / 10 };
275
+ });
276
+ const line = points.map((point, index) => `${index === 0 ? "M" : "L"}${point.x} ${point.y}`).join(" ");
277
+ const first = points[0] ?? { x: 0, y: height / 2 };
278
+ const last = points.at(-1) ?? first;
279
+
280
+ return { line, area: `${line} L${last.x} ${height} L${first.x} ${height} Z`, points };
281
+ }
282
+
283
+ function formatChartValue(value: number, unit: GuiPulseWorkerChartSeries["unit"]): string {
284
+ if (unit === "percent") return `${value}%`;
285
+ if (unit === "cost_ref") return `$${value.toFixed(2)}`;
286
+ if (unit === "milliseconds") return `${Math.round(value)}ms`;
287
+ if (unit === "tokens") return `${new Intl.NumberFormat("en", { notation: "compact" }).format(value)} tokens`;
288
+ return new Intl.NumberFormat("en").format(value);
289
+ }
290
+
291
+ function trendSummary(delta: number, unit: GuiPulseWorkerChartSeries["unit"]): string {
292
+ const sign = delta > 0 ? "+" : "";
293
+ return `${sign}${formatChartValue(delta, unit)}`;
294
+ }
295
+
236
296
  function PulseDrilldownPanel({ event }: { event: PulseEvent | undefined }): ReactElement {
237
297
  if (event === undefined) {
238
298
  return (
@@ -72,6 +72,7 @@ export const recommendedApps = ([
72
72
  { name: "Nextcloud Talk", description: "Open source video calls, chat, and collaboration for Nextcloud.", websiteUrl: "https://nextcloud.com/talk/", repoUrl: "https://github.com/nextcloud/spreed", iosUrl: "https://apps.apple.com/us/app/nextcloud-talk/id1296825574", androidUrl: "https://play.google.com/store/apps/details?id=com.nextcloud.talk2&hl=en", os: ["ios", "android"], platforms: ["webapp"] },
73
73
  { name: "OBS Studio", description: "Open source video recording and live streaming.", websiteUrl: "https://obsproject.com/", alternativeToUrl: "https://alternativeto.net/software/open-broadcaster-software/", repoUrl: "https://github.com/obsproject/obs-studio", os: ["macos", "linux", "windows"], platforms: [] },
74
74
  { name: "ONLYOFFICE", description: "Office and document collaboration.", websiteUrl: "https://www.onlyoffice.com/", alternativeToUrl: "https://alternativeto.net/software/onlyoffice/", repoUrl: "https://github.com/ONLYOFFICE/", iosUrl: "https://apps.apple.com/us/app/onlyoffice-documents/id944896972", androidUrl: "https://play.google.com/store/apps/details?id=com.onlyoffice.documents", os: ["macos", "linux", "windows", "ios", "android"], platforms: ["webapp", "saas", "api"] },
75
+ { name: "OpenPanel", description: "Open source web and product analytics with privacy-first dashboards, funnels, retention, session replay, revenue, and MCP access.", websiteUrl: "https://openpanel.dev/", repoUrl: "https://github.com/Openpanel-dev/openpanel", os: ["linux"], platforms: ["webapp", "saas", "api"] },
75
76
  { name: "OpenScreen", description: "Open screen-sharing project.", websiteUrl: "https://github.com/getopenscreen/openscreen/releases", alternativeToUrl: "https://alternativeto.net/software/openscreen/", repoUrl: "https://github.com/getopenscreen/openscreen", os: ["macos", "linux", "windows"], platforms: [] },
76
77
  { name: "Osaurus", description: "AI app workspace.", websiteUrl: "https://osaurus.ai/", alternativeToUrl: "https://alternativeto.net/software/osaurus/", os: ["macos"], platforms: [] },
77
78
  { name: "Parallels Desktop", description: "Run Windows, Linux, and virtual machines on macOS.", websiteUrl: "https://www.parallels.com/products/desktop/", alternativeToUrl: "https://alternativeto.net/software/parallels-desktop/about/", os: ["macos"], platforms: [] },
@@ -2428,7 +2428,7 @@ code {
2428
2428
  }
2429
2429
 
2430
2430
  .pulse-overview-layout {
2431
- grid-template-columns: minmax(280px, 0.8fr) minmax(320px, 1.2fr);
2431
+ grid-template-columns: minmax(0, 1fr);
2432
2432
  }
2433
2433
 
2434
2434
  .pulse-kpi-card {
@@ -2450,16 +2450,66 @@ code {
2450
2450
  line-height: 1;
2451
2451
  }
2452
2452
 
2453
+ .pulse-chart-heading {
2454
+ align-items: start;
2455
+ display: grid;
2456
+ gap: 10px;
2457
+ grid-template-columns: minmax(0, 1fr) minmax(180px, 0.7fr);
2458
+ }
2459
+
2460
+ .pulse-chart-heading h4 {
2461
+ font-size: 1rem;
2462
+ margin: 0;
2463
+ }
2464
+
2465
+ .pulse-chart-meta {
2466
+ display: grid;
2467
+ gap: 6px;
2468
+ grid-template-columns: repeat(2, minmax(0, 1fr));
2469
+ }
2470
+
2471
+ .pulse-chart-meta span {
2472
+ background: color-mix(in srgb, var(--accent) 9%, var(--surface-muted));
2473
+ border: 1px solid var(--border);
2474
+ border-radius: 10px;
2475
+ display: grid;
2476
+ gap: 2px;
2477
+ padding: 8px;
2478
+ }
2479
+
2480
+ .pulse-chart-meta strong {
2481
+ color: var(--accent);
2482
+ font-size: 0.98rem;
2483
+ letter-spacing: -0.04em;
2484
+ line-height: 1;
2485
+ }
2486
+
2487
+ .pulse-chart-meta small {
2488
+ color: var(--text-muted);
2489
+ font-size: 0.72rem;
2490
+ font-weight: 800;
2491
+ letter-spacing: 0.08em;
2492
+ text-transform: uppercase;
2493
+ }
2494
+
2453
2495
  .pulse-attention-panel,
2454
2496
  .pulse-chart-panel,
2455
2497
  .pulse-activity-panel,
2456
2498
  .pulse-drilldown-panel,
2457
2499
  .pulse-actions-panel,
2458
- .pulse-filter-panel {
2500
+ .pulse-filter-panel,
2501
+ .pulse-trends-panel {
2459
2502
  display: grid;
2460
2503
  gap: 12px;
2461
2504
  }
2462
2505
 
2506
+ .pulse-chart-panel {
2507
+ background: var(--surface-muted);
2508
+ border: 1px solid var(--border);
2509
+ border-radius: 16px;
2510
+ padding: 14px;
2511
+ }
2512
+
2463
2513
  .pulse-attention-critical,
2464
2514
  .pulse-status-blocked strong,
2465
2515
  .pulse-status-failed strong {
@@ -2480,19 +2530,24 @@ code {
2480
2530
 
2481
2531
  .pulse-chart-placeholder {
2482
2532
  align-items: end;
2483
- background: var(--surface-muted);
2533
+ background:
2534
+ linear-gradient(180deg, transparent 24%, color-mix(in srgb, var(--border) 42%, transparent) 24% 25%, transparent 25% 49%, color-mix(in srgb, var(--border) 34%, transparent) 49% 50%, transparent 50% 74%, color-mix(in srgb, var(--border) 28%, transparent) 74% 75%, transparent 75%),
2535
+ var(--surface-elevated);
2484
2536
  border: 1px solid var(--border);
2485
- border-radius: 16px;
2537
+ border-radius: 12px;
2486
2538
  display: grid;
2487
- gap: 8px;
2488
- grid-template-columns: repeat(6, minmax(0, 1fr));
2489
- min-height: 180px;
2490
- padding: 14px;
2539
+ gap: 10px;
2540
+ grid-template-columns: repeat(4, minmax(0, 1fr));
2541
+ min-height: 148px;
2542
+ overflow: hidden;
2543
+ padding: 28px 14px 12px;
2544
+ position: relative;
2491
2545
  }
2492
2546
 
2493
2547
  .pulse-chart-placeholder span {
2494
2548
  align-items: end;
2495
- background: color-mix(in srgb, var(--accent) 26%, transparent);
2549
+ background:
2550
+ linear-gradient(180deg, color-mix(in srgb, var(--accent) 52%, transparent), color-mix(in srgb, var(--accent) 18%, transparent));
2496
2551
  border: 1px solid var(--border-accent);
2497
2552
  border-radius: 12px 12px 4px 4px;
2498
2553
  color: var(--accent-strong);
@@ -2500,9 +2555,40 @@ code {
2500
2555
  font-size: 0.75rem;
2501
2556
  font-weight: 900;
2502
2557
  justify-content: center;
2503
- min-height: max(40px, var(--bar-height, calc(40px + var(--bar-index, 1) * 18px)));
2558
+ min-height: max(32px, var(--bar-height, calc(40px + var(--bar-index, 1) * 18px)));
2504
2559
  padding: 8px 4px;
2560
+ position: relative;
2505
2561
  text-transform: uppercase;
2562
+ z-index: 1;
2563
+ }
2564
+
2565
+ .pulse-chart-sparkline {
2566
+ height: 44%;
2567
+ inset: 16px 14px auto;
2568
+ opacity: 0.72;
2569
+ pointer-events: none;
2570
+ position: absolute;
2571
+ width: calc(100% - 28px);
2572
+ z-index: 2;
2573
+ }
2574
+
2575
+ .pulse-chart-area {
2576
+ fill: color-mix(in srgb, var(--accent) 18%, transparent);
2577
+ stroke: none;
2578
+ }
2579
+
2580
+ .pulse-chart-line {
2581
+ fill: none;
2582
+ stroke: var(--accent);
2583
+ stroke-linecap: round;
2584
+ stroke-linejoin: round;
2585
+ stroke-width: 2.6;
2586
+ }
2587
+
2588
+ .pulse-chart-dot {
2589
+ fill: var(--surface-elevated);
2590
+ stroke: var(--accent);
2591
+ stroke-width: 1.4;
2506
2592
  }
2507
2593
 
2508
2594
  .pulse-chart-grid,
@@ -2512,6 +2598,10 @@ code {
2512
2598
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
2513
2599
  }
2514
2600
 
2601
+ .pulse-chart-stack {
2602
+ grid-template-columns: minmax(0, 1fr);
2603
+ }
2604
+
2515
2605
  .pulse-filter-group {
2516
2606
  border: 1px solid var(--border);
2517
2607
  border-radius: 14px;
@@ -4108,6 +4198,15 @@ button.managed-toggle:focus-visible {
4108
4198
  grid-template-columns: 1fr;
4109
4199
  }
4110
4200
 
4201
+ .pulse-chart-heading,
4202
+ .pulse-chart-meta {
4203
+ grid-template-columns: 1fr;
4204
+ }
4205
+
4206
+ .pulse-chart-placeholder {
4207
+ min-height: 128px;
4208
+ }
4209
+
4111
4210
  .pulse-activity-table {
4112
4211
  display: none;
4113
4212
  }
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.31.29
15
+ # Version: 3.31.31
16
16
  #
17
17
  # Quick Install:
18
18
  # npm install -g aidevops && aidevops update (recommended)