Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions .ai-workspace/flock-default-setup-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Flock: good setup out of the box

A fresh `flock` install is byte-for-byte plain Zellij. Everything that makes it
Flock — the sidebar dock, the project selector, agent badges — is opt-in through
hand-authored KDL that the user has to know to write. This plan closes that gap.

Target: **a non-Nix user installs `flock`, runs it, and lands in the real Flock
experience.** Pointing it at project folders should take a few lines of
`config.kdl` at most, stated once.

## Locked decisions

| Decision | Choice |
| --- | --- |
| Bare `flock` | Always opens the project selector |
| Selector keybind | `Super s` |
| Unconfigured project list | Show nothing — no filesystem auto-discovery |
| Scope | This repo only |

`Super s` does not reach the TUI in a number of terminals (it is intercepted by
the OS or the terminal emulator). That is a documented caveat, not a blocker —
the docs need to say so and name the rebind.

Because the project list will not auto-discover, the *empty state* carries the
whole first-run experience. See Phase 3.

## Current gaps

1. **`default_layout` is unset.** `zellij-utils/assets/config/default.kdl:351`
has it commented out, so bare `flock` loads the built-in `default` layout
(tab-bar / pane / status-bar). The bundled `flock.kdl` layout — which does
have the sidebar dock — is unreachable without the user setting
`default_layout "flock"` themselves.

2. **No keybind opens the selector.** `Alt b` toggles the dock
(`default.kdl:206`) but nothing launches the picker. Users write their own
`LaunchOrFocusPlugin` bind and restate every plugin arg. When the arg set
does not match the layout's, a *second* selector launches — the near-miss
documented in `zellij-server/src/plugins/plugin_map.rs:563`.

3. **Selector config is stated three or more times:** the entry layout's plugin
block, the keybind body, and `sidebar_args` re-emitted into every generated
project/remote layout. There is no single "where my projects live."

4. **New project sessions get plain Zellij.** `DEFAULT_SESSION_LAYOUT = "default"`
(`default-plugins/flock-selector/src/config.rs:22`), so a session the selector
creates has no dock unless the user authors their own project layout and points
`session_layout` at it. Remote sessions are already fine — `codespaces.rs:145`
has a built-in dock template fallback.

5. **Zero-config selector is blank.** `root_dirs` / `individual_dirs` default to
`Vec::new()` (`config.rs:79-80`). The UI does distinguish the case
(`" no project folders configured"`, `ui.rs:291`) but the message is not
actionable.

6. **The entry point is a shell alias over a hack layout.** The bundled
`flock-selector.kdl` keeps its session alive with
`pane command="tail" { args "-f" "/dev/null" }` and ships commented-out
placeholder args. With bare `flock` becoming the selector, this stops being a
side path and becomes *the* startup path — it has to be solid.

## Phase 0 — ship working defaults

Small, self-contained, no new config surface. Gets a fresh install from "plain
Zellij" to "sidebar + working selector keybind."

- Make the built-in `flock` layout the startup **fallback**, not a shipped
`default_layout` value. `default_layout` stays unset, so a user's own
`layouts/default.kdl` is still what loads on startup; only when they have not
written one does Flock use its own layout. Setting the option instead would
skip that lookup and silently ignore a file the user expects to be their
startup layout — a breaking change for no benefit. The rule lives in two
places that must agree (`LayoutInfo::from_config` and the loader in
`layout.rs`), keyed off `FALLBACK_BUILTIN_LAYOUT`.
- `config.rs`: `DEFAULT_SESSION_LAYOUT` → `"flock"`, so selector-created
sessions get the dock.
- `default.kdl`: register `flock-selector` and `flock-sidebar` in the
`plugins {}` alias block (alias config already merges with call-site config
winning — `zellij-utils/src/input/layout.rs:122-145`).
- `default.kdl`: bind `Super s` → `LaunchOrFocusPlugin "flock-selector"`
(floating, move-to-focused-tab), alongside the existing `Alt b`.

## Phase 1 — a `flock { }` config block

The single source of truth. Parse a top-level `flock { }` node in
`Config::from_kdl` alongside `plugins` / `ui` / `web_client`
(`zellij-utils/src/kdl/mod.rs:4908-4938`) into a `FlockConfig` on `Config`:

