Skip to content
154 changes: 154 additions & 0 deletions .cli-flags.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ type = "bool"
default = "false"
help = "Allow signed mirror metadata to answer resolution requests."

[flags.local_path_map]
env = "ZED_PKG_LOCAL_REGISTRY_PATH_MAP"
aliases = ["local-path-map"]
type = "string"
help = "host=container path rewrites for local registry entries, comma separated."

[flags.local_link_policy]
env = "ZED_PKG_LOCAL_LINK_POLICY"
aliases = ["local-link-policy"]
type = "string"
help = "How a local checkout reaches zed_modules/: auto, symlink, or copy."

[flags.local_ephemeral]
env = "ZED_PKG_LOCAL_REGISTRY_EPHEMERAL"
aliases = ["local-ephemeral"]
type = "bool"
default = "false"
help = "Copy every local checkout because its media will not outlive this process."

[flags.frozen]
env = "ZED_PKG_FROZEN"
aliases = ["frozen"]
Expand Down Expand Up @@ -394,13 +413,148 @@ help = "Remove a dependency."
aliases = ["i"]
help = "Install dependencies."

[commands.install.flags.local_registry]
env = "ZED_PKG_LOCAL_REGISTRY"
aliases = ["local-registry"]
type = "string"
default = "auto"
help = "Authority of `zed local` registrations over resolution: off, auto, prefer, or only."

[commands.overtake]
help = "Adopt Git submodule packages into .zpkg.toml and .zpkg.lock."

[commands.uninstall]
aliases = ["un"]
help = "Remove materialized dependencies while retaining the lockfile."

[commands.local]
help = "Register local project directories so installs resolve them from this filesystem."

[commands.local.commands.register]
help = "Register or refresh a project directory containing .zpkg.toml."

[commands.local.commands.register.flags.local_register_priority]
env = "ZED_PKG_LOCAL_REGISTER_PRIORITY"
aliases = ["priority"]
type = "int"
help = "Tie-breaking priority between checkouts of one package; higher wins."

[commands.local.commands.register.flags.local_register_disabled]
env = "ZED_PKG_LOCAL_REGISTER_DISABLED"
aliases = ["disabled"]
type = "bool"
default = "false"
help = "Record the registration without making it selectable."

[commands.local.commands.register.flags.local_register_link]
env = "ZED_PKG_LOCAL_REGISTER_LINK"
aliases = ["link"]
type = "string"
help = "Materialization preference for this checkout: auto, symlink, or copy."

[commands.local.commands.unregister]
help = "Forget a registration by path, org/name, or entry id."

[commands.local.commands.unregister.flags.local_unregister_all]
env = "ZED_PKG_LOCAL_UNREGISTER_ALL"
aliases = ["all"]
type = "bool"
default = "false"
help = "Allow a selector that matches several registrations."

[commands.local.commands.list]
help = "Show every registration and whether it still resolves."

[commands.local.commands.list.flags.local_list_json]
env = "ZED_PKG_LOCAL_LIST_JSON"
aliases = ["json"]
type = "bool"
default = "false"
help = "Emit deterministic machine-readable local registry output."

[commands.local.commands.enable]
help = "Make a registration selectable again."

[commands.local.commands.enable.flags.local_enable_all]
env = "ZED_PKG_LOCAL_ENABLE_ALL"
aliases = ["all"]
type = "bool"
default = "false"
help = "Allow a selector that matches several registrations."

[commands.local.commands.disable]
help = "Keep a registration but stop selecting it."

[commands.local.commands.disable.flags.local_disable_all]
env = "ZED_PKG_LOCAL_DISABLE_ALL"
aliases = ["all"]
type = "bool"
default = "false"
help = "Allow a selector that matches several registrations."

[commands.local.commands.prune]
help = "Drop registrations whose directory, manifest, or identity broke."

[commands.local.commands.prune.flags.local_prune_dry_run]
env = "ZED_PKG_LOCAL_PRUNE_DRY_RUN"
aliases = ["dry-run"]
type = "bool"
default = "false"
help = "Report what would be dropped without writing the index."

[commands.local.commands.scan]
help = "Discover and register every project beneath a directory."

[commands.local.commands.scan.flags.local_scan_max_depth]
env = "ZED_PKG_LOCAL_SCAN_MAX_DEPTH"
aliases = ["max-depth"]
type = "int"
default = "6"
help = "How deep to descend below the scanned root."

[commands.local.commands.scan.flags.local_scan_priority]
env = "ZED_PKG_LOCAL_SCAN_PRIORITY"
aliases = ["priority"]
type = "int"
help = "Priority applied to every registration this scan creates."

[commands.local.commands.scan.flags.local_scan_dry_run]
env = "ZED_PKG_LOCAL_SCAN_DRY_RUN"
aliases = ["dry-run"]
type = "bool"
default = "false"
help = "List discovered projects without writing the index."

[commands.local.commands.resolve]
help = "Show which registered local project would satisfy a dependency."

[commands.local.commands.resolve.flags.local_resolve_require]
env = "ZED_PKG_LOCAL_RESOLVE_REQUIRE"
aliases = ["require"]
type = "string"
default = "*"
help = "Version requirement the local project must satisfy."

[commands.local.commands.resolve.flags.local_resolve_json]
env = "ZED_PKG_LOCAL_RESOLVE_JSON"
aliases = ["json"]
type = "bool"
default = "false"
help = "Emit deterministic machine-readable local registry output."

[commands.local.commands.path]
help = "Print the path of the shared local registry index."

