fpf-cli 1.6.7 → 1.6.9

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/README.md +7 -0
  2. package/fpf +117 -73
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -38,6 +38,8 @@ On every OS, default auto mode now includes all supported package managers that
38
38
 
39
39
  For no-query startup (`fpf`), each manager uses a lighter default query and per-manager result cap to keep startup responsive.
40
40
 
41
+ Live reload is enabled by default for single-manager mode (`--manager`), and disabled by default in all-manager mode to avoid lag/flicker while navigating.
42
+
41
43
  ## Supported Managers
42
44
 
43
45
  - Linux: `apt`, `dnf`, `pacman`, `zypper`, `emerge`
@@ -68,6 +70,7 @@ For no-query startup (`fpf`), each manager uses a lighter default query and per-
68
70
  - `-l, --list-installed` list installed packages
69
71
  - `-R, --remove` remove selected packages
70
72
  - `-U, --update` run update/upgrade flow
73
+ - `-v, --version` print version and exit
71
74
  - `-h, --help` show help
72
75
 
73
76
  ## Keybinds
@@ -78,8 +81,12 @@ For no-query startup (`fpf`), each manager uses a lighter default query and per-
78
81
  - `ctrl-n` next selected item
79
82
  - `ctrl-b` previous selected item
80
83
 
84
+ Installed packages are marked with `*` in the result list.
85
+
81
86
  ## Notes
82
87
 
83
88
  - Requires: `bash` + `fzf`
84
89
  - If `fzf` is missing, `fpf` auto-installs it using a compatible detected manager.
85
90
  - Root managers (`apt`, `dnf`, `pacman`, `zypper`, `emerge`, `snap`) use `sudo` when needed.
91
+ - `FPF_DYNAMIC_RELOAD`: `single` (default), `always`, or `never`
92
+ - `FPF_DISABLE_INSTALLED_CACHE=1` disables installed-package marker cache
package/fpf CHANGED
@@ -3,9 +3,11 @@
3
3
  set -euo pipefail
4
4
 
5
5
  SCRIPT_NAME="fpf"
6
+ SCRIPT_VERSION="1.6.9"
6
7
  TMP_ROOT="${TMPDIR:-/tmp}/fpf"
7
- HELP_FILE="${TMP_ROOT}/help"
8
- KBINDS_FILE="${TMP_ROOT}/keybinds"
8
+ SESSION_TMP_ROOT=""
9
+ HELP_FILE=""
10
+ KBINDS_FILE=""
9
11
 
10
12
  ACTION="search"
11
13
  MANAGER_OVERRIDE=""
@@ -28,6 +30,22 @@ ensure_tmp_root() {
28
30
  mkdir -p "${TMP_ROOT}"
29
31
  }
30
32
 
