@pixelkit-labs/cli 1.5.1 → 1.5.6

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2026 Traves Theberge
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Traves Theberge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,69 +1,70 @@
1
- # @pixelkit-labs/cli
2
-
3
- Command-line diagnostics for [PixelKit](https://www.npmjs.com/package/@pixelkit-labs/sdk). Its only command,
4
- `pixelkit doctor`, answers "why is everything showing —?" before you file an issue.
5
-
6
- PixelKit hooks report `source: 'unavailable'` and render an em dash when a reading cannot be taken
7
- on real hardware. That is correct behaviour, but on the wrong device, without a development build,
8
- or without the native packages installed, every hook looks that way at once. `doctor` runs the
9
- checks a maintainer would run by hand.
10
-
11
- **Zero third-party dependencies.** It uses only Node's `child_process` and `util`.
12
-
13
- ```bash
14
- npx @pixelkit-labs/cli doctor
15
- ```
16
-
17
- or, installed as a dev dependency:
18
-
19
- ```bash
20
- npm install --save-dev @pixelkit-labs/cli
21
- npx pixelkit doctor
22
- ```
23
-
24
- ## What it checks
25
-
26
- 1. **adb on PATH and exactly one device connected** (`adb devices`). If several devices are
27
- connected, they are listed and you are told to pass `--serial`.
28
- 2. **The connected device's identity** manufacturer, model, Android release, SDK int
29
- (`adb shell getprop`). States plainly whether it is a Pixel, because most hooks report
30
- `unavailable` otherwise.
31
- 3. **Whether a PixelKit-based development build is installed** (`adb shell pm path <package>`),
32
- keyed off `--package` (default `com.pixelkit.sdk`). Expo Go can never satisfy this: the Kotlin
33
- Expo Modules (`@pixelkit-labs/native`, `@pixelkit-labs/mlkit`) must be compiled in.
34
- 4. **Whether `@pixelkit-labs/native` and `@pixelkit-labs/mlkit` resolve from the current project**
35
- (`require.resolve` from the working directory), and which hook groups are therefore available.
36
- A missing `@pixelkit-labs/mlkit` is reported as informational, not a failure it is the opt-in ML
37
- Kit package.
38
- 5. **Whether AICore is present on the device** (`adb shell pm list packages`, matched for
39
- "aicore"), which Gemini Nano needs. Not-applicable on a non-Pixel.
40
- 6. **Whether `adb reverse tcp:8081 tcp:8081` is set**, so a development client can reach Metro on
41
- localhost.
42
-
43
- Each check is reported as `PASS`, `FAIL`, `N/A` or `UNKN` ("could not be determined" — the check
44
- itself could not be run, for example because adb is missing or the device went offline).
45
- `doctor` never guesses a result: an inconclusive check is reported as such, the same discipline
46
- PixelKit hooks use for `source: 'unavailable'`.
47
-
48
- ## Options
49
-
50
- ```
51
- pixelkit doctor [--package <id>] [--serial <serial>]
52
-
53
- --package <id> Application id of the installed PixelKit development build
54
- (default: com.pixelkit.sdk)
55
- --serial <id> adb serial to target when more than one device is connected
56
- -h, --help Show help
57
- ```
58
-
59
- ## Exit code
60
-
61
- `0` when every check passed or was not applicable. `1` when any check failed, or could not be
62
- determined at all.
63
-
64
- ## Documentation
65
-
66
- [https://pixelkit-labs.github.io/pixelkit-docs/](https://pixelkit-labs.github.io/pixelkit-docs/) covers the hooks this command
67
- diagnoses: each one's inputs, outputs, and what `source: 'unavailable'` means for it.
68
-
69
- MIT
1
+ # @pixelkit-labs/cli
2
+
3
+ Command-line diagnostics and autonomous hardware agents for [PixelKit](https://www.npmjs.com/package/@pixelkit-labs/sdk).
4
+ - `pixelkit doctor`: answers "why is everything showing —?" before you file an issue.
5
+ - `pixelkit agent`: runs autonomous on-device hardware telemetry triage (CPU, battery, thermals, AICore) and queries the Google Gen AI hardware agent.
6
+
7
+ PixelKit hooks report `source: 'unavailable'` and render an em dash when a reading cannot be taken
8
+ on real hardware. That is correct behaviour, but on the wrong device, without a development build,
9
+ or without the native packages installed, every hook looks that way at once. `doctor` and `agent`
10
+ run the checks and diagnostics an expert systems engineer would run by hand.
11
+
12
+ **Zero third-party dependencies.** It uses only Node's `child_process`, `util`, and native `fetch`.
13
+
14
+ ```bash
15
+ # Verify environment readiness
16
+ npx @pixelkit-labs/cli doctor
17
+
18
+ # Run autonomous hardware diagnostic triage
19
+ npx @pixelkit-labs/cli agent --diagnose
20
+
21
+ # Query the hardware agent with custom diagnostic prompt
22
+ npx @pixelkit-labs/cli agent --query "Check battery wear and thermal throttling headroom"
23
+ ```
24
+
25
+ ## What it checks
26
+
27
+ 1. **adb on PATH and exactly one device connected** (`adb devices`). If several devices are
28
+ connected, they are listed and you are told to pass `--serial`.
29
+ 2. **The connected device's identity** manufacturer, model, Android release, SDK int
30
+ (`adb shell getprop`). States plainly whether it is a Pixel, because most hooks report
31
+ `unavailable` otherwise.
32
+ 3. **Whether a PixelKit-based development build is installed** (`adb shell pm path <package>`),
33
+ keyed off `--package` (default `com.pixelkit.sdk`). Expo Go can never satisfy this: the Kotlin
34
+ Expo Modules (`@pixelkit-labs/native`, `@pixelkit-labs/mlkit`) must be compiled in.
35
+ 4. **Whether `@pixelkit-labs/native` and `@pixelkit-labs/mlkit` resolve from the current project**
36
+ (`require.resolve` from the working directory), and which hook groups are therefore available.
37
+ A missing `@pixelkit-labs/mlkit` is reported as informational, not a failure — it is the opt-in ML
38
+ Kit package.
39
+ 5. **Whether AICore is present on the device** (`adb shell pm list packages`, matched for
40
+ "aicore"), which Gemini Nano needs. Not-applicable on a non-Pixel.
41
+ 6. **Whether `adb reverse tcp:8081 tcp:8081` is set**, so a development client can reach Metro on
42
+ localhost.
43
+
44
+ Each check is reported as `PASS`, `FAIL`, `N/A` or `UNKN` ("could not be determined" the check
45
+ itself could not be run, for example because adb is missing or the device went offline).
46
+ `doctor` never guesses a result: an inconclusive check is reported as such, the same discipline
47
+ PixelKit hooks use for `source: 'unavailable'`.
48
+
49
+ ## Options
50
+
51
+ ```
52
+ pixelkit doctor [--package <id>] [--serial <serial>]
53
+
54
+ --package <id> Application id of the installed PixelKit development build
55
+ (default: com.pixelkit.sdk)
56
+ --serial <id> adb serial to target when more than one device is connected
57
+ -h, --help Show help
58
+ ```
59
+
60
+ ## Exit code
61
+
62
+ `0` when every check passed or was not applicable. `1` when any check failed, or could not be
63
+ determined at all.
64
+
65
+ ## Documentation
66
+
67
+ [https://pixelkit-labs.github.io/pixelkit-docs/](https://pixelkit-labs.github.io/pixelkit-docs/) covers the hooks this command
68
+ diagnoses: each one's inputs, outputs, and what `source: 'unavailable'` means for it.
69
+
70
+ MIT
package/build/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @file packages/cli/index.ts
3
+ * @file index.ts
4
4
  * @description `pixelkit doctor`: the first and only command of `@pixelkit-labs/cli`. PixelKit hooks
5
5
  * report `source: 'unavailable'` and render an em dash when a reading cannot be taken on real
6
6
  * hardware; on the wrong device, without a development build, or without the native packages
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  /**
4
- * @file packages/cli/index.ts
4
+ * @file index.ts
5
5
  * @description `pixelkit doctor`: the first and only command of `@pixelkit-labs/cli`. PixelKit hooks
6
6
  * report `source: 'unavailable'` and render an em dash when a reading cannot be taken on real
7
7
  * hardware; on the wrong device, without a development build, or without the native packages
@@ -351,6 +351,21 @@ function parseArgs(argv) {
351
351
  else if (arg.startsWith('--serial=')) {
352
352
  flags.serial = arg.slice('--serial='.length);
353
353
  }
354
+ else if (arg === '--diagnose') {
355
+ flags.diagnose = true;
356
+ }
357
+ else if (arg === '--query' || arg === '-q') {
358
+ flags.query = argv[++i];
359
+ }
360
+ else if (arg.startsWith('--query=')) {
361
+ flags.query = arg.slice('--query='.length);
362
+ }
363
+ else if (arg === '--model' || arg === '-m') {
364
+ flags.model = argv[++i];
365
+ }
366
+ else if (arg.startsWith('--model=')) {
367
+ flags.model = arg.slice('--model='.length);
368
+ }
354
369
  else if (arg === '--help' || arg === '-h') {
355
370
  flags.help = true;
356
371
  }
@@ -361,15 +376,23 @@ function parseArgs(argv) {
361
376
  return { command, flags };
362
377
  }
363
378
  function printUsage() {
364
- console.log(`${ANSI.bold}pixelkit doctor${ANSI.reset} - diagnose why PixelKit hooks might report source: "unavailable"
365
-
366
- Usage:
367
- pixelkit doctor [--package <id>] [--serial <serial>]
368
-
369
- Options:
370
- --package <id> Application id of the installed PixelKit development build (default: com.pixelkit.sdk)
371
- --serial <id> adb serial to target when more than one device is connected
372
- -h, --help Show this help
379
+ console.log(`${ANSI.bold}pixelkit${ANSI.reset} - developer tooling and diagnostics for Google Pixel hardware
380
+
381
+ Usage:
382
+ pixelkit doctor [--package <id>] [--serial <serial>]
383
+ pixelkit agent [--diagnose] [--query <prompt>] [--model <model>] [--serial <serial>]
384
+
385
+ Commands:
386
+ doctor Diagnose why PixelKit hooks might report source: "unavailable"
387
+ agent Run autonomous hardware diagnostics and query the device agent
388
+
389
+ Options:
390
+ --diagnose Run complete hardware diagnostic triage (CPU, battery, thermals, AICore)
391
+ --query, -q Query the hardware agent with a specific diagnostic question
392
+ --model, -m Gemini model for cloud reasoning (default: gemini-2.5-flash)
393
+ --package <id> Application id of the installed PixelKit development build (default: com.pixelkit.sdk)
394
+ --serial <id> adb serial to target when more than one device is connected
395
+ -h, --help Show this help
373
396
  `);
374
397
  }
375
398
  function printResult(r) {
@@ -377,6 +400,172 @@ function printResult(r) {
377
400
  if (r.fix)
378
401
  console.log(`${ANSI.dim} fix: ${r.fix}${ANSI.reset}`);
379
402
  }
403
+ /**
404
+ * Runs the autonomous hardware agent diagnostic command.
405
+ */
406
+ async function runHardwareAgent(flags) {
407
+ console.log(`${ANSI.bold}PixelKit Hardware Diagnostics Agent${ANSI.reset}\n`);
408
+ const step1 = await checkAdbAndDevice(flags.serial);
409
+ if (step1.result.status === 'fail' || !step1.serial) {
410
+ printResult(step1.result);
411
+ return 1;
412
+ }
413
+ const serial = step1.serial;
414
+ console.log(`${ANSI.dim}Collecting hardware telemetry from ${serial}...${ANSI.reset}`);
415
+ // 1. Device identity & SoC
416
+ const modelRes = await runAdb(['-s', serial, 'shell', 'getprop', 'ro.product.model']);
417
+ const manufacturerRes = await runAdb(['-s', serial, 'shell', 'getprop', 'ro.product.manufacturer']);
418
+ const releaseRes = await runAdb(['-s', serial, 'shell', 'getprop', 'ro.build.version.release']);
419
+ const sdkRes = await runAdb(['-s', serial, 'shell', 'getprop', 'ro.build.version.sdk']);
420
+ const socRes = await runAdb(['-s', serial, 'shell', 'getprop', 'ro.soc.model']);
421
+ const platformRes = await runAdb(['-s', serial, 'shell', 'getprop', 'ro.board.platform']);
422
+ const model = modelRes.ok ? modelRes.stdout.trim() : 'Unknown';
423
+ const manufacturer = manufacturerRes.ok ? manufacturerRes.stdout.trim() : 'Unknown';
424
+ const release = releaseRes.ok ? releaseRes.stdout.trim() : 'Unknown';
425
+ const sdk = sdkRes.ok ? sdkRes.stdout.trim() : 'Unknown';
426
+ const soc = socRes.ok && socRes.stdout.trim() ? socRes.stdout.trim() : (platformRes.ok ? platformRes.stdout.trim() : 'Google Tensor');
427
+ // 2. Battery telemetry
428
+ const batteryRes = await runAdb(['-s', serial, 'shell', 'dumpsys', 'battery']);
429
+ let batteryLevel = 'N/A';
430
+ let batteryTemp = 'N/A';
431
+ let batteryVoltage = 'N/A';
432
+ let batteryStatus = 'N/A';
433
+ let batteryHealth = 'N/A';
434
+ if (batteryRes.ok) {
435
+ const lines = batteryRes.stdout.split(/\r?\n/);
436
+ for (const line of lines) {
437
+ const trimmed = line.trim();
438
+ if (trimmed.startsWith('level:'))
439
+ batteryLevel = trimmed.split(':')[1].trim() + '%';
440
+ else if (trimmed.startsWith('temperature:')) {
441
+ const raw = parseFloat(trimmed.split(':')[1].trim());
442
+ batteryTemp = (raw / 10).toFixed(1) + '°C';
443
+ }
444
+ else if (trimmed.startsWith('voltage:')) {
445
+ const raw = parseFloat(trimmed.split(':')[1].trim());
446
+ batteryVoltage = (raw / 1000).toFixed(2) + 'V';
447
+ }
448
+ else if (trimmed.startsWith('status:')) {
449
+ const code = trimmed.split(':')[1].trim();
450
+ batteryStatus = code === '2' ? 'Charging' : code === '3' ? 'Discharging' : code === '5' ? 'Full' : 'Not Charging';
451
+ }
452
+ else if (trimmed.startsWith('health:')) {
453
+ const code = trimmed.split(':')[1].trim();
454
+ batteryHealth = code === '2' ? 'Good' : 'Degraded';
455
+ }
456
+ }
457
+ }
458
+ // 3. Thermal telemetry
459
+ const thermalRes = await runAdb(['-s', serial, 'shell', 'dumpsys', 'thermalservice']);
460
+ let thermalStatus = 'Normal (0)';
461
+ if (thermalRes.ok) {
462
+ const match = thermalRes.stdout.match(/mStatus=(\d+)/i) || thermalRes.stdout.match(/Current thermal status: (\d+)/i);
463
+ if (match) {
464
+ const code = parseInt(match[1], 10);
465
+ const labels = ['None', 'Light', 'Moderate', 'Severe', 'Critical', 'Emergency', 'Shutdown'];
466
+ thermalStatus = `${labels[code] ?? 'Unknown'} (${code})`;
467
+ }
468
+ }
469
+ // 4. Memory telemetry
470
+ const memRes = await runAdb(['-s', serial, 'shell', 'cat', '/proc/meminfo']);
471
+ let memTotal = 'N/A';
472
+ let memAvail = 'N/A';
473
+ let memPct = 'N/A';
474
+ if (memRes.ok) {
475
+ const totalMatch = memRes.stdout.match(/MemTotal:\s+(\d+)\s+kB/);
476
+ const availMatch = memRes.stdout.match(/MemAvailable:\s+(\d+)\s+kB/);
477
+ if (totalMatch && availMatch) {
478
+ const totalMB = Math.round(parseInt(totalMatch[1], 10) / 1024);
479
+ const availMB = Math.round(parseInt(availMatch[1], 10) / 1024);
480
+ memTotal = `${totalMB} MB`;
481
+ memAvail = `${availMB} MB`;
482
+ memPct = `${Math.round((availMB / totalMB) * 100)}% available`;
483
+ }
484
+ }
485
+ // 5. AICore status
486
+ const aicoreRes = await runAdb(['-s', serial, 'shell', 'pm', 'list', 'packages', 'com.google.android.aicore']);
487
+ const aicoreInstalled = aicoreRes.ok && aicoreRes.stdout.includes('com.google.android.aicore');
488
+ // Print Telemetry Matrix
489
+ console.log(`\n${ANSI.bold}Hardware Telemetry Snapshot:${ANSI.reset}`);
490
+ console.log(` Device: ${manufacturer} ${model} (Android ${release}, API ${sdk})`);
491
+ console.log(` Silicon: ${soc}`);
492
+ console.log(` Battery: ${batteryLevel} · ${batteryTemp} · ${batteryVoltage} · ${batteryStatus} (${batteryHealth})`);
493
+ console.log(` Thermals: ${thermalStatus}`);
494
+ console.log(` Memory: ${memAvail} / ${memTotal} (${memPct})`);
495
+ console.log(` AICore: ${aicoreInstalled ? `${ANSI.green}Active${ANSI.reset}` : `${ANSI.yellow}Not Installed${ANSI.reset}`}\n`);
496
+ const apiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_GENAI_API_KEY;
497
+ const userQuery = flags.query || 'Perform a comprehensive hardware diagnostic check on this Pixel device.';
498
+ if (!apiKey) {
499
+ console.log(`${ANSI.bold}Autonomous Diagnostic Assessment (Local Heuristics):${ANSI.reset}`);
500
+ const issues = [];
501
+ if (batteryHealth !== 'Good' && batteryHealth !== 'N/A')
502
+ issues.push('Battery reports degraded health.');
503
+ if (thermalStatus.includes('Severe') || thermalStatus.includes('Critical'))
504
+ issues.push('Thermal throttling active.');
505
+ if (!aicoreInstalled)
506
+ issues.push('AICore is not installed; Gemini Nano hardware acceleration disabled.');
507
+ if (issues.length === 0) {
508
+ console.log(` ${ANSI.green}✔ System hardware health nominal. All silicon, battery, and thermal parameters within optimal operational thresholds.${ANSI.reset}`);
509
+ }
510
+ else {
511
+ for (const iss of issues) {
512
+ console.log(` ${ANSI.yellow}⚠ ${iss}${ANSI.reset}`);
513
+ }
514
+ }
515
+ console.log(`\n${ANSI.dim}💡 Tip: Export GEMINI_API_KEY to activate cloud agent multi-turn LLM reasoning and custom hardware triage queries.${ANSI.reset}`);
516
+ return 0;
517
+ }
518
+ // Cloud Agent Reasoning
519
+ const modelName = flags.model || 'gemini-2.5-flash';
520
+ console.log(`${ANSI.bold}Cloud Hardware Agent (${modelName}):${ANSI.reset}`);
521
+ console.log(`${ANSI.dim}Reasoning over hardware telemetry with Google Gen AI...${ANSI.reset}\n`);
522
+ const prompt = `Target Device Telemetry:
523
+ - Manufacturer: ${manufacturer}
524
+ - Model: ${model}
525
+ - Android Release: ${release} (SDK ${sdk})
526
+ - SoC Silicon: ${soc}
527
+ - Battery: Level=${batteryLevel}, Temp=${batteryTemp}, Voltage=${batteryVoltage}, Status=${batteryStatus}, Health=${batteryHealth}
528
+ - Thermal Headroom: ${thermalStatus}
529
+ - Memory: Available=${memAvail}, Total=${memTotal} (${memPct})
530
+ - AICore: ${aicoreInstalled ? 'Installed and available' : 'Not installed'}
531
+
532
+ User Diagnostic Query: "${userQuery}"
533
+
534
+ Provide a concise, professional hardware engineering assessment. Evaluate thermal headroom, battery wear/charging, memory constraints, and AICore readiness. Give specific actionable recommendations.`;
535
+ try {
536
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(modelName)}:generateContent?key=${encodeURIComponent(apiKey)}`;
537
+ const res = await fetch(url, {
538
+ method: 'POST',
539
+ headers: { 'Content-Type': 'application/json' },
540
+ body: JSON.stringify({
541
+ contents: [{ role: 'user', parts: [{ text: prompt }] }],
542
+ systemInstruction: {
543
+ parts: [{ text: 'You are the PixelKit Hardware Diagnostics Agent, an expert embedded and systems engineer specializing in Google Pixel hardware (Tensor G-series, Android, AICore).' }]
544
+ }
545
+ })
546
+ });
547
+ if (!res.ok) {
548
+ const errText = await res.text();
549
+ console.error(`Agent API error (${res.status}): ${errText}`);
550
+ return 1;
551
+ }
552
+ const data = await res.json();
553
+ const replyText = data?.candidates?.[0]?.content?.parts?.[0]?.text;
554
+ if (replyText) {
555
+ console.log(replyText.trim());
556
+ console.log(`\n${ANSI.green}✔ Hardware diagnostic triage complete.${ANSI.reset}`);
557
+ return 0;
558
+ }
559
+ else {
560
+ console.error('No response text received from agent.');
561
+ return 1;
562
+ }
563
+ }
564
+ catch (err) {
565
+ console.error(`Agent reasoning failed: ${err.message || String(err)}`);
566
+ return 1;
567
+ }
568
+ }
380
569
  /**
381
570
  * Runs all six checks in order and prints them. Returns the process exit code: 0 only when every
382
571
  * check passed or was not applicable; 1 when any check failed or — per the "never fabricate a
@@ -416,15 +605,19 @@ async function main() {
416
605
  process.exitCode = 1;
417
606
  return;
418
607
  }
419
- if (command !== 'doctor') {
420
- console.error(`Unknown command "${command}". The only command is "doctor".\n`);
421
- printUsage();
422
- process.exitCode = 1;
608
+ if (command === 'doctor') {
609
+ process.exitCode = await doctor(flags);
610
+ return;
611
+ }
612
+ if (command === 'agent') {
613
+ process.exitCode = await runHardwareAgent(flags);
423
614
  return;
424
615
  }
425
- process.exitCode = await doctor(flags);
616
+ console.error(`Unknown command "${command}". Available commands: "doctor", "agent".\n`);
617
+ printUsage();
618
+ process.exitCode = 1;
426
619
  }
427
620
  main().catch((err) => {
428
- console.error(`pixelkit doctor crashed: ${err.stack || String(err)}`);
621
+ console.error(`pixelkit crashed: ${err.stack || String(err)}`);
429
622
  process.exitCode = 1;
430
623
  });