[commands.local.commands.doctor]
help = "Explain the index location, container detection, path mapping, and every registration's volume."

[commands.local.commands.doctor.flags.local_doctor_json]
env = "ZED_PKG_LOCAL_DOCTOR_JSON"
aliases = ["json"]
type = "bool"
default = "false"
help = "Emit deterministic machine-readable local registry output."

[commands.env]
help = "Import, verify, and export project-local developer environments."

Expand Down
19 changes: 19 additions & 0 deletions .dev-cli-flags.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ type = "bool"
default = "false"
help = "Confirm each mutating lifecycle step in a real terminal."

[flags.local_path_map]
env = "ZED_PKG_LOCAL_REGISTRY_PATH_MAP"
aliases = ["local-path-map"]
type = "string"
help = "host=container path rewrites for local registry entries, comma separated."

[flags.local_link_policy]
env = "ZED_PKG_LOCAL_LINK_POLICY"
aliases = ["local-link-policy"]
type = "string"
help = "How a local checkout reaches zed_modules/: auto, symlink, or copy."

[flags.local_ephemeral]
env = "ZED_PKG_LOCAL_REGISTRY_EPHEMERAL"
aliases = ["local-ephemeral"]
type = "bool"
default = "false"
help = "Copy every local checkout because its media will not outlive this process."

[flags.git_submodules]
env = "ZED_PKG_GIT_SUBMODULES"
aliases = ["git-submodules"]
Expand Down
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ the legacy version route by default or the additive target-qualified route with
| `zed install --cli <tool> [--cli <tool> ...]` | Resolve exact project-owned CLI runtimes into `.zed/environment.lock.toml` and copy their complete runtime roots below `.zed/tools`; built-ins are `nodejs` and `python3` |
| `zed install --frozen` | Install exactly what the manifest/lock pair pins; a manifestless lock-only restore additionally requires `--do-not-write-new-manifest` |
| `zed uninstall [<org>/<name> ...]` (`zed un`) | Transactionally remove all or selected materialized packages while retaining the manifest and lockfile for a frozen reinstall |
| `zed local register\|unregister\|list\|enable\|disable\|prune\|scan\|resolve\|path\|doctor` | Manage the machine-wide registry of local project checkouts that `zed install` may resolve from this filesystem instead of the network; entries are keyed by canonical path, so several checkouts of one package can coexist and are chosen by explicit priority |
| `zed install --local-registry off\|auto\|prefer\|only` | Choose how much authority those registrations have: remote only, local-first for ordinary installs (default), local-first including `--frozen`, or fully offline |
| `zed inspect --root ABSOLUTE_PATH [--format json]` | Fully offline, read-only manifest, lock, store, Git-submodule, mise, and Nix analysis for IDEs and automation |
| `zed env import mise [--config PATH] [--lock PATH] [--frozen] [--json]` | Import the supported project-local mise tool/lock subset as the shared normalized `EnvironmentPlan`; never loads parent/global config or executes hooks |
| `zed env verify mise [--config PATH] [--lock PATH] --frozen [--json]` | Fail closed on missing lock coverage, drift, malformed checksums, unsupported semantics, or non-portable frozen state and report the stable plan digest |
Expand Down Expand Up @@ -446,6 +448,49 @@ prepared in a writable staging copy and copied into the consumer instead, so
its finalized files never point at an ephemeral staging directory. Members are
not pinned in `.zpkg.lock` (there is no published artifact).

### Local project registry (offline installs)

Workspace members only help inside one tree. A checkout somewhere else on the
same machine — or a dependency you need while the registry is down or the
laptop is offline — is registered instead:

```sh
cd ~/src/widget && zed local register # this checkout provides acme/widget
cd ~/src/app && zed install # acme/widget resolves from the filesystem
```

Registrations live in a shared index under `ZED_PKG_HOME` and are keyed by
**canonical filesystem path**, not by package name, so several checkouts of one
package can be registered at once; `--priority` chooses between them and an
unbreakable tie is an error rather than a guess. Selected projects are linked
from source through exactly the same path as workspace members, so symlink mode
gives a live link into the checkout and copy mode gives a standalone tree.

`--local-registry` decides how much authority those registrations have: `off`,
`auto` (default; ordinary installs only), `prefer` (also `--frozen`), or `only`
(fully offline — a dependency with no healthy local entry is an error instead of
a network lookup).

Registrations record the volume they live on. A checkout on an external disk, a
network share, or a container bind mount is **copied** into `zed_modules/`
rather than symlinked, because a link into media that can disappear is a project
that breaks when it does; `--local-link-policy` overrides that either way. When
such a volume is unmounted the entry reports `unavailable` rather than
`missing`, so `zed local prune` never forgets a checkout that is merely
unplugged, and resolution falls through to the registry with a reason instead of
failing.

For containers, `ZED_PKG_LOCAL_REGISTRY_FILE` points at an index shared through
a volume and `--local-path-map /host/path=/container/path` translates registered
paths across the bind mount, in both directions, so one index serves a host and
the containers it runs. `--local-ephemeral` copies everything, which is what a
`docker build` step with a BuildKit mount needs. `zed local doctor` prints that
whole picture — index, container detection, mapping, policy, and each entry's
volume — when an install went to the network and you expected it not to.

Full behavior, precedence against workspace members, and the index hardening
rules are in [local-registry.md](docs/local-registry.md).

### Native dependencies, install hooks, and builds

A package declares host prerequisites by package-manager name. Package specs
Expand Down
Loading
Loading