33
+ initialize_session_tmp_root() {
34
+ if [[ -n "${SESSION_TMP_ROOT}" ]]; then
35
+ return
36
+ fi
37
+
38
+ SESSION_TMP_ROOT="$(mktemp -d "${TMP_ROOT}/session.XXXXXX")"
39
+ HELP_FILE="${SESSION_TMP_ROOT}/help"
40
+ KBINDS_FILE="${SESSION_TMP_ROOT}/keybinds"
41
+ }
42
+
43
+ cleanup_session_tmp_root() {
44
+ if [[ -n "${SESSION_TMP_ROOT}" && -d "${SESSION_TMP_ROOT}" ]]; then
45
+ rm -rf "${SESSION_TMP_ROOT}"
46
+ fi
47
+ }
48
+
31
49
  run_as_root() {
32
50
  if [[ "${EUID}" -eq 0 ]]; then
33
51
  "$@"
@@ -412,7 +430,7 @@ build_help_file() {
412
430
  local default_managers="$1"
413
431
 
414
432
  cat >"${HELP_FILE}" <<EOF
415
- ${SCRIPT_NAME} - ultimate fuzzy package finder
433
+ ${SCRIPT_NAME} - fuzzy package finder
416
434
 
417
435
  Syntax:
418
436
  ${SCRIPT_NAME} [manager option] [action option] [query]
@@ -428,6 +446,7 @@ Action options:
428
446
  -l, --list-installed Fuzzy-search installed packages and show details
429
447
  -R, --remove Fuzzy-search installed packages and remove selected
430
448
  -U, --update Run manager update/upgrade flow
449
+ -v, --version Print version and exit
431
450
  -h, --help Show this help
432
451
 
433
452
  Manager options (one or two-letter style):
@@ -479,12 +498,19 @@ print_help() {
479
498
  cat "${HELP_FILE}"
480
499
  }
481
500
 
501
+ print_version() {
502
+ printf "%s %s\n" "${SCRIPT_NAME}" "${SCRIPT_VERSION}"
503
+ }
504
+
482
505
  parse_args() {
483
506
  while (($#)); do
484
507
  case "$1" in
485
508
  -h|--help)
486
509
  ACTION="help"
487
510
  ;;
511
+ -v|--version)
512
+ ACTION="version"
513
+ ;;
488
514
  -l|--list-installed)
489
515
  ACTION="list"
490
516
  ;;
@@ -582,6 +608,23 @@ join_query() {
582
608
  printf "%s" "${query}"
583
609
  }
584
610
 
611
+ dynamic_reload_enabled() {
612
+ local manager_count="$1"
613
+ local mode="${FPF_DYNAMIC_RELOAD:-single}"
614
+
615
+ case "${mode}" in
616
+ always|on|1|true|yes)
617
+ return 0
618
+ ;;
619
+ never|off|0|false|no)
620
+ return 1
621
+ ;;
622
+ single|auto|*)
623
+ [[ "${manager_count}" -eq 1 ]]
624
+ ;;
625
+ esac
626
+ }
627
+
585
628
  query_is_single_token() {
586
629
  local query="$1"
587
630
  [[ -n "${query}" && "${query}" != *[[:space:]]* ]]
@@ -922,14 +965,45 @@ manager_installed_names() {
922
965
  manager_installed_entries "${manager}" | awk -F'\t' 'NF > 0 { print $1 }'
923
966
  }
924
967
 
968
+ manager_installed_names_cached() {
969
+ local manager="$1"
970
+ local output_file="$2"
971
+ local cache_enabled="${FPF_DISABLE_INSTALLED_CACHE:-0}"
972
+ local cache_dir="${TMP_ROOT}/cache"
973
+ local cache_file="${cache_dir}/installed.${manager}"
974
+
975
+ if [[ "${cache_enabled}" != "1" && -s "${cache_file}" ]]; then
976
+ cp "${cache_file}" "${output_file}"
977
+ return
978
+ fi
979
+
980
+ manager_installed_names "${manager}" >"${output_file}" 2>/dev/null || true
981
+
982
+ if [[ "${cache_enabled}" != "1" && -s "${output_file}" ]]; then
983
+ mkdir -p "${cache_dir}"
984
+ cp "${output_file}" "${cache_file}"
985
+ fi
986
+ }
987
+
925
988
  mark_installed_packages() {
926
989
  local manager="$1"
927
990
  local source_file="$2"
928
991
  local output_file="$3"
929
992
  local installed_file
930
993
 
931
- installed_file="$(mktemp "${TMP_ROOT}/installed.XXXXXX")"
932
- manager_installed_names "${manager}" >"${installed_file}" 2>/dev/null || true
994
+ if [[ "${FPF_SKIP_INSTALLED_MARKERS:-0}" == "1" ]]; then
995
+ awk -F'\t' '
996
+ {
997
+ desc = $2
998
+ if (desc == "") desc = "-"
999
+ print $1 "\t " desc
1000
+ }
1001
+ ' "${source_file}" >"${output_file}"
1002
+ return
1003
+ fi
1004
+
1005
+ installed_file="$(mktemp "${SESSION_TMP_ROOT}/installed.XXXXXX")"
1006
+ manager_installed_names_cached "${manager}" "${installed_file}"
933
1007
 
934
1008
  awk -F'\t' '
935
1009
  FILENAME == ARGV[1] {
@@ -964,12 +1038,12 @@ collect_search_display_rows() {
964
1038
  local gather_pid
965
1039
 
966
1040
  for manager in "${managers[@]-}"; do
967
- part_file="$(mktemp "${TMP_ROOT}/part.XXXXXX")"
1041
+ part_file="$(mktemp "${SESSION_TMP_ROOT}/part.XXXXXX")"
968
1042
  part_files+=("${part_file}")
969
1043
 
970
1044
  (
971
- local_source_file="$(mktemp "${TMP_ROOT}/source.XXXXXX")"
972
- local_marked_file="$(mktemp "${TMP_ROOT}/marked.XXXXXX")"
1045
+ local_source_file="$(mktemp "${SESSION_TMP_ROOT}/source.XXXXXX")"
1046
+ local_marked_file="$(mktemp "${SESSION_TMP_ROOT}/marked.XXXXXX")"
973
1047
  manager_search_entries "${manager}" "${query}" >"${local_source_file}" || true
974
1048
  if [[ -s "${local_source_file}" ]]; then
975
1049
  mark_installed_packages "${manager}" "${local_source_file}" "${local_marked_file}"
@@ -1011,58 +1085,12 @@ build_dynamic_reload_command() {
1011
1085
  fi
1012
1086
 
1013
1087
  if [[ -n "${manager_override}" ]]; then
1014
- printf "%q --feed-search --manager %q -- {q} 2>/dev/null || true" "${script_path}" "${manager_override}"
1088
+ printf "FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search --manager %q -- {q} 2>/dev/null || true" "${script_path}" "${manager_override}"
1015
1089
  else
1016
- printf "%q --feed-search -- {q} 2>/dev/null || true" "${script_path}"
1090
+ printf "FPF_SKIP_INSTALLED_MARKERS=1 %q --feed-search -- {q} 2>/dev/null || true" "${script_path}"
1017
1091
  fi
1018
1092
  }
1019
1093
 
1020
- manager_preview_command() {
1021
- local manager="$1"
1022
-
1023
- case "${manager}" in
1024
- apt)
1025
- printf '%s' 'pkg={1}; apt-cache show "$pkg" 2>/dev/null; printf "\n"; dpkg -L "$pkg" 2>/dev/null'
1026
- ;;
1027
- dnf)
1028
- printf '%s' 'pkg={1}; dnf info "$pkg" 2>/dev/null; printf "\n"; rpm -ql "$pkg" 2>/dev/null'
1029
- ;;
1030
- pacman)
1031
- printf '%s' 'pkg={1}; pacman -Si "$pkg" 2>/dev/null; printf "\n"; pacman -Fl "$pkg" 2>/dev/null | awk "{print \$2}"'
1032
- ;;
1033
- zypper)
1034
- printf '%s' 'pkg={1}; zypper --non-interactive info "$pkg" 2>/dev/null'
1035
- ;;
1036
- emerge)
1037
- printf '%s' 'pkg={1}; emerge --search --color=n "$pkg" 2>/dev/null'
1038
- ;;
1039
- brew)
1040
- printf '%s' 'pkg={1}; brew info "$pkg" 2>/dev/null'
1041
- ;;
1042
- winget)
1043
- printf '%s' 'pkg={1}; winget show --id "$pkg" --exact --source winget --accept-source-agreements --disable-interactivity 2>/dev/null'
1044
- ;;
1045
- choco)
1046
- printf '%s' 'pkg={1}; choco info "$pkg" 2>/dev/null'
1047
- ;;
1048
- scoop)
1049
- printf '%s' 'pkg={1}; scoop info "$pkg" 2>/dev/null'
1050
- ;;
1051
- snap)
1052
- printf '%s' 'pkg={1}; snap info "$pkg" 2>/dev/null'
1053
- ;;
1054
- flatpak)
1055
- printf '%s' 'pkg={1}; flatpak info "$pkg" 2>/dev/null || flatpak remote-info flathub "$pkg" 2>/dev/null'
1056
- ;;
1057
- npm)
1058
- printf '%s' 'pkg={1}; npm view "$pkg" 2>/dev/null'
1059
- ;;
1060
- bun)
1061
- printf '%s' 'pkg={1}; bun info "$pkg" 2>/dev/null || npm view "$pkg" 2>/dev/null'
1062
- ;;
1063
- esac
1064
- }
1065
-
1066
1094
  manager_install() {
1067
1095
  local manager="$1"
1068
1096
  shift
@@ -1306,14 +1334,15 @@ run_fuzzy_selector() {
1306
1334
  --layout=reverse \
1307
1335
  --marker='>>' \
1308
1336
  --header="${header_line}" \
1309
- --info=hidden \
1337
+ --info=inline-right \
1310
1338
  --margin="2%,1%,2%,1%" \
1311
1339
  --cycle \
1340
+ --tiebreak=begin,chunk,length \
1312
1341
  --bind=ctrl-k:preview:"cat ${KBINDS_FILE}" \
1313
1342
  --bind=ctrl-h:preview:"cat ${HELP_FILE}" \
1314
1343
  --bind='ctrl-/:change-preview-window(hidden|)' \
1315
1344
  --bind=ctrl-n:next-selected,ctrl-b:prev-selected \
1316
- --bind='focus:transform-preview-label:echo {1}: {2}')
1345
+ --bind='focus:transform-preview-label:echo [{1}] {2}')
1317
1346
 
