@kaynooo/cli-doctor 0.10.0 → 0.12.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 (3) hide show
  1. package/dist/cli-bin.js +118 -26
  2. package/dist/cli.js +110 -16
  3. package/package.json +2 -2
package/dist/cli-bin.js CHANGED
@@ -481,7 +481,6 @@ var DASHES = /^-+/;
481
481
  function jsonOption(holds) {
482
482
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
483
483
  }
484
-
485
484
  class Exit extends Error {
486
485
  code;
487
486
  hint;
@@ -1480,6 +1479,28 @@ var BASH_FUNCTION = `__kaynooo_cli_complete() {
1480
1479
  done
1481
1480
  COMPREPLY+=($(compgen -W "$words" -- "$cur"))
1482
1481
  }`;
1482
+ var ZSH_FUNCTION = `__kaynooo_cli_complete() {
1483
+ local line
1484
+ local -a described plain
1485
+ for line in \${(f)"$("\${words[1]}" __complete "\${(@)words[2,CURRENT]}" 2>/dev/null)"}; do
1486
+ if [[ $line == __files__ ]]; then
1487
+ _files
1488
+ elif [[ $line == *$'\\t'* ]]; then
1489
+ described+=("\${\${line%%$'\\t'*}//:/\\\\:}:\${line#*$'\\t'}")
1490
+ else
1491
+ plain+=("$line")
1492
+ fi
1493
+ done
1494
+ (( \${#described} )) && _describe -V value described
1495
+ (( \${#plain} )) && compadd -- "\${plain[@]}"
1496
+ return 0
1497
+ }`;
1498
+ function zshScript(bins) {
1499
+ return `# Generated by \`cli completions zsh\`
1500
+ ${ZSH_FUNCTION}
1501
+ compdef __kaynooo_cli_complete ${bins.join(" ")}
1502
+ `;
1503
+ }
1483
1504
  function bashScript(bins) {
1484
1505
  return `# Generated by \`cli completions bash\`
1485
1506
  ${BASH_FUNCTION}
@@ -1578,13 +1599,68 @@ import process16 from "process";
1578
1599
 
1579
1600
  // ../kit/src/prompt.ts
1580
1601
  import process15 from "process";
1581
- var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
1582
- var CONTROL = /^[\x00-\x1F\x7F]/;
1602
+
1603
+ // ../kit/src/picker/score.ts
1604
+ var BOUNDARY_AFTER = new Set([" ", "/", "-", "_", ".", ":", "\\", "(", "[", ",", "\t"]);
1605
+ var NONE = Number.NEGATIVE_INFINITY;
1606
+
1607
+ // ../kit/src/picker/state.ts
1608
+ var IS_CONTROL = /[\x00-\x1F\x7F-\x9F]/;
1609
+ var PASTE_START = "\x1B[200~";
1610
+ var PASTE_END = "\x1B[201~";
1611
+ var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|\x1B[\s\S]|[\s\S]/gu;
1612
+ var KEYS = {
1613
+ "\x1B[A": "up",
1614
+ "\x1BOA": "up",
1615
+ "\x10": "up",
1616
+ "\x1B[B": "down",
1617
+ "\x1BOB": "down",
1618
+ "\x0E": "down",
1619
+ "\x1B[5~": "pageup",
1620
+ "\x1B[6~": "pagedown",
1621
+ "\x1B[H": "home",
1622
+ "\x1B[1~": "home",
1623
+ "\x1BOH": "home",
1624
+ "\x1B[F": "end",
1625
+ "\x1B[4~": "end",
1626
+ "\x1BOF": "end",
1627
+ "\r": "enter",
1628
+ "\n": "enter",
1629
+ "\x03": "cancel",
1630
+ "\x1B": "cancel",
1631
+ "\x7F": "erase",
1632
+ "\b": "erase",
1633
+ "\x15": "clear",
1634
+ "\x17": "word",
1635
+ "\t": "tab",
1636
+ "\x1F": "help",
1637
+ "\x1BOP": "help",
1638
+ "\x1B[11~": "help"
1639
+ };
1640
+ function parseKeys(input, paste = { active: false }, ctrls = []) {
1641
+ const codes = new Map(ctrls.map((name) => [String.fromCharCode(name.codePointAt(name.length - 1) - 96), name]));
1642
+ return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => {
1643
+ const named = codes.get(sequence);
1644
+ const key = named === undefined ? KEYS[sequence] : { ctrl: named };
1645
+ if (sequence === PASTE_START || sequence === PASTE_END) {
1646
+ paste.active = sequence === PASTE_START;
1647
+ return [];
1648
+ }
1649
+ if (paste.active && (sequence === "\r" || sequence === `
1650
+ `))
1651
+ return [];
1652
+ if (key !== undefined)
1653
+ return [key];
1654
+ return IS_CONTROL.test(sequence) || sequence.startsWith("\x1B") ? [] : [{ text: sequence }];
1655
+ });
1656
+ }
1657
+
1658
+ // ../kit/src/prompt.ts
1583
1659
  var WORDS = /\s+/;
1584
1660
  var MAX_ROWS = 10;
1585
- var KEYS = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
1586
- function readKeys(input) {
1587
- return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => KEYS[sequence] ?? (CONTROL.test(sequence) ? [] : [{ text: sequence }]));
1661
+ var PROMPT_KEYS = new Set(["up", "down", "pageup", "pagedown", "home", "end", "enter", "cancel", "erase"]);
1662
+ function readKeys(input, paste = { active: false }) {
1663
+ return parseKeys(input, paste).filter((key) => typeof key === "object" ? "text" in key : PROMPT_KEYS.has(key));
1588
1664
  }
1589
1665
  function flip(checked, value) {
1590
1666
  const next = new Set(checked);
@@ -1683,6 +1759,12 @@ class List {
1683
1759
  this.cursor = (this.cursor + (key === "up" ? this.matches.length - 1 : 1)) % this.matches.length;
1684
1760
  return;
1685
1761
  }
1762
+ if (key === "home" || key === "end" || key === "pageup" || key === "pagedown") {
1763
+ const last = this.matches.length - 1;
1764
+ const to = { home: 0, end: last, pageup: this.cursor - MAX_ROWS, pagedown: this.cursor + MAX_ROWS }[key];
1765
+ this.cursor = Math.max(0, Math.min(last, to));
1766
+ return;
1767
+ }
1686
1768
  if (key === "erase")
1687
1769
  this.filter([...this.query].slice(0, -1).join(""));
1688
1770
  else if (typeof key === "object")
@@ -1724,22 +1806,29 @@ async function ask(question, { hint, terminal = process15 }, render, press) {
1724
1806
  if (!isInteractive(terminal))
1725
1807
  fail(`cannot ask "${question}" off a terminal`, 2, hint);
1726
1808
  const colors = paint(stderr);
1727
- let drawn = 0;
1809
+ let drawn = [];
1810
+ let current = "asking";
1728
1811
  let outcome;
1812
+ const columns = () => stderr.columns || 80;
1729
1813
  const draw = (state) => {
1730
- const frame = render(colors, state, { columns: stderr.columns || 80, rows: stderr.rows || 24 });
1731
- stderr.write(`${drawn > 1 ? `\x1B[${drawn - 1}A` : ""}\r\x1B[0J${frame.join(`
1814
+ const frame = render(colors, state, { columns: columns(), rows: stderr.rows || 24 });
1815
+ const rows = drawn.reduce((sum, line) => sum + Math.max(1, Math.ceil(Bun.stringWidth(line) / columns())), 0);
1816
+ stderr.write(`${rows > 1 ? `\x1B[${rows - 1}A` : ""}\r\x1B[0J${frame.join(`
1732
1817
  `)}`);
1733
- drawn = frame.length;
1818
+ drawn = frame;
1819
+ current = state;
1734
1820
  };
1821
+ const redraw = () => draw(current);
1822
+ const paste = { active: false };
1735
1823
  try {
1824
+ stderr.on?.("resize", redraw);
1736
1825
  stdin.setRawMode(true);
1737
1826
  stdin.resume();
1738
- stderr.write("\x1B[?25l");
1827
+ stderr.write("\x1B[?25l\x1B[?2004h");
1739
1828
  draw("asking");
1740
1829
  return await new Promise((resolve) => {
1741
1830
  function onData(data) {
1742
- for (const key of readKeys(data.toString())) {
1831
+ for (const key of readKeys(data.toString(), paste)) {
1743
1832
  const pressed = press(key);
1744
1833
  if (pressed !== undefined) {
1745
1834
  settle(pressed);
@@ -1761,9 +1850,10 @@ async function ask(question, { hint, terminal = process15 }, render, press) {
1761
1850
  stdin.on("end", onEnd);
1762
1851
  });
1763
1852
  } finally {
1853
+ stderr.off?.("resize", redraw);
1764
1854
  draw(outcome ?? "cancel");
1765
1855
  stderr.write(`
1766
- \x1B[?25h`);
1856
+ \x1B[?25h\x1B[?2004l`);
1767
1857
  stdin.setRawMode(false);
1768
1858
  stdin.pause();
1769
1859
  }
@@ -1854,7 +1944,7 @@ async function install(host, options, recheck, choosing = ask2) {
1854
1944
  console.log("everything is installed");
1855
1945
  return 0;
1856
1946
  }
1857
- if (pool.length === 0 && options.names.length > 0) {
1947
+ if (pool.length === 0 && (options.names.length > 0 || !isInteractive())) {
1858
1948
  console.log("everything is installed");
1859
1949
  return 0;
1860
1950
  }
@@ -1917,10 +2007,14 @@ async function install(host, options, recheck, choosing = ask2) {
1917
2007
  return 1;
1918
2008
  }
1919
2009
  }
1920
- if (removing.plugin && host.which("claude") !== null) {
2010
+ if (removing.plugin && host.which("claude") === null) {
2011
+ console.error(`${yellow("not run")} ${PLUGIN_UNINSTALL.join(" ")}: claude is not on PATH`);
2012
+ } else if (removing.plugin) {
1921
2013
  const ran = await host.run(PLUGIN_UNINSTALL);
1922
- if (ran.code !== 0)
2014
+ if (ran.code !== 0) {
1923
2015
  console.error(`${red("failed")} ${PLUGIN_UNINSTALL.join(" ")}: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
2016
+ return 1;
2017
+ }
1924
2018
  }
1925
2019
  console.log(`
1926
2020
  after the install:`);
@@ -2045,26 +2139,24 @@ async function list(args, host = hostHere()) {
2045
2139
  print2(listed);
2046
2140
  return 0;
2047
2141
  }
2048
- var COMPLETIONS_USAGE = `cli completions <fish|bash> [--install] [--dir <folder>]: shell completions for every installed bin of the @kaynooo/cli-* packages
2142
+ var COMPLETIONS_USAGE = `cli completions <fish|bash|zsh> [--install] [--dir <folder>]: shell completions for every installed bin of the @kaynooo/cli-* packages
2049
2143
 
2050
2144
  Each bin completes itself through \`<bin> __complete\`; this writes the glue a shell needs per bin.
2051
2145
  fish: without --install the files print on stdout, with it they go to ~/.cache/fish/kaynooo-cli (or $XDG_CACHE_HOME), one <bin>.fish each, the stale ones this command wrote taken out. That folder has to be in fish_complete_path, first so a completion fish ships for the same name does not win: \`set -p fish_complete_path ~/.cache/fish/kaynooo-cli\`.
2052
2146
  bash: one script on stdout, sourced from ~/.bashrc with \`source <(cli completions bash)\`; --install and --dir are fish's.
2053
- zsh: has no script of its own, it reads the bash one through bashcompinit: \`autoload -U +X compinit && compinit\`, \`autoload -U +X bashcompinit && bashcompinit\`, then the bash line above in ~/.zshrc (untested, zsh is not installed on the machine this was written on).`;
2147
+ zsh: one script on stdout like bash's, after compinit in ~/.zshrc: \`autoload -U +X compinit && compinit\`, then \`source <(cli completions zsh)\` (written without zsh at hand to run it, so untested).`;
2054
2148
  var COMPLETIONS_OPTIONS = {
2055
2149
  install: { type: "boolean", description: "write the files to the folder instead of printing them" },
2056
2150
  dir: { type: "string", short: "d", description: "the folder to write to, instead of ~/.cache/fish/kaynooo-cli" }
2057
2151
  };
2058
2152
  async function completions(args, host = hostHere()) {
2059
- const { values, positionals } = parse(args, COMPLETIONS_USAGE, COMPLETIONS_OPTIONS, { positionals: ["<shell>"], exits: [[0, "it printed or wrote them"], [2, "a usage error or a shell without a script"]], candidates: { "<shell>": () => ["fish", "bash"] }, examples: ["cli completions fish", ["cli completions fish --install", "write one file per bin"], ["source <(cli completions bash)", "in ~/.bashrc"]] });
2060
- if (positionals[0] === "zsh")
2061
- badUsage("zsh has no script of its own: it reads the bash one through bashcompinit, see cli completions --help");
2062
- if (positionals[0] !== "fish" && positionals[0] !== "bash")
2063
- badUsage("completions exist for fish and bash: cli completions fish");
2064
- if (positionals[0] === "bash") {
2153
+ const { values, positionals } = parse(args, COMPLETIONS_USAGE, COMPLETIONS_OPTIONS, { positionals: ["<shell>"], exits: [[0, "it printed or wrote them"], [2, "a usage error or a shell without a script"]], candidates: { "<shell>": () => ["fish", "bash", "zsh"] }, examples: ["cli completions fish", ["cli completions fish --install", "write one file per bin"], ["source <(cli completions bash)", "in ~/.bashrc"], ["source <(cli completions zsh)", "in ~/.zshrc after compinit"]] });
2154
+ if (positionals[0] !== "fish" && positionals[0] !== "bash" && positionals[0] !== "zsh")
2155
+ badUsage("completions exist for fish, bash and zsh: cli completions fish");
2156
+ if (positionals[0] === "bash" || positionals[0] === "zsh") {
2065
2157
  if (values.install === true || values.dir !== undefined)
2066
- badUsage("--install and --dir are for fish: source the bash script instead");
2067
- console.log(bashScript(installedBins(host)).trimEnd());
2158
+ badUsage(`--install and --dir are for fish: source the ${positionals[0]} script instead`);
2159
+ console.log((positionals[0] === "zsh" ? zshScript : bashScript)(installedBins(host)).trimEnd());
2068
2160
  return 0;
2069
2161
  }
2070
2162
  if (values.install !== true) {
package/dist/cli.js CHANGED
@@ -481,7 +481,6 @@ var DASHES = /^-+/;
481
481
  function jsonOption(holds) {
482
482
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
483
483
  }
484
-
485
484
  class Exit extends Error {
486
485
  code;
487
486
  hint;
@@ -1318,6 +1317,28 @@ var BASH_FUNCTION = `__kaynooo_cli_complete() {
1318
1317
  done
1319
1318
  COMPREPLY+=($(compgen -W "$words" -- "$cur"))
1320
1319
  }`;
1320
+ var ZSH_FUNCTION = `__kaynooo_cli_complete() {
1321
+ local line
1322
+ local -a described plain
1323
+ for line in \${(f)"$("\${words[1]}" __complete "\${(@)words[2,CURRENT]}" 2>/dev/null)"}; do
1324
+ if [[ $line == __files__ ]]; then
1325
+ _files
1326
+ elif [[ $line == *$'\\t'* ]]; then
1327
+ described+=("\${\${line%%$'\\t'*}//:/\\\\:}:\${line#*$'\\t'}")
1328
+ else
1329
+ plain+=("$line")
1330
+ fi
1331
+ done
1332
+ (( \${#described} )) && _describe -V value described
1333
+ (( \${#plain} )) && compadd -- "\${plain[@]}"
1334
+ return 0
1335
+ }`;
1336
+ function zshScript(bins) {
1337
+ return `# Generated by \`cli completions zsh\`
1338
+ ${ZSH_FUNCTION}
1339
+ compdef __kaynooo_cli_complete ${bins.join(" ")}
1340
+ `;
1341
+ }
1321
1342
  function bashScript(bins) {
1322
1343
  return `# Generated by \`cli completions bash\`
1323
1344
  ${BASH_FUNCTION}
@@ -1576,13 +1597,68 @@ import process16 from "process";
1576
1597
 
1577
1598
  // ../kit/src/prompt.ts
1578
1599
  import process15 from "process";
1579
- var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
1580
- var CONTROL = /^[\x00-\x1F\x7F]/;
1600
+
1601
+ // ../kit/src/picker/score.ts
1602
+ var BOUNDARY_AFTER = new Set([" ", "/", "-", "_", ".", ":", "\\", "(", "[", ",", "\t"]);
1603
+ var NONE = Number.NEGATIVE_INFINITY;
1604
+
1605
+ // ../kit/src/picker/state.ts
1606
+ var IS_CONTROL = /[\x00-\x1F\x7F-\x9F]/;
1607
+ var PASTE_START = "\x1B[200~";
1608
+ var PASTE_END = "\x1B[201~";
1609
+ var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|\x1B[\s\S]|[\s\S]/gu;
1610
+ var KEYS = {
1611
+ "\x1B[A": "up",
1612
+ "\x1BOA": "up",
1613
+ "\x10": "up",
1614
+ "\x1B[B": "down",
1615
+ "\x1BOB": "down",
1616
+ "\x0E": "down",
1617
+ "\x1B[5~": "pageup",
1618
+ "\x1B[6~": "pagedown",
1619
+ "\x1B[H": "home",
1620
+ "\x1B[1~": "home",
1621
+ "\x1BOH": "home",
1622
+ "\x1B[F": "end",
1623
+ "\x1B[4~": "end",
1624
+ "\x1BOF": "end",
1625
+ "\r": "enter",
1626
+ "\n": "enter",
1627
+ "\x03": "cancel",
1628
+ "\x1B": "cancel",
1629
+ "\x7F": "erase",
1630
+ "\b": "erase",
1631
+ "\x15": "clear",
1632
+ "\x17": "word",
1633
+ "\t": "tab",
1634
+ "\x1F": "help",
1635
+ "\x1BOP": "help",
1636
+ "\x1B[11~": "help"
1637
+ };
1638
+ function parseKeys(input, paste = { active: false }, ctrls = []) {
1639
+ const codes = new Map(ctrls.map((name) => [String.fromCharCode(name.codePointAt(name.length - 1) - 96), name]));
1640
+ return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => {
1641
+ const named = codes.get(sequence);
1642
+ const key = named === undefined ? KEYS[sequence] : { ctrl: named };
1643
+ if (sequence === PASTE_START || sequence === PASTE_END) {
1644
+ paste.active = sequence === PASTE_START;
1645
+ return [];
1646
+ }
1647
+ if (paste.active && (sequence === "\r" || sequence === `
1648
+ `))
1649
+ return [];
1650
+ if (key !== undefined)
1651
+ return [key];
1652
+ return IS_CONTROL.test(sequence) || sequence.startsWith("\x1B") ? [] : [{ text: sequence }];
1653
+ });
1654
+ }
1655
+
1656
+ // ../kit/src/prompt.ts
1581
1657
  var WORDS = /\s+/;
1582
1658
  var MAX_ROWS = 10;
1583
- var KEYS = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
1584
- function readKeys(input) {
1585
- return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => KEYS[sequence] ?? (CONTROL.test(sequence) ? [] : [{ text: sequence }]));
1659
+ var PROMPT_KEYS = new Set(["up", "down", "pageup", "pagedown", "home", "end", "enter", "cancel", "erase"]);
1660
+ function readKeys(input, paste = { active: false }) {
1661
+ return parseKeys(input, paste).filter((key) => typeof key === "object" ? "text" in key : PROMPT_KEYS.has(key));
1586
1662
  }
1587
1663
  function flip(checked, value) {
1588
1664
  const next = new Set(checked);
@@ -1681,6 +1757,12 @@ class List {
1681
1757
  this.cursor = (this.cursor + (key === "up" ? this.matches.length - 1 : 1)) % this.matches.length;
1682
1758
  return;
1683
1759
  }
1760
+ if (key === "home" || key === "end" || key === "pageup" || key === "pagedown") {
1761
+ const last = this.matches.length - 1;
1762
+ const to = { home: 0, end: last, pageup: this.cursor - MAX_ROWS, pagedown: this.cursor + MAX_ROWS }[key];
1763
+ this.cursor = Math.max(0, Math.min(last, to));
1764
+ return;
1765
+ }
1684
1766
  if (key === "erase")
1685
1767
  this.filter([...this.query].slice(0, -1).join(""));
1686
1768
  else if (typeof key === "object")
@@ -1722,22 +1804,29 @@ async function ask(question, { hint, terminal = process15 }, render, press) {
1722
1804
  if (!isInteractive(terminal))
1723
1805
  fail(`cannot ask "${question}" off a terminal`, 2, hint);
1724
1806
  const colors = paint(stderr);
1725
- let drawn = 0;
1807
+ let drawn = [];
1808
+ let current = "asking";
1726
1809
  let outcome;
1810
+ const columns = () => stderr.columns || 80;
1727
1811
  const draw = (state) => {
1728
- const frame = render(colors, state, { columns: stderr.columns || 80, rows: stderr.rows || 24 });
1729
- stderr.write(`${drawn > 1 ? `\x1B[${drawn - 1}A` : ""}\r\x1B[0J${frame.join(`
1812
+ const frame = render(colors, state, { columns: columns(), rows: stderr.rows || 24 });
1813
+ const rows = drawn.reduce((sum, line) => sum + Math.max(1, Math.ceil(Bun.stringWidth(line) / columns())), 0);
1814
+ stderr.write(`${rows > 1 ? `\x1B[${rows - 1}A` : ""}\r\x1B[0J${frame.join(`
1730
1815
  `)}`);
1731
- drawn = frame.length;
1816
+ drawn = frame;
1817
+ current = state;
1732
1818
  };
1819
+ const redraw = () => draw(current);
1820
+ const paste = { active: false };
1733
1821
  try {
1822
+ stderr.on?.("resize", redraw);
1734
1823
  stdin.setRawMode(true);
1735
1824
  stdin.resume();
1736
- stderr.write("\x1B[?25l");
1825
+ stderr.write("\x1B[?25l\x1B[?2004h");
1737
1826
  draw("asking");
1738
1827
  return await new Promise((resolve) => {
1739
1828
  function onData(data) {
1740
- for (const key of readKeys(data.toString())) {
1829
+ for (const key of readKeys(data.toString(), paste)) {
1741
1830
  const pressed = press(key);
1742
1831
  if (pressed !== undefined) {
1743
1832
  settle(pressed);
@@ -1759,9 +1848,10 @@ async function ask(question, { hint, terminal = process15 }, render, press) {
1759
1848
  stdin.on("end", onEnd);
1760
1849
  });
1761
1850
  } finally {
1851
+ stderr.off?.("resize", redraw);
1762
1852
  draw(outcome ?? "cancel");
1763
1853
  stderr.write(`
1764
- \x1B[?25h`);
1854
+ \x1B[?25h\x1B[?2004l`);
1765
1855
  stdin.setRawMode(false);
1766
1856
  stdin.pause();
1767
1857
  }
@@ -1852,7 +1942,7 @@ async function install(host, options, recheck, choosing = ask2) {
1852
1942
  console.log("everything is installed");
1853
1943
  return 0;
1854
1944
  }
1855
- if (pool.length === 0 && options.names.length > 0) {
1945
+ if (pool.length === 0 && (options.names.length > 0 || !isInteractive())) {
1856
1946
  console.log("everything is installed");
1857
1947
  return 0;
1858
1948
  }
@@ -1915,10 +2005,14 @@ async function install(host, options, recheck, choosing = ask2) {
1915
2005
  return 1;
1916
2006
  }
1917
2007
  }
1918
- if (removing.plugin && host.which("claude") !== null) {
2008
+ if (removing.plugin && host.which("claude") === null) {
2009
+ console.error(`${yellow("not run")} ${PLUGIN_UNINSTALL.join(" ")}: claude is not on PATH`);
2010
+ } else if (removing.plugin) {
1919
2011
  const ran = await host.run(PLUGIN_UNINSTALL);
1920
- if (ran.code !== 0)
2012
+ if (ran.code !== 0) {
1921
2013
  console.error(`${red("failed")} ${PLUGIN_UNINSTALL.join(" ")}: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
2014
+ return 1;
2015
+ }
1922
2016
  }
1923
2017
  console.log(`
1924
2018
  after the install:`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaynooo/cli-doctor",
3
3
  "type": "module",
4
- "version": "0.10.0",
4
+ "version": "0.12.0",
5
5
  "description": "doctor: checks the @kaynooo/cli-* install of a host: each bin's version against the registry, tea's login to Forgejo, the Claude Code hooks and the Bun floor; cli list: every bin in one line, read from its own help.",
6
6
  "author": "Kaynooo <contact@kaynooo.fr>",
7
7
  "license": "MIT",
@@ -37,6 +37,6 @@
37
37
  "doctor": "$npm_execpath src/cli.ts"
38
38
  },
39
39
  "devDependencies": {
40
- "@kaynooo/cli-kit": "0.23.0"
40
+ "@kaynooo/cli-kit": "0.25.0"
41
41
  }
42
42
  }