Projects
Fut can keep an explicit project catalog and bootstrap new project sessions from trusted recipes.
Project catalog
Declare projects in the global configuration:
[projects.fut]
path = "~/dev/fut"
[projects.website]
path = "/Users/me/dev/website"
recipe = "~/.config/fut/recipes/website.toml"
Project names use ASCII letters, numbers, -, or _ and must be unique. Paths
must be absolute or begin with ~/; Fut performs no filesystem scanning or
shell interpolation. Open and attach to a catalog entry with
fut open -p fut (or --project fut), or add -b to open it in the background.
An optional path may select a linked checkout, for example
fut open ../fut-feature --project fut; Fut verifies that the path has the same
Git project identity as the configured root before opening it as a peer
workspace.
From an attached client, press Ctrl-b Shift-S to fuzzy-filter the configured
catalog or type any path. Suggestions come only from configuration—Fut does not
scan the filesystem. The exact typed value appears as an Open path choice,
resolved relative to the focused workspace. Opening a location that is already
live navigates to it rather than creating a duplicate. A newly bootstrapped
project attaches directly to the focus terminal selected by its recipe.
Path and recipe preparation runs in the background. The opener shows
preparing… while it resolves the project, remains current as other client
updates arrive, and can be cancelled with Escape or Ctrl-C. Once preparation
has sent the open request, the existing opening… phase remains non-cancellable.
List the configured catalog without starting a daemon:
fut project list
fut project ls
The daemon loads the catalog at startup. Restart it after changing a project
path or recipe; Ctrl-b Shift-R reloads the active recipe’s extension table,
but does not change the daemon’s project catalog. Repository recipe approvals
are checked separately each time Fut boots or reloads a project session, so
approving or revoking one does not require a daemon restart.
Trusted recipes
Each catalog entry may initialize the declared workspaces of a newly created project session from a recipe. Once the project session is live, opening a linked checkout or creating another workspace starts one ordinary terminal; it does not recreate the recipe’s workspaces, tabs, panes, or commands. Reopening a live workspace never rereads or reconciles its recipe and never reruns its commands. A linked Git worktree uses the recipe configured for the catalog root when it is the checkout that initially boots the project session, not a file from the linked checkout.
By default Fut looks for .fut/project.toml under the configured catalog root.
Because that repository-owned file can run commands, review it and approve its
exact current contents with:
fut project init
fut project trust fut
The in-client project opener performs the same approval safely without a shell:
it displays the recipe contents, asks for y or n, and records approval
through Fut’s machine-local trust store. It never asks you to copy a hash or
edit trust state. If the recipe changes while it is displayed, approval fails
and the opener asks you to review the new contents again.
fut project init creates a small starter recipe in the current directory with
the published schema, an agent tab, a Vim tab, and a link back to this page. It
refuses to overwrite an existing .fut/project.toml.
This command works without a running daemon. It resolves the configured
project, reads a canonical regular recipe file, and fully validates the recipe
before recording machine-local approval for its exact bytes. Changing any byte
makes the recipe untrusted again. Run fut project untrust fut to revoke
approval. The next attempt to bootstrap or reload the project session will fail
before running recipe commands. Existing live layouts and processes are not
reconciled on reload; only the extension configuration captured by the session
is replaced.
Approval state is managed exclusively by Fut under
$XDG_STATE_HOME/fut/trusted-recipes.toml, falling back to
~/.local/state/fut/trusted-recipes.toml. Do not edit this file. A malformed or
unsafe state file causes repository recipes to fail closed.
Alternatively, set recipe to an absolute or ~/ path in global config. That
explicit path is inherently trusted. fut project trust NAME reports a
validated no-op for it, while fut project untrust NAME explains that the
global recipe setting must be removed to revoke trust. Recipe files must be
regular UTF-8 files no larger than 64 KiB.
Recipe format
#:schema https://fut.sh/schemas/project.json
focus = "main.code.agent"
environment = { RUST_BACKTRACE = "1" }
[extension.wt]
open_existing = true
command = ["pi"]
[extension.run]
command = ["mise", "run", "dev"]
auto_start = true
[[workspaces]]
id = "main"
[[workspaces.tabs]]
id = "code"
title = "Code"
cwd = "."
environment = { PROJECT_ROLE = "development" }
panes = [
{ id = "editor", command = ["nvim", "."] },
{ id = "agent", command = ["pi", "--model", "fast"], exec = true, environment = { AGENT_ROLE = "reviewer" }, split = { target = "editor", direction = "right" } },
]
[[workspaces.tabs]]
title = "project console"
panes = [
{ },
]
Editor completion
Fut publishes a JSON Schema for project recipes. Add
this Taplo schema directive as the first line of .fut/project.toml to get
validation, documentation, and completion in compatible TOML editors:
#:schema https://fut.sh/schemas/project.json
The schema catches structural errors such as missing pane splits. Fut remains the authority for checks that JSON Schema cannot express, including unique IDs, valid focus targets, split targets that name earlier panes, installed extension configuration, and the total pane limit.
Workspace, tab, and pane IDs are optional recipe-local references. Add an ID
only when the item must be referenced by focus = "WORKSPACE.TAB.PANE" or a
pane’s split.target. IDs use ASCII letters, numbers, -, or _ and never
affect presentation. Set title on a workspace or tab when it needs a fixed
display name; without one it follows Fut’s normal automatic naming. The first
pane in a tab has no split. Every later pane must split an earlier, named pane
in the same tab with direction right or down.
Commands are direct argument arrays and are never evaluated by a shell. By
default, Fut runs a configured command as a child process and starts the user’s
shell when it exits, so stopping the command leaves the pane open at a prompt.
Set exec = true on a pane to make its configured command the pane’s top-level
process instead. Omit command to start the configured shell immediately.
Relative cwd values are resolved from the workspace checkout root; pane cwd
overrides tab cwd, and a tab cwd applies to panes that omit one. Environment
values layer from recipe to tab to pane, with the most specific value winning.
FUT_* variables are reserved for Fut and cannot be set by a recipe.
Namespaced [extension.ID] tables are also part of the exact trusted recipe.
They layer over global extension defaults and are captured when the project
session is created, so the main checkout, linked worktrees, and logical peer
workspaces share one approved project configuration. The bundled wt
extension can open existing worktrees with open_existing = true; the bundled
run extension can start its managed command once in the first declared
workspace when the project session starts with auto_start = true. It does not
repeat that command in other declared or subsequently created workspaces. Both
extensions are disabled by default.
The wt extension’s optional command array supplies the default command in
its New worktree form. For example, ["pi"] launches Pi in the new
worktree and allows a one-off initial prompt. Clear the form’s command field to
leave process selection to the project recipe.
fut open -- COMMAND... still takes priority for the recipe-selected focus
pane when it bootstraps a project session; the rest of the recipe is unchanged.
When it adds a workspace to a live session, the explicit command instead runs
in that workspace’s single initial terminal. An explicit open command remains
the pane’s top-level process. Recipe topology and working directories are fully
validated before processes start. Fut starts every declared terminal before
publishing the initial resources and closes any terminals already started if a
later spawn fails.