```kdl
flock {
root_dirs "~/src" "~/work" // scanned one level deep
individual_dirs "~/nixos" // each is itself one project
devcontainers true
ssh true
}
```

Inject the resolved `FlockConfig` **underneath** the call-site configuration for
every `zellij:flock-selector` / `zellij:flock-sidebar` instance, at plugin-load
time in `zellij-server`. Doing it there (rather than only in alias resolution)
means it applies uniformly to a direct `zellij:flock-selector` in a layout, to
the alias, and to the keybind.

Layout args still override, so a project layout can opt out per-session.

Two payoffs:

- Zero args needed anywhere in the common case.
- **The duplicate-selector bug class disappears by construction.** All three call
sites now derive identical configuration, so `running_plugin_satisfies_request`
always matches instead of near-missing.

Also add a `flock { }` key to disable the selector-on-startup behavior, so Flock
stays usable as a drop-in Zellij replacement for anyone who wants a shell when
they type `flock`.

## Phase 2 — bare `flock` opens the selector

Bare `flock` attaches the fixed `flock-selector` session if it is live, else
creates it. Note `--session NAME` does **not** attach when the session is already
live (`AGENTS.md:26`), so this needs real attach-or-create logic rather than a
flag.

- Replace the `tail -f /dev/null` keepalive with something intentional now that
this is the primary startup path.
- Keep an explicit `flock pick` subcommand as well: bare `flock` is now
overloaded, and scripts and docs benefit from a name. Gets shell completion
free via the existing `setup --generate-completion`.
- `attach_to_session` is effectively subsumed — the selector lists live sessions
with agent badges, so picking one *is* attaching. Confirm the interaction
rather than assuming it.
- Explicit forms (`flock attach`, `flock --session X`, `flock -l LAYOUT`) keep
working unchanged.

## Phase 3 — make the empty state carry first-run

This is load-bearing: with no auto-discovery and the selector as the startup
screen, the unconfigured empty state is the first thing every new user sees.

- Replace `" no project folders configured"` (`ui.rs:291`) with actionable text
that names the fix.
- Add an in-app "add a project folder" action that writes to the user's
`flock { }` block. Precedent already exists in the SSH tab:
`" no saved SSH hosts — Ctrl-o adds one"` (`ui.rs:333`).

Result: a fresh install shows a picker that explains itself and gets you
configured in a few keystrokes — without scanning the filesystem behind the
user's back.

## Phase 4 — docs and diagnostics

- Rewrite the README "Enable Flock" section. It currently teaches arg-pasting
into layouts; it should read "works out of the box, here is how to point it at
your projects," plus the `Super s` terminal caveat and how to rebind.
- Add the `flock { }` block, commented and documented, to `default.kdl` so
`flock setup --dump-config` teaches it.
- Extend `flock setup --check` to report the resolved Flock config: project
roots, which providers are enabled, which layout is default.

## Open risks

- **`Super s` reachability.** Needs testing across the terminals people actually
use. If it is dead in most, the default is decorative and the docs have to lead
with the rebind.
- **Selector-always is a visible divergence from Zellij.** The opt-out in Phase 1
is the mitigation; it should be easy to find.
- **The flock fallback changes the startup chrome for existing users** who have
no `layouts/default.kdl` and were getting the plain default layout. They keep
every pane they had plus a sidebar dock, and `default_layout "default"` opts
out. Worth a CHANGELOG note, but not breaking: anyone with their own
`default.kdl` is unaffected by construction.
- **Verifying startup layout resolution by hand is booby-trapped.** On a config
dir with no `config.kdl`, the first-run setup wizard both overrides the layout
and *writes a `config.kdl` into that directory* — which silently pollutes a
test fixture if you point the binary at one. Pre-seed a `config.kdl` in a temp
config dir when checking this by hand.
1 change: 0 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

permissions:
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [Unreleased]
* feat(setup): make a fresh install land in Flock rather than in plain Zellij.
Everything that distinguishes Flock — the sidebar dock, the project selector,
agent status — was previously opt-in through hand-authored KDL, so `flock` with
no config behaved exactly like upstream `zellij`. Now:
- `default_layout` ships as `"flock"` (the default chrome plus the sidebar as a
left-edge dock) instead of being unset.
- `Super s` opens the project selector.
- `flock-selector` and `flock-sidebar` are registered as plugin aliases, so
their folder args are stated **once** in `config.kdl` and reach the bundled
layouts, the keybinding, and each project session's sidebar alike. The
bundled layouts now reference those aliases instead of restating args, which
removes by construction the arg-set disagreement that made a keybinding
launch a second selector.
- Sessions created from the selector default to the `flock` layout, so a
project opened through Flock has the sidebar. Previously they got Zellij's
`default` layout with no way back to the selector.

