diff --git a/README.md b/README.md index 7395d65..2e829a2 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,15 @@ Design principles: - **Thin over custom.** Prefer plain `git` porcelain/plumbing and well-known directory conventions over bespoke state files. The workspace layout is encoded in a single `workspace.json` manifest. -- **Conservative mutation.** Commands that write or move state (update, update - refuses to touch dirty repositories, feature branches, missing repos, or - unmanaged checkouts. `update` only fetches and fast-forwards clean default - branches; it never resets, rebases, stashes, or rewrites history. When the - workspace root is itself a git checkout, `update` treats it like any other - clean default branch and `check` reports it as `(workspace root)`. The root's - dirty probe ignores untracked files, so its own `repos/` and `worktrees/` - contents never mark it dirty. Scoped runs (`--workspace `) leave the - root out entirely. +- **Conservative mutation.** Commands that write or move state (`update`, + `install`, `add`, `remove`) refuse to touch dirty repositories, feature + branches, missing repos, or unmanaged checkouts. `update` only fetches and + fast-forwards clean default branches; it never resets, rebases, stashes, or + rewrites history. When the workspace root is itself a git checkout, `update` + treats it like any other clean default branch and `check` reports it as + `(workspace root)`. The root's dirty probe ignores untracked files, so its own + `repos/` and `worktrees/` contents never mark it dirty. Scoped runs + (`--workspace `) leave the root out entirely. - **Machine-readable output.** `check --json` emits structured results for tools; plain output is for humans. - **Exit code contract.** `wspace check` exits `0` when the workspace is clean @@ -58,10 +58,10 @@ Design principles: [--visibility ]` — append an entry to the manifest: a bare or `owner/name` shorthand string, or an object entry via `--url` (name defaults to the URL basename, overridable - with `--name` or a positional name). Edits are surgical manifests survive. - GitHub shorthand entries are probed with `gh`; pass `--create` to create a - missing repository first (default private). Nothing is cloned; run - `wspace install ` afterwards. + with `--name` or a positional name). Edits are surgical and preserve existing + manifest content. GitHub shorthand entries are probed with `gh`; pass + `--create` to create a missing repository first (default private). Nothing is + cloned; run `wspace install ` afterwards. - `wspace remove ` — delete the entry whose effective name matches. Surgical edit; local checkouts are never deleted. - `wspace update` — fetch remotes and fast-forward only clean default branches, diff --git a/skills/wspace/references/COMMANDS.md b/skills/wspace/references/COMMANDS.md index b534f26..d57bea6 100644 --- a/skills/wspace/references/COMMANDS.md +++ b/skills/wspace/references/COMMANDS.md @@ -16,12 +16,14 @@ auto-detection. - **Provider-agnostic.** Manifest URLs are passed directly to `git clone`; any Git host works (GitHub, GitLab, Bitbucket, SourceHut, Gitea, SSH remotes). Shorthand expansion defaults to `github.com` and is retargetable via the - `host` key. `env`) refuse dirty repositories, feature branches, missing repos, - and unmanaged checkouts. `update` only fetches and fast-forwards clean default - branches; it never resets, rebases, stashes, or rewrites history. When the - workspace root directory is itself a git checkout, it is treated like any - other repository under the same policy (`update` fast-forwards a clean root; - `check` reports it as `(workspace root)`). + `host` key. +- **Conservative mutation.** Commands that write or move state (`update`, + `install`, `add`, `remove`) refuse to touch dirty repositories, feature + branches, missing repos, and unmanaged checkouts. `update` only fetches and + fast-forwards clean default branches; it never resets, rebases, stashes, or + rewrites history. When the workspace root directory is itself a git checkout, + it is treated like any other repository under the same policy (`update` + fast-forwards a clean root; `check` reports it as `(workspace root)`). - **Machine-readable output.** `check --json` emits structured results for tools; plain output is for humans. - **Exit code contract.** `wspace check` exits `0` when the workspace is clean @@ -29,51 +31,57 @@ auto-detection. ## Manifest schema -Schema version 4 keeps one `repositories[]` array with exactly two entry forms: +Schema version 4 keeps ordinary repositories and workspace repositories in +separate arrays, `repositories[]` and `workspaces[]`, with the same two entry +forms in either: 1. **Shorthand** `"repo"`, `"owner/repo"`, or `{ "name", "owner" }` — expands - against the manifest's `host` (default `github.com`) and top-level/default - `owner` to `https:////.git`. + against the manifest's `host` (default `github.com`) and top-level `owner` to + `https:////.git`. 2. **Object** `{ "name", "url" }` — a plain repository for any Git host. `url` and `owner` are mutually exclusive on object entries. -Legacy keys removed in v4 produce migration errors: `workspaces[]` (composition -is now automatic through detection), entry fields `path`, `groups`, -`localFiles`, and `manifest`. The removed management keys `vaultDirectory`, -`worktreesDirectory`, and `secretsDirectory` are rejected. +A `workspaces[]` entry must be a Git repository containing a valid +`workspace.json` child manifest. Workspace checkouts live under +`workspacesDirectory` when set, otherwise under `repositoriesDirectory`; the +child manifest's repositories resolve against that child's own `repos/` +directory. Use `wspace add --as-workspace` to write a `workspaces[]` entry. + +Unknown keys are ignored by the loader (v3 leftovers such as entry-level `path`, +`groups`, `localFiles`, and `manifest` fall through silently). The only removed +key that errors is `vaultDirectory`. ## Local names A repository checks out at `/`, where `name` is the post-expansion label: ownership lives in URLs, never in paths. Names reject -slashes, backslashes, and traversal; duplicates - within a manifest or across -the composed tree - are errors. To use a different local label than the -shorthand name, write the explicit form with your chosen `name` plus a full -`url`. Child manifests are self-contained: their own `host` and `owner` apply. - -Recursion conventions: child manifests re-root their own directory defaults. +slashes, backslashes, and traversal. Within one manifest, names must be unique +across `repositories[]` and `workspaces[]`, even when the directories differ; +across workspaces the same name is allowed because each checks out under its own +directory. The only conflict is when two entries resolve to the same checkout +path. To use a different local label than the shorthand name, write the explicit +form with your chosen `name` plus a full `url`. Child manifests are +self-contained: their own `host` and `owner` apply, and they re-root their own +directory defaults. ## Commands -| Command | Purpose | -| :------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `wspace check` | Read-only baseline check. Reports `CLEAN`, `DIRTY`, `FEATURE_CLEAN`, `DIVERGED`, `UNKNOWN`, `MISSING`, `UNMANAGED` states. A git workspace root is reported first as `(workspace root)`; untracked files there never count as dirty. Omitted when scoped with `--workspace`. | -| `wspace check --json` | Structured per-repo state for tools. | -| `wspace install []` | Clone missing repos, converging in one invocation: each pass re-resolves the tree, so newly detected sub-workspaces bootstrap without reruns. Scoped targets stay single-pass. Fresh clones lack gitignored files and repo setup. | -| `wspace add [] [--url ] [--name ] [--create] [--visibility ]` | Append a manifest entry: shorthand string, or object via `--url`. Surgical edit (comments preserved). GitHub shorthands probed with `gh`; `--create` makes a missing repo first (default private). Never clones; run `install` after. | -| `wspace remove ` | Delete the manifest entry by effective name. Surgical edit; local checkouts are never deleted. | -| `wspace update` | Fetch remotes and fast-forward only clean default branches, including the workspace root's own checkout when it is a git repository (reported first; untracked workspace content never marks it dirty). Omitted when scoped with `--workspace`. | -| `wspace validate` | Validate the manifest without touching any repository. | - -`origin/HEAD`) using `--no-track`, preventing forks from dirty local `HEAD` -references. - -- **Path rule**: When running raw git, use the absolute `"$PWD/..."` form. - `git -C repos/` changes Git's working directory to the repo root, so a - instead of the workspace root. `$PWD` is the workspace root. - - ```sh - ``` +| Command | Purpose | +| :------------------------------------------------------------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `wspace check [--json] [--workspace ]` | Read-only baseline check. Reports `CLEAN`, `DIRTY`, `FEATURE_CLEAN`, `DIVERGED`, `UNKNOWN`, `MISSING`, `UNMANAGED` states. A git workspace root is reported first as `(workspace root)`; untracked files there never count as dirty. Omitted when scoped with `--workspace`. | +| `wspace init [--host ] [--owner ] []` | One-time scaffold for an empty directory: writes a fresh `workspace.json` (schema v4) with optional host/owner and seeded shorthand entries, and creates the standard `repos/` directory. Fails closed if any manifest already exists. | +| `wspace install [] [--json] [--workspace ] [--dry-run]` | Clone missing repos, converging in one invocation: each pass re-resolves the tree, so newly detected sub-workspaces bootstrap without reruns. Scoped targets stay single-pass. Fresh clones lack gitignored files and repo setup. `i` is an alias. | +| `wspace add [] [--url ] [--name ] [--as-workspace] [--create] [--visibility ]` | Append a manifest entry: shorthand string, or object via `--url`. `--as-workspace` places the entry in the `workspaces[]` array. Surgical edit (comments preserved). GitHub shorthands probed with `gh`; `--create` makes a missing repo first (default private). Never clones; run `install` after. | +| `wspace remove ` | Delete the manifest entry by effective name. Surgical edit; local checkouts are never deleted. | +| `wspace path [--json]` | Fuzzy-find a workspace directory (repo or sub-workspace). Use in command substitution: `cd "$(wspace path workspace-cli)"`. | +| `wspace update [--json] [--workspace ] [--dry-run]` | Fetch remotes and fast-forward only clean default branches, including the workspace root's own checkout when it is a git repository (reported first; untracked workspace content never marks it dirty). Omitted when scoped with `--workspace`. | +| `wspace workspaces [--json]` | List discovered sub-workspaces with repo counts. | +| `wspace validate` | Validate the manifest without touching any repository. | + +All paths resolve relative to the directory containing the manifest. When +running raw git, use the absolute `"$PWD/..."` form (e.g. +`git -C repos/ worktree add "$PWD/worktrees//" -b `) +so paths anchor to the workspace root regardless of the current directory. ## Check status values @@ -89,8 +97,9 @@ references. | `MISSING` | Expected repository path is not a clone. | | `UNMANAGED` | Checkout present but not tracked by the manifest. | -or returns any state other than `CLEAN` or `FEATURE_CLEAN`, halt or request user -resolution before applying multi-repo edits. +If `wspace check` exits `1` — or reports any state other than `CLEAN` or +`FEATURE_CLEAN` — halt and request user resolution before applying multi-repo +edits. A `FEATURE_CLEAN` checkout can be fully landed upstream (squash merges detach branch commits); run the root AGENTS.md **Upstream verification** tip diff @@ -99,6 +108,8 @@ before treating its content as stranded. ## Common pitfalls - **`PATH_BLOCKED` or `INVALID` during `install`**: An existing path occupies - the expected repository location but is not a Git repository. `wspace install` - fails closed without touching it. Remove or relocate the blocking path - manually. any destination path that is a symbolic link. + the expected repository location but is not a Git repository — including a + destination that is a symbolic link not pointing at a Git checkout. + `wspace + install` fails closed without touching it. Remove or relocate the + blocking path manually.