pyre-cli 9.5.1 → 10.0.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.
Files changed (94) hide show
  1. package/README.md +84 -14
  2. package/dist/alertChannels-TPSQIJ7S.js +8 -0
  3. package/dist/blender-FGH3ASEQ.js +7 -0
  4. package/dist/brewHealth-P2GW5Y5C.js +176 -0
  5. package/dist/buildCmd-CDPZ2ZFA.js +38 -0
  6. package/dist/{checkCmd-EWFNLBWE.js → checkCmd-RKW6QGDR.js} +10 -11
  7. package/dist/{chunk-RVGKEJXT.js → chunk-53O2RGSL.js} +47 -17
  8. package/dist/{chunk-MOAEGMFP.js → chunk-6UHZP4QP.js} +204 -46
  9. package/dist/chunk-GHRDW646.js +2506 -0
  10. package/dist/{prometheus-LFYHF6VO.js → chunk-HT4VQ4OH.js} +1 -0
  11. package/dist/chunk-M73HCQD2.js +95 -0
  12. package/dist/chunk-MZO5QKUR.js +32 -0
  13. package/dist/chunk-VIACN2RU.js +133 -0
  14. package/dist/chunk-Z2YV5HKV.js +87 -0
  15. package/dist/chunk-ZLVIVFSO.js +110 -0
  16. package/dist/{collectors-WD6VKH6M.js → collectors-H43PN4PS.js} +8 -2
  17. package/dist/{completions-VQMHQCXE.js → completions-JKYEJNGR.js} +34 -1
  18. package/dist/{config-4ZLF4HTS.js → config-GYGJXU2Q.js} +9 -1
  19. package/dist/doctor-GYHFTNZQ.js +387 -0
  20. package/dist/explainCmd-MF6PIMLW.js +222 -0
  21. package/dist/extensions-XJGHG7X3.js +134 -0
  22. package/dist/index.js +841 -206
  23. package/dist/ioreport.py +72 -0
  24. package/dist/netUsageCmd-WKBTW2YG.js +90 -0
  25. package/dist/{pipeCmd-7THXEGDH.js → pipeCmd-7REDBLBU.js} +5 -3
  26. package/dist/profileProcCmd-N4H6MCBT.js +107 -0
  27. package/dist/prometheus-5BL5PVLC.js +6 -0
  28. package/dist/prometheusCmd-PL64BLN5.js +93 -0
  29. package/dist/replayCmd-Z3PYXY7V.js +164 -0
  30. package/dist/run-HOGQDDTA.js +8 -0
  31. package/dist/serveCmd-2H5VLF3P.js +153 -0
  32. package/dist/service-Q74BBILO.js +181 -0
  33. package/dist/smartCmd-ODWKSFNG.js +166 -0
  34. package/dist/syncCmd-A2W3QB3S.js +126 -0
  35. package/dist/topoCmd-DU2HHZNH.js +148 -0
  36. package/dist/watchdogCmd-UF53JPZB.js +161 -0
  37. package/dist/{wizard-GVIAHIVV.js → wizard-TD7BGX6O.js} +1 -1
  38. package/install.ps1 +29 -0
  39. package/install.sh +10 -9
  40. package/package.json +7 -3
  41. package/src/ai/models.ts +138 -0
  42. package/src/alertChannels.ts +119 -0
  43. package/src/anomalies.ts +114 -60
  44. package/src/brewHealth.ts +157 -40
  45. package/src/buildCmd.ts +48 -0
  46. package/src/checkCmd.ts +5 -5
  47. package/src/completions.ts +35 -0
  48. package/src/doctor.ts +235 -7
  49. package/src/easy_features.test.ts +40 -0
  50. package/src/explainCmd.ts +261 -0
  51. package/src/extensions.ts +71 -7
  52. package/src/formatters/index.ts +1 -1
  53. package/src/formatters/output.ts +100 -13
  54. package/src/formatters/render.ts +274 -38
  55. package/src/formatters/themes.ts +8 -0
  56. package/src/formatters/types.ts +6 -1
  57. package/src/index.ts +298 -56
  58. package/src/live/input.ts +122 -4
  59. package/src/live/render.ts +98 -0
  60. package/src/live/state.ts +70 -52
  61. package/src/live/types.ts +2 -2
  62. package/src/monitors/batteryPredictor.ts +143 -0
  63. package/src/monitors/blender.ts +144 -0
  64. package/src/monitors/buildTracker.ts +121 -0
  65. package/src/monitors/collectors.ts +558 -147
  66. package/src/monitors/dns_cache.ts +48 -0
  67. package/src/monitors/index.ts +3 -2
  68. package/src/monitors/ioreport.py +72 -0
  69. package/src/monitors/platform/linux.ts +756 -0
  70. package/src/monitors/platform/windows.ts +519 -0
  71. package/src/monitors/run.ts +27 -3
  72. package/src/monitors/smc.ts +21 -1
  73. package/src/monitors/types.ts +112 -14
  74. package/src/netUsageCmd.ts +121 -0
  75. package/src/platform.test.ts +99 -0
  76. package/src/plugins/index.ts +143 -0
  77. package/src/profileProcCmd.ts +133 -0
  78. package/src/prometheusCmd.ts +107 -0
  79. package/src/replayCmd.ts +186 -0
  80. package/src/serveCmd.ts +180 -0
  81. package/src/service.ts +125 -7
  82. package/src/smartCmd.ts +205 -0
  83. package/src/state/config.ts +64 -17
  84. package/src/syncCmd.ts +166 -0
  85. package/src/topoCmd.ts +176 -0
  86. package/src/watchdogCmd.ts +198 -0
  87. package/src/xbar.ts +22 -11
  88. package/dist/brewHealth-F2BXF5M6.js +0 -74
  89. package/dist/chunk-FH5WDJBF.js +0 -15
  90. package/dist/chunk-HGRBBPYX.js +0 -937
  91. package/dist/doctor-L6FZYA7F.js +0 -184
  92. package/dist/extensions-DVA5YLGH.js +0 -74
  93. package/dist/service-WTS3NBQU.js +0 -73
  94. /package/dist/{chunk-SG26Y3WF.js → chunk-IM3IQMMT.js} +0 -0