Your own layouts still win. `default_layout` is deliberately left unset, and
the `flock` layout is a *fallback*: startup still loads your
`layouts/default.kdl` if you have written one, and only uses the built-in
`flock` layout when you have not. Naming a layout explicitly — including
`default_layout "default"` for the plain upstream Zellij chrome with no
sidebar — takes precedence as always.

## [26.10.1] - 2026-07-28
* fix(remote): make the sidebar's upgrade confirmation mouse-reachable, report
Expand Down
67 changes: 54 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,68 @@ cargo xtask run
cargo xtask test
```

## Enable Flock
## Configure Flock

The bundled `flock-selector` and `flock-sidebar` layouts work out of the box.
Remote providers are opt-in:
Flock works with no configuration: it starts with the sidebar docked to the left
edge, and `Super s` opens the project selector. The one thing it cannot guess is
where your projects live.

Point it at them in `~/.config/flock/config.kdl` by giving the bundled
`flock-selector` alias its folder args:

```kdl
plugin location="zellij:flock-selector" {
root_dirs "~/src"
codespaces_enabled "true"
devcontainers_enabled "true"
coder_enabled "true"
plugins {
flock-selector location="zellij:flock-selector" {
cwd "/"
root_dirs "~/src;~/work" // each scanned one level deep
individual_dirs "~/dotfiles" // each is itself one project
}
flock-sidebar location="zellij:flock-sidebar" {
cwd "/"
root_dirs "~/src;~/work"
individual_dirs "~/dotfiles"
}
}
```

These are *aliases*, so this is the only place the args are stated — the bundled
layouts, the `Super s` keybinding, and the sidebar in each project session all
resolve through them. Run `flock setup --dump-config` to see the shipped defaults
with every option documented inline.

plugin location="zellij:flock-sidebar" {
root_dirs "~/src"
codespaces_enabled "true"
devcontainers_enabled "true"
coder_enabled "true"
If `Super s` does nothing, your terminal is intercepting `Super` (Cmd on macOS)
before Flock sees it. Bind a key that does reach the app — this adds to the
shipped binding rather than replacing it, which is harmless:

```kdl
keybinds {
shared_except "locked" {
bind "Alt s" {
LaunchOrFocusPlugin "flock-selector" {
floating true
move_to_focused_tab true
};
}
}
}
```

Remote providers are opt-in, because each needs an authenticated CLI on `PATH`.
Add them to the aliases above:

```kdl
codespaces_enabled "true"
devcontainers_enabled "true"
coder_enabled "true"
ssh_enabled "true"
```

Your own layouts take precedence over Flock's chrome. If you have a
`~/.config/flock/layouts/default.kdl`, that is still what loads on startup — the
built-in `flock` layout is only the fallback for when you have not written one.
To get the plain upstream Zellij chrome with no sidebar, set
`default_layout "default"`.

Provider requirements:

- Codespaces: authenticated `gh` CLI.
Expand Down
8 changes: 7 additions & 1 deletion default-plugins/flock-selector/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

/// Layout new sessions open with when none is configured.
pub const DEFAULT_SESSION_LAYOUT: &str = "default";
///
/// The bundled `flock` layout, not Zellij's `default`: a session opened *from
/// the selector* that lacked the sidebar dock had no way back to the selector
/// and no agent status, which is the entire reason to pick a project through
/// Flock. Remote-bound sessions already default to a dock-bearing layout (see
/// `codespaces::FLOCK_LAYOUT_TEMPLATE`); this makes local ones agree.
pub const DEFAULT_SESSION_LAYOUT: &str = "flock";
pub const DEFAULT_CODER_DOTFILES_PARAMETER: &str = "dotfiles_uri";
pub const DEFAULT_CODER_DOTFILES_BRANCH_PARAMETER: &str = "dotfiles_branch";

Expand Down
Loading
Loading