On this page
Manage monorepo workspaces with multiple independently-versioned projects.
#rlsbl monorepo
Manage monorepo workspaces with multiple independently-versioned projects. Initialize workspaces, add or remove projects, sync CI workflows, check name availability, and analyze dependency graphs. Provides 17 monorepo subcommands: init, add, remove, list, sync, status, check-names, outdated, snapshot, snapshot-check, mirror, graph, impact, extract, absorb, cleanup, rename-releasable. Plus 1 subgroup: release. Supports all 17 release targets in a single workspace.toml (the app help enumerates them).
#monorepo init
Create a new monorepo workspace by generating the .rlsbl-monorepo directory and a workspace.toml at the current directory, carrying the mandatory root member whose kind you declare and a [[releasables]] section. This must be run at the repository root before adding individual projects with the add subcommand. Each workspace tracks multiple independently-versioned projects that share a single git repository.
Effect: mutating
Dry run: not supported — it bootstraps the workspace every other command reads, and there is no workspace to preview against until it exists
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
root-member | choice | required | Selection (not typed as a flag). Elect exactly one of --root-dev-node, --root-releasable. What kind of member owns the repository root. Every workspace has exactly one root member, and it owns every tracked file no other member claims; whether those files need changelog coverage is a per-repository decision with no default. | ||
--root-dev-node | required | Elects root-member = root-dev-node. The root member is a dev node: files at the repository root that no other member claims need no changelog coverage. | |||
--root-releasable | str | required | Elects root-member = root-releasable. The root member belongs to a named releasable: files at the repository root that no other member claims get changelog coverage under it. Its value: name of the releasable the root member belongs to; it is created in [[releasables]] | ||
--tag-format | str | required | Only with --root-releasable. tag format for that releasable, e.g. "v{version}" for bare version tags or "{name}@v{version}" for the workspace scheme; a root releasable never inherits a default | ||
--auto-commit, --no-auto-commit | bool | optional | Automatically commit the generated workspace.toml configuration file to git (the handler commits when neither form is passed) |
#monorepo add
Register a project directory in the monorepo workspace.toml configuration. The path argument specifies the project's location relative to the repo root. Optional settings cover display name, target registry, inter-project dependencies, releasable membership, registry identity, and flags marking the project as a shared library or a dev-only leaf. A --releasable naming a group [[releasables]] does not declare yet creates it, as absorb creates one for an arriving member: a singleton entry whose tag_format is written out explicitly, derived from the member's primary target scheme unless --tag-format states it. The mirror destination is not among them: it is a releasable-level key, declared in workspace.toml beside the releasable it binds. What CI reacts to is not among them: the router's paths filters are derived from the workspace, never declared per project.
Effect: mutating
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--name | str | optional | Display name for the project in workspace.toml (defaults to directory name) | ||
--target | str | optional | Registry this project publishes to (e.g. npm, pypi, go) | ||
--depends-on | str | optional | Comma-separated names of workspace projects this project depends on | ||
--library | str | optional | Mark as a shared library consumed by other workspace projects (true/false) | ||
--dev-only | str | optional | Mark as a dev-only leaf node excluded from the dependency boundary guardrail (true/false) | ||
--releasable | str | optional | Releasable group this project belongs to (name of a [[releasables]] entry, which is created when it does not exist yet, or 'false' to opt out of versioning) | ||
--tag-format | str | optional | The tag format of the releasable this command creates, e.g. "{name}@v{version}" or "pkgs/thing/v{version}". Derived from the member's primary target when omitted; required when its targets span both tag schemes. Illegal when --releasable names a releasable that already exists, which brings its own format, and with --releasable false, which creates none. | ||
--registry-name | str | optional | Package registry identity for this project (used verbatim for name checks; overrides prefix/suffix) | ||
--auto-commit, --no-auto-commit | bool | optional | Auto-commit workspace.toml and trigger scaffold/sync commits (the handler commits when neither form is passed) |
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
path | str | required | Relative path from the repo root to the project directory to register |
#monorepo remove
Unregister a project from the monorepo workspace.toml by its path. This removes the project entry from the workspace configuration file but does not delete any files, directories, or git history on disk. The project's code remains intact and can be re-added later with the add subcommand if needed.
Effect: mutating
Dry run: not supported — the whole edit is deleting the one workspace.toml entry you just named, and a preview of it would restate the path back to you
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
path | str | required | Relative path from the repo root of the project to unregister from workspace.toml |
#monorepo list
Display every member registered in the monorepo workspace.toml file, one row each: the member's name, its path relative to the repo root, the releasable it is versioned under (or false when it is opted out of versioning, or -- when it declares none), and the member flags it carries (library, dev-only, test-only). A release target is not among them -- targets are detected from each member's own manifests, never declared in workspace.toml -- and neither is a mirror destination, which belongs to the releasable rather than the member.
Effect: read_only
#monorepo sync
Inline every project's CI jobs into a single generated ci-router.yml (and publish jobs into publish.yml) in the shared .github/workflows directory at the repository root. Jobs are inlined rather than routed via reusable-workflow calls because GitHub rejects workflows that reference 20 or more reusable workflows. Stale per-project workflow copies at the root are removed via saferm.
Effect: mutating
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--auto-commit, --no-auto-commit | bool | optional | Auto-commit merged workflow files in .github/workflows/ (the handler commits when neither form is passed) |
#monorepo status
Show the current version, last release tag, and changelog coverage for every project in the monorepo workspace. Coverage is the real JSONL figure -- the commits since the project's last tag, scoped to the project and minus the exempt ones, rendered covered/tracked with an (N exempted) suffix, or 'no changelog' when the project has no changes directory. A publish-suppressed member's version comes from its releasable's version file, annotated (version file): nothing publishes such a member, so nothing bumps its manifest and the version-consistency check reads the same file rather than the manifest. Provides a quick overview of which projects have pending changes and are ready for their next release.
Effect: read_only
#monorepo check-names
Check package name availability on a target registry for all projects in the monorepo workspace. Queries the registry API for each project name and reports whether it is available or already taken. Supports optional prefix and suffix arguments to test naming conventions like scoped packages, with a configurable delay between registry queries to avoid rate limiting.
Effect: read_only
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--target | str | required | Registry to query for name availability across all workspace projects Values: npm (the npm registry), pypi (the Python Package Index), go (the Go module proxy), github (GitHub repository names). | ||
--prefix | str | optional | String to prepend to each project name before checking availability | ||
--suffix | str | optional | String to append to each project name before checking availability | ||
--delay | str | default: 200 | Milliseconds to wait between consecutive registry API queries |
#monorepo outdated
Scan all projects in the monorepo workspace for intra-workspace dependencies that reference older versions than what is currently available in the workspace. Lists each outdated dependency with the referenced version and the latest available version, helping identify which downstream projects need a version bump after upstream releases.
Effect: read_only
#monorepo snapshot
Regenerate the committed JSON artifact at .rlsbl-monorepo/snapshot.json summarizing all packages, versions, dependencies, and graph structure, and commit it. Verifying without regenerating is a separate command, rlsbl monorepo snapshot-check. Under --dry-run the artifact is computed but neither written nor committed, and the preview names both steps.
Effect: mutating
#monorepo snapshot-check
Verify that .rlsbl-monorepo/snapshot.json matches the workspace it describes, without regenerating it. Exits 1 when the artifact is stale or missing. This is the read-only half of the former monorepo snapshot --check flag; rlsbl monorepo snapshot is the half that writes.
Effect: read_only
#monorepo mirror
Reconcile a monorepo project's subtree mirror toward its desired state. The mirror is a tool-owned, derived artifact: it observes the remote, then converges it to exactly one scaffold commit atop the current deterministic subtree split, force-pushing (with lease) as the routine write. A tripwire refuses to touch a mirror carrying foreign (hand-authored) commits. Use --dry-run to print a plan (converged, scaffold-stale, behind, scaffold-missing, contract-violated, ancestry-undetermined, or virgin) without writing.
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
project | str | required | Name of the workspace project to split and push as a standalone mirror repo |
#monorepo graph
Export the monorepo dependency graph as DOT (Graphviz) or an indented text tree; the framework-owned --json yields the same graph as a structured document. Supports filtering by a root package (transitive deps) or reverse package (transitive rdeps), with optional depth limiting. Use --output to write the rendering to a file instead of stdout.
Effect: mutating
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--format | str | optional | Rendering for the dependency graph (the handler renders text when omitted) Values: text (an indented text tree), dot (a Graphviz DOT document). | ||
--output | str | optional | File path to write the graph output to instead of printing to stdout | ||
--root | str | optional | Filter to show only transitive dependencies reachable from this package | ||
--reverse | str | optional | Filter to show only transitive reverse dependencies of this package | ||
--depth | int | optional | Maximum number of dependency hops to traverse from the root or reverse node |
#monorepo impact
Analyze the impact of changes to a package, file, or git diff range on the monorepo dependency graph. Shows direct and transitive dependents, test scope, and release candidates as a human report, or as a structured document under the framework-owned --json. Supports package names, file paths, and --since for git-based change detection.
Effect: read_only
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--depth | int | optional | Maximum number of dependency hops to traverse when computing transitive impact | ||
--since | str | optional | Git ref to diff against HEAD (e.g. HEAD~3, v1.0.0) |
#monorepo extract
Extract a releasable out of the monorepo into its own repository. The releasable is the portable unit: its members' history is filtered into a new repo (hoisted to the root when it has a single member), its whole release state -- version, changelog, release archives with their release commits, config and hooks -- is transplanted, the release commits and changelog hashes are remapped onto the rewritten commits, and its tags are translated to the destination's scheme with one boundary alias at the current version. The source loses the members, the releasable and its state in one commit, with the CI router re-synced and the snapshot regenerated. A mirrored releasable is extracted by promotion instead of by filtering: the destination is cloned from the mirror and adopts the standalone history consumers already resolve, with the monorepo-to-mirror commit correspondence derived by subtree split and recorded in the destination's transition record. A promotion refuses a mirror whose contract is violated or whose split ancestry cannot be established, and one whose tree is behind the source. Refuses a releasable owning the root member, and a remaining member that depends on a departing one (naming the rewrite command that severs the edge). Use --dry-run to see the whole plan first.
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--delete-with-rm, --no-delete-with-rm | bool | optional | Delete the departed members' directories with a plain recursive rm instead of saferm (which is what an unset flag means). Without it, a missing saferm is a hard error rather than a silent downgrade to an unrecoverable delete. |
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
releasable_name | str | required | Name of the releasable group in workspace.toml to extract, with every member it owns |
target_path | str | required | Filesystem path where the new repository will be created (must not exist) |
#monorepo absorb
Absorb an external repository into this workspace as a releasable. The source's history is rewritten under the destination path and merged in (full history, rewritten paths), its version tags are imported under the destination's tag scheme with one boundary alias at the current version, and its whole release state -- changelog, release archives with their release commits, config and version -- moves into a releasable's state directory with every hash and release commit remapped onto the rewritten commits. Without --releasable a singleton releasable named after the member is created, with its tag_format written explicitly. Nothing is fetched as a tag, so a tag this repository already owns is never moved or deleted; a colliding tag name or version is refused before anything is written. A crashed run is completed by re-running it. Use --dry-run to see the whole plan first.
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Flags
| Name | Short | Type | Presence | Env | Description |
|---|---|---|---|---|---|
--name | str | optional | Workspace member name for the absorbed package (the basename of the destination path when omitted) | ||
--registry-name | str | optional | Package registry identity recorded in workspace.toml (used verbatim for name checks) | ||
--releasable | str | optional | An existing releasable group to join. When omitted, a singleton releasable named after the member is created for it. | ||
--tag-format | str | optional | The tag format of the releasable this command creates, e.g. "{name}@v{version}" or "pkgs/thing/v{version}". Derived from the member's primary target when omitted; required when its targets span both tag schemes. Illegal with --releasable, which brings its own format. | ||
--delete-with-rm, --no-delete-with-rm | bool | optional | Delete the per-package release state that moves to the releasable with a plain recursive rm instead of saferm (which is what an unset flag means). Without it, a missing saferm is a hard error rather than a silent downgrade to an unrecoverable delete. |
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
source_repo | str | required | Filesystem path to the external git repository to absorb |
dest_path | str | required | Destination directory (and workspace member path) the source repo's history is rewritten under |
#monorepo cleanup
Remove per-package release-state residue from releasable member packages: .rlsbl/changes/, .rlsbl/releases/, .rlsbl/bases/, .rlsbl/lint/, .rlsbl/version, per-package CHANGELOG.md, and .rlsbl/config.json when identical to the releasable-level config. Per-package hooks/ directories are preserved (live feature), and members whose path is the workspace root are exempt. Deletions go through saferm (audit trail, recoverable) and are committed automatically. Detect residue first with rlsbl check --name releasable-residue.
Effect: mutating
#monorepo rename-releasable
Rename a releasable group. Rewrites the [[releasables]] name and every member's releasable field in workspace.toml (preserving comments), moves the state directory, drops the stale changelog validation cache, re-runs monorepo sync, and commits it all as one commit. When tag_format contains {name}, a boundary alias tag for the current version is created at the old tag's commit and pushed; historical releases stay under the old prefix. Idempotent: re-running heals a crash between the commit and the tag push.
Effect: mutating · consequential (prompts before running; --approve-consequential skips)
#Arguments
| Name | Type | Presence | Description |
|---|---|---|---|
old_name | str | required | Current name of the releasable group in workspace.toml |
new_name | str | required | New name for the releasable group in workspace.toml and state directories |