1318
1347
  if [[ -n "${reload_cmd}" ]]; then
1319
1348
  fzf_args+=(--disabled --bind="start:reload:${reload_cmd}" --bind="change:reload:${reload_cmd}")
@@ -1326,9 +1355,16 @@ run_fuzzy_selector() {
1326
1355
 
1327
1356
  main() {
1328
1357
  ensure_tmp_root
1358
+ initialize_session_tmp_root
1359
+ trap cleanup_session_tmp_root EXIT
1329
1360
 
1330
1361
  parse_args "$@"
1331
1362
 
1363
+ if [[ "${ACTION}" == "version" ]]; then
1364
+ print_version
1365
+ exit 0
1366
+ fi
1367
+
1332
1368
  local detected_manager
1333
1369
  local detected_managers=()
1334
1370
  while IFS= read -r detected_manager; do
@@ -1374,8 +1410,11 @@ main() {
1374
1410
  local query
1375
1411
  query="$(join_query)"
1376
1412
 
1377
- if [[ "${ACTION}" == "update" ]]; then
1413
+ if [[ "${ACTION}" != "feed-search" ]]; then
1378
1414
  log "Using manager(s): ${manager_display}"
1415
+ fi
1416
+
1417
+ if [[ "${ACTION}" == "update" ]]; then
1379
1418
  if confirm_action "Run update/upgrade for ${manager_display}?"; then
1380
1419
  for manager in "${managers[@]-}"; do
1381
1420
  log "Updating with $(manager_label "${manager}")"
@@ -1388,7 +1427,7 @@ main() {
1388
1427
  fi
1389
1428
 
1390
1429
  local display_file
1391
- display_file="$(mktemp "${TMP_ROOT}/display.XXXXXX")"
1430
+ display_file="$(mktemp "${SESSION_TMP_ROOT}/display.XXXXXX")"
1392
1431
  : >"${display_file}"
1393
1432
 
1394
1433
  if [[ "${ACTION}" == "feed-search" ]]; then
@@ -1411,14 +1450,14 @@ main() {
1411
1450
  local gather_pid
1412
1451
 
1413
1452
  for manager in "${managers[@]-}"; do
1414
- part_file="$(mktemp "${TMP_ROOT}/part.XXXXXX")"
1415
- part_files+=("${part_file}")
1416
-
1417
- (
1418
- local_source_file="$(mktemp "${TMP_ROOT}/source.XXXXXX")"
1419
- manager_installed_entries "${manager}" >"${local_source_file}" || true
1420
- if [[ -s "${local_source_file}" ]]; then
1421
- awk -F'\t' -v mgr="${manager}" '
1453
+ part_file="$(mktemp "${SESSION_TMP_ROOT}/part.XXXXXX")"
1454
+ part_files+=("${part_file}")
1455
+
1456
+ (
1457
+ local_source_file="$(mktemp "${SESSION_TMP_ROOT}/source.XXXXXX")"
1458
+ manager_installed_entries "${manager}" >"${local_source_file}" || true
1459
+ if [[ -s "${local_source_file}" ]]; then
1460
+ awk -F'\t' -v mgr="${manager}" '
1422
1461
  NF >= 1 {
1423
1462
  desc = $2
1424
1463
  if (desc == "") desc = "-"
@@ -1449,13 +1488,16 @@ main() {
1449
1488
 
1450
1489
  if [[ ! -s "${display_file}" ]]; then
1451
1490
  rm -f "${display_file}"
1452
- die "No packages found for manager(s) '${manager_display}' and query '${query}'"
1491
+ if [[ -n "${query}" ]]; then
1492
+ die "No packages found for ${manager_display} matching '${query}'. Try a broader query or --manager."
1493
+ fi
1494
+ die "No packages found for ${manager_display}. Try adding a query or using --manager."
1453
1495
  fi
1454
1496
 
1455
1497
  local header
1456
1498
  case "${ACTION}" in
1457
1499
  search)
1458
- header="Select package(s) to install with ${manager_display} (TAB to multi-select)"
1500
+ header="Select package(s) to install with ${manager_display} (TAB to multi-select, * = installed)"
1459
1501
  ;;
1460
1502
  list)
1461
1503
  header="Select installed package(s) to inspect from ${manager_display}"
@@ -1470,7 +1512,9 @@ main() {
1470
1512
 
1471
1513
  local reload_cmd=""
1472
1514
  if [[ "${ACTION}" == "search" && -z "${query}" ]]; then
1473
- reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}")"
1515
+ if dynamic_reload_enabled "${#managers[@]}"; then
1516
+ reload_cmd="$(build_dynamic_reload_command "${MANAGER_OVERRIDE}")"
1517
+ fi
1474
1518
  fi
1475
1519
 
1476
1520
  local selected
@@ -1479,7 +1523,7 @@ main() {
1479
1523
  rm -f "${display_file}"
1480
1524
 
1481
1525
  if [[ -z "${selected}" ]]; then
1482
- log "No package selected"
1526
+ log "Selection canceled"
1483
1527
  exit 0
1484
1528
  fi
1485
1529
 
@@ -1497,7 +1541,7 @@ main() {
1497
1541
  done <<<"${selected}"
1498
1542
 
1499
1543
  if [[ "${#selected_packages[@]}" -eq 0 ]]; then
1500
- log "No package selected"
1544
+ log "Selection canceled"
1501
1545
  exit 0
1502
1546
  fi
1503
1547
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fpf-cli",
3
- "version": "1.6.7",
3
+ "version": "1.6.9",
4
4
  "description": "Cross-platform fuzzy package finder powered by fzf",
5
5
  "bin": {
6
6
  "fpf": "fpf"