Projects & Artifacts
These commands cover the lifecycle around a project and the artifacts inside it: authenticating the CLI, initializing a repo, keeping the local project registry accurate, reading and writing individual artifacts, syncing artifact blobs with the service, and inspecting the workflow's type catalog and schemas.
If you're setting up for the first time, Getting Started walks these in order — sign in, authenticate the CLI, create a project, and author artifacts. This page is the per-command reference.
subak init
Initialize a Subak project — the onboarding orchestrator.
subak init [flags]
init walks a fresh git repo to a ready-to-author Subak project. It confirms you're authenticated, resolves an onboarding mode, reads your git origin remote for owner/repo, creates the project under your workspace, nudges the GitHub App install if the repo is unmapped, writes subak.json, installs pre-commit and pre-push hooks, registers Subak as an MCP server in detected agent clients, installs the mode-specific agent skill, extends AGENTS.md and writes SUBAK_USAGE.md, and prints the next step. It is idempotent; --force overwrites a subak.json whose slugs don't match.
The --mode flag selects an onboarding mode: greenfield (a brand-new project), characterize (capture intent for an existing codebase), or rearchitect. When omitted it is auto-detected or prompted.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workflow | string | software | Which workflow this project uses (software or marketing). |
--workspace | string | "" | Workspace slug (default: the workspace from /me). |
--project | string | "" | Project slug (default: the git repo name). |
--mode | string | "" | Onboarding mode: greenfield, characterize, or rearchitect (default: auto-detect or prompt). |
--force | bool | false | Overwrite an existing subak.json with mismatched slugs. |
--service | string | "" | Service base URL (overrides SUBAK_SERVICE_URL). |
--no-browser | bool | false | Don't try to open the GitHub install URL in a browser. |
--skip-github | bool | false | Skip the GitHub App install step (project-only init). |
Requires: a prior subak login, and a git origin remote to infer the GitHub owner/repo.
When to use: The first command you run in a repo you want Subak to manage. It sets up everything — manifest, hooks, MCP registration, agent skills, and the local registry entry.
subak init
subak init --mode greenfield
subak init --workspace acme --project marketplace --skip-github
subak login
Authenticate against the Subak service and store an API key locally.
subak login [flags]
login runs an OAuth-style device-code flow: it prints a short user code and opens the verification page in your browser (--no-browser skips that). On approval it mints an API key and stores it in your OS keychain — or in ~/.subak/credentials.json with --file, which is the right choice for CI and containers. Two shortcuts skip the device flow: --bootstrap redeems a wpkx_ bootstrap token (the primary onboarding handoff from the app), and --key stores a raw API key directly (e.g. one from a welcome email or the app's API Keys settings).
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--bootstrap | string | "" | Redeem a wpkx_ bootstrap token (primary onboarding path). |
--key | string | "" | Store an API key directly, skipping the device-code flow. |
--workspace | string | "" | Workspace slug to select non-interactively (skips the multi-workspace prompt). |
--service | string | "" | Service base URL (overrides SUBAK_SERVICE_URL). |
--no-browser | bool | false | Don't try to open the verification URL in a browser. |
--file | bool | false | Store credentials in ~/.subak/credentials.json instead of the OS keychain (CI/containers). |
--timeout | duration | 15m | How long to wait for approval before giving up. |
When to use: Before init, pull, push, or remote validate. Use --bootstrap for the standard handoff from the app, and --file in CI.
subak login
subak login --bootstrap wpkx_abc123
subak login --file --key wpk_live_xxx
subak login --workspace acme --no-browser
subak logout
Remove the stored Subak API key.
subak logout [flags]
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--file | bool | false | Remove credentials from ~/.subak/credentials.json instead of the OS keychain. |
When to use: To sign the CLI out on a machine, or to clear a stale credential before logging in again.
subak logout
subak logout --file
subak project
Manage the local project registry at ~/.subak/projects.json.
The registry maps (workspace, project) → absolute path. The MCP server consults it to route artifact.* calls to the correct subak.json. (CLI commands like validate and status instead walk up from the current directory.) subak init auto-registers your project; use these subcommands to fix drift or register a project you set up by hand.
subak project register
Register or update a (workspace, project) → path mapping.
subak project register [flags]
With no flags, it walks up from the current directory to subak.json and reads the workspace, project, and path from there; explicit flags win. Re-registering the same path is a no-op; a different path updates the entry in place.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workspace, -w | string | "" | Workspace slug; defaults to workspaceSlug in the resolved subak.json. |
--project, -p | string | "" | Project slug; defaults to projectSlug in the resolved subak.json. |
--path | string | "" | Path to the project root; defaults to the walk-up result. |
cd /path/to/proj && subak project register
subak project unregister
Remove a (workspace, project) entry from the registry. Aliases: remove, rm.
subak project unregister [flags]
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workspace, -w | string | "" | Workspace slug (required). |
--project, -p | string | "" | Project slug (required). |
subak project unregister -w acme -p marketplace
subak project list
List every registry entry. Alias: ls.
subak project list
Takes no flags. Output is a table with WORKSPACE / PROJECT / PATH / ADDED columns.
subak project list
subak project path
Print only the absolute path for a registered (workspace, project).
subak project path [flags]
Output is just the path, which makes it convenient for shell wrappers. If the entry isn't registered it exits non-zero with no stdout (the error goes to stderr), so a wrapper can chain || cleanly.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workspace, -w | string | "" | Workspace slug (required). |
--project, -p | string | "" | Project slug (required). |
subak project path -w acme -p marketplace
# Shell wrapper to cd into a registered project
wp-cd() { cd "$(subak project path -w "$1" -p "$2")" || return; }
subak artifact
CLI fallback for the MCP artifact tools — for reading and writing artifacts when an MCP client isn't configured.
get, put, delete, and move mirror the MCP artifact.get / artifact.put / artifact.delete / artifact.move tools exactly: they resolve (workspace, project) via the local registry, build the same composite store the MCP uses (customer or local — the dogfood on-disk mode was removed, ADR-036), and perform atomic blob + manifest writes. In customer mode the blob uploads to S3 immediately, so no separate subak push is needed.
Writes are version-scoped (ADR-034 / ADR-036): subak.json's artifact map is an ordered sequence of phased versions, and every create/edit/delete names the version it targets — there is no silent default to the active version.
The artifact group has one persistent flag inherited by every subcommand:
| Flag | Type | Default | Description |
|---|---|---|---|
--workflow | string | software | Workflow name. |
subak artifact get
Fetch an artifact via the same path as the MCP artifact.get tool.
subak artifact get --path <path> [flags]
Resolves the project, builds the composite store, looks up the manifest-pinned blob for --path, and prints it (or writes it to --out). Workspace and project default from the nearest subak.json.
Flags (plus inherited --workflow)
| Flag | Type | Default | Description |
|---|---|---|---|
--path | string | "" | Artifact path, extensionless (e.g. personas/alice). Required. |
--workspace | string | "" | Workspace slug; defaults from the nearest subak.json. |
--project | string | "" | Project slug; defaults from the nearest subak.json. |
--out | string | "" | Write content to this file instead of stdout. |
subak artifact get --path personas/alice
subak artifact put
Write an artifact via the same path as the MCP artifact.put tool.
subak artifact put --path <path> [flags]
Resolves the project, builds the store, uploads the blob (S3 in customer mode; local cache in local mode), and atomically updates the named --version in subak.json. The content source is one of --file, --payload, or stdin (when neither is set) — they're mutually exclusive. Output is a single JSON object {"path": ..., "hash": ...}.
--version is required — every write is scoped to an explicit phased version (ADR-034 / ADR-036); there is no default to the active version.
Flags (plus inherited --workflow)
| Flag | Type | Default | Description |
|---|---|---|---|
--path | string | "" | Artifact path, extensionless (e.g. personas/alice). Required. |
--version | string | "" | Version to write the artifact into. Required. |
--file | string | "" | Read content from this file (mutually exclusive with --payload). |
--payload | string | "" | Inline content (mutually exclusive with --file). |
--content-type | string | json | Content type: json, markdown, yaml, png, or webm. |
--workspace | string | "" | Workspace slug; defaults from the nearest subak.json. |
--project | string | "" | Project slug; defaults from the nearest subak.json. |
subak artifact put --path personas/alice --version v1-alpha --file alice.json
echo '{"slug":"alice"}' | subak artifact put --path personas/alice --version v1-alpha
subak artifact put --path notes/readme --version v1-alpha --content-type markdown --payload "# Hi"
subak artifact delete
Remove an artifact from a version — mirrors the MCP artifact.delete tool.
subak artifact delete --path <path> --version <name> [flags]
Resolves the project, calls the store's Delete (the blob is retained server-side for historical-commit resolution), and removes the artifact from the named --version in subak.json. Versioned semantics (ADR-034): in the base version the entry is removed outright; in a later version a path that resolves upstream is tombstoned (path: null), while a path introduced by that version's own patch simply drops its PUT. Output is a single JSON object {"path": ...}.
Flags (plus inherited --workflow)
| Flag | Type | Default | Description |
|---|---|---|---|
--path | string | "" | Artifact path, extensionless (e.g. personas/alice). Required. |
--version | string | "" | Version to delete the artifact from. Required. |
--workspace | string | "" | Workspace slug; defaults from the nearest subak.json. |
--project | string | "" | Project slug; defaults from the nearest subak.json. |
subak artifact delete --path flows/legacy-import --version v2-beta
subak artifact move
Relocate an artifact between versions — mirrors the MCP artifact.move tool.
subak artifact move --path <path> --from <version> --to <version> [flags]
Moves an artifact from one version to another within a workflow (ADR-034 §9 — the manual "assign an artifact to a phase" act). Manifest-only: the content hash already names an existing blob, so no blob is uploaded and no subak push is required. A move relocates (it does not copy): the band between --from and --to is vacated, while everything downstream of --to is preserved. Output is a single JSON object {"path": ..., "from": ..., "to": ..., "hash": ...}.
Most moves auto-resolve. Two cases are genuinely ambiguous and are rejected until you pass --strategy (no mutation happens until you do):
- leapfrog — a version strictly between
--fromand--tostill holds a live edit of--path. - destination-tombstone —
--to's own patch already tombstones--path.
On an ambiguous move the command prints the two named choices to stderr and exits non-zero:
--strategy=source— the destination takes the source version's content; intervening edits are dropped.--strategy=latest— the destination takes the most-recent edit of--pathin the band.
Flags (plus inherited --workflow)
| Flag | Type | Default | Description |
|---|---|---|---|
--path | string | "" | Artifact path, extensionless (e.g. personas/alice). Required. |
--from | string | "" | Source version to move the artifact out of. Required. |
--to | string | "" | Destination version to move the artifact into. Required. |
--strategy | string | "" | Resolve an ambiguous move: source or latest. |
--workspace | string | "" | Workspace slug; defaults from the nearest subak.json. |
--project | string | "" | Project slug; defaults from the nearest subak.json. |
subak artifact move --path personas/alice --from v1-alpha --to v2-beta
subak artifact move --path personas/alice --from v1-alpha --to v3-mvp --strategy=source
subak artifact ping
Verify the MCP server starts and responds correctly.
subak artifact ping [flags]
Starts subak mcp serve as a subprocess, sends an MCP initialize handshake plus tools/list, and confirms the expected artifact tools (artifact.get, artifact.list, artifact.put, artifact.delete, artifact.move) are registered. Exits 0 on success, 1 on failure. Takes only the inherited --workflow flag.
When to use: To confirm the MCP server is healthy before a session that relies on artifact.put/get.
subak artifact ping
subak pull
Download remote artifact blobs into the local cache.
subak pull [flags]
pull reads subak.json, finds the artifact blobs missing from your local cache (~/.subak/cache/), and downloads only those from the service — content-addressed by hash and verified on receipt. Already-cached blobs are never re-fetched, and pull never modifies subak.json or the .subak/ tree. It's the cache-warming complement to push; run it after a git pull brings in teammates' artifacts.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workflow | string | software | Workflow to warm. |
--workspace | string | "" | Workspace slug (overrides subak.json). |
--project | string | "" | Project slug (overrides subak.json). |
--cache-root | string | "" | Override the local cache directory (default ~/.subak/cache). |
--dry-run | bool | false | Print what would be downloaded without fetching. |
--json | bool | false | Emit structured JSON output. |
Requires: login.
When to use: Warm the cache so a subsequent validate doesn't make a per-artifact service round trip.
subak pull
subak pull --dry-run
subak push
Upload local artifact blobs to the Subak service.
subak push [flags]
push reads subak.json, asks the service which blobs it lacks, and uploads only those — content-addressed, so identical content already uploaded under any path is skipped.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workflow | string | software | Workflow to push. |
--workspace | string | "" | Workspace slug (overrides subak.json). |
--project | string | "" | Project slug (overrides subak.json). |
--dry-run | bool | false | Print what would be uploaded without uploading. |
Requires: login.
When to use: Upload new or changed artifact blobs so the service has every blob subak.json names. The pre-push git hook runs this automatically before a git push reaches the remote, so others never see a hash without a blob.
subak push
subak push --dry-run
subak workflow types
Print a workflow's artifact types as type:pattern lines.
subak workflow types [<workflow>] [flags]
workflow types prints one type:pattern line per artifact type in the embedded metagraph, sorted by type id (the pattern is the path glob for that type). The optional positional <workflow> defaults to software. It takes no flags.
When to use: A machine-readable enumeration of the workflow's node types — used by tooling rather than parsing a workflow definition by hand.
subak workflow types
subak workflow types software
subak workflow also has an update subcommand, but it is hidden / pre-alpha and not part of the stable CLI surface. See Experimental / hidden commands.
subak schema
Print the embedded JSON Schema for an artifact type.
subak schema [name] [flags]
schema prints the JSON Schema bundled with the CLI binary for a given artifact type (for example persona, feature, or aggregate). Schemas come from the embedded workflows/<workflow>/schemas/ tree — there is no per-project schemas/ folder. The positional name is required unless you pass --list.
Flags
| Flag | Type | Default | Description |
|---|---|---|---|
--workflow | string | software | Workflow the schema belongs to. |
--list | bool | false | List all schema names in the workflow instead of printing one. |
When to use: Discover the available artifact types and their exact field contracts when authoring artifacts by hand.
subak schema persona
subak schema --workflow software --list
subak schema --workflow marketing audience