package/src/anomalies.ts CHANGED
@@ -18,84 +18,138 @@ export interface AnomalyAlert {
18
18
 
19
19
  const ZSCORE_THRESHOLD = 2.5;
20
20
  const CRITICAL_ZSCORE = 3.5;
21
- const MIN_SAMPLES = 5;
21
+ const MIN_SAMPLES = 10;
22
+
23
+ // Minimum absolute deviation from the mean required to trigger an anomaly
24
+ const MIN_DEV_CPU = 10; // 10%
25
+ const MIN_DEV_MEM = 5; // 5%
26
+ const MIN_DEV_TEMP = 5; // 5 degrees
27
+ const MIN_DEV_NET = 500_000; // 500 KB
28
+ const MIN_DEV_POWER = 5; // 5 Watts
29
+ const MIN_DEV_GPU = 10; // 10%
22
30
 
23
31
  export function detectAnomalies(data: StatsData, history: History): AnomalyAlert[] {
24
32
  const alerts: AnomalyAlert[] = [];
25
33
 
26
- const cpuZ = History.zScore(data.cpu.usage, history.cpuUsage);
27
- if (Math.abs(cpuZ) > ZSCORE_THRESHOLD && history.cpuUsage.length >= MIN_SAMPLES) {
28
- alerts.push({
29
- metric: 'CPU',
30
- value: data.cpu.usage,
31
- zScore: cpuZ,
32
- severity: Math.abs(cpuZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
33
- });
34
+ if (history.cpuUsage.length >= MIN_SAMPLES) {
35
+ const cpuMean = History.mean(history.cpuUsage);
36
+ if (Math.abs(data.cpu.usage - cpuMean) > MIN_DEV_CPU) {
37
+ const cpuZ = History.zScore(data.cpu.usage, history.cpuUsage);
38
+ if (Math.abs(cpuZ) > ZSCORE_THRESHOLD) {
39
+ alerts.push({
40
+ metric: 'CPU',
41
+ value: data.cpu.usage,
42
+ zScore: cpuZ,
43
+ severity: Math.abs(cpuZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
44
+ });
45
+ }
46
+ }
47
+ }
48
+
49
+ if (history.memUsage.length >= MIN_SAMPLES) {
50
+ const memMean = History.mean(history.memUsage);
51
+ if (Math.abs(data.memory.usagePercent - memMean) > MIN_DEV_MEM) {
52
+ const memZ = History.zScore(data.memory.usagePercent, history.memUsage);
53
+ if (Math.abs(memZ) > ZSCORE_THRESHOLD) {
54
+ alerts.push({
55
+ metric: 'Memory',
56
+ value: data.memory.usagePercent,
57
+ zScore: memZ,
58
+ severity: Math.abs(memZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
59
+ });
60
+ }
61
+ }
34
62
  }
35
63
 
36
- const memZ = History.zScore(data.memory.usagePercent, history.memUsage);
37
- if (Math.abs(memZ) > ZSCORE_THRESHOLD && history.memUsage.length >= MIN_SAMPLES) {
38
- alerts.push({
39
- metric: 'Memory',
40
- value: data.memory.usagePercent,
41
- zScore: memZ,
42
- severity: Math.abs(memZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
43
- });
64
+ if (data.cpu.temperature !== undefined && history.temp.length >= MIN_SAMPLES) {
65
+ const tempMean = History.mean(history.temp);
66
+ if (Math.abs(data.cpu.temperature - tempMean) > MIN_DEV_TEMP) {
67
+ const tempZ = History.zScore(data.cpu.temperature, history.temp);
68
+ if (Math.abs(tempZ) > ZSCORE_THRESHOLD) {
69
+ alerts.push({
70
+ metric: 'Temp',
71
+ value: data.cpu.temperature,
72
+ zScore: tempZ,
73
+ severity: Math.abs(tempZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
74
+ });
75
+ }
76
+ }
44
77
  }
45
78
 
46
- if (data.cpu.temperature !== undefined) {
47
- const tempZ = History.zScore(data.cpu.temperature, history.temp);
48
- if (Math.abs(tempZ) > ZSCORE_THRESHOLD && history.temp.length >= MIN_SAMPLES) {
49
- alerts.push({
50
- metric: 'Temp',
51
- value: data.cpu.temperature,
52
- zScore: tempZ,
53
- severity: Math.abs(tempZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
54
- });
79
+ if (history.netRxRate.length >= MIN_SAMPLES) {
80
+ const currentRxRate = history.netRxRate[history.netRxRate.length - 1];
81
+ const netRxMean = History.mean(history.netRxRate);
82
+ if (Math.abs(currentRxRate - netRxMean) > MIN_DEV_NET) {
83
+ const netRxZ = History.zScore(currentRxRate, history.netRxRate);
84
+ if (Math.abs(netRxZ) > ZSCORE_THRESHOLD) {
85
+ alerts.push({
86
+ metric: 'Net RX',
87
+ value: currentRxRate,
88
+ zScore: netRxZ,
89
+ severity: Math.abs(netRxZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
90
+ });
91
+ }
55
92
  }
56
93
  }
57
94
 
58
- const netRxZ = History.zScore(data.network.rxBytes, history.netRxRate);
59
- if (Math.abs(netRxZ) > ZSCORE_THRESHOLD && history.netRxRate.length >= MIN_SAMPLES) {
60
- alerts.push({
61
- metric: 'Net RX',
62
- value: data.network.rxBytes,
63
- zScore: netRxZ,
64
- severity: Math.abs(netRxZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
65
- });
95
+ if (history.netTxRate.length >= MIN_SAMPLES) {
96
+ const currentTxRate = history.netTxRate[history.netTxRate.length - 1];
97
+ const netTxMean = History.mean(history.netTxRate);
98
+ if (Math.abs(currentTxRate - netTxMean) > MIN_DEV_NET) {
99
+ const netTxZ = History.zScore(currentTxRate, history.netTxRate);
100
+ if (Math.abs(netTxZ) > ZSCORE_THRESHOLD) {
101
+ alerts.push({
102
+ metric: 'Net TX',
103
+ value: currentTxRate,
104
+ zScore: netTxZ,
105
+ severity: Math.abs(netTxZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
106
+ });
107
+ }
108
+ }
66
109
  }
67
110
 
68
- const netTxZ = History.zScore(data.network.txBytes, history.netTxRate);
69
- if (Math.abs(netTxZ) > ZSCORE_THRESHOLD && history.netTxRate.length >= MIN_SAMPLES) {
70
- alerts.push({
71
- metric: 'Net TX',
72
- value: data.network.txBytes,
73
- zScore: netTxZ,
74
- severity: Math.abs(netTxZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
75
- });
111
+ if (data.power?.combinedWatts !== undefined && history.powerWatts.length >= MIN_SAMPLES) {
112
+ const powerMean = History.mean(history.powerWatts);
113
+ if (Math.abs(data.power.combinedWatts - powerMean) > MIN_DEV_POWER) {
114
+ const powerZ = History.zScore(data.power.combinedWatts, history.powerWatts);
115
+ if (Math.abs(powerZ) > ZSCORE_THRESHOLD) {
116
+ alerts.push({
117
+ metric: 'Power',
118
+ value: data.power.combinedWatts,
119
+ zScore: powerZ,
120
+ severity: Math.abs(powerZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
121
+ });
122
+ }
123
+ }
76
124
  }
77
125
 
78
- if (data.power?.combinedWatts !== undefined) {
79
- const powerZ = History.zScore(data.power.combinedWatts, history.powerWatts);
80
- if (Math.abs(powerZ) > ZSCORE_THRESHOLD && history.powerWatts.length >= MIN_SAMPLES) {
81
- alerts.push({
82
- metric: 'Power',
83
- value: data.power.combinedWatts,
84
- zScore: powerZ,
85
- severity: Math.abs(powerZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
86
- });
126
+ if (history.connections.length >= MIN_SAMPLES) {
127
+ const connMean = History.mean(history.connections);
128
+ if (Math.abs((data.packets?.connections || data.network.connections || 0) - connMean) > 5) {
129
+ const connZ = History.zScore(data.packets?.connections || data.network.connections || 0, history.connections);
130
+ if (Math.abs(connZ) > ZSCORE_THRESHOLD) {
131
+ alerts.push({
132
+ metric: 'Connections',
133
+ value: data.packets?.connections || data.network.connections || 0,
134
+ zScore: connZ,
135
+ severity: Math.abs(connZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
136
+ });
137
+ }
87
138
  }
88
139
  }
89
140
 
90
- if (data.gpu) {
91
- const gpuZ = History.zScore(data.gpu.utilization, history.gpuUtil);
92
- if (Math.abs(gpuZ) > ZSCORE_THRESHOLD && history.gpuUtil.length >= MIN_SAMPLES) {
93
- alerts.push({
94
- metric: 'GPU',
95
- value: data.gpu.utilization,
96
- zScore: gpuZ,
97
- severity: Math.abs(gpuZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
98
- });
141
+ if (data.gpu && history.gpuUtil.length >= MIN_SAMPLES) {
142
+ const gpuMean = History.mean(history.gpuUtil);
143
+ if (Math.abs(data.gpu.utilization - gpuMean) > MIN_DEV_GPU) {
144
+ const gpuZ = History.zScore(data.gpu.utilization, history.gpuUtil);
145
+ if (Math.abs(gpuZ) > ZSCORE_THRESHOLD) {
146
+ alerts.push({
147
+ metric: 'GPU',
148
+ value: data.gpu.utilization,
149
+ zScore: gpuZ,
150
+ severity: Math.abs(gpuZ) > CRITICAL_ZSCORE ? 'critical' : 'warning',
151
+ });
152
+ }
99
153
  }
100
154
  }
101
155
 
package/src/brewHealth.ts CHANGED
@@ -7,76 +7,193 @@ export interface BrewHealthInfo {
7
7
  cellarSize: string;
8
8
  doctorWarnings: string[];
9
9
  brewAvailable: boolean;
10
+ managerName: string;
10
11
  }
11
12
 
12
13
  export async function getBrewHealth(): Promise<BrewHealthInfo> {
14
+ const isWindows = process.platform === 'win32';
15
+
16
+ // 1. Try Homebrew (macOS or Linuxbrew)
13
17
  try {
14
- const brewPath = await run('which brew 2>&1', '');
15
- if (!brewPath.trim() || brewPath.includes('not found')) {
16
- return { installedCount: 0, outdatedCount: 0, cellarSize: 'N/A', doctorWarnings: [], brewAvailable: false };
18
+ const brewPath = await run(isWindows ? 'where brew 2>nul' : 'which brew 2>&1', '');
19
+ if (brewPath.trim() && !brewPath.includes('not found') && !brewPath.includes('Could not find')) {
20
+ const outdatedRaw = await run('brew outdated --quiet 2>&1', '');
21
+ const outdatedLines = outdatedRaw.trim().split('\n').filter(l => l.length > 0 && !l.startsWith('Homebrew') && !l.startsWith('==>'));
22
+
23
+ const listRaw = await run('brew list --formula -1 2>&1', '');
24
+ const listLines = listRaw.trim().split('\n').filter(l => l.length > 0);
25
+
26
+ let cellarSize = 'N/A';
27
+ try {
28
+ const cellarPath = await run('brew --cellar 2>&1', '');
29
+ if (cellarPath.trim()) {
30
+ const duRaw = await run(`du -sh "${cellarPath.trim()}" 2>&1`, '');
31
+ const duMatch = duRaw.match(/^([0-9.,]+[KMGTP]?i?B?)/i);
32
+ if (duMatch) cellarSize = duMatch[1];
33
+ }
34
+ } catch {
35
+ cellarSize = 'N/A';
36
+ }
37
+
38
+ const doctorRaw = await run('brew doctor 2>&1', '');
39
+ const doctorWarnings: string[] = [];
40
+ const docLines = doctorRaw.split('\n');
41
+ for (const line of docLines) {
42
+ if (line.startsWith('Warning:')) {
43
+ doctorWarnings.push(line.replace(/^Warning:\s*/, '').trim());
44
+ }
45
+ }
46
+
47
+ return {
48
+ installedCount: listLines.length,
49
+ outdatedCount: outdatedLines.length,
50
+ cellarSize,
51
+ doctorWarnings,
52
+ brewAvailable: true,
53
+ managerName: 'Homebrew',
54
+ };
17
55
  }
56
+ } catch {
57
+ // continue to alternative managers
58
+ }
18
59
 
19
- // Outdated count without triggering brew update
20
- const outdatedRaw = await run('brew outdated --quiet 2>&1', '');
21
- const outdatedLines = outdatedRaw.trim().split('\n').filter(l => l.length > 0 && !l.startsWith('Homebrew') && !l.startsWith('==>'));
60
+ // 2. Linux Package Managers (APT, DNF, Pacman)
61
+ if (process.platform === 'linux') {
62
+ // Try APT
63
+ try {
64
+ const whichApt = (await run('which apt-get 2>/dev/null', '')).trim();
65
+ if (whichApt) {
66
+ const listRaw = (await run('dpkg-query -l 2>/dev/null', '')).trim();
67
+ const installed = listRaw ? listRaw.split('\n').filter(l => l.startsWith('ii')).length : 0;
68
+ const upgradeRaw = (await run('apt list --upgradable 2>/dev/null', '')).trim();
69
+ const outdated = upgradeRaw ? upgradeRaw.split('\n').filter(l => l.includes('/') && !l.startsWith('Listing')).length : 0;
70
+ return {
71
+ installedCount: installed,
72
+ outdatedCount: outdated,
73
+ cellarSize: '/var/cache/apt',
74
+ doctorWarnings: [],
75
+ brewAvailable: true,
76
+ managerName: 'APT (Debian/Ubuntu)',
77
+ };
78
+ }
79
+ } catch {
80
+ // ignore
81
+ }
22
82
 
23
- // Installed list count
24
- const listRaw = await run('brew list --formula -1 2>&1', '');
25
- const listLines = listRaw.trim().split('\n').filter(l => l.length > 0);
83
+ // Try Pacman
84
+ try {
85
+ const whichPacman = (await run('which pacman 2>/dev/null', '')).trim();
86
+ if (whichPacman) {
87
+ const listRaw = (await run('pacman -Q 2>/dev/null', '')).trim();
88
+ const installed = listRaw ? listRaw.split('\n').filter(Boolean).length : 0;
89
+ const upgradeRaw = (await run('checkupdates 2>/dev/null || pacman -Qu 2>/dev/null', '')).trim();
90
+ const outdated = upgradeRaw ? upgradeRaw.split('\n').filter(Boolean).length : 0;
91
+ return {
92
+ installedCount: installed,
93
+ outdatedCount: outdated,
94
+ cellarSize: '/var/cache/pacman',
95
+ doctorWarnings: [],
96
+ brewAvailable: true,
97
+ managerName: 'Pacman (Arch)',
98
+ };
99
+ }
100
+ } catch {
101
+ // ignore
102
+ }
26
103
 
27
- // Disk usage of cellar
28
- let cellarSize = 'N/A';
104
+ // Try DNF
29
105
  try {
30
- const cellarPath = await run('brew --cellar 2>&1', '');
31
- if (cellarPath.trim()) {
32
- const duRaw = await run(`du -sh "${cellarPath.trim()}" 2>&1`, '');
33
- const duMatch = duRaw.match(/^([0-9\.\,]+[KMGTP]?i?B?)/i);
34
- if (duMatch) cellarSize = duMatch[1];
106
+ const whichDnf = (await run('which dnf 2>/dev/null', '')).trim();
107
+ if (whichDnf) {
108
+ const listRaw = (await run('dnf list installed 2>/dev/null', '')).trim();
109
+ const installed = listRaw ? listRaw.split('\n').filter(Boolean).length - 1 : 0;
110
+ return {
111
+ installedCount: Math.max(0, installed),
112
+ outdatedCount: 0,
113
+ cellarSize: '/var/cache/dnf',
114
+ doctorWarnings: [],
115
+ brewAvailable: true,
116
+ managerName: 'DNF (Fedora/RHEL)',
117
+ };
35
118
  }
36
119
  } catch {
37
- cellarSize = 'N/A';
120
+ // ignore
38
121
  }
122
+ }
39
123
 
40
- // Doctor warnings
41
- const doctorRaw = await run('brew doctor 2>&1', '');
42
- const doctorWarnings: string[] = [];
43
- const docLines = doctorRaw.split('\n');
44
- for (const line of docLines) {
45
- if (line.startsWith('Warning:')) {
46
- doctorWarnings.push(line.replace(/^Warning:\s*/, '').trim());
124
+ // 3. Windows Package Managers (winget, choco, scoop)
125
+ if (isWindows) {
126
+ // Try winget
127
+ try {
128
+ const whichWinget = (await run('where winget 2>nul', '')).trim();
129
+ if (whichWinget) {
130
+ const listRaw = (await run('winget list 2>nul', '', 4000)).trim();
131
+ const installed = listRaw ? listRaw.split('\n').filter(Boolean).length - 2 : 0;
132
+ return {
133
+ installedCount: Math.max(0, installed),
134
+ outdatedCount: 0,
135
+ cellarSize: 'N/A',
136
+ doctorWarnings: [],
137
+ brewAvailable: true,
138
+ managerName: 'Windows Package Manager (winget)',
139
+ };
47
140
  }
141
+ } catch {
142
+ // ignore
48
143
  }
49
144
 
50
- return {
51
- installedCount: listLines.length,
52
- outdatedCount: outdatedLines.length,
53
- cellarSize,
54
- doctorWarnings,
55
- brewAvailable: true,
56
- };
57
- } catch (err) {
58
- return { installedCount: 0, outdatedCount: 0, cellarSize: 'N/A', doctorWarnings: [], brewAvailable: false };
145
+ // Try scoop
146
+ try {
147
+ const whichScoop = (await run('where scoop 2>nul', '')).trim();
148
+ if (whichScoop) {
149
+ const listRaw = (await run('scoop list 2>nul', '')).trim();
150
+ const installed = listRaw ? listRaw.split('\n').filter(Boolean).length - 4 : 0;
151
+ return {
152
+ installedCount: Math.max(0, installed),
153
+ outdatedCount: 0,
154
+ cellarSize: 'N/A',
155
+ doctorWarnings: [],
156
+ brewAvailable: true,
157
+ managerName: 'Scoop',
158
+ };
159
+ }
160
+ } catch {
161
+ // ignore
162
+ }
59
163
  }
164
+
165
+ return {
166
+ installedCount: 0,
167
+ outdatedCount: 0,
168
+ cellarSize: 'N/A',
169
+ doctorWarnings: [],
170
+ brewAvailable: false,
171
+ managerName: 'None',
172
+ };
60
173
  }
61
174
 
62
175
  export async function printBrewHealthReport(): Promise<void> {
63
- console.log(chalk.bold('\n pyre brew — Homebrew Health Summary\n'));
64
176
  const health = await getBrewHealth();
177
+ const label = health.brewAvailable ? health.managerName : 'Package Manager';
178
+ console.log(chalk.bold(`\n pyre brew — ${label} Health Summary\n`));
65
179
 
66
180
  if (!health.brewAvailable) {
67
- console.log(chalk.yellow(' ⚠ Homebrew is not installed or not available on PATH.\n'));
181
+ console.log(chalk.yellow(' ⚠ No supported package manager detected on PATH.\n'));
68
182
  return;
69
183
  }
70
184
 
71
- console.log(` 📦 Installed Formulae: ${chalk.bold(health.installedCount)}`);
72
- console.log(` ⏳ Outdated Packages: ${health.outdatedCount > 0 ? chalk.yellow.bold(health.outdatedCount) : chalk.green('0 (Up to date)')}`);
73
- console.log(` 💾 Cellar Disk Usage: ${chalk.bold(health.cellarSize)}`);
185
+ console.log(` 📦 Package Manager: ${chalk.cyan.bold(health.managerName)}`);
186
+ console.log(` 📦 Installed Packages: ${chalk.bold(health.installedCount)}`);
187
+ console.log(` ⏳ Outdated Packages: ${health.outdatedCount > 0 ? chalk.yellow.bold(health.outdatedCount) : chalk.green('0 (Up to date)')}`);
188
+ if (health.cellarSize !== 'N/A') {
189
+ console.log(` 💾 Cache / Cellar: ${chalk.bold(health.cellarSize)}`);
190
+ }
74
191
 
75
192
  console.log();
76
193
  if (health.doctorWarnings.length === 0) {
77
- console.log(chalk.green(' ✔ brew doctor: No warnings detected system-wide.'));
194
+ console.log(chalk.green(` ✔ ${health.managerName}: Ready and healthy.`));
78
195
  } else {
79
- console.log(chalk.yellow(` ⚠ brew doctor warnings (${health.doctorWarnings.length}):`));
196
+ console.log(chalk.yellow(` ⚠ Diagnostics warnings (${health.doctorWarnings.length}):`));
80
197
  for (const warn of health.doctorWarnings.slice(0, 5)) {
81
198
  console.log(chalk.dim(` • ${warn}`));
82
199
  }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * pyre build — Active Build System Tracker.
3
+ *
4
+ * Discovers running compilation and build tools (xcodebuild, cargo, swift,
5
+ * make, ninja, bazel, go), displaying real-time compiler workers, parallelism,
6
+ * and CPU/RAM footprints.
7
+ */
8
+ import chalk from 'chalk';
9
+ import { collectBuilds } from './monitors/buildTracker.js';
10
+
11
+ export interface BuildOptions {
12
+ watch?: boolean;
13
+ json?: boolean;
14
+ }
15
+
16
+ export async function runBuildCommand(opts: BuildOptions = {}): Promise<void> {
17
+ const builds = await collectBuilds();
18
+
19
+ if (opts.json) {
20
+ console.log(JSON.stringify({ activeBuilds: builds }, null, 2));
21
+ return;
22
+ }
23
+
24
+ console.log(chalk.bold('\n 🔥 pyre build — Active Build System Tracker\n'));
25
+
26
+ if (builds.length === 0) {
27
+ console.log(chalk.dim(' No active build or compilation processes detected.'));
28
+ console.log(chalk.dim(' (Supports: xcodebuild, cargo, swift build, make, ninja, bazel, go build)\n'));
29
+ return;
30
+ }
31
+
32
+ for (const b of builds) {
33
+ const elapsedMins = Math.floor(b.elapsedSec / 60);
34
+ const elapsedSecs = b.elapsedSec % 60;
35
+ const timeStr = elapsedMins > 0 ? `${elapsedMins}m ${elapsedSecs}s` : `${elapsedSecs}s`;
36
+
37
+ console.log(` ${chalk.bgCyan.black.bold(` ${b.tool.toUpperCase()} `)} ${chalk.bold(`PID ${b.rootPid}`)}`);
38
+ console.log(` Command: ${chalk.white(b.command)}`);
39
+ if (b.target) {
40
+ console.log(` Target: ${chalk.cyan(b.target)}`);
41
+ }
42
+ console.log(` Compiler Workers: ${chalk.green.bold(String(b.compilerProcesses))} active thread/processes`);
43
+ console.log(` Combined CPU: ${chalk.yellow.bold(b.totalCpu.toFixed(1) + '%')}`);
44
+ console.log(` Combined RAM: ${chalk.magenta(b.totalMem.toFixed(1) + '%')}`);
45
+ console.log(` Elapsed Time: ${chalk.dim(timeStr)}`);
46
+ console.log();
47
+ }
48
+ }
package/src/checkCmd.ts CHANGED
@@ -15,7 +15,7 @@ export async function runCheckCommand(opts: CheckOptions = {}): Promise<void> {
15
15
  const cpuUsage = data.cpu.usage;
16
16
  const tempC = data.cpu.temperature ?? data.thermal.temperatures?.cpu_die;
17
17
  const memUsage = data.memory.usagePercent;
18
- const disk = data.disk.mainVolume;
18
+ const disk = data.disk && data.disk.length > 0 ? data.disk[0] : null;
19
19
 
20
20
  const sentences: string[] = [];
21
21
  let isWarning = false;
@@ -61,10 +61,10 @@ export async function runCheckCommand(opts: CheckOptions = {}): Promise<void> {
61
61
 
62
62
  // Disk sentence
63
63
  if (disk) {
64
- const freeBytes = disk.free;
65
- const freeStr = formatBytes(freeBytes);
66
- sentences.push(`${freeStr} free on ${disk.mount}.`);
67
- if (disk.usePercent >= 90) {
64
+ const freeStr = disk.available;
65
+ sentences.push(`${freeStr} free on ${disk.mountpoint}.`);
66
+ const cap = parseInt(disk.capacity, 10);
67
+ if (!isNaN(cap) && cap >= 90) {
68
68
  isWarning = true;
69
69
  }
70
70
  }
@@ -116,3 +116,38 @@ complete -c pyre -l sort -x -a "cpu mem pid user command state threads runtime"
116
116
  complete -c pyre -l temp-unit -x -a "c f" -d "Temperature unit"
117
117
  `;
118
118
  }
119
+
120
+ export function generatePowerShellCompletions(): string {
121
+ return `# PowerShell completion for pyre
122
+ Register-ArgumentCompleter -Native -CommandName pyre -ScriptBlock {
123
+ param($wordToComplete, $commandAst, $cursorPosition)
124
+ $commands = @('live', 'check', 'pipe', 'stress', 'ui', 'web', 'ssh', 'fleet', 'bench', 'benchmark', 'anomalies', 'doctor', 'extensions', 'brew', 'update', 'profile', 'config', 'history', 'diff', 'info', 'completions', 'p2p', 'blender', 'prometheus', 'smart', 'replay', 'explain')
125
+ $options = @('--json', '--html', '--md', '--csv', '--tsv', '--detailed', '--theme', '--interval', '--once', '--out', '--export-dir', '--log', '--tree', '--sort', '--packets', '--limit', '--alert-cpu', '--alert-temp', '--temp-unit', '--plain', '--help', '--version')
126
+
127
+ $elements = $commandAst.CommandElements
128
+ if ($elements.Count -le 2) {
129
+ $commands | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
130
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_)
131
+ }
132
+ }
133
+ $options | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
134
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterName', $_)
135
+ }
136
+ }
137
+ `;
138
+ }
139
+
140
+ export function printCompletions(shell: string): void {
141
+ const s = (shell || '').toLowerCase();
142
+ if (s === 'bash') {
143
+ console.log(generateBashCompletions());
144
+ } else if (s === 'fish') {
145
+ console.log(generateFishCompletions());
146
+ } else if (s === 'powershell' || s === 'pwsh') {
147
+ console.log(generatePowerShellCompletions());
148
+ } else {
149
+ console.log(generateZshCompletions());
150
+ }
151
+ }
152
+
153
+