adb-ready 0.5.0 → 0.5.1

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/CHANGELOG.md CHANGED
@@ -9,6 +9,23 @@ breaking changes.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.5.1] - 2026-09-25
13
+
14
+ ### Added
15
+
16
+ - Add a first-device guide under Start that walks new users through USB,
17
+ Android 11+ Wireless debugging, or an existing emulator before handing off
18
+ to the normal verified devices, pairing, or connection workflow.
19
+
20
+ ### Fixed
21
+
22
+ - Make interactive selection unmistakable with one directional cursor and a
23
+ high-contrast active row, while retaining explicit no-color and ASCII
24
+ fallbacks and removing distracting markers from inactive choices.
25
+ - Group session launch, planning, bounded verification, wireless connection,
26
+ and pairing under one focused Start workflow instead of scattering entry
27
+ actions across unrelated top-level categories.
28
+
12
29
  ## [0.5.0] - 2026-09-21
13
30
 
14
31
  ### Fixed
@@ -406,7 +423,8 @@ breaking changes.
406
423
  explainable configuration precedence.
407
424
  - Human, plain, JSON, and NDJSON output across Node, Bun, and Deno entrypoints.
408
425
 
409
- [Unreleased]: https://github.com/Adam014/adb-ready/compare/v0.5.0...HEAD
426
+ [Unreleased]: https://github.com/Adam014/adb-ready/compare/v0.5.1...HEAD
427
+ [0.5.1]: https://github.com/Adam014/adb-ready/compare/v0.5.0...v0.5.1
410
428
  [0.5.0]: https://github.com/Adam014/adb-ready/compare/v0.4.1...v0.5.0
411
429
  [0.4.1]: https://github.com/Adam014/adb-ready/compare/v0.4.0...v0.4.1
412
430
  [0.4.0]: https://github.com/Adam014/adb-ready/compare/v0.3.5...v0.4.0
package/README.md CHANGED
@@ -71,6 +71,8 @@ them or require an ADB Ready account, hosted service, or model API key.
71
71
 
72
72
  Running `adb-ready` without a command opens the interactive workflow home.
73
73
  `adbr` is the shorter alias for the same CLI.
74
+ New to Android device setup? Open **Start → Set up first device** for a guided
75
+ USB, Wireless debugging, or emulator path.
74
76
  The home and its focused menus adapt to the detected terminal width and height,
75
77
  including narrow split panes and short terminals, without relying on scrolling
76
78
  to reveal an action.
package/dist/cli.js CHANGED
@@ -3304,7 +3304,7 @@ import process15 from "node:process";
3304
3304
  // package.json
