sugarcraft/sugar-wishlist

SSH endpoint launcher — a TUI directory of `ssh user@host` shortcuts. Port of charmbracelet/wishlist. Loads endpoints from YAML/JSON, renders an interactive picker, then `exec`s into the chosen ssh client (with full host-key prompt / agent forwarding fidelity).

Maintainers

Package info

github.com/sugarcraft/sugar-wishlist

Documentation

pkg:composer/sugarcraft/sugar-wishlist

Statistics

Installs: 0

Dependents: 1

Suggesters: 1

Stars: 1

Open Issues: 0

v0.2.0 2026-05-07 01:29 UTC

This package is auto-updated.

Last update: 2026-05-18 22:50:49 UTC


README

sugar-wishlist

SugarWishlist

CI codecov Packagist Version License PHP

demo

PHP port of charmbracelet/wishlist — a TUI directory of SSH endpoints. Launch wishlist, pick a host, hit Enter, and the current process is replaced with ssh connecting to it.

── wishlist ──
filter:
▸ production  ─  deploy@prod.example.com:2222
  staging     ─  stage.example.com
  dev         ─  dev.example.com

  ↑/↓ select · Enter connect · Esc quit · type to filter

Install

The wishlist binary lives at bin/wishlist. Composer adds it to your global vendor/bin/ when installed as a project dependency, or you can add the repo's bin/ to your $PATH.

composer require sugarcraft/sugar-wishlist
~/.composer/vendor/bin/wishlist

Configure

wishlist looks for, in order:

  1. --config <path> (CLI flag)
  2. ~/.config/wishlist.yml
  3. ~/.config/wishlist.yaml
  4. ~/.config/wishlist.json
  5. wishlist.{yml,yaml,json} in the current directory

YAML

- name: production
  host: prod.example.com
  port: 2222
  user: deploy
  identity_file: ~/.ssh/prod-deploy

- name: staging
  host: stage.example.com
  user: deploy

- name: jumpbox
  host: bastion.example.com
  options:
    - ServerAliveInterval=30
    - ProxyJump=gw.example.com

JSON

[
  { "name": "production", "host": "prod.example.com", "port": 2222, "user": "deploy" },
  { "name": "staging",    "host": "stage.example.com" }
]

Keybindings

Key Action
↑ / k Move up
↓ / j Move down
Enter Connect to highlighted endpoint
Esc / ^C Quit without connecting
(typing) Type-to-filter; Backspace clears

Implementation

The picker is a tiny standalone widget — not a full SugarBits List. The lifecycle is

read config → render picker → read keys → choose → pcntl_exec(ssh, argv)

That last pcntl_exec is the critical line: it replaces the PHP process with ssh. File descriptors, environment, and the controlling tty all flow through unchanged, so the user sees a normal ssh session — host-key prompts, agent forwarding, MOTD, exit status, all native. We never proxy bytes; we get out of the way.

Programmatic use

use SugarCraft\Wishlist\Config;
use SugarCraft\Wishlist\Picker;
use SugarCraft\Wishlist\Launcher;

$endpoints = Config::load('/etc/wishlist.yml');
$picked    = (new Picker())->pick($endpoints);
if ($picked !== null) {
    (new Launcher())->dispatch($picked);
}

Status

Phase 9+ — first cut. 26 tests / 67 assertions. Endpoint, Config (JSON + flat-YAML), Picker, Launcher are all covered.