Set up a workspace
01Several packages developed together can live in one repository and share one record of the versions they use. This page turns a folder into such a workspace and shows how members refer to each other by path.
Turn the VibeVM project in the current folder into a workspace with two member packages under packages/: org.acme/notes-flow and org.acme/notes-docs, where notes-docs documents notes-flow. Run an install and show me that one lock file at the root covers both members.
the vibevm skill installed for your agent; a project with vibe.toml at the root
the root vibe.toml carries a [workspace] table listing both members, each member folder carries its own vibe.toml with a [package] table, and one vibe.lock at the root records the resolution
test -f packages/notes-flow/vibe.tomltest -f packages/notes-docs/vibe.tomltest ! -e packages/notes-flow/vibe.lockvibe check --quiet
What happens
03The agent adds a [workspace] table to the root manifest naming the member paths, and writes each member's vibe.toml by hand with a [package] table: a member is a plain package folder, and vibe init package scaffolds a different layout, the in-tree slot the authoring pages describe. Membership is explicit: a folder with a manifest that is not listed is not a member. Then it runs vibe install at the root: vibe discovers the workspace, resolves the requirements of every member in one unified resolution, and writes a single lock file at the root.
04
Membership is explicit — there is no auto-discovery of directories that happen to carry a vibe.toml. The structure is declared, per the owner's "the whole structure is in the project description" requirement.
05One source tree and one lock file at its root: members are folders, the split into packages is logical, and publishing copies a member's folder into a repository of its own. A command run inside a member walks up to the root and works against the root's lock, so a developer can work inside a sub-project without noticing the workspace around it.
06 Decision. The development tree is one source tree (one git repository, or not in git at all if the project is private). Workspace members are subdirectories; the split into packages is logical, at the vibevm resolver level. Publishing is a separate operation that copies the content of a package's directory into a new, separate repository in the registry org and tags the version — exactly whatvibe registry publishdoes today for one package, repeated per self-published member byvibe workspace publish.
07
Decision. One vibe.lock, at the absolute root of the workspace tree (§2.3). No per-member lockfiles.
08 Command bubbling. A command (vibe install,vibe build) run inside a member's directory walks up to the absolute root, findsvibe.lock, and operates against it. The member "does not notice" it is part of something larger — this realises the owner's requirement that a developer can work inside a sub-project unaware of the surrounding workspace.
By hand
091. In the root vibe.toml, declare the members; globs are allowed:
cat vibe.toml
[project]
name = "work"
version = "0.0.1"
authors = ["vibevm docs fixtures"]
[workspace]
members = ["packages/*"]
112. Give each member its own vibe.toml with a [package] table, the same fields vibe init package writes, with the kind you mean:
cat packages/notes-flow/vibe.toml
[package]
group = "org.acme"
name = "notes-flow"
kind = "flow"
version = "0.1.0"
epoch = 1
authors = ["vibevm docs fixtures"]
license = "UPL-1.0"
description = ""
format = "normal"
133. Install from the root, or from anywhere inside the workspace; the command finds the root and resolves the whole tree:
vibe install --assume-yes
nothing declared — regenerating boot artifacts for the empty world
vibe install: vibe.lock unchanged — nothing to re-resolve (3 nodes up to date)
One manifest, three roles
15Every node has a file named vibe.toml, and what the file contains decides what the node is. A [package] table makes it a publishable package; a [project] table makes it a consumer that is never published; a [workspace] table makes it coordinate members. A node cannot be both a package and a project, but a workspace root may be either, or neither.
16[package]and[project]are mutually exclusive in one file — a node is either a publishable package or a plain project, not both. (Decision 7-α from the design session: keep the two sections distinct rather than folding[project]into a[package]with optionalkind. Explicitness wins;kindstays strictly mandatory wherever[package]appears.)
Members referring to each other
17A member requires a sibling by path rather than by registry, with a path source in its requirements. The lock file records such an entry with a source kind of path and the member's folder relative to the root, so the lock stays portable across machines. When the workspace is published, the path becomes an ordinary coordinate in each published copy.
18
Each member is a directory carrying its own vibe.toml (§2.2).
19A path is a third source of packages beside registries and git. A member requires another by path during development and by version once published, in one line of two halves, { path = "../flow-wal", version = "^0.1" }: the published copy names the registry version, which an outside consumer can resolve, and the path never leaves the workspace. A version placeholder declared once at the root stands in for a number repeated across members.
20 Decision. A third dependency source-kind joins registry-resolved (PROP-002 §2.2) and git-source (PROP-002 §2.4.1): path-source.
21 Dual-form.pathis used during local development inside the workspace;versiontakes effect when the consuming node is itself published — the published copy referencesorg.vibevm.world/wal@^0.1from a registry, not../flow-wal(which an external consumer does not have). This is cargo's{ path = ..., version = ... }shape. Dual-form is required for any path-dep whose consumer is publishable.
22
Decision. Named version placeholders, the equivalent of Maven <properties>:
23Each publishable member declares its posture with publish in [package]. vibe workspace publish walks the members dependency-first, skips publish = false, and stops at the first failure with a report of what was published and what remains; there is no rollback, because a clear partial report is better than a pretended transaction.
24
Decision. Each publishable node declares its publish posture in [package]:
25vibe workspace publish [--member <m>]walks members in topological order (dependency-first) and skipspublish = false.
26 Publish is not atomic: on the first failure the command stops and reports what was already published and what remains. (Distributed publishing across N independent host repos has no transaction; a rollback would be a worse lie than a clear partial-progress report.)
Edge cases and rules
27Workspaces nest: a member may itself carry a [workspace] table. Nesting groups members; it does not create separate resolution domains, and the one lock file stays at the absolute root.
28 Nesting is hierarchical grouping, not independent resolution domains. The lockfile and unified resolution always live at the absolute root of the workspace tree. A nested[workspace]provides (a) the[workspace.versions]matryoshka (§2.6) and (b) logical grouping of members — never its own lockfile, never its own resolution pass.
29vibe install -p <member> narrows what is reported, not what is resolved: the lock file and the dependency tree are always workspace-wide.
30
-p <member> scopes resolution reporting to one member; the materialisation and the single root lockfile are always workspace-wide — unified resolution admits no per-member subset.
31The dependency tree lives once, at the root; members do not get their own copies of shared packages.
32 Materialised dependencies — avibedeps/tree at the absolute workspace root (PROP-007 §2.3), written only byvibe. One slot per resolved package,vibedeps/<group>.<name>/<version>/(identity-keyed, PROP-022 §2.1 — owner ruling 2026-08-13), holding the package's published tree verbatim (PROP-024 §2.2 re-scopes "published" to the shippable tree — source minus build output — for code-bearing packages). A package's prompt content lives under its ownspec/, so a boot snippet materialises atvibedeps/<slot>/spec/boot/<file>(PROP-024 §2.1). Unified resolution (PROP-007 §2.4) guarantees one version per package, so one slot serves the whole workspace.