3305
3305
  var package_default = {
3306
3306
  name: "adb-ready",
3307
- version: "0.5.0",
3307
+ version: "0.5.1",
3308
3308
  description: "Agent-ready Android CLI for reliable ADB sessions, app automation, and verified evidence.",
3309
3309
  private: false,
3310
3310
  type: "module",
@@ -41609,7 +41609,7 @@ async function pruneSessions(options) {
41609
41609
  if (!listed.ok)
41610
41610
  return;
41611
41611
  const completed = listed.value.filter(({ status }) => status !== "running").sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
41612
- const cutoff = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
41612
+ const cutoff = (options.clock ?? (() => new Date))().getTime() - maxAgeDays * 24 * 60 * 60 * 1000;
41613
41613
  const groups = new Map;
41614
41614
  for (const manifest of completed) {
41615
41615
  const key = manifest.projectFingerprint ?? "legacy-unscoped";
@@ -43635,7 +43635,8 @@ async function runDev(options, config4 = {}, dependencies = {}, signal) {
43635
43635
  projectRoot: options.cwd
43636
43636
  }, {
43637
43637
  ...options.sessionStore,
43638
- projectRoot: options.sessionStore.projectRoot ?? options.cwd
43638
+ projectRoot: options.sessionStore.projectRoot ?? options.cwd,
43639
+ ...dependencies.clock === undefined ? {} : { clock: dependencies.clock }
43639
43640
  });
43640
43641
  } catch {
43641
43642
  problems.push({
@@ -51402,10 +51403,14 @@ function symbols(capabilities) {
51402
51403
  function color(code, value, capabilities) {
51403
51404
  return capabilities.color ? `\x1B[${String(code)}m${value}\x1B[0m` : value;
51404
51405
  }
51406
+ function selected(value, capabilities) {
51407
+ return capabilities.color ? `\x1B[1;7m${value}\x1B[0m` : value;
51408
+ }
51405
51409
  var style = {
51406
51410
  accent: (value, capabilities) => color(36, value, capabilities),
51407
51411
  dim: (value, capabilities) => color(2, value, capabilities),
51408
51412
  failure: (value, capabilities) => color(31, value, capabilities),
51413
+ selected,
51409
51414
  strong: (value, capabilities) => color(1, value, capabilities),
51410
51415
  success: (value, capabilities) => color(32, value, capabilities),
51411
51416
  warning: (value, capabilities) => color(33, value, capabilities)
@@ -51465,7 +51470,7 @@ function initialIndex(options) {
51465
51470
  }
51466
51471
  return options.findIndex((option) => option.disabled !== true);
51467
51472
  }
51468
- function renderMenu(options, selected, previousLineCount, frame) {
51473
+ function renderMenu(options, selected2, previousLineCount, frame) {
51469
51474
  const { capabilities, sink } = options;
51470
51475
  const glyphs = symbols(capabilities);
51471
51476
  const availableColumns = Math.max(19, capabilities.columns - 1);
@@ -51481,14 +51486,15 @@ function renderMenu(options, selected, previousLineCount, frame) {
51481
51486
  const lines = [...options.preamble?.(frame) ?? []];
51482
51487
  lines.push(`${style.dim(top, capabilities)} ${style.strong(truncate(sanitizeTerminalText(options.title), Math.max(8, availableColumns - 4)), capabilities)}`);
51483
51488
  options.options.forEach((option, index) => {
51484
- const isSelected = index === selected;
51485
- const pointer = isSelected ? style.accent(glyphs.active, capabilities) : style.dim(option.disabled === true ? glyphs.skipped : glyphs.pending, capabilities);
51486
- const number8 = style.dim(`[${String(index + 1)}]`, capabilities);
51489
+ const isSelected = index === selected2;
51490
+ const pointer = isSelected ? style.accent(capabilities.unicode ? "›" : ">", capabilities) : option.disabled === true ? style.dim(glyphs.skipped, capabilities) : " ";
51491
+ const number8 = `[${String(index + 1)}]`;
51487
51492
  const disabled = option.disabled === true ? " · unavailable" : "";
51488
51493
  const recommended = option.recommended === true ? " · recommended" : "";
51489
51494
  const label = truncate(`${sanitizeTerminalText(option.label)}${disabled}${recommended}`, Math.max(8, availableColumns - 10));
51490
- const styledLabel = option.disabled === true ? style.dim(label, capabilities) : isSelected ? style.strong(label, capabilities) : label;
51491
- lines.push(` ${pointer} ${number8} ${styledLabel}`);
51495
+ const row = `${number8} ${label}`;
51496
+ const styledRow = option.disabled === true ? style.dim(row, capabilities) : isSelected ? style.selected(row, capabilities) : `${style.dim(number8, capabilities)} ${label}`;
51497
+ lines.push(` ${pointer} ${styledRow}`);
51492
51498
  if (isSelected && option.description !== undefined) {
51493
51499
  for (const description of wrap(option.description, Math.max(10, availableColumns - 9))) {
51494
51500
  lines.push(` ${style.accent(rail, capabilities)} ${style.dim(description, capabilities)}`);
@@ -51512,8 +51518,8 @@ async function selectOne(options) {
51512
51518
  if (!options.capabilities.interactive || options.input.setRawMode === undefined) {
51513
51519
  return { kind: "unavailable" };
51514
51520
  }
51515
- let selected = initialIndex(options.options);
51516
- if (selected === -1) {
51521
+ let selected2 = initialIndex(options.options);
51522
+ if (selected2 === -1) {
51517
51523
  return { kind: "unavailable" };
51518
51524
  }
51519
51525
  return await new Promise((resolve) => {
@@ -51556,7 +51562,7 @@ async function selectOne(options) {
51556
51562
  resolve(result);
51557
51563
  };
51558
51564
  const draw = () => {
51559
- lineCount = renderMenu(options, selected, lineCount, frame);
51565
+ lineCount = renderMenu(options, selected2, lineCount, frame);
51560
51566
  };
51561
51567
  const onAbort = () => settle3({ kind: "cancelled", reason: "signal" });
51562
51568
  const cancelEscape = () => {
@@ -51571,13 +51577,13 @@ async function selectOne(options) {
51571
51577
  }
51572
51578
  if (bufferedInput.startsWith("\x1B[A")) {
51573
51579
  bufferedInput = bufferedInput.slice(3);
51574
- selected = nextEnabled(options.options, selected, -1);
51580
+ selected2 = nextEnabled(options.options, selected2, -1);
51575
51581
  draw();
51576
51582
  continue;
51577
51583
  }
51578
51584
  if (bufferedInput.startsWith("\x1B[B")) {
51579
51585
  bufferedInput = bufferedInput.slice(3);
51580
- selected = nextEnabled(options.options, selected, 1);
51586
+ selected2 = nextEnabled(options.options, selected2, 1);
51581
51587
  draw();
51582
51588
  continue;
51583
51589
  }
@@ -51593,7 +51599,7 @@ async function selectOne(options) {
51593
51599
  bufferedInput = bufferedInput.slice(1);
51594
51600
  if (character === "\r" || character === `
51595
51601
  `) {
51596
- const selectedOption = options.options[selected];
51602
+ const selectedOption = options.options[selected2];
51597
51603
  if (selectedOption !== undefined && selectedOption.disabled !== true) {
51598
51604
  settle3({ kind: "selected", value: selectedOption.value });
51599
51605
  }
@@ -51602,7 +51608,7 @@ async function selectOne(options) {
51602
51608
  if (character !== undefined && /^[1-9]$/u.test(character)) {
51603
51609
  const index = Number(character) - 1;
51604
51610
  if (options.options[index] !== undefined && options.options[index]?.disabled !== true) {
51605
- selected = index;
51611
+ selected2 = index;
51606
51612
  draw();
51607
51613
  }
51608
51614
  }
@@ -51788,6 +51794,190 @@ function startDevControls(options) {
51788
51794
  return { dispose, stopRequested };
51789
51795
  }
51790
51796
 
51797
+ // src/ui/device-setup.ts
51798
+ function wrap2(value, width) {
51799
+ const words = sanitizeTerminalText(value).split(" ");
51800
+ const lines = [];
51801
+ let current = "";
51802
+ for (const word of words) {
51803
+ const candidate = current === "" ? word : `${current} ${word}`;
51804
+ if (candidate.length <= width) {
51805
+ current = candidate;
51806
+ continue;
51807
+ }
51808
+ if (current !== "")
51809
+ lines.push(current);
51810
+ current = word;
51811
+ }
51812
+ if (current !== "")
51813
+ lines.push(current);
51814
+ return lines;
51815
+ }
51816
+ function guide(title, steps, capabilities) {
51817
+ const width = Math.max(18, capabilities.columns - 8);
51818
+ const rail = capabilities.unicode ? "│" : "|";
51819
+ const lines = [style.accent(`DEVICE SETUP · ${title}`, capabilities), ""];
51820
+ for (const [index, step] of steps.entries()) {
51821
+ const prefix = `${String(index + 1)}. `;
51822
+ const wrapped = wrap2(step, Math.max(12, width - prefix.length));
51823
+ for (const [lineIndex, line] of wrapped.entries()) {
51824
+ const marker = lineIndex === 0 ? prefix : " ".repeat(prefix.length);
51825
+ lines.push(`${style.dim(rail, capabilities)} ${marker}${line}`);
51826
+ }
51827
+ }
51828
+ lines.push("");
51829
+ return lines;
51830
+ }
51831
+ function intro(title, detail2, capabilities) {
51832
+ return [
51833
+ style.accent(`DEVICE SETUP · ${title}`, capabilities),
51834
+ ...wrap2(detail2, Math.max(18, capabilities.columns - 4)).map((line) => style.dim(line, capabilities)),
51835
+ ""
51836
+ ];
51837
+ }
51838
+ async function nextStep(options, title, steps, action, label) {
51839
+ const selected2 = await selectOne({
51840
+ title: "READY FOR ADB READY?",
51841
+ options: [
51842
+ {
51843
+ value: action,
51844
+ label,
51845
+ description: "ADB Ready will verify the target before using it.",
51846
+ recommended: true
51847
+ },
51848
+ { value: "back", label: "Choose another method" }
51849
+ ],
51850
+ input: options.input,
51851
+ sink: options.sink,
51852
+ capabilities: options.capabilities,
51853
+ preamble: () => guide(title, steps, options.capabilities),
51854
+ escapeLabel: "back",
51855
+ ...options.signal === undefined ? {} : { signal: options.signal }
51856
+ });
51857
+ if (selected2.kind !== "selected")
51858
+ return selected2;
51859
+ return selected2.value === "back" ? { kind: "back" } : { kind: "action", action: selected2.value };
51860
+ }
51861
+ async function showDeviceSetup(options) {
51862
+ while (true) {
51863
+ const target = await selectOne({
51864
+ title: "SET UP YOUR FIRST ANDROID TARGET",
51865
+ options: [
51866
+ {
51867
+ value: "physical",
51868
+ label: "Physical Android device",
51869
+ description: "Connect a phone, tablet, foldable, watch, TV, car, or XR target.",
51870
+ recommended: true
51871
+ },
51872
+ {
51873
+ value: "emulator",
51874
+ label: "Android emulator",
51875
+ description: "Use an existing Android Virtual Device on this computer."
51876
+ }
51877
+ ],
51878
+ input: options.input,
51879
+ sink: options.sink,
51880
+ capabilities: options.capabilities,
51881
+ preamble: () => intro("ANDROID", "Choose how this project will reach its development target.", options.capabilities),
51882
+ escapeLabel: "home",
51883
+ ...options.signal === undefined ? {} : { signal: options.signal }
51884
+ });
51885
+ if (target.kind !== "selected")
51886
+ return target;
51887
+ if (target.value === "emulator") {
51888
+ const result = await nextStep(options, "ANDROID EMULATOR", [
51889
+ "Open Android Studio, then open Device Manager.",
51890
+ "Start an existing Android Virtual Device and wait for its home screen.",
51891
+ "Continue here; ADB Ready will list and verify the running emulator."
51892
+ ], "devices", "Verify running emulator");
51893
+ if (result.kind === "back")
51894
+ continue;
51895
+ return result;
51896
+ }
51897
+ while (true) {
51898
+ const method = await selectOne({
51899
+ title: "HOW SHOULD THIS DEVICE CONNECT?",
51900
+ options: [
51901
+ {
51902
+ value: "usb",
51903
+ label: "USB cable",
51904
+ description: "The most direct first connection; Android asks you to trust this computer.",
51905
+ recommended: true
51906
+ },
51907
+ {
51908
+ value: "wireless",
51909
+ label: "Wireless debugging",
51910
+ description: "Pair Android 11 or newer over the same trusted network."
51911
+ }
51912
+ ],
51913
+ input: options.input,
51914
+ sink: options.sink,
51915
+ capabilities: options.capabilities,
51916
+ preamble: () => [style.accent("DEVICE SETUP · PHYSICAL DEVICE", options.capabilities), ""],
51917
+ escapeLabel: "back",
51918
+ ...options.signal === undefined ? {} : { signal: options.signal }
51919
+ });
51920
+ if (method.kind !== "selected") {
51921
+ if (method.kind === "cancelled" && method.reason === "escape")
51922
+ break;
51923
+ return method;
51924
+ }
51925
+ if (method.value === "usb") {
51926
+ const result2 = await nextStep(options, "USB", [
51927
+ "On Android, open Settings > About phone and tap Build number seven times. Manufacturer paths can vary; Settings search can find Build number.",
51928
+ "Open Developer options and enable USB debugging.",
51929
+ "Connect a data-capable USB cable, unlock the device, and approve the RSA trust prompt."
51930
+ ], "devices", "Verify USB device");
51931
+ if (result2.kind === "back")
51932
+ continue;
51933
+ return result2;
51934
+ }
51935
+ const wireless = await selectOne({
51936
+ title: "WIRELESS DEBUGGING",
51937
+ options: [
51938
+ {
51939
+ value: "pair",
51940
+ label: "Pair this computer",
51941
+ description: "Use Android's temporary six-digit pairing code.",
51942
+ recommended: true
51943
+ },
51944
+ {
51945
+ value: "connect",
51946
+ label: "Connect an already paired device",
51947
+ description: "Discover and verify a device this computer already trusts."
51948
+ }
51949
+ ],
51950
+ input: options.input,
51951
+ sink: options.sink,
51952
+ capabilities: options.capabilities,
51953
+ preamble: () => guide("WIRELESS", [
51954
+ "Keep the computer and Android device on the same trusted network.",
51955
+ "On Android 11 or newer, open Developer options > Wireless debugging and turn it on."
51956
+ ], options.capabilities),
51957
+ escapeLabel: "back",
51958
+ ...options.signal === undefined ? {} : { signal: options.signal }
51959
+ });
51960
+ if (wireless.kind !== "selected") {
51961
+ if (wireless.kind === "cancelled" && wireless.reason === "escape")
51962
+ continue;
51963
+ return wireless;
51964
+ }
51965
+ const result = await nextStep(options, wireless.value === "pair" ? "PAIR WIRELESSLY" : "CONNECT WIRELESSLY", wireless.value === "pair" ? [
51966
+ "In Wireless debugging, choose Pair device with pairing code.",
51967
+ "Keep that screen open. Its pairing address and six-digit code are temporary.",
51968
+ "Continue here; the code is read through a hidden prompt and is never placed in shell history."
51969
+ ] : [
51970
+ "Keep Wireless debugging enabled and stay on the same trusted network.",
51971
+ "Return to the main Wireless debugging screen; its connection port normally differs from the pairing port.",
51972
+ "Continue here; ADB Ready will discover and verify the connected target."
51973
+ ], wireless.value, wireless.value === "pair" ? "Start secure pairing" : "Find and connect device");
51974
+ if (result.kind === "back")
51975
+ continue;
51976
+ return result;
51977
+ }
51978
+ }
51979
+ }
51980
+
51791
51981
  // src/ui/ascii-scene.ts
51792
51982
  var FACE_SHADES = ".,:;=+*#%@";
51793
51983
  var LINK_PATH = [
@@ -52011,16 +52201,11 @@ async function showHomeScreen(options) {
52011
52201
  clearInteractiveScreen(options.sink, options.capabilities);
52012
52202
  const rootOptions = [
52013
52203
  {
52014
- value: "dev",
52015
- label: "Start development",
52016
- description: "Prepare one target, ports, logs, and your project command.",
52204
+ value: "start",
52205
+ label: "Start",
52206
+ description: "Develop, connect a device, or run a bounded verification.",
52017
52207
  recommended: true
52018
52208
  },
52019
- {
52020
- value: "automation",
52021
- label: "Test & automate",
52022
- description: "Preview readiness, run bounded checks, and keep evidence."
52023
- },
52024
52209
  {
52025
52210
  value: "device",
52026
52211
  label: "Device & app",
@@ -52039,7 +52224,28 @@ async function showHomeScreen(options) {
52039
52224
  { value: "exit", label: "Exit", description: "Close ADB Ready." }
52040
52225
  ];
52041
52226
  const sectionOptions = {
52042
- automation: [
52227
+ start: [
52228
+ {
52229
+ value: "dev",
52230
+ label: "Development session",
52231
+ description: "Prepare one target, ports, logs, and your project command.",
52232
+ recommended: true
52233
+ },
52234
+ {
52235
+ value: "setup-device",
52236
+ label: "Set up first device",
52237
+ description: "Follow a guided USB, Wireless debugging, or emulator setup."
52238
+ },
52239
+ {
52240
+ value: "connect",
52241
+ label: "Connect wirelessly",
52242
+ description: "Discover and verify an already paired Wireless debugging target."
52243
+ },
52244
+ {
52245
+ value: "pair",
52246
+ label: "Pair a new device",
52247
+ description: "Pair safely with Android's temporary six-digit code."
52248
+ },
52043
52249
  {
52044
52250
  value: "dev-plan",
52045
52251
  label: "Preview project plan",
@@ -52049,16 +52255,6 @@ async function showHomeScreen(options) {
52049
52255
  value: "run-help",
52050
52256
  label: "Run a bounded verification",
52051
52257
  description: "See how to gate a test on readiness and retain its evidence."
52052
- },
52053
- {
52054
- value: "sessions",
52055
- label: "Review recent runs",
52056
- description: "Find completed, failed, or interrupted project sessions."
52057
- },
52058
- {
52059
- value: "context",
52060
- label: "Create AI debug context",
52061
- description: "Compile the latest run into a compact redacted brief."
52062
52258
  }
52063
52259
  ],
52064
52260
  device: [
@@ -52076,16 +52272,6 @@ async function showHomeScreen(options) {
52076
52272
  value: "app-restart",
52077
52273
  label: "Restart project app",
52078
52274
  description: "Verify a clean stop and launch on the selected target."
52079
- },
52080
- {
52081
- value: "connect",
52082
- label: "Connect wirelessly",
52083
- description: "Discover and verify an already paired Wireless debugging target."
52084
- },
52085
- {
52086
- value: "pair",
52087
- label: "Pair a new target",
52088
- description: "Pair safely with Android's temporary six-digit code."
52089
52275
  }
52090
52276
  ],
52091
52277
  debug: [
@@ -52157,7 +52343,7 @@ async function showHomeScreen(options) {
52157
52343
  const visibleOptions = density === "minimal" ? currentOptions.map(({ description: _description, ...option }) => option) : currentOptions;
52158
52344
  const numericShortcut = `1-${String(Math.min(currentOptions.length, 9))} jump`;
52159
52345
  const selection2 = await selectOne({
52160
- title: atRoot ? "WHAT DO YOU WANT TO DO?" : `HOME / ${section === "device" ? "DEVICE & APP" : section === "debug" ? "DEBUG & EVIDENCE" : section === "automation" ? "TEST & AUTOMATE" : "PROJECT & SETUP"}`,
52346
+ title: atRoot ? "WHAT DO YOU WANT TO DO?" : `HOME / ${section === "start" ? "START" : section === "device" ? "DEVICE & APP" : section === "debug" ? "DEBUG & EVIDENCE" : "PROJECT & SETUP"}`,
52161
52347
  options: visibleOptions,
52162
52348
  input: options.input,
52163
52349
  sink: options.sink,
@@ -52188,7 +52374,7 @@ async function showHomeScreen(options) {
52188
52374
  }
52189
52375
  return { kind: "cancelled", reason: selection2.reason };
52190
52376
  }
52191
- if (atRoot && selection2.value !== "dev" && selection2.value !== "exit") {
52377
+ if (atRoot && selection2.value !== "exit") {
52192
52378
  section = selection2.value;
52193
52379
  continue;
52194
52380
  }
@@ -54463,14 +54649,14 @@ function parseArguments(argv) {
54463
54649
  } else if (uiAction === "swipe" || uiAction === "scroll") {
54464
54650
  const direction = uiOperands[0];
54465
54651
  if ((uiOperands.length === 1 || uiAction === "scroll" && uiOperands.length === 2) && direction !== undefined && new Set(["down", "left", "right", "up"]).has(direction)) {
54466
- const selected = selector(uiOperands[1]);
54467
- if (uiAction === "scroll" && uiOperands.length === 2 && selected === undefined) {
54652
+ const selected2 = selector(uiOperands[1]);
54653
+ if (uiAction === "scroll" && uiOperands.length === 2 && selected2 === undefined) {
54468
54654
  return failure4("CLI_USAGE", "ui scroll accepts an optional valid selector after direction.");
54469
54655
  }
54470
54656
  uiRequest = uiAction === "scroll" ? {
54471
54657
  action: "scroll",
54472
54658
  direction,
54473
- ...selected === undefined ? {} : { selector: selected }
54659
+ ...selected2 === undefined ? {} : { selector: selected2 }
54474
54660
  } : { action: "swipe", direction };
54475
54661
  } else if (uiAction === "swipe" && uiOperands.length === 4) {
54476
54662
  const values2 = uiOperands.map(coordinate);
@@ -54489,13 +54675,13 @@ function parseArguments(argv) {
54489
54675
  }
54490
54676
  } else if (uiAction === "type" || uiAction === "fill") {
54491
54677
  if (uiAction === "fill") {
54492
- const selected = selector(uiOperands[0]);
54493
- if (uiOperands.length !== 2 || selected === undefined) {
54678
+ const selected2 = selector(uiOperands[0]);
54679
+ if (uiOperands.length !== 2 || selected2 === undefined) {
54494
54680
  return failure4("CLI_USAGE", "ui fill requires one valid selector and one text value.");
54495
54681
  }
54496
54682
  uiRequest = {
54497
54683
  action: "fill",
54498
- selector: selected,
54684
+ selector: selected2,
54499
54685
  text: uiOperands[1] ?? "",
54500
54686
  ...uiSubmit ? { submit: true } : {}
54501
54687
  };
@@ -54515,13 +54701,13 @@ function parseArguments(argv) {
54515
54701
  }
54516
54702
  uiRequest = { action: "press", key: key2 };
54517
54703
  } else if (uiAction === "find" || uiAction === "get" || uiAction === "assert" || uiAction === "clear") {
54518
- const selected = selector(uiOperands[0]);
54519
- if (uiOperands.length !== 1 || selected === undefined) {
54704
+ const selected2 = selector(uiOperands[0]);
54705
+ if (uiOperands.length !== 1 || selected2 === undefined) {
54520
54706
  return failure4("CLI_USAGE", `ui ${uiAction} requires one valid selector.`);
54521
54707
  }
54522
- uiRequest = uiAction === "find" ? { action: "find", selector: selected } : uiAction === "get" ? { action: "get", selector: selected } : uiAction === "clear" ? { action: "clear", selector: selected } : {
54708
+ uiRequest = uiAction === "find" ? { action: "find", selector: selected2 } : uiAction === "get" ? { action: "get", selector: selected2 } : uiAction === "clear" ? { action: "clear", selector: selected2 } : {
54523
54709
  action: "assert",
54524
- selector: selected,
54710
+ selector: selected2,
54525
54711
  ...uiWaitState === undefined ? {} : { state: uiWaitState }
54526
54712
  };
54527
54713
  } else if (uiAction === "compare") {
@@ -55260,6 +55446,26 @@ async function runInteractiveSession(io, dependencies, terminal2, signal) {
55260
55446
  if (home.action === "exit") {
55261
55447
  return 0 /* Success */;
55262
55448
  }
55449
+ if (home.action === "setup-device") {
55450
+ clearInteractiveScreen(io.error, terminal2);
55451
+ const setup = await showDeviceSetup({
55452
+ input: io.input,
55453
+ sink: io.error,
55454
+ capabilities: terminal2,
55455
+ ...signal === undefined ? {} : { signal }
55456
+ });
55457
+ if (setup.kind === "cancelled" && setup.reason !== "escape") {
55458
+ return 130 /* Interrupted */;
55459
+ }
55460
+ if (setup.kind === "action") {
55461
+ clearInteractiveScreen(io.error, terminal2);
55462
+ await runCliInternal([setup.action], io, dependencies, signal);
55463
+ }
55464
+ if (signal?.aborted === true)
55465
+ return 130 /* Interrupted */;
55466
+ presentation = "menu";
55467
+ continue;
55468
+ }
55263
55469
  const actionArguments = {
55264
55470
  "app-info": ["app", "info"],
55265
55471
  "app-restart": ["app", "restart"],
@@ -55702,13 +55908,13 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
55702
55908
  }
55703
55909
  }
55704
55910
  const shouldPrompt = options.select || (options.command === "app" && options.appAction !== "resolve" || options.command === "apps" || options.command === "capture" || options.command === "dev" || options.command === "run" || options.command === "inspect" || options.command === "logs" || options.command === "open" || options.command === "ui") && errorCapabilities.interactive && inventory.result.data?.selected === undefined && selectable.length !== 1;
55705
- const selected = shouldPrompt ? await selectDevice(inventory, io, errorCapabilities, lastTarget, signal) : inventory;
55706
- if (!selected.result.ok || selected.result.data?.selected === undefined) {
55911
+ const selected2 = shouldPrompt ? await selectDevice(inventory, io, errorCapabilities, lastTarget, signal) : inventory;
55912
+ if (!selected2.result.ok || selected2.result.data?.selected === undefined) {
55707
55913
  if (shouldPrompt) {
55708
55914
  if (autonomousRun) {
55709
- return await finishAutonomousFailure([...selected.result.problems], selected.exitCode);
55915
+ return await finishAutonomousFailure([...selected2.result.problems], selected2.exitCode);
55710
55916
  }
55711
- renderResult({ ...selected.result, command: options.command }, {
55917
+ renderResult({ ...selected2.result, command: options.command }, {
55712
55918
  format: "human",
55713
55919
  capabilities: errorCapabilities,
55714
55920
  sink: io.error,
@@ -55716,10 +55922,10 @@ async function runCliInternal(argv, io, dependencies = {}, signal) {
55716
55922
  });
55717
55923
  await preparedAvd?.release(signal);
55718
55924
  preparedAvd = undefined;
55719
- return selected.exitCode;
55925
+ return selected2.exitCode;
55720
55926
  }
55721
55927
  } else {
55722
- preparedSelection = selected.result.data.selected;
55928
+ preparedSelection = selected2.result.data.selected;
55723
55929
  const transport = preparedSelection.transport;
55724
55930
  if (options.avdName !== undefined || transport.transportId === undefined) {
55725
55931
  config4.targetSelector = transport.serial;
@@ -56332,4 +56538,4 @@ try {
56332
56538
  process16.removeListener("SIGTERM", abort);
56333
56539
  }
56334
56540
 
56335
- //# debugId=3E670EE2440F454C64756E2164756E21
56541
+ //# debugId=3D497281264093F564756E2164756E21
@@ -28,7 +28,12 @@ npx adb-ready doctor
28
28
  ```
29
29
 
30
30
  Run `npx adb-ready` without a command for the interactive workflow home. Its
31
- layout automatically switches to a compact form in narrow terminal panes.
31
+ layout automatically switches to a compact form in narrow terminal panes. Open
32
+ **Start** for development sessions, bounded verification, wireless connection,
33
+ pairing, or **Set up first device**. The guided setup walks through a physical
34
+ Android device over USB, Android 11+ Wireless debugging, or an existing Android
35
+ emulator, then hands off to the normal verified target workflow. The remaining
36
+ sections keep device operations, debugging, and project setup separate.
32
37
 
33
38
  The full source build can be tested from a repository checkout:
34
39
 
@@ -3,6 +3,10 @@
3
3
  ADB Ready models USB devices, emulators, TCP transports, and TLS Wireless
4
4
  debugging transports behind one deterministic selection contract.
5
5
 
6
+ For a first connection, run `adb-ready` and open **Start → Set up first
7
+ device**. The interactive guide explains the Android-side steps, then reuses the
8
+ same verified `devices`, `pair`, or `connect` workflow documented below.
9
+
6
10
  ## Inspect visible targets
7
11
 
8
12
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adb-ready",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Agent-ready Android CLI for reliable ADB sessions, app automation, and verified evidence.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "title": "ADB Ready agent tool contract",
3
3
  "schemaVersion": 1,
4
- "packageVersion": "0.5.0",
4
+ "packageVersion": "0.5.1",
5
5
  "protocolVersion": "2025-11-25",
6
6
  "transport": "stdio",
7
7
  "tools": [