Last Updated: 2025-12-23
CLI Version: 0.2.0-dev
Status: Feature-complete for core local operations
This document describes the implemented Cambria command-line interface and how
it maps onto the underlying Go code. It supersedes the earlier planning document
CAMBRIA_CLI_PLAN.md.
This is not a design plan and does not contain implementation snippets.
Each section points to the concrete implementation files under cmd/cambria and
the associated APIs under pkg/vcs, pkg/auth, and pkg/store.
1. CLI Entry Point and Globals
- Entry file:
cmd/cambria/main.go - Framework:
github.com/urfave/cli/v3 - Top-level command name:
cambria - Description: "a fossil-like version control system"
- Version string:
0.2.0-dev
1.1 Registered Subcommands
Defined in the Commands slice of the root cli.Command in main.go:
init–initCommand()(seecmd/cambria/init.go)open–openCommand()(seecmd/cambria/open.go)close–closeCommand()(seecmd/cambria/close.go)add–addCommand()(seecmd/cambria/add.go)rm–rmCommand()(seecmd/cambria/rm.go)mv–mvCommand()(seecmd/cambria/mv.go)commit–commitCommand()(seecmd/cambria/commit.go)checkout–checkoutCommand()(seecmd/cambria/checkout.go)status–statusCommand()(seecmd/cambria/status.go)diff–diffCommand()(seecmd/cambria/diff.go)log–logCommand()(seecmd/cambria/log.go)branch–branchCommand()(seecmd/cambria/branch.go)tag–tagCommand()(seecmd/cambria/tag.go)merge–mergeCommand()(seecmd/cambria/merge.go)user–userCommand()(seecmd/cambria/user.go)
This list represents the current, complete CLI surface of the cambria
binary.
1.2 Global Flags
Configured on the root command in main.go and available to all subcommands:
--verbose,-v
Enables verbose output (currently available as a flag; individual commands decide whether to consult it).--repository,-R <path>
Overrides repository discovery and forces use of the specified repository database path.
Repository discovery logic is centralized in FindRepository in
cmd/cambria/common.go (see below).
2. Shared CLI Utilities
2.1 Common Helpers (cmd/cambria/common.go)
This file implements reusable helpers used across multiple commands:
const CheckoutMetaFile = "_cambria"
Name of the metadata file written into a working directory byopenand the initial commit, used to locate the backing repository.FindRepository(ctx, cmd)
Responsibility: Resolve the repository database path for commands that operate against an existing repo.
Resolution order:- Explicit
--repositoryflag on the current command. _cambriametadata file in the current directory._cambriametadata files in parent directories up to the filesystem root.CAMBRIA_REPOenvironment variable.
- Explicit
ResolveVersion(repo, version)
Responsibility: Map user-facing version strings to full manifest UUIDs using thevcs.RepositoryAPIs and direct DB lookups.
Supported inputs:- Special names:
tip(most recent manifest),current(currently checked-out manifest). - Prefixed labels:
tag:NAME,branch:NAME(resolved viarepo.ResolveTag/repo.ResolveBranch). - Exact 64-character hex UUIDs (validated to exist in
blob.uuid). - Hex prefixes (6–63 chars), with ambiguity checks against
manifestrows. - Unprefixed branch/tag names (tries branch first, then tag).
- Special names:
FormatError(err)/Fatal(err)
Helpers for standard CLI error formatting and process termination. Most commands return errors tourfave/clidirectly instead of callingFatal.
These helpers rely on vcs.Repository and the SQLite-backed store.DB for
database access.
3. Repository Lifecycle Commands
3.1 cambria init
- File:
cmd/cambria/init.go - Purpose: Initialize a new Cambria repository database file.
- Usage:
cambria init [--force] <repository-path> - Key behavior:
- Requires exactly one positional argument, the repository file path.
- Refuses to overwrite an existing file unless
--forceis provided. - Invokes
vcs.InitRepositoryto create the SQLite-backed repository and closes it immediately after initialization. - Prints a confirmation including the created repository path.
3.2 cambria open
- File:
cmd/cambria/open.go - Purpose: Open an existing repository into a working directory and perform an initial checkout.
- Usage:
cambria open [--force] [--version <version>] <repository-path> [<directory>] - Key behavior:
- Opens the repository via
vcs.OpenRepository. - Resolves the target manifest using
ResolveVersion; the default--versionis"trunk"(interpreted byResolveVersion). - If
<directory>is omitted, uses the current working directory as the checkout root. - Performs a checkout via
repo.Checkoutwithvcs.CheckoutOptions(honoring--force). - Writes a
_cambriametadata file into the checkout directory containing the absolute repository path (seeCheckoutMetaFile). - Prints a short summary including repository path, local root, and checked-out manifest UUID.
- Opens the repository via
3.3 cambria close
- File:
cmd/cambria/close.go - Purpose: Disassociate the current working directory from its repository checkout.
- Usage:
cambria close - Key behavior:
- Operates on the current working directory.
- Fails if no
_cambriametadata file exists (i.e., not a Cambria checkout root). - Deletes the
_cambriafile and confirms closure.
4. Working Directory and File Operations
All of these commands rely on FindRepository and, where applicable, on
vcs.NewWorkDir to interact with the VFILE-backed working directory state.
4.1 cambria add
- File:
cmd/cambria/add.go - Purpose: Add files to version control in the current checkout.
- Usage:
cambria add <file>... - Key behavior:
- Requires at least one path argument.
- Uses
FindRepositoryto resolve the backing repository andvcs.OpenRepositoryto open it. - Constructs a
vcs.WorkDirfor the current working directory viavcs.NewWorkDir. - Delegates the actual staging semantics to
repo.Add(workDir, paths...)inpkg/vcs.
4.2 cambria rm
- File:
cmd/cambria/rm.go - Aliases:
remove,delete - Purpose: Remove tracked files from version control and mark them as deleted.
- Usage:
cambria rm <file>... - Key behavior:
- Requires one or more path arguments.
- Resolves the repository via
FindRepository, opens it withvcs.OpenRepository. - Uses the current working directory as the working root.
- Calls
repo.Remove(workRoot, paths...)to update VFILE and working directory state. - Prints a confirmation line for each removed path.
4.3 cambria mv
- File:
cmd/cambria/mv.go - Aliases:
rename,move - Purpose: Rename or move a tracked file inside the working directory, preserving history.
- Usage:
cambria mv <old-path> <new-path> - Key behavior:
- Requires exactly two positional arguments: old and new path.
- Resolves and opens the repository via
FindRepositoryandvcs.OpenRepository. - Uses the current working directory as the working root.
- Delegates rename semantics (including VFILE
orignametracking) torepo.Rename(workRoot, oldPath, newPath). - Prints a summary of the rename operation.
5. Commit, Status, and Diff
5.1 cambria commit
- File:
cmd/cambria/commit.go - Aliases:
ci - Purpose: Record a new manifest from the working directory state.
- Usage:
cambria commit --message <text> [--tag <name> ...] [--allow-empty]
- Key behavior:
- Uses
FindRepositoryandvcs.OpenRepositoryto locate and open the repo. - Builds a
vcs.WorkDirfor the current directory usingvcs.NewWorkDir. - Detects whether a current checkout exists via
vcs.GetCurrentCheckoutin a DB transaction.
- Uses
Initial-commit path:
- If no checkout exists,
performInitialCommitis invoked:- Scans the working directory via
workDir.Scan(). - Writes blobs for each file (excluding metadata like
_cambriaand.cambriaignore) viastore.WriteBlobin a transaction. - Builds
artifact.FileEntryvalues and callsrepo.Checkinwithvcs.CheckinOptions. - Performs a forced checkout of the new manifest via
repo.Checkoutto establish baseline state. - Optionally writes
_cambriain the working directory, pointing at the repository path.
- Scans the working directory via
Normal-commit path:
- If a checkout exists, builds
vcs.CommitOptions(including nestedvcs.CheckinOptions) based on CLI flags:--message(required) becomes the commit comment.- Repeated
--tagflags add labels. --allow-emptydetermines whether empty commits are permitted.
- Checks for a pending merge via
repo.GetPendingMergeto distinguish merge commits from regular ones. - Calls
repo.Commit(workDir, opts). - After commit, attempts to rescan the working directory and prints a summary
of added/modified/deleted files based on
vcs.FileStatusvalues.
5.2 cambria status
- File:
cmd/cambria/status.go - Purpose: Show the state of the working directory relative to the current baseline.
- Usage:
cambria status - Key behavior:
- Resolves repository with
FindRepositoryand opens it. - Builds a
vcs.WorkDirfrom the current directory and callsworkDir.Scan(). - Sorts reported files by status and path for stable output.
- Derives counts of files by status (
untracked,added,modified,deleted). - Attempts to show the currently checked-out manifest UUID by using
vcs.GetCurrentCheckoutin a DB transaction. - Integrates merge state by calling
repo.GetPendingMerge; if a merge is in progress, prints a warning and referencescambria merge --abortfor cancellation. - Prints per-file lines keyed by status (for example,
ADDED,MODIFIED,DELETED,UNTRACKED) and a short summary line.
- Resolves repository with
5.3 cambria diff
- File:
cmd/cambria/diff.go - Purpose: Show differences between manifests or between a manifest and the working directory.
Usage patterns:
cambria diff
Diff working directory against the current checkout.cambria diff --from <version>
Diff working directory against a specific manifest.cambria diff --from <version1> --to <version2>
Diff two manifests.
Relevant flags:
--from <version>– Starting manifest (resolved byResolveVersion).--to <version>– Ending manifest (resolved byResolveVersion); when omitted, the working directory is used.--unified,-u <n>– Number of context lines for unified diff output.
Key behavior:
- Resolves and opens the repository with
FindRepositoryandvcs.OpenRepository. - Builds
vcs.DiffOptionsfrom CLI flags (unified mode, context lines). - Dispatches among three modes:
- Working dir vs current checkout (no
--from/--to):- Gets the current checkout UUID with
vcs.GetCurrentCheckout. - Uses
vcs.NewWorkDirandrepo.DiffWorkDir.
- Gets the current checkout UUID with
- Working dir vs specific manifest (
--fromset,--toempty):- Resolves the
fromversion withResolveVersion. - Uses
repo.DiffWorkDiras above.
- Resolves the
- Manifest vs manifest (
--fromand--toset):- Resolves both versions with
ResolveVersion. - Uses
repo.DiffManifests.
- Resolves both versions with
- Hands the resulting
[]vcs.FileDifftoprintDiffsfor formatted output, using a unified diff style withdiff --cambriaheaders.
- Resolves and opens the repository with
6. History, Branches, and Tags
6.1 cambria log
- File:
cmd/cambria/log.go - Aliases:
history - Purpose: Display the commit history (timeline) for a repository or branch.
- Usage:
cambria log [options] Key flags:
--limit,-n <N>– Max number of entries (0 means unlimited).--offset <N>– Skip N entries from the start.--reverse,-r– Show in chronological order (oldest first).--branch,-b <name>– Restrict output to a specific branch (user-facing name).--current-branch,-c– Derive the branch from the current checkout.--oneline– Render each entry as<short-hash> <first-line-of-comment>.
Key behavior:
- Opens the repository via
FindRepository/vcs.OpenRepository. - Constructs
vcs.LogOptionsfrom flags and passes them torepo.Log. - For
--current-branch, determines the current manifest usingvcs.GetCurrentCheckout, then retrieves associated labels viavcs.GetLabelsForManifestand extracts the branch label usingvcs.ParseLabelName. The resulting user-facing branch name is stored inLogOptions.Branch. - Renders either a compact one-line format (oneline mode) or a more verbose Git-like view including commit hash, user, date, and multi-line comments.
- Opens the repository via
6.2 cambria branch
- File:
cmd/cambria/branch.go - Aliases:
br - Purpose: Manage named branches via labels in the repository.
Usage forms:
cambria branch
Default action: list branches.cambria branch listcambria branch create <branch-name> [manifest-uuid-or-version]cambria branch delete <branch-name>
Subcommand implementations:
branchListAction
UsesFindRepository, opens the repo, callsrepo.ListBranches, and prints each branch name and associated manifest UUID (truncated for display).branchCreateAction- Resolves repo and opens it.
- If a manifest argument is given, resolves it via
ResolveVersion; otherwise usesrepo.FindTip()as the basis. - Calls
repo.CreateBranch(branchName, manifestUUID)inpkg/vcs, which is responsible for label prefixing (for example,"branch:"). branchDeleteAction- Resolves repo and opens it.
- Invokes
repo.DeleteBranch(branchName)to remove the branch label(s).
Branch listing and manipulation act on the user-facing names; the label prefix
logic (for example, "branch:") is encapsulated in the vcs package.
6.3 cambria tag
- File:
cmd/cambria/tag.go - Purpose: Manage tags on manifests.
Usage forms:
cambria tag
Default action: list tags.cambria tag listcambria tag create <tag-name> [manifest-uuid-or-version]cambria tag delete <tag-name>
Subcommand implementations:
tagListAction
Usesrepo.ListTags()to obtain all tags and prints tag names with associated manifest UUIDs (truncated for display).tagCreateAction- Resolves repo and opens it.
- Resolves the target manifest via
ResolveVersionwhen provided, or falls back torepo.FindTip(). - Calls
repo.CreateTag(tagName, manifestUUID); internal label prefixing (for example,"tag:") is handled byvcs. tagDeleteAction
Usesrepo.DeleteTag(tagName)to remove tag labels.
7. Merge Operations
7.1 cambria merge
- File:
cmd/cambria/merge.go - Purpose: Merge changes from another branch or manifest into the current checkout.
Usage forms:
cambria merge <branch-or-version>cambria merge --baseline <version> <branch-or-version>cambria merge --abort
Key flags:
--abort– Abort an in-progress merge and restore pre-merge state.--baseline <version>– Explicitly specify a baseline (pivot) manifest for the three-way merge.
Key behavior:
- Resolves the repository with
FindRepositoryand opens it viavcs.OpenRepository. - When
--abortis set, callsrepo.AbortMerge(workDir)and reports success. - If no merge is in progress and no
--abortflag is present: - Requires a target argument (branch or manifest identifier).
- Resolves the target to a manifest UUID via the shared
ResolveVersionhelper. - Builds
vcs.MergeOptionswith target UUID, working directory, and optional baseline UUID. - Calls
repo.Merge(opts)inpkg/vcsand inspects the resulting*vcs.MergeResult. - The helper
reportMergeResultprints a detailed summary usingMergeResultfields such asBaseUUID,Added,Updated,Deleted,Renamed,Merged,Conflicts, and flags likeFastForwardandUpToDate. - If conflicts are present, instructs the user to resolve them manually and
then run
cambria commit. For a clean merge, suggests committing immediately.
- Resolves the repository with
Merge state and detection of an in-progress merge integrate with
repo.GetPendingMerge and the VFILE or metadata structures in pkg/vcs.
8. User Management
8.1 cambria user
- File:
cmd/cambria/user.go - Purpose: Manage repository users and their capabilities using the authentication and store layers.
Subcommands:
cambria user new [USERNAME] [CONTACT-INFO] [PASSWORD]
Creates a new user.cambria user list
Lists existing users in a tabular format.cambria user password USERNAME [NEW-PASSWORD]
Changes a user's password.cambria user capabilities USERNAME [CAPABILITIES]
Queries or updates capability flags for a user.
All subcommands use FindRepository and vcs.OpenRepository to locate the
repository database and then operate via pkg/store and pkg/auth.
8.1.1 user new
Dependencies:
store.GetConfigforproject-code.store.UserExists,store.CreateUser.auth.HashPasswordfor Argon2id hashing.
Behavior:
- Prompts for missing username, email, or password if not provided as
arguments (password via
promptPassword, which usesgolang.org/x/term). - Refuses empty usernames or passwords.
- Hashes passwords using the username and project code as part of the hashing context.
- Persists the user with
store.CreateUserand prints the assigned UID. - Detects if this is the first user and notes automatic admin capabilities as provided by the store or auth logic.
- Prompts for missing username, email, or password if not provided as
arguments (password via
8.1.2 user list {#812-user-list}
- Dependencies:
store.ListUsers. - Behavior:
- Retrieves all users and prints a table including UID, login, email, capabilities, and creation date.
- Marks accounts with empty password hashes as disabled.
8.1.3 user password
Dependencies:
store.GetConfig(project code),store.UserExists,store.UpdateUserPassword.auth.HashPassword.
Behavior:
- Validates that the target user exists.
- Prompts for and confirms a new password if not provided.
- Hashes and saves the new password.
8.1.4 user capabilities
Dependencies:
store.GetUserByLogin,store.UpdateUserCapabilities.auth.ParseCapabilitiesto interpret capability strings.
Behavior:
- Without a capabilities argument, prints the current capability string and a human-readable explanation of each active flag (setup, admin, write, read, clone, and so on).
- With a capabilities string, validates each flag character against the
allowed set (
g,i,o,a,s) and updates the user record.
9. Repository Discovery and Version Semantics
While individual commands make direct use of FindRepository and
ResolveVersion, the following behaviors are shared across the CLI:
Repository discovery:
- The
_cambriametadata file, created byopenand the initial commit, is the primary link between a working directory and its backing repository database. - The
--repositoryflag can override this mechanism when running commands outside a checkout root. CAMBRIA_REPOserves as a final fallback when no metadata file is found.
- The
Version names and labels:
- User-facing branch and tag names are presented without prefixes.
- Internal label prefixes (for example,
"branch:","tag:") are encapsulated inpkg/vcslabel operations such asCreateBranch,CreateTag,ResolveBranch, andResolveTag. - The special names
tipandcurrentare understood byResolveVersionand map to the most recent manifest and the current checkout, respectively. - Hex-encoded UUID prefixes (at least six characters) are accepted as long as they resolve to a unique manifest.
10. Fossil Mapping and Coverage Summary
The table below maps implemented Cambria CLI commands to their Fossil counterparts and highlights where functionality is present in the current codebase.
| Fossil Command | Cambria Command | Implementation Files | Status (Local-only) |
|---|---|---|---|
fossil new |
cambria init |
cmd/cambria/init.go, pkg/vcs/repo.go |
Implemented |
fossil open |
cambria open |
cmd/cambria/open.go, cmd/cambria/common.go |
Implemented |
fossil close |
cambria close |
cmd/cambria/close.go |
Implemented |
fossil add |
cambria add |
cmd/cambria/add.go, pkg/vcs/workdir.go |
Implemented |
fossil rm |
cambria rm |
cmd/cambria/rm.go, pkg/vcs/remove.go |
Implemented |
fossil mv |
cambria mv |
cmd/cambria/mv.go, pkg/vcs/rename.go |
Implemented |
fossil commit |
cambria commit |
cmd/cambria/commit.go, pkg/vcs/checkin.go |
Implemented |
fossil checkout |
cambria checkout |
cmd/cambria/checkout.go, pkg/vcs/checkout.go |
Implemented |
fossil status |
cambria status |
cmd/cambria/status.go, pkg/vcs/vfile.go |
Implemented |
fossil diff |
cambria diff |
cmd/cambria/diff.go, pkg/vcs/diff.go |
Implemented |
fossil timeline |
cambria log |
cmd/cambria/log.go, pkg/vcs/log.go |
Implemented |
fossil branch |
cambria branch |
cmd/cambria/branch.go, pkg/vcs/label.go |
Implemented |
fossil tag |
cambria tag |
cmd/cambria/tag.go, pkg/vcs/label.go |
Implemented |
fossil merge |
cambria merge |
cmd/cambria/merge.go, pkg/vcs/merge.go |
Implemented |
fossil user |
cambria user |
cmd/cambria/user.go, pkg/auth/*, pkg/store/user.go |
Implemented |
| Network commands | (none yet) | doc_cambria/CAMBRIA_SYNC_PLAN.md |
Not yet implemented |
"Status" here refers only to the local, file-system based operations currently wired through the CLI and backing VCS, auth, and store APIs.
11. How to Extend the CLI
When adding new commands or extending existing ones, follow these patterns used in the current implementation:
Route through
vcs.Repository:
Always usevcs.OpenRepositoryorvcs.InitRepositoryand the high-level methods onvcs.Repositoryrather than callingpkg/storedirectly from CLI code.Use
FindRepositoryandResolveVersion:
New commands that need repository resolution or version interpretation should reuse the helpers incmd/cambria/common.gofor consistent behavior.Keep CLI logic thin:
Parse flags and arguments incmd/cambria, but keep business logic (checkin, merge, network sync, and so on) inpkg/vcs,pkg/auth, andpkg/store.Follow existing UX patterns:
- Reuse status and summary formats from
status,commit,merge, andlogwhere applicable. - Prefer descriptive error messages that wrap underlying errors with context.
- Reuse status and summary formats from
Update this document:
Whenever a new CLI command is added undercmd/cambria, add a corresponding section here with:- Command name and purpose.
- Usage synopsis and key flags.
- Pointers to implementation files and relevant
pkg/*APIs.
This document, together with AGENTS.md and the doc_cambria/* design
documents, should give a complete and current picture of the Cambria CLI
implementation without duplicating source code.