Phases 0-1 + Sprint A: module split, CLI layer, org-refresh fix, hygiene

- LICENSE (MIT) + README truth-pass; docs/cli.md CLI contract
- CLI subcommands (src/cli_cmds/) per "TUI for users, CLI for users+AI"
- Hub/detail refactor: keymap dispatch, modal split, action enum, BgMsg bus
- Fix: org repos were never refreshed (owner==username match); refresh now
  parallel — 49 repos in ~11s, surfaced 70 previously invisible PRs
- Fix: 3 date bugs in detail/git.rs (format-local, Local::now, 1y month math)
- Stash indicator + [m] actions, session persistence, gh CLI token fallback
- Legacy UI modules removed; plan/ docs (roadmap, audit, debate, handoff)
This commit is contained in:
dualfroz committed 2026-07-06 14:42:52 +02:00
1 parent 5787b14880
commit 0468c032c6
100 files changed
+11614 -9840

No files matched your search

+41 -41
View File
@@ -1,4 +1,4 @@
# fxg — strategia cross-platform
# vulp — strategia cross-platform
> Cel: jedna binarka, trzy systemy, instalacja w 30 sekund, zero wiedzy technicznej wymaganej.
@@ -14,9 +14,9 @@ Obecny problem z `keyring`: na Linuksie wymaga `libdbus` lub `libsecret`. Do roz
## Cel UX instalacji
**Windows:** Pobierasz plik `.exe`. Klikasz go dwukrotnie. Otwiera się terminal z konfiguratorem. Po 2 minutach piszesz `fxg status` i to działa.
**Windows:** Pobierasz plik `.exe`. Klikasz go dwukrotnie. Otwiera się terminal z konfiguratorem. Po 2 minutach piszesz `vulp status` i to działa.
**Linux:** Wklejasz jedną linię w terminal. Po 10 sekundach piszesz `fxg status` i to działa.
**Linux:** Wklejasz jedną linię w terminal. Po 10 sekundach piszesz `vulp status` i to działa.
**macOS:** `brew install`. Jedna komenda.
@@ -35,19 +35,19 @@ strategy:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-musl # statyczna binarka — działa na każdym Linuksie
artifact: fxg-linux-x64
artifact: vulp-linux-x64
- os: ubuntu-latest
target: aarch64-unknown-linux-musl # ARM (np. Raspberry Pi, nowe serwery)
artifact: fxg-linux-arm64
artifact: vulp-linux-arm64
- os: windows-latest
target: x86_64-pc-windows-msvc
artifact: fxg-windows-x64.exe
artifact: vulp-windows-x64.exe
- os: macos-latest
target: x86_64-apple-darwin
artifact: fxg-macos-x64
artifact: vulp-macos-x64
- os: macos-latest
target: aarch64-apple-darwin # Apple Silicon (M1/M2/M3)
artifact: fxg-macos-arm64
artifact: vulp-macos-arm64
```
> Używamy `x86_64-unknown-linux-musl` zamiast `gnu` — binarka statyczna, nie wymaga żadnych systemowych `.so`. Keyring na Linuksie fallback do pliku szyfrowanego (jeśli brak dbus).
@@ -61,15 +61,15 @@ Pliki lądują w GitHub Releases jako Assets. Użytkownik pobiera odpowiedni pli
### Skrypt jednolinijkowy (docelowy UX)
```bash
curl -fsSL https://github.com/twoj-user/fxg/releases/latest/download/install.sh | bash
curl -fsSL https://github.com/twoj-user/vulp/releases/latest/download/install.sh | bash
```
Skrypt robi:
1. Wykrywa architekturę (`uname -m`)
2. Pobiera odpowiednią binarkę z GitHub Releases
3. Umieszcza ją w `~/.local/bin/fxg`
3. Umieszcza ją w `~/.local/bin/vulp`
4. Dodaje `~/.local/bin` do `$PATH` w `.bashrc`/`.zshrc` jeśli jeszcze nie ma
5. Jeśli brak konfiguracji → uruchamia `fxg setup` (first-run wizard)
5. Jeśli brak konfiguracji → uruchamia `vulp setup` (first-run wizard)
```bash
#!/bin/bash
@@ -77,17 +77,17 @@ set -e
ARCH=$(uname -m)
OS=linux
case $ARCH in
x86_64) FILE="fxg-linux-x64" ;;
aarch64) FILE="fxg-linux-arm64" ;;
x86_64) FILE="vulp-linux-x64" ;;
aarch64) FILE="vulp-linux-arm64" ;;
*) echo "Nieobsługiwana architektura: $ARCH"; exit 1 ;;
esac
LATEST=$(curl -s https://api.github.com/repos/twoj-user/fxg/releases/latest | grep tag_name | cut -d '"' -f 4)
URL="https://github.com/twoj-user/fxg/releases/download/${LATEST}/${FILE}"
LATEST=$(curl -s https://api.github.com/repos/twoj-user/vulp/releases/latest | grep tag_name | cut -d '"' -f 4)
URL="https://github.com/twoj-user/vulp/releases/download/${LATEST}/${FILE}"
mkdir -p ~/.local/bin
curl -fsSL "$URL" -o ~/.local/bin/fxg
chmod +x ~/.local/bin/fxg
curl -fsSL "$URL" -o ~/.local/bin/vulp
chmod +x ~/.local/bin/vulp
# Dodaj do PATH jeśli brak
if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
@@ -95,8 +95,8 @@ if ! echo "$PATH" | grep -q "$HOME/.local/bin"; then
export PATH="$HOME/.local/bin:$PATH"
fi
echo "fxg zainstalowany. Uruchamianie konfiguracji..."
fxg setup
echo "vulp zainstalowany. Uruchamianie konfiguracji..."
vulp setup
```
### Opcjonalnie: pakiety .deb i .rpm
@@ -105,10 +105,10 @@ Generowane przez `cargo-deb` i `cargo-rpm` w CI — przydatne dla systemów zarz
```bash
# Ubuntu/Debian
sudo dpkg -i fxg_0.1.0_amd64.deb
sudo dpkg -i vulp_0.1.0_amd64.deb
# Fedora/RHEL/CentOS
sudo rpm -i fxg-0.1.0.x86_64.rpm
sudo rpm -i vulp-0.1.0.x86_64.rpm
```
---
@@ -117,18 +117,18 @@ sudo rpm -i fxg-0.1.0.x86_64.rpm
### Opcja A: Kliknięcie `.exe` (docelowy UX dla grafika)
Gdy użytkownik dwukrotnie kliknie `fxg-windows-x64.exe` bez żadnych argumentów i brak konfiguracji, zamiast wyświetlić help i zamknąć okno — **otwiera PowerShell z konfiguratorem**.
Gdy użytkownik dwukrotnie kliknie `vulp-windows-x64.exe` bez żadnych argumentów i brak konfiguracji, zamiast wyświetlić help i zamknąć okno — **otwiera PowerShell z konfiguratorem**.
Implementacja w `src/main.rs`:
```rust
#[cfg(windows)]
fn ensure_console() {
// Jeśli uruchomiono przez dwuklik (nie z terminala), otwórz nowe okno konsoli
// i uruchom fxg setup w nim
// i uruchom vulp setup w nim
use std::os::windows::process::CommandExt;
if !is_launched_from_terminal() {
std::process::Command::new("cmd")
.args(["/k", "fxg.exe", "setup"])
.args(["/k", "vulp.exe", "setup"])
.creation_flags(0x00000010) // CREATE_NEW_CONSOLE
.spawn().ok();
std::process::exit(0);
@@ -140,19 +140,19 @@ fn ensure_console() {
```powershell
# Wklej w PowerShell jako Administrator:
iwr https://github.com/twoj-user/fxg/releases/latest/download/install.ps1 | iex
iwr https://github.com/twoj-user/vulp/releases/latest/download/install.ps1 | iex
```
Skrypt:
1. Pobiera `fxg-windows-x64.exe`
2. Umieszcza w `%USERPROFILE%\AppData\Local\fxg\`
1. Pobiera `vulp-windows-x64.exe`
2. Umieszcza w `%USERPROFILE%\AppData\Local\vulp\`
3. Dodaje ten folder do `$PATH` (przez `[Environment]::SetEnvironmentVariable`)
4. Uruchamia `fxg setup` w nowym oknie terminalu
4. Uruchamia `vulp setup` w nowym oknie terminalu
### Opcja C: WinGet (Microsoft Store CLI) — przyszłość
```
winget install fxg
winget install vulp
```
Wymaga zgłoszenia pakietu do repozytorium `winget-pkgs`.
@@ -164,30 +164,30 @@ Wymaga zgłoszenia pakietu do repozytorium `winget-pkgs`.
### Homebrew (docelowy UX)
```bash
brew install twoj-user/tap/fxg
brew install twoj-user/tap/vulp
```
Wymaga stworzenia własnego "tap" (repozytorium z formularzem Homebrew):
```ruby
# Formula/fxg.rb
class Fxg < Formula
# Formula/vulp.rb
class Vulp < Formula
desc "Multi-repository git workflow manager"
homepage "https://github.com/twoj-user/fxg"
homepage "https://github.com/twoj-user/vulp"
version "0.1.0"
on_macos do
if Hardware::CPU.arm?
url "https://github.com/twoj-user/fxg/releases/download/v0.1.0/fxg-macos-arm64"
url "https://github.com/twoj-user/vulp/releases/download/v0.1.0/vulp-macos-arm64"
sha256 "..."
else
url "https://github.com/twoj-user/fxg/releases/download/v0.1.0/fxg-macos-x64"
url "https://github.com/twoj-user/vulp/releases/download/v0.1.0/vulp-macos-x64"
sha256 "..."
end
end
def install
bin.install "fxg-macos-arm64" => "fxg"
bin.install "vulp-macos-arm64" => "vulp"
end
end
```
@@ -196,7 +196,7 @@ end
## First-run wizard
Kluczowy element UX. Gdy użytkownik uruchamia **dowolną komendę** po raz pierwszy (brak `~/.fxg/config.toml`), zamiast error'a dostaje automatyczny setup.
Kluczowy element UX. Gdy użytkownik uruchamia **dowolną komendę** po raz pierwszy (brak `~/.vulp/config.toml`), zamiast error'a dostaje automatyczny setup.
Implementacja w `src/app.rs`, przed każdym `match cli.command`:
@@ -205,7 +205,7 @@ Implementacja w `src/app.rs`, przed każdym `match cli.command`:
pub async fn run(cli: Cli) -> Result<()> {
// Jeśli brak konfiguracji → uruchom first-run setup
if !config::config_path().exists() {
println!("Wygląda na to, że uruchamiasz fxg po raz pierwszy.");
println!("Wygląda na to, że uruchamiasz vulp po raz pierwszy.");
println!("Przeprowadźmy krótką konfigurację (zajmie ~2 minuty).\n");
first_run_setup().await?;
}
@@ -223,7 +223,7 @@ async fn first_run_setup() -> Result<()> {
// Uruchom auth add wizard
auth::run_add_wizard(&cfg).await?;
}
println!("\nGotowe! Wpisz 'fxg status' żeby zobaczyć swoje projekty.");
println!("\nGotowe! Wpisz 'vulp status' żeby zobaczyć swoje projekty.");
Ok(())
}
```
@@ -235,7 +235,7 @@ async fn first_run_setup() -> Result<()> {
| Zależność | Problem | Rozwiązanie |
|---|---|---|
| `libgit2` | Wymagana na Linuksie przy dynamicznym linkowaniu | Używamy `git2` z feature `vendored` — kompiluje libgit2 statycznie |
| `keyring` / `libsecret` | Wymaga `dbus` na Linuksie headless | Fallback do zaszyfrowanego pliku `~/.fxg/credentials.enc` gdy brak dbus |
| `keyring` / `libsecret` | Wymaga `dbus` na Linuksie headless | Fallback do zaszyfrowanego pliku `~/.vulp/credentials.enc` gdy brak dbus |
| `openssl` | Różne wersje na różnych systemach | Feature `native-tls` → `rustls` — TLS w całości wbudowany w Rust |
```toml
@@ -250,7 +250,7 @@ keyring = { version = "3", features = ["sync-secret-service"] }
## O nazwie
Aktualna nazwa `fxg` ("finally executable git") to placeholder — dobra jako codename, ale przed publicznym wydaniem warto rozważyć zmianę.
Aktualna nazwa `vulp` ("finally executable git") to placeholder — dobra jako codename, ale przed publicznym wydaniem warto rozważyć zmianę.
Kryteria dobrej nazwy narzędzia CLI:
- Krótka (2–4 znaki idealne, max 6)