CLI Reference
The rasen CLI (rasen) provides terminal commands for project setup, validation, status inspection, and management. These commands complement the AI slash commands (like /rasen-propose) documented in Commands.
Summary
| Category | Commands | Purpose |
|---|---|---|
| Setup | init, update |
Initialize and update rasen in your project |
| Stores (standalone rasen repos) | store setup, store register, store upgrade-identity, store unregister, store remove, store list, store doctor |
Manage stores — standalone rasen repos you've registered |
| Store membership | store add-project, store adopt, store eject, store migrate-membership |
Manage which projects belong to a store, and their planning content |
| Health | doctor |
Report relationship health for the resolved root |
| Working context | context |
Assemble the working set (root + referenced stores) |
| Personal worksets | workset create, workset list, workset open, workset remove |
Keep and open personal, local working views in your tool |
| Browsing | list, view, show |
Explore changes and specs |
| Validation | validate |
Check changes and specs for issues |
| Lifecycle | archive |
Finalize completed changes |
| Workflow | new change, status, instructions, templates, schemas |
Artifact-driven workflow support |
| Workflow library | workflow list/show/which/init/validate/import/export/delete |
Manage user-wide installable workflows |
| Schemas | schema init, schema fork, schema validate, schema which |
Create and manage custom workflows |
| Config | profile, config |
Manage workflow profiles and other settings |
| Utility | feedback, completion |
Feedback and shell integration |
Human vs Agent Commands
Most CLI commands are designed for human use in a terminal. Some commands also support agent/script use via JSON output.
Human-Only Commands
These commands are interactive and designed for terminal use:
| Command | Purpose |
|---|---|
rasen init |
Initialize project (interactive prompts) |
rasen view |
Interactive dashboard |
rasen workset open <name> |
Open a saved workset (editor window or terminal agent session) |
rasen config edit |
Open config in editor |
rasen feedback |
Submit feedback via GitHub |
rasen completion install |
Install shell completions |
Agent-Compatible Commands
These commands support --json output for programmatic use by AI agents and scripts:
| Command | Human Use | Agent Use |
|---|---|---|
rasen list |
Browse changes/specs | --json for structured data |
rasen show <item> |
Read content | --json for parsing |
rasen validate |
Check for issues | --all --json for bulk validation |
rasen status |
See artifact progress | --json for structured status |
rasen instructions |
Get next steps | --json for agent instructions |
rasen templates |
Find template paths | --json for path resolution |
rasen schemas |
List available schemas | --json for schema discovery |
rasen store setup <id> |
Create and register a local store | --json with explicit inputs for structured setup output |
rasen store register <path> |
Register an existing store | --json for structured registration output |
rasen store upgrade-identity <id> |
Give a store a permanent identity | --apply --json; previews by default |
rasen store unregister <id> |
Forget a local store registration | --json for structured cleanup output |
rasen store remove <id> |
Delete a registered local store folder | --yes --json for non-interactive deletion |
rasen store list |
Browse registered stores | --json for structured registrations |
rasen store doctor |
Check local store setup | --json for structured diagnostics |
rasen store add-project <path> --to <store> |
Add a project to a store's roster | --json; --dry-run previews both repositories, --set-primary opts into the planning binding |
rasen store adopt [path] --to <store> |
Move a project's planning into a store | --dry-run --json for an inert preview |
rasen store eject <project-id> --from <store> |
Restore a store-hosted project | --into <path> --json; the destination is resolved explicitly, never guessed |
rasen store migrate-membership <store> |
Convert legacy membership data into records | --apply --json; previews by default |
rasen bootstrap --check |
See what this machine still needs | --json for the whole gap in one report; reports only, writes nothing |
rasen bootstrap --apply |
Prepare repositories and knowledge, then offer each declared portable bundle as a separate confirmed import | --yes covers project-config declarations; Store-only bundles and Store projects still require an explicit choice |
rasen new change <id> |
Create repo-local change scaffolding | --json, plus --store <id> to use a registered store as the Rasen root |
rasen workset create [name] |
Compose a personal working view | --member <path> --json for non-interactive composition |
rasen workset list |
Browse saved worksets | --json for structured views |
rasen workset remove <name> |
Delete a saved view | --yes --json for non-interactive removal |
Global Options
These options work with all commands:
| Option | Description |
|---|---|
--version, -V |
Show version number |
--no-color |
Disable color output |
--help, -h |
Display help for command |
Setup Commands
rasen init
Initialize rasen in your project. Creates the folder structure and configures AI tool integrations.
Default behavior uses global config defaults: profile full (every workflow), delivery both.
rasen init [path] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
path |
No | Target directory (default: current directory) |
Options:
| Option | Description |
|---|---|
--tools <list> |
Configure AI tools non-interactively. Use all, none, or comma-separated list |
--force |
Auto-cleanup legacy files without prompting |
--profile <profile> |
Install a profile and lock it into rasen/config.yaml (full, core, a saved profile name, or custom) |
An explicit --profile value other than custom is persisted as the project's locked profile (profile: in rasen/config.yaml): later rasen update runs keep resolving the project's workflows from that profile instead of the user-wide one. --profile custom uses whatever workflows are currently selected in global config (rasen profile) for this run only and is never persisted. Saved profile names come from rasen profile new/import; note that saved definitions live per machine (<global-config-dir>/profiles/), so a teammate without the named profile sees a warning and falls back to their user-wide profile until they import it.
Installable tool IDs (--tools): claude, codex, hermes, omp — the agents
Rasen has adapted its orchestration for. --tools all expands to exactly these four.
The installable set is the
adapted: trueentries ofAI_TOOLSinsrc/core/config.ts. Every other agent in that registry stays defined but is not offered; naming one explicitly fails with a "recognized but not yet adapted" message rather than the unrecognized-token error. See Supported Tools for each tool's skills path.
Examples:
# Interactive initialization
rasen init
# Initialize in a specific directory
rasen init ./my-project
# Non-interactive: configure for Claude and Oh My Pi
rasen init --tools claude,omp
# Configure for all supported tools
rasen init --tools all
# Install the core profile and lock the project to it
rasen init --profile core
# Lock the project to a saved named profile
rasen init --profile team-web
# Skip prompts and auto-cleanup legacy files
rasen init --force
What it creates:
rasen/
├── specs/ # Your specifications (source of truth)
├── changes/ # Proposed changes
└── config.yaml # Project configuration
.claude/skills/ # Claude Code skills (if claude selected)
.cursor/skills/ # Cursor skills (if cursor selected)
.cursor/commands/ # Cursor rasen commands (if delivery is both)
... (other tool configs)
rasen update
Update rasen instruction files after upgrading the CLI. Re-generates AI tool configuration files using your current global profile, selected workflows, and delivery mode.
rasen update [path] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
path |
No | Target directory (default: current directory) |
Options:
| Option | Description |
|---|---|
--force |
Force update even when files are up to date |
Example:
# Update instruction files after npm upgrade
npm update @atelierai/rasen
rasen update
Stores (standalone rasen repos)
Beta. Stores and the features built on them (references, working context, worksets) are new; command names, flags, file formats, and JSON output may change shape between releases. For the problem-first walkthrough, see the stores guide.
A store is a standalone rasen repo you've registered on this machine — for example a planning repo or a contracts repo. Registering a store lets normal commands (list, show, status, validate, new change, archive, ...) act in it from anywhere by passing --store <id>.
--store accepts a store's display name or its permanent identity. The two are not equivalent: a display name may be shared by two registered stores, and naming a shared one fails as ambiguous (listing every candidate with its identity and root) rather than picking one — the permanent identity is how you say which you meant.
rasen store setup
Create and register a local store. With no arguments in a terminal,
Rasen guides the user through setup. Agents and scripts should pass explicit
inputs and use --json.
rasen store setup [id] [options]
Options:
| Option | Description |
|---|---|
--path <path> |
Folder where the store should live (for example ~/rasen/<id>) |
--remote <url> |
Record the canonical remote in the new store's store.yaml |
--init-git |
Initialize a Git repository with an initial commit (default) |
--no-init-git |
Skip every Git action: no init, no initial commit |
--json |
Output JSON |
Non-interactive runs (--json, scripts, agents) must pass both the store id and --path. In an interactive terminal, setup prompts for the location with an editable suggestion in a visible, user-owned place (for example ~/rasen/<id>); it never defaults to rasen's managed data directory.
Examples:
rasen store setup
rasen store setup team-context
rasen store setup team-context --path ~/rasen/team-context --no-init-git
rasen store setup team-context --path ~/rasen/team-context --no-init-git --json
rasen store register
Register an existing local store folder.
rasen store register [path] [options]
Options:
| Option | Description |
|---|---|
--id <id> |
Store id; defaults to store metadata or folder name |
--yes |
Confirm creating store identity metadata for a healthy Rasen root |
--json |
Output JSON |
rasen store unregister
Forget a local store registration without deleting files.
rasen store unregister <id> [--json]
Use this when a store was moved, cloned somewhere else, or should no longer be shown by rasen on this machine.
unregister, remove, and doctor accept a store's display name or its
permanent identity. A display name that matches two registered stores is
refused as ambiguous — nothing is unregistered or deleted on a guess — and the
identity is how you say which one you mean (rasen store list shows it).
rasen store remove
Forget a local store registration and delete its local folder.
rasen store remove <id> [--yes] [--json]
remove shows the exact folder before deleting in an interactive terminal.
Agents, scripts, and JSON callers must pass --yes to confirm deletion.
Rasen refuses to delete a folder that does not contain matching
store metadata.
rasen store upgrade-identity
Give a store created before permanent identities one, and record it everywhere it belongs.
rasen store upgrade-identity <id> [--uid <identity>] [--dry-run] [--apply] [--json]
| Option | Description |
|---|---|
--uid <identity> |
Disambiguate a display name that matches more than one registered store |
--dry-run |
Report every file that would be written and change nothing (the default) |
--apply |
Write the plan |
Without --apply the command previews: it prints every file it would write and
changes nothing. With --apply it writes, in this order, so a partial failure
still leaves a coherent state:
- the store's own
.rasen-store/store.yaml(written, then read back and verified), - the machine store registry, re-keyed by permanent identity,
- the project's
store:declaration, when you run it from a project that declares this store by name.
Running it twice is a no-op: the identity is minted once and reused. It never commits or pushes — the output names the files you need to commit yourself.
The registry moves to its identity-keyed form only once every registered store has an identity. Until then it stays in its existing form and the command names the stores that still need upgrading, rather than inventing identities for them.
rasen store list
List locally registered stores, with each store's permanent identity.
rasen store list [--json]
rasen store ls [--json]
rasen store doctor
Check local store registration, metadata, and Git presence.
rasen store doctor [id] [--json]
Doctor is diagnostic-only; it reports missing roots, metadata mismatches, permanent identities (or their absence), display names shared by more than one store, and invalid local registry state — without modifying the store.
rasen store add-project
Add an in-repo project to a store's roster.
rasen store add-project <path> --to <store> [--as <id>] [--set-primary] [--dry-run] [--json]
In one invocation it registers the project in the machine's project namespace,
writes the store's membership record for it, appends a project:<id> entry
to the store's references: list (the documentation index), and appends a
membership locator hint to the project's own rasen/config.yaml.
The two repositories are written in a defined order — the store's authority record first, verified, then the project's hint — and the result reports what landed in each. They do not change atomically and the command does not pretend otherwise: anything still needing repair is reported with the command that finishes it. If the project-side write fails, the store record stands and is never rolled back.
--dry-run lists every file it would write in each repository and changes
nothing. Nothing is staged, committed, pushed, fetched, or pulled; the output
prints a path-scoped commit suggestion per repository for you to run.
--set-primary is an opt-in that never overwrites. By default the command
does not change which store the project plans in — membership and planning
binding are different relations. With the flag:
| Project's planning store | Result |
|---|---|
| none | the target store is recorded, reported separately from the membership |
| already the target store | a no-op that succeeds and rewrites nothing |
| a different store | refused — it names the store bound, the store requested, and the command that rebinds deliberately |
A refusal is scoped to the pointer only: the membership record and locator hint the same invocation established still stand. The flag is never inferred from another option, from the project's state, or from this being the project's only membership. Both stores in a refusal are named by permanent identity as well as display name, because two stores are allowed to share a name — and a refusal that named only the name would read "plans in 'team-context', not 'team-context'".
{
"project": { "id": "elftia", "root": "/repos/elftia", "metadata_created": true, "already_registered": false },
"target": { "id": "team-context", "root": "/stores/team-context", "reference_added": true },
"membership": {
"project_id": "ed2cf5bf-2525-45ed-b665-c47a5b8d5450",
"roles": { "planning": false, "knowledge": true },
"store_writes": ["/stores/team-context/.rasen-store/projects/ed2cf5bf-2525-45ed-b665-c47a5b8d5450.yaml"],
"project_writes": ["/repos/elftia/rasen/config.yaml"],
"repair_needed": [],
"suggested_commits": [
{ "repo_root": "/stores/team-context", "command": "git -C /stores/team-context add ... && git -C /stores/team-context commit -m ...", "purpose": "Store repo: record the membership authority record." }
]
},
"planning_binding": { "requested": false, "changed": false, "refused": false, "bound_to": null, "bound_to_uid": null, "requested_store": "team-context", "requested_store_uid": "8f0c2e7a-13d5-4a1e-9c6b-2b7d4e5f6a80" },
"dry_run": false,
"status": []
}
A refusal reports the same shape with the binding block filled in:
{
"planning_binding": {
"requested": true,
"changed": false,
"refused": true,
"bound_to": "other-store",
"requested_store": "team-context",
"rebind_command": "rasen store upgrade-identity team-context --apply"
}
}
rasen store adopt
Move an in-repo project's planning content into a store and convert the repo to a config-only pointer.
rasen store adopt [path] --to <store> [--archive move|leave] [--dry-run] [--verify-hash] [--json]
--archive decides what happens to the source repo's existing archive: move
(default) brings it into the store, leave keeps it in the source repo. The
retired --archive external is rejected — archives always land in a planning
root, never the machine home.
Adopt binds by definition: it writes the project's store: declaration as part
of the migration, and is not routed through --set-primary. It records the
project as a planning member in the store's membership record — and asserts
no knowledge role, because an adoption proves where a project plans and proves
nothing about what knowledge it shares. The record lists the adopted spec names,
change names, and the adoption timestamp — and no path.
Restoring the project later resolves its destination explicitly (see below)
rather than following a path captured on the machine that ran the adoption.
An interrupted adopt is resumable: the ownership record is written before any source content is deleted, so a rerun detects the partial state and completes.
rasen store eject
Restore a store-hosted project back to in-repo planning.
rasen store eject <project-id> --from <store> [--into <path>] [--all] [--force] [--dry-run] [--json]
Ownership comes from the store's membership record for that project, with the legacy adoption manifest read as a fallback while one still exists.
The destination is resolved by an explicit ordered rule, and by nothing else:
--into <path>, when given;- otherwise the current checkout, when its project identity is the project being ejected;
- otherwise the machine registry's single live checkout for that project.
Several candidates, or none, is an error that lists what it found and names
--into. Eject never reads a source path recorded in legacy shared data, never
infers a local path from a remote, never guesses from a display name, and never
takes the first of several checkouts. Checkout comparison is canonical, so a
Windows path differing only by drive-letter case or separator form still
matches.
Eject removes where the project plans; it does not remove it from the store's roster. The membership record keeps any knowledge role, and the ownership block goes. A record whose only role was the planning one the eject just ended, and which owns nothing, is removed rather than left behind expressing nothing — so a project that was only ever adopted leaves no empty record when it is ejected.
rasen store migrate-membership
Convert a store's legacy membership data into per-project membership records.
rasen store migrate-membership <store> [--dry-run] [--apply] [--json]
It reads .rasen-store/adoptions.yaml, the store's references: list, and the
machine's project namespace, and emits one
.rasen-store/projects/<projectId>.yaml per resolvable project. sourcePath is
dropped and the adoption timestamp becomes adoption.adoptedAt.
Previewing is the default; --apply writes. It is idempotent and safe to re-run.
A project whose identity cannot be determined on this machine is reported and
left untouched rather than guessed at.
It deletes adoptions.yaml — only under --apply, and only after every
record it produced has been written and read back successfully. That is this
change's one non-reversible step; see the migration guide for why it is removed
rather than renamed, and for the git log / git show commands that recover
the pre-migration file from the store's history. The removal is reported for you
to commit; the command never touches the git index.
{
"store": { "id": "team-context", "root": "/stores/team-context" },
"applied": true,
"converted": [
{
"project_id": "ed2cf5bf-2525-45ed-b665-c47a5b8d5450",
"alias": "elftia",
"source": "legacy-adoption",
"roles": { "planning": true, "knowledge": false },
"record_path": "/stores/team-context/.rasen-store/projects/ed2cf5bf-2525-45ed-b665-c47a5b8d5450.yaml"
}
],
"unresolved": [],
"legacy_manifest_removed": true,
"legacy_manifest_path": "/stores/team-context/.rasen-store/adoptions.yaml",
"status": []
}
rasen store migrate-layout (0.1.7 compatibility bridge)
Convert one checked-out ref of a legacy flat Store to layout v2. Preview is the
default; writes happen only with --apply, and the command never commits,
fetches, or pushes.
rasen store migrate-layout <store> --mapping rasen/migration-mapping.yaml --json
rasen store migrate-layout <store> --mapping rasen/migration-mapping.yaml --apply --json
rasen store migrate-layout <store> --status --json
rasen store migrate-layout <store> --resume --json
rasen store migrate-layout <store> --rollback --json
rasen store migrate-layout <store> --retire-flat --json
Mapping version 1 keeps the original project-copy contract and continues to produce plan-schema-v1 canonical bytes and plan ids for equal inputs. Mapping version 2 adds an explicit choice between a project-owned Change and a Store-level Issue:
version: 2
defaultTargetLine: release-0-1
targetLines:
release-0-1:
storeRef: refs/heads/main
projects:
scene-bridge:
codeRef: refs/heads/main
changes:
render-worker:
kind: project-change
project: scene-bridge
release-coordinator:
kind: store-issue
issueId: release-coordinator
title: Coordinate the cross-project release
plan: rasen/migration-inputs/release-coordinator.yaml
archive:
historical-coordinator:
kind: store-issue
issueId: historical-coordinator
title: Historical release coordination
state: resolved
reason: Operator declares the historical coordination concluded; acceptance is unproven.
Recorded Change identity remains binding. An active store-issue always imports
as open and cannot declare a reason. An archived source must explicitly
declare open, resolved, or dropped; terminal states require an operator
reason and are recorded with acceptance evidence unproven. Names, prose,
branches, cwd, archive placement, and commits never infer ownership, state, or
acceptance.
An optional Issue plan input must be inside the Store, Git-tracked, and
byte-identical in HEAD, index, and worktree. It must be strict UTF-8 without a
BOM. A migration-only sourceChange node still declares project and target
line; planning verifies it against one project Change in the same immutable
plan and serializes only the canonical changeInstanceId. Without a plan input,
the Issue is created without nodes and preview reports
no plan supplied; no nodes invented plus the ordinary follow-up command:
rasen store issue plan <issue-id> --store <store> --from-file <path>
Publication stages and verifies the complete ref, writes project partitions and
generated Issue roots without copying a coordinator's legacy tree, records a
typed receipt, and flips layoutVersion: 2 last. A failure can be continued with
--resume or reverted with --rollback while flat sources remain. After the
publication commit is secured, --retire-flat removes only the receipt-listed
legacy paths and is safe to retry after partial removal. The receipt identifies
the source as a Store planning revision, never as a member code commit:
git restore --source=<receipt.sourceRevision.headOid> --worktree -- <conversion.source.path>
After restoring, compare the recursive source digest with
conversion.source.digest. Member code repositories are never migration write
targets.
After retirement, ordinary direct rasen archive <old-active-alias> reports
legacy_coordinator_became_issue and the existing rasen store issue show
command when one current-ref v2 receipt proves the conversion. It does not
redirect or translate archive outcome, reason, token, commit, confirmation, or
acceptance into Issue state. Archived-source aliases do not redirect, token
--apply-plan/--abort-plan routes do not query receipts, and a real Change
still follows normal finalization (including finalization_outcome_required).
This is an explicitly bounded Rasen 0.1.7 compatibility-only bridge for retiring flat Store coordinators through the already available minimal Issue record and Execution Plan v1 resources. It does not implement the later Issue-centered automation platform, Dispatch, Reconciliation, Acceptance, Delivery, Board, or coordinator runtime. Remove the bridge when supported Stores no longer require flat-to-v2 migration; keep typed historical receipt readers as long as committed receipts remain supported evidence.
Store membership
Membership answers "which projects belong to this store", and is a different question from "where does this project plan".
- Authority is the store's own record, one file per member project:
<store>/.rasen-store/projects/<projectId>.yaml. It is named and keyed by the project's permanent identity, so two projects sharing a display name never share a record and two people adding two different projects never edit the same file. roles.planningandroles.knowledgeare separate facts. A project can share knowledge with a store without planning in it, and the two never collapse into one ambiguous flag.- Membership expresses roster and eligibility only. It does not determine, imply, or stand in for the decision of where a change is implemented.
- The project side is a locator, never authority.
storeMemberships:in the project'srasen/config.yamlcarries a permanent identity, a display alias, and a credential-free remote — so a fresh clone can discover its stores. A hint that disagrees with the store's record is reported as drift.
# <store>/.rasen-store/projects/<projectId>.yaml
version: 1
projectId: ed2cf5bf-2525-45ed-b665-c47a5b8d5450
id: elftia
remote: git@github.com:org/elftia.git
roles:
planning: true
knowledge: true
adoption:
specs: [fundraising]
changes: [fundraising-p0-p1]
adoptedAt: 2026-07-25T10:00:00Z
# <project>/rasen/config.yaml
storeMemberships:
- uid: 8f0c2e7a-13d5-4a1e-9c6b-2b7d4e5f6a80
id: team-context
remote: git@github.com:org/team-context.git
rasen doctor and rasen store doctor report membership health read-only —
a planning store with no record, a record with no project-side hint, a hint
whose store is not available here, a machine path left in git-shared data, a
record whose filename and identity disagree, an unmappable legacy reference, and
a store still carrying legacy adoption data. Each names its repair command.
See Troubleshooting.
A store's identity and its name
A store has two different things:
- a permanent identity, minted once when the store is created, recorded in
the store's own
.rasen-store/store.yaml, and travelling with the store's repository. It never changes — not on rename, not on re-registration, not on re-clone. No command accepts it as input and no command replaces it. - a display name (the
id), which is what you type and read. It may be renamed, and two different stores may legitimately carry the same one.
Naming a store by display name therefore has explicit arity:
| Matches | Outcome |
|---|---|
| 0 | The store is declared but not available on this machine, with the command that would make it available |
| 1 | It resolves, with a note offering the upgrade to a durable declaration |
| 2 or more | Ambiguous: every candidate is listed with its identity and local root, and nothing is picked |
Resolving by permanent identity is exact and never consults the name index.
Declaring a store durably
A project's store: declaration can record the permanent identity, the display
name for readability, and a credential-free remote so the store can be located
on a machine that has never seen it:
# rasen/config.yaml
store:
uid: 9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7
id: team-context
remote: git@github.com:acme/team-context.git
The identity is the authority. A declared name that no longer matches the store's own name is reported as drift and does not block resolution; a declared remote that differs from the store's canonical remote is an informational note. Nothing machine-specific — no filesystem path from your machine — is ever written into this declaration.
The single-name form keeps working and resolves whenever that name matches exactly one registered store:
store: team-context
rasen store upgrade-identity <id> --apply, run from the project, rewrites it
into the durable form.
When a declared store cannot be used
A project that declares a store which cannot be resolved no longer resolves configuration as though it had no store. The command stops and prints what was expected, why it could not be used, and a copy-pasteable repair command. The reasons are distinguished: not registered on this machine, missing store metadata, a checkout carrying a different identity, an unhealthy store root, an ambiguous name, and an unreadable declaration.
rasen doctor, rasen store doctor, rasen store list, and rasen config --global keep working in exactly those states — they are how you find out what
is wrong. They write nothing, clone nothing, and register nothing.
A remote that embeds a username-and-password or token credential is rejected on
write and shown redacted wherever it is displayed, in both human and JSON
output. The ordinary SSH form (git@github.com:acme/team-context.git) carries a
user name but no secret and is unaffected.
Referencing stores from a project
A project repo can declare which stores its work draws on in rasen/config.yaml:
schema: spec-driven
references:
- team-context
From then on, rasen instructions output in that repo (both the per-artifact and apply surfaces, JSON and human modes) carries an index of each referenced store's specs — spec ids, a one-line summary from each spec's Purpose section, and the fetch command (rasen show <spec-id> --type spec --store <id>). The index is built live from the registered checkout on every run; spec content is never copied into the output.
References are read-only context. They never change where commands act: work stays in the repo's own root, and writing to a referenced store remains an explicit --store action. A reference that cannot be resolved (for example, a store not registered on this machine) degrades to a warning in the index with the exact fix, and instructions still generate. rasen doctor reports reference health in one place.
Recording where a store is cloned from
A store can record its canonical clone source in its committed identity file, so onboarding never dead-ends at "register the store":
rasen store setup team-context --path ~/rasen/team-context \
--remote git@github.com:acme/team-context.git
The remote lands in .rasen-store/store.yaml inside the initial commit, so every clone is born knowing it. For an existing store, edit store.yaml by hand and commit. store doctor shows the recorded remote (and the checkout's observed Git origin); setup/register sharing guidance names it; and register records the checkout's origin in the machine-local registry.
A reference declaration can carry the clone source too, so a teammate who doesn't have the store yet gets a complete, pasteable fix (git clone <remote> <path> && rasen store register <path> --id <id>):
references:
- { id: team-context, remote: "git@github.com:acme/team-context.git" }
Recording a remote is not sync: rasen never clones, pulls, or pushes on its own.
Declaring a default store
A repo whose planning is fully externalized — no local rasen/specs/ or rasen/changes/ — can declare its store once instead of passing --store on every command:
# rasen/config.yaml (the only file under rasen/)
store: team-context
Normal commands then resolve to the declared store automatically; the root banner and JSON root block report source: "declared" with the store id, and printed hints still carry --store <id>. The declaration is a fallback, never an override: explicit --store always wins, and a directory with real planning folders ignores the pointer (with a warning).
To add or refresh an adapted tool without changing where planning lives, run an explicit, non-empty tool selection from the exact pointer-repo root:
rasen init --tools codex
This installs only the selected tool's Rasen assets. It preserves the store: declaration and does not create local rasen/specs/ or rasen/changes/. Plain rasen init remains refused; to convert a pointer repo into a local Rasen root, remove the store: line first and then run rasen init.
Bootstrap (what this machine still needs)
rasen bootstrap answers one question in one run: what does this machine
need before this project works? It reads the project's identity, its planning
store declaration, and its store membership hints, works out the state of every
expected store, and reports the whole result — instead of telling you about one
missing store per failed command.
rasen bootstrap --check [--json] [--path <selector>=<dir>] [--into <dir>]
rasen bootstrap --dry-run [--json] [--path <selector>=<dir>] [--into <dir>]
rasen bootstrap --apply [--yes] [--json] [--path <selector>=<dir>] [--into <dir>]
--check and --dry-run report; they do not repair. They obtain nothing,
register nothing, and write nothing. --apply acts: it registers the current
checkout, registers present-unregistered stores the user names a location for,
obtains declared stores that are absent with a recorded remote (cloning from
the remote to the location --dry-run previewed), prepares the knowledge
location, and writes the durable store declaration when the project's
declaration is in the earlier form. A failed retrieval cleans up only the
directory this run created — a pre-existing target is never deleted.
That is also why rasen bootstrap with no mode flag reports which modes exist
and exits rather than doing something.
The three modes are three different promises
| reads local declarations | resolves remotes and target paths | registers / writes | runs git | |
|---|---|---|---|---|
--check |
yes | no | no | no |
--dry-run (preview) |
yes | yes | no | no |
--apply |
yes | no | yes | yes (clones declared stores from their remotes) |
--checkcontacts no network at all. It is the mode to run when you do not yet trust the tool with your network: everything it reports comes from files already on this machine.--dry-runadditionally resolves which clone source would be used and names the exact path each repository would be placed at. It still creates no directory and runs no version-control operation.--applyacts on what is local AND obtains what is not. It registers the current checkout, registers each present-unregistered store the user names a location for, obtains each declared store that is absent with a recorded remote (cloning from the remote to the previewed location, consent-gated), prepares the knowledge location, and writes the durable store declaration. It then previews each safely resolved declared knowledge bundle through the same importer asrasen knowledge bundle importand imports only confirmed actions.--yescovers project-owned declarations and obtaining declared stores, never Store-only bundle declarations or obtaining a Store's projects.
They are requested separately, never through one combined "safe mode" option,
and passing more than one is rejected before any work happens. --yes without
--apply is also rejected — it confirms nothing when no action is requested.
Flags
| Flag | Meaning |
|---|---|
--check |
Check mode: report from local information only, contacting no network. |
--dry-run |
Preview mode: additionally resolve remotes and the exact location each repository would be placed at. |
--apply |
Apply mode: prepare repositories and the knowledge location, then offer every declared portable bundle as a separate confirmed import. |
--yes |
Confirm project-declared actions, including a bundle named by committed project config. It never imports a bundle named only by Store records and never obtains a Store's projects; those require an explicit choice. |
--json |
Emit the report as JSON. Human and JSON carry the same states, the same missing items, and the same repair commands. |
--path <selector>=<dir> |
The location for one store or project. Repeatable. The selector is required because a location belongs to one repository — the store's display name, or its permanent identity when the name is ambiguous here. |
--into <dir> |
A parent directory. Each repository that has no explicit --path is previewed at this parent plus a safe name derived from its clone source. |
The report exits 0 whatever it finds; the outcome is the state field, not the
exit code. Only an invalid invocation exits 1.
How each expected store is classified
Every store the project expects is reported in exactly one state, together with what would resolve it:
- available and verified — registered here, identity and root verify.
- present on this machine but not registered — a location you supplied holds this store's checkout; registering that location is the repair.
- not here, obtainable from its recorded remote — the declaration records a clone source.
- not here, and no location is recorded for it — bootstrap reports that a path is required. It never guesses one from a display name, a sibling directory, or a path some other machine recorded.
- cannot be resolved on this machine — an identity mismatch, an unhealthy root, missing metadata, or a name matching two stores. This blocks the report.
Bootstrap never searches your disk. "Present but not registered" is only
reported for a location you name with --path or --into. An unregistered
store you do not point at is reported as absent — with git clone as its
repair, which would give you a second checkout. If you already have the store
somewhere, name it: rasen bootstrap --check --path <store>=<dir>. Scanning the
filesystem for unregistered stores is deliberately not done, and no landed
surface offers it.
A store's own record of the project is reported alongside: confirmed, not
recorded (with the repair that would record it), or cannot be verified from
this machine. "Cannot be verified" covers both causes — the store is not here,
or the store is here and its record for this project will not parse. Neither
is ever reported as a store that does not record the project: the answer is
unknown, not "no". And bootstrap prints no state-changing repair on an
unknown — on an unreadable record the repair is to make the record readable,
never rasen store add-project, which would write over an answer that may
already be correct.
The whole run ends in exactly one of three states:
complete— nothing is missing.degraded— something is missing, and every item names its repair.blocked— something cannot be resolved or read at all.
blocked is a reported result, not a crash. A store declaration that cannot
be understood, a checkout that does not verify as the store it claims, and state
this machine keeps that cannot be read — an unparseable store registry, a corrupt
store.yaml — all come back as blocked naming the file and the repair, in
human and JSON alike. A broken machine is precisely what this command exists to
describe, so it describes it rather than failing on it.
Where a previewed location comes from
Preview picks a location by stated priority, and never invents one:
- an explicit
--path <selector>=<dir>; - otherwise
--into <dir>plus a safe name derived from the clone source — with no separator, no traversal, and no name a filesystem reserves; - otherwise it reports that a location must be supplied, and names no candidate.
A location that already has contents, or that already holds a checkout, is reported as refused rather than presented as one that would be used. Paths are compared canonically, so a drive-letter or separator difference is not a different location. A path recorded by another machine never influences the choice.
Clone target safety and failed-retrieval cleanup
In --apply mode, when bootstrap obtains a store or project from its remote,
the clone target is enforced, not just previewed: bootstrap never clones
into a directory that already has contents, never overwrites an existing
checkout, and never takes a location from a path another machine recorded. The
remote is passed as an argument vector to git clone — never assembled into a
shell command line.
When a retrieval fails, bootstrap removes the target directory only when it can prove this run created it (the directory did not exist before the clone attempt). If the directory pre-existed — or its provenance is unknown — it is left exactly as it is and the failure names it and what to inspect. Bootstrap never attempts partial cleanup of a pre-existing directory: a half-corrupted clone in a directory the user already had is the user's to diagnose, not bootstrap's to "fix" by deleting.
The --yes asymmetry
--yes means different things from different starting points:
- From a project checkout,
--yescovers obtaining the stores the project itself declares. The expected set comes from the user's own committed declarations, so confirming ahead of time is safe. - From a store checkout,
--yescovers registering the store's own checkout only. It never obtains any of the store's projects — a store's roster is authored by other people and can grow without the local user knowing. To obtain a project from a store, the user must explicitly select it (interactively or via--path <projectId>=<dir>).
The two flows are not unified behind one predicate.
Declared portable bundles are a separate action
Bootstrap offers a portable bundle only when one of two durable files names it:
# <project>/rasen/config.yaml — relative to the project root
knowledgeBundle: carry/project-knowledge.bundle.json
# <store>/.rasen-store/projects/<projectId>.yaml — relative to the Store root
knowledgeBundle: rasen/knowledge-bundles/<projectId>/<bundleId>.bundle.json
The value must be a non-empty repository-relative file locator. Windows drive
paths, Windows network shares, POSIX absolute paths, lexical .. escapes, and
existing symlinks that escape the declaring repository are unsafe and are
never passed to the importer. Missing and unreadable files remain visible with
the exact path to restore or declaration to edit.
bundleImports is distinct from knowledge: knowledge reports only that the
empty canonical knowledge directories were prepared; each bundleImports
entry reports one declared file, its permanent target project, every durable
source that named the canonical path, trust, availability, outcome, F3 plan
counts and conflicts, warnings, refusal, repair, retained paths, and
changed (true, false, or "unknown"). With no declaration,
bundleImports is absent and no import preview or apply call occurs.
Same-project declarations that resolve to one canonical path become one action and retain every source. If project config is among those sources, the action has project trust. Different paths remain separate actions; none silently overrides another. A Store record can list a bundle while its project checkout is absent, but import waits until that permanent project has been explicitly obtained.
Consent is deliberately asymmetric:
- a project-config-trusted action is imported by
--apply --yes; - a Store-record-only action is still listed under
--yes, with the explicit choice that would import it, but remainsunconfirmed; - interactive apply asks for each usable action after a complete F3 preview.
Missing, unreadable, unsafe, unconfirmed, malformed, wrong-project,
conflicting, or rollback-unknown actions make the report degraded, never
blocked by themselves, and unrelated registration, obtain, hydration, and
declaration work continues. A successful import with
staging_cleanup_deferred remains successful and carries the warning.
Project-trusted JSON entry:
{
"bundleImports": [{
"actionKey": "import-bundle:<projectId>:<canonical-path>",
"projectId": "<projectId>",
"locator": "carry/project-knowledge.bundle.json",
"sources": [{ "kind": "project-config", "declarationPath": "<project>/rasen/config.yaml" }],
"trust": "project-config",
"availability": "usable",
"outcome": "imported",
"added": [{ "id": "portable-routing" }],
"alreadyPresent": [],
"conflicts": [],
"changed": true
}]
}
Store-only and missing entries:
{
"bundleImports": [
{
"projectId": "<projectId>",
"sources": [{ "kind": "store-record", "storeId": "team-store" }],
"trust": "store-record-only",
"availability": "usable",
"outcome": "unconfirmed",
"changed": false
},
{
"projectId": "<projectId>",
"locator": "carry/missing.bundle.json",
"trust": "project-config",
"availability": "missing",
"outcome": "unavailable",
"repair": [{ "kind": "restore-file", "path": "<project>/carry/missing.bundle.json" }],
"changed": false
}
]
}
A conflicting entry carries the complete F3 plan rather than only the first conflict:
{
"outcome": "refused",
"added": [{ "id": "clean-routing" }],
"alreadyPresent": [],
"conflicts": [{ "id": "portable-routing", "reason": "content-differs" }],
"refusal": { "code": "knowledge_bundle_import_conflict" },
"changed": false
}
Starting from a store checkout
Run inside a store checkout instead of a project, and bootstrap reports the
store's identity and lists every project the store records — each as already
present on this machine, as obtainable from a recorded remote, as neither, or as
undetermined when a registered project's own identity cannot be read and so this
machine cannot say whether it already holds it. A project the store records that
nothing here can locate is said to be exactly that; it is never quietly called
obtainable. A run that could not read one of the store's records is never
reported as complete.
In --apply mode from a store checkout, bootstrap registers the store's own
checkout (consent is implied by running apply from the store) and then obtains a
project only when the user explicitly selects it — either interactively (the
prompt lists each obtainable project) or via --path <projectId>=<dir>.
--yes does not count as selection here: a store's roster is authored by
other people and can grow without the local user knowing, so --yes covers
registering the store's own checkout only. Bootstrap never obtains every project
a store records, under any option.
A checkout that does not verify as the store it claims to be is reported as
blocked, naming the mismatch; so is one whose store.yaml exists but cannot be
parsed, which is reported as unreadable state rather than as "not a store
checkout".
JSON examples
A complete result — this machine needs nothing:
{
"ok": true,
"report": {
"mode": "check",
"origin": "project",
"state": "complete",
"project": {
"root": "/home/dev/acme-api",
"projectId": "3c0f0a3e-9e2b-4a0e-8c2f-6d5b1f0a7e11",
"declaresStore": true,
"declarationPath": "/home/dev/acme-api/rasen/config.yaml"
},
"stores": [
{
"key": "root:/home/dev/stores/team-context",
"sources": ["planning", "hint"],
"uid": "9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"id": "team-context",
"root": "/home/dev/stores/team-context",
"selector": "team-context",
"class": "verified",
"membership": { "state": "confirmed", "repair": [] },
"repair": [],
"diagnostics": []
}
],
"projects": [],
"problems": [],
"diagnostics": []
}
}
A degraded result — a store is missing, and the repair is named:
{
"ok": true,
"report": {
"mode": "preview",
"origin": "project",
"state": "degraded",
"project": {
"root": "/home/dev/acme-api",
"projectId": "3c0f0a3e-9e2b-4a0e-8c2f-6d5b1f0a7e11",
"declaresStore": true,
"declarationPath": "/home/dev/acme-api/rasen/config.yaml"
},
"stores": [
{
"key": "uid:9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"sources": ["planning", "hint"],
"uid": "9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"id": "team-context",
"remote": "git@github.com:acme/team-context.git",
"selector": "team-context",
"class": "absent-with-remote",
"reason": "not-registered",
"membership": {
"state": "unverifiable-here",
"repair": [
{
"kind": "command",
"command": "git clone git@github.com:acme/team-context.git <path> && rasen store register <path>",
"mutates": true
},
{ "kind": "command", "command": "rasen doctor", "mutates": false }
]
},
"repair": [
{
"kind": "command",
"command": "git clone git@github.com:acme/team-context.git <path> && rasen store register <path>",
"mutates": true
},
{ "kind": "command", "command": "rasen doctor", "mutates": false }
],
"location": {
"kind": "usable",
"path": "/home/dev/stores/team-context",
"source": "parent-and-derived-name"
},
"diagnostics": []
}
],
"projects": [],
"problems": [],
"diagnostics": []
}
}
A blocked result — the declaration itself cannot be understood:
{
"ok": true,
"report": {
"mode": "check",
"origin": "project",
"state": "blocked",
"project": {
"root": "/home/dev/acme-api",
"projectId": "3c0f0a3e-9e2b-4a0e-8c2f-6d5b1f0a7e11",
"declaresStore": false,
"declarationPath": "/home/dev/acme-api/rasen/config.yaml"
},
"stores": [],
"projects": [],
"problems": [
{
"kind": "declaration-malformed",
"path": "/home/dev/acme-api/rasen/config.yaml",
"reason": "pointer-malformed",
"repair": [
{ "kind": "manual", "instruction": "Edit /home/dev/acme-api/rasen/config.yaml" },
{ "kind": "command", "command": "rasen doctor", "mutates": false }
],
"diagnostics": [
{
"severity": "error",
"code": "invalid_store_pointer",
"message": "The store declaration in /home/dev/acme-api/rasen/config.yaml cannot be read (the store key must be a single store id string).",
"target": "store.pointer",
"fix": "Fix or remove the store: declaration in /home/dev/acme-api/rasen/config.yaml."
}
]
}
],
"diagnostics": []
}
}
An apply result — a store was registered and the declaration upgraded:
{
"ok": true,
"report": {
"mode": "apply",
"origin": "project",
"state": "complete",
"project": {
"root": "/home/dev/acme-api",
"projectId": "3c0f0a3e-9e2b-4a0e-8c2f-6d5b1f0a7e11",
"declaresStore": true,
"declarationPath": "/home/dev/acme-api/rasen/config.yaml"
},
"stores": [
{
"key": "uid:9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"sources": ["planning", "hint"],
"uid": "9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"id": "team-context",
"selector": "team-context",
"class": "verified",
"membership": { "state": "confirmed", "repair": [] },
"repair": [],
"diagnostics": [],
"action": "registered",
"alreadyRegistered": false
}
],
"projects": [],
"problems": [],
"diagnostics": [],
"knowledge": {
"root": "/home/dev/.local/share/rasen/project-knowledge/3c0f0a3e-9e2b-4a0e-8c2f-6d5b1f0a7e11",
"catalogDir": "/home/dev/.local/share/rasen/project-knowledge/3c0f0a3e-9e2b-4a0e-8c2f-6d5b1f0a7e11/learned-skills",
"alreadyHydrated": false
},
"declaration": {
"outcome": "written",
"path": "/home/dev/acme-api/rasen/config.yaml"
}
}
}
A degraded apply — one store was registered, another was obtained, a third could not be obtained:
{
"ok": true,
"report": {
"mode": "apply",
"origin": "project",
"state": "degraded",
"stores": [
{
"selector": "team-context",
"class": "verified",
"membership": { "state": "confirmed", "repair": [] },
"repair": [],
"action": "registered"
},
{
"selector": "design-context",
"class": "verified",
"membership": { "state": "confirmed", "repair": [] },
"repair": [],
"action": "obtained"
},
{
"selector": "infra-context",
"class": "absent-with-remote",
"membership": { "state": "unverifiable-here", "repair": [] },
"repair": [
{
"kind": "command",
"command": "git clone git@github.com:acme/infra-context.git <path> && rasen store register <path>",
"mutates": true
}
],
"action": "obtain-failed",
"diagnostics": [
{
"severity": "error",
"code": "store_clone_failed",
"message": "Failed to clone the repository: ...",
"target": "store.git"
}
]
}
],
"knowledge": { "root": "...", "catalogDir": "...", "alreadyHydrated": false },
"declaration": { "outcome": "written", "path": "..." }
}
}
An idempotent rerun — nothing was acted on; everything was already in place:
{
"ok": true,
"report": {
"mode": "apply",
"origin": "project",
"state": "complete",
"stores": [
{
"selector": "team-context",
"class": "verified",
"membership": { "state": "confirmed", "repair": [] },
"repair": [],
"action": "already-registered",
"alreadyRegistered": true
}
],
"knowledge": { "root": "...", "catalogDir": "...", "alreadyHydrated": true },
"declaration": { "outcome": "already-durable" }
}
}
Doctor (relationship health)
One read-only question, one place: is the Rasen root healthy, and are the stores it references available on this machine?
rasen doctor [--store <id>] [--json]
The report separates root health, store metadata health (including a note when the recorded remote and the checkout's origin diverge), and reference health (the same diagnostics instructions show, with clone fixes for unresolved references). Health findings of any severity exit 0 — agents read the status arrays; only command failures (no root, unknown store) exit 1. Doctor never clones, syncs, or repairs. To get the assembled set itself rather than its health, use rasen context.
The store block reports the resolved identity, how the project declared the
store, and every identity diagnostic. Human and --json output carry the same
codes, the same messages, and the same repair commands.
A store that resolved by its permanent identity, with a legacy display name still recorded in the declaration:
{
"store": {
"id": "platform-context",
"uid": "9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"metadata": { "present": true, "valid": true, "uid": "9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7" },
"pointer": {
"shape": "durable",
"declared_id": "team-context",
"declared_uid": "9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7",
"resolved_by": "uid"
},
"status": [
{
"severity": "warning",
"code": "store_pointer_alias_drift",
"message": "This project declares store name 'team-context', but that store's name is now 'platform-context'. The permanent identity still matches, so it resolved.",
"target": "store.pointer",
"fix": "rasen store upgrade-identity platform-context --uid 9d7a6f8d-6b8e-4f6a-b5c4-2e31fd3525c7 --apply"
}
]
}
}
A store that is declared but unavailable — reported, never rendered as though the project had no store:
{
"store": {
"id": "team-context",
"metadata": { "present": false, "valid": false },
"pointer": { "shape": "alias", "declared_id": "team-context" },
"unavailable": {
"reason": "not-registered",
"repair": ["rasen bootstrap", "git clone git@github.com:acme/team-context.git <path> && rasen store register <path>", "rasen doctor"]
},
"status": [
{
"severity": "error",
"code": "store_bootstrap_required",
"message": "Store team-context is declared by this project but is not registered on this machine.",
"target": "store.registry",
"fix": "rasen bootstrap"
}
]
}
}
When a declared store is not available, the failure names rasen bootstrap as
the primary repair — the one command that registers, obtains, and prepares
everything the project declares. The single-step rasen store register and
rasen doctor remain in the repair array for the user who wants one step or
diagnosis. A store with no recorded remote and no supplied path asks for a path
or remote rather than suggesting bootstrap, because bootstrap cannot infer a
location either. A checkout that carries a different identity is reported as a
mismatch — bootstrap cannot repair it, and the failure does not name it.
Bootstrap readiness (doctor)
Doctor reports a bootstrap-readiness section that composes the same facts
rasen bootstrap --check reports into a single answer: is this machine
ready, and if not, what does it need? The section is read-only — doctor
reports the gap and changes nothing.
The three states match bootstrap's own:
| State | Meaning |
|---|---|
complete |
The planning store resolves, membership is confirmed, and the checkout is registered. |
degraded |
Something is missing that bootstrap can close: a declared store not registered (with a remote), an unconfirmed membership, or an unregistered checkout. |
blocked |
A declared store has no recorded remote and no supplied path — bootstrap can register a local checkout but cannot obtain one from nowhere. |
Each finding carries a copy-pasteable repair (rasen bootstrap). A
mismatched-identity store does NOT produce a bootstrap finding — it produces
the existing doctor finding in the Store section above, because bootstrap
cannot repair a mismatch.
rasen doctor and rasen bootstrap --check name the same stores as missing
and the same repairs for each, because both compose from the same resolved
store binding.
The identity diagnostic codes:
| Code | Severity | Meaning |
|---|---|---|
store_bootstrap_required |
error | the declaration names a store not registered on this machine |
store_uid_mismatch |
error | the registered checkout is not the expected store |
store_alias_ambiguous |
error | the display name matches more than one registered store |
store_pointer_legacy |
info | the declaration is a bare display name |
store_pointer_remote_divergence |
info | the declared remote differs from the store's canonical remote |
store_pointer_alias_drift |
warning | the declared name no longer matches the store's own name |
store_metadata_legacy |
info | the store has no permanent identity yet |
store_remote_credentials |
error | a remote carrying credentials was supplied |
store_alias_numeric |
warning | a newly assigned display name is all digits |
store_remote_divergence |
info | the store's recorded remote differs from its checkout's origin |
store_registry_rekey_blocked |
info | the machine registry stays keyed by display name; the named stores have no permanent identity yet |
store_alias_repeated |
warning | a registration succeeded under a display name another store already uses |
store_alias_renamed |
info | re-registering moved a registry entry's display name; the permanent identity is unchanged |
Working context (the assembled set)
Everything this work relates to through rasen declarations, in one working set: the Rasen root and the stores it references.
rasen context [--store <id>] [--json] [--code-workspace <path> [--force]]
The JSON brief is agent-consumable (each available referenced store carries its fetch recipe; unresolved members carry the same fixes instructions and doctor show). --code-workspace additionally writes a VS Code workspace file containing the root plus the available referenced stores (ref:<id> folders) — the one write this command performs, refused without --force if the file exists. Unavailable members are reported, never guessed at.
"Working context" is the assembled set; the context: field in rasen/config.yaml is project background injected into instructions — two different things. rasen doctor answers whether the set is healthy; rasen context answers what the set is.
Session runtime context
A supervised session (rasen ui → Launch, or the sessions API) already asks the
right question — plan in this Store, implement in that project checkout — and
now it keeps the answer. The session records its planning space, the project it
works on, and the exact checkout of that project on this machine, and hands its
agent process the location of a machine-local context file describing all three.
Everything here is machine-local. The file lives under the global data dir at
sessions/<sessionId>/context.json, is written before the agent starts,
removed when the session ends, and never enters Git. It is the one place
absolute roots are allowed, precisely because it is never shared.
{
"version": 1,
"sessionId": "0f2a…",
"planning": { "type": "store", "id": "team-store", "root": "/stores/team" },
"execution": { "kind": "project", "projectId": "app-7f3c…", "root": "/projects/app" }
}
A session that plans in a Store without working on any project records
"execution": { "kind": "planning-only" } — an explicit fact, not a missing
field.
The child process receives RASEN_SESSION_CONTEXT carrying that file's path,
never its contents: the document would otherwise land in the process table,
every ps listing, and any log that dumps the environment.
How a command resolves its context
For the first command in a session, in this order and no other:
- an explicit selector given on the command (
--store,--project); - the session's own recorded context;
- only when neither applies, the working directory and the pointer nearest to it.
A later step is not consulted once an earlier one has answered. A context file that is missing, unparseable, or names a different session is reported, not worked around — a silent fallback to the working directory is exactly how a command ends up resolving the checkout's own Store instead of the one the session plans in.
Resuming a frozen run
rasen pipeline resume follows a different rule, because a frozen run already
knows which project it belongs to:
- the frozen identity is the authority — it says which project;
- the session context, or failing that the current checkout, is the local locator — it says where that project is on this machine;
- an explicit selector only cross-checks; it cannot retarget the run.
When the frozen project does not match the project the session executes in, the command fails, naming both identities and the checkout. It never continues in another clone of the same project: a resume into the wrong working tree produces a plausible-looking diff, which is far more expensive to discover than an error.
With no session context, the current directory is used only if its own recorded
identity matches the frozen project; failing that, a single registered checkout
of that project is used; and when several match, the command reports
project_binding_ambiguous and lists every candidate rather than choosing one.
Checkout comparison is canonical, so a checkout differing only by drive-letter case or path-separator form is recognized as the same checkout on every platform.
What a planning-only session cannot do
A planning-only Store session runs at the Store root and has an empty set of
code write roots. It may write planning artifacts in its Store exactly as any
Store session does; it performs no project-scoped materialization and changes
no project's code. The restriction is stated where the session is launched and
in the action context the agent reads (see rasen status).
Personal worksets
Beta. Worksets are part of the new beta surface; commands, flags, and file formats may change shape between releases. For the walkthrough, see the stores guide.
A workset is a personal, named view of the folders you work on together — a planning root plus whatever else you choose — kept on your machine and reopened by name in your tool. It is purely local: never committed, never shared, never derived from declarations, and removing one never touches a member folder.
rasen workset create [name] [--member <path> | --member <name>=<path>]... [--tool <id>] [--json]
rasen workset list [--json]
rasen workset open <name> [--tool <id>]
rasen workset remove <name> [--yes] [--json]
create runs a short guided flow (or takes --member flags non-interactively; the first member is the primary — sessions start there). open launches the chosen tool: editors (VS Code, Cursor) open a window with every member and return; CLI agents (Claude Code, codex) take over this terminal as a session with every member attached and no prompt pre-filled, ending when you exit. A member folder missing at open time is skipped with a note; the rest opens. The saved tool preference is overridable per open with --tool.
Supporting a new tool is configuration, not code. Every tool is one of two launch styles — workspace-file (launched with the generated .code-workspace) or attach-dirs (one attach flag per member) — and the openers key in the global config.json (open it with rasen config edit) adds tools or adjusts built-ins per field:
{
"openers": {
"zed": { "style": "workspace-file" },
"claude": { "attach_flag": "--dir" }
}
}
All workset state lives under the global data dir's worksets/ folder (the saved views plus the generated <name>.code-workspace files, regenerated on every open); deleting that folder removes every trace.
Machine data location: the global data dir (worksets, the store registry, the project registry, user schemas/pipelines) and the global config dir (
config.json) both default to~/.rasenon every platform. SetRASEN_HOMEto relocate both to one custom directory;XDG_DATA_HOME/XDG_CONFIG_HOMEare still honored belowRASEN_HOMEas compatibility aliases. Data found at the old per-platform locations is adopted into~/.rasenautomatically and losslessly on first run after upgrading.
Browsing Commands
rasen list
List changes or specs in your project.
rasen list [options]
Options:
| Option | Description |
|---|---|
--specs |
List specs instead of changes |
--changes |
List changes (default) |
--sort <order> |
Sort by recent (default) or name |
--json |
Output as JSON |
Examples:
# List all active changes
rasen list
# List all specs
rasen list --specs
# JSON output for scripts
rasen list --json
Output (text):
Changes:
add-dark-mode No tasks just now
rasen view
Display an interactive dashboard for exploring specs and changes.
rasen view
Opens a terminal-based interface for navigating your project's specifications and changes.
rasen show
Display details of a change or spec.
rasen show [item-name] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
item-name |
No | Name of change or spec (prompts if omitted) |
Options:
| Option | Description |
|---|---|
--type <type> |
Specify type: change or spec (auto-detected if unambiguous) |
--json |
Output as JSON |
--no-interactive |
Disable prompts |
Change-specific options:
| Option | Description |
|---|---|
--deltas-only |
Show only delta specs (JSON mode) |
Spec-specific options:
| Option | Description |
|---|---|
--requirements |
Show only requirements, exclude scenarios (JSON mode) |
--no-scenarios |
Exclude scenario content (JSON mode) |
-r, --requirement <id> |
Show specific requirement by 1-based index (JSON mode) |
Examples:
# Interactive selection
rasen show
# Show a specific change
rasen show add-dark-mode
# Show a specific spec
rasen show auth --type spec
# JSON output for parsing
rasen show add-dark-mode --json
Validation Commands
rasen validate
Validate changes and specs for structural issues.
rasen validate [item-name] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
item-name |
No | Specific item to validate (prompts if omitted) |
Options:
| Option | Description |
|---|---|
--all |
Validate all changes and specs |
--changes |
Validate all changes |
--specs |
Validate all specs |
--type <type> |
Specify type when name is ambiguous: change or spec |
--strict |
Enable strict validation mode |
--json |
Output as JSON |
--concurrency <n> |
Max parallel validations (default: 6, or RASEN_CONCURRENCY env) |
--no-interactive |
Disable prompts |
Examples:
# Interactive validation
rasen validate
# Validate a specific change
rasen validate add-dark-mode
# Validate all changes
rasen validate --changes
# Validate everything with JSON output (for CI/scripts)
rasen validate --all --json
# Strict validation with increased parallelism
rasen validate --all --strict --concurrency 12
Output (text):
Validating add-dark-mode...
✓ proposal.md valid
✓ specs/ui/spec.md valid
⚠ design.md: missing "Technical Approach" section
1 warning found
Output (JSON):
{
"version": "1.0.0",
"results": {
"changes": [
{
"name": "add-dark-mode",
"valid": true,
"warnings": ["design.md: missing 'Technical Approach' section"]
}
]
},
"summary": {
"total": 1,
"valid": 1,
"invalid": 0
}
}
Lifecycle Commands
rasen archive
Archive a completed change and merge delta specs into main specs.
rasen archive [change-name] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
change-name |
No | Change to archive (prompts if omitted) |
Options:
| Option | Description |
|---|---|
-y, --yes |
Skip confirmation prompts |
--skip-specs |
Skip spec updates (for infrastructure/tooling/doc-only changes) |
--no-validate |
Skip validation (requires confirmation) |
Examples:
# Interactive archive
rasen archive
# Archive specific change
rasen archive add-dark-mode
# Archive without prompts (CI/scripts)
rasen archive add-dark-mode --yes
# Archive a tooling change that doesn't affect specs
rasen archive update-ci-config --skip-specs
What it does:
- Validates the change (unless
--no-validate) - Prompts for confirmation (unless
--yes) - Merges delta specs into
rasen/specs/(standalone; in a Store v2 project scope, intorasen/projects/<projectId>/specs/) - Moves change folder to
rasen/changes/archive/YYYY-MM-DD-<name>/(standalone; in a Store v2 project scope, intorasen/projects/<projectId>/changes/archive/<targetLineId>/) - Captures a quality summary (scanned files + metric-line counts) into the archive's metadata
In the full delivery flow, archive runs after the profile's retention step (ship → retain → archive). It preserves whatever retention produced — a report-mode retro.md is moved with the rest of the change — but archive itself performs no reporting or codification.
Behavior break (v0.1.5): archive no longer interprets [RULE] markers in quality artifacts and no longer appends them to the project's quality-rules. [RULE] lines are ordinary archived content, existing quality-rules are preserved exactly and keep participating in instruction injection, and the archive summary no longer reports an extracted-rule count. Evidence-gated durable guidance is now the codify mode of rasen-retain, stored as managed learned skills rather than config entries.
rasen knowledge
Inspect and mutate canonical learned skills — the durable, evidence-gated guidance rasen-retain's codify mode produces. This group is the only seam that writes learned-skill state; agents submit a strict candidate rather than editing skill directories directly.
rasen knowledge apply --from <absolute-json-file> [--project <id> | --store <id>] [--run-state-dir <absolute-dir>] [--approve-store <store>] [--approve-global] [--json]
rasen knowledge list [--scope project|store|global] [--project <id> | --store <id>] [--run-state-dir <absolute-dir>] [--json]
rasen knowledge show <id> [--scope project|store|global] [--project <id> | --store <id>] [--run-state-dir <absolute-dir>] [--json]
rasen knowledge retire <id> [--scope project|store|global] [--project <id> | --store <id>] [--run-state-dir <absolute-dir>] [--yes] [--json]
rasen knowledge effective [--project <id> | --store <id>] [--run-state-dir <absolute-dir>] [--json]
rasen knowledge migrate [--dry-run] [--project <id> | --store <id>] [--run-state-dir <absolute-dir>] [--json]
rasen knowledge bundle export --project <projectId|root> --to <path> [--to-store <store>] [--json]
rasen knowledge bundle import <bundle> --project <projectId|root> [--dry-run] [--json]
Subcommands:
| Subcommand | Description |
|---|---|
apply |
Read a strict versioned candidate from an absolute JSON file, compute the deterministic plan, and commit it (create / rewrite / promote / retire / no-op). |
list |
List canonical learned skills in a scope, including active and retired status. |
show <id> |
Show one learned skill's provenance, applicability, evidence, and status. |
retire <id> |
Retire a managed learned skill (requires --yes outside a TTY). |
effective |
Show what this project actually receives — the resolved set, its sources by permanent identity, conflicts, unreachable Stores, and the three roots. Reads only; writes nothing. |
migrate |
Move per-clone knowledge into the project's canonical home and re-key ownership records onto permanent identity. Both steps preview with --dry-run. |
bundle export |
Export the named project's own canonical learned knowledge to one new portable file and optionally place the same file in a Store as transport. |
bundle import <bundle> |
Validate and classify a complete portable bundle, then add every new record to the named project's canonical knowledge home. Multi-record import is atomic for catchable failures (all published records are rolled back on error) but not crash-safe across SIGKILL or power loss — a crash may leave a subset published, detected and reported as degraded on the next import. |
Options:
| Option | Description |
|---|---|
--from <path> |
Absolute path to the candidate JSON file (apply). |
--scope project|store|global |
Which canonical catalog to read or mutate (default: project, or store when --store selects a store owner). |
--project <id> |
Select the typed project knowledge owner without changing the active planning root. Mutually exclusive with --store. |
--store <id> |
Select the store knowledge owner, by its permanent identity or its display name. A display name matching more than one registered store is refused with both named, rather than one being picked. Mutually exclusive with --project. |
--run-state-dir <absolute-dir> |
Load auto-run.json from the exact directory returned as runStateDir by rasen pipeline resume, then revalidate and use its frozen planning root and owner. A project/store selector becomes a consistency check and cannot override the frozen owner. |
--approve-store <store> |
Consent to publishing into the named store in a non-interactive run (apply). The value must name the store the publication actually targets; an approval for one store never authorizes another. |
--approve-global |
Consent to a global create/promotion in a non-interactive run (apply). Rejected for a project or store mutation so consent cannot be reused. |
--yes |
Skip the retirement confirmation (retire). |
--dry-run |
Preview both migrations (migrate) or validate and classify a complete bundle import; writes nothing at all. |
--to <path> |
New bundle file to create (bundle export). Any existing filesystem entry at this path is an occupied destination and is never replaced. |
--to-store <store> |
Also place the same bundle in the registered Store's reserved rasen/knowledge-bundles/ transport directory. This grants no ownership and changes no Store catalog or membership. |
--json |
Emit a single JSON document on stdout (agent contract). |
Project-knowledge bundle export
rasen knowledge bundle export is an explicit, export-only route for carrying a
project's own learned knowledge. --project is required and accepts either the
permanent project identity or a registered project root. --to is required and
names the one user-selected file the command may create:
rasen knowledge bundle export \
--project 3f0b0a2c-… \
--to ./web-project-knowledge.bundle.json
To carry that same validated bundle through a Store, add its permanent identity or unambiguous display alias:
rasen knowledge bundle export \
--project 3f0b0a2c-… \
--to ./web-project-knowledge.bundle.json \
--to-store 9f0c1e2a-…
The Store copy is written to
<store>/rasen/knowledge-bundles/<projectId>/<bundleId>.bundle.json. The
bundle identity makes every placement distinct; an existing entry is never
replaced. Transport does not write .rasen-store/store.yaml, project
membership records, the Store knowledge catalog, or any other Store-owned
metadata. It does not stage, commit, or push. Human and JSON output name the
transported file the user may commit.
When --to-store is used, --to must resolve outside the selected Store,
including through symlink or junction spellings. Transport staging is private
and outside the Store on the same filesystem, so the Store gains only the one
derived untracked bundle file. If Store placement fails after the independent
user file was published, both human and JSON errors report that user file as
successful and name the Store failure separately.
The strict versioned file contains exactly these bundle fields:
version, bundleId, projectId, createdAt, baseProjectCommit, and
records. Each record contains exactly id, knowledgeKey, contentDigest,
the strict managed manifest, and its canonical content. Retired records are
included with their retired status.
The exporter deliberately never reads or serializes Store-owned knowledge, machine-wide knowledge, generated-file ownership records, generated tool files, tokens, session handles, or run state. It validates the complete serialized bundle for Windows drive-letter paths, Windows network-share paths, and POSIX absolute paths on every platform before opening any destination-side temporary file. A non-portable record fails by record and field.
Without --to-store, success creates exactly one new file at the resolved
--to destination. With Store transport, it additionally creates exactly one
derived file in the reserved Store path.
An occupied file, directory, or link refuses before any temporary file is
created. Schema, path, catalog-read, write, and publication failures leave the
destination tree unchanged; the project catalog, checkout, and machine
registrations are read-only throughout.
Stable JSON success output (the transport object is present only with
--to-store):
{
"ok": true,
"state": "exported",
"project": "3f0b0a2c-…",
"recordCount": 4,
"destination": "/carry/web-project-knowledge.bundle.json",
"transport": {
"store": {
"id": "team",
"uid": "9f0c1e2a-…"
},
"destination": "/stores/team/rasen/knowledge-bundles/3f0b0a2c-…/7c18.bundle.json",
"filesToCommit": [
"rasen/knowledge-bundles/3f0b0a2c-…/7c18.bundle.json"
]
},
"warnings": []
}
When Git cannot determine a commit, export still succeeds and writes
"baseProjectCommit": null; the output carries the
base_project_commit_unavailable warning. baseProjectCommit is provenance
for auditing the capture, never a gate and never a portable run checkpoint.
Occupied destination refusal:
{
"ok": false,
"error": {
"code": "knowledge_bundle_destination_occupied",
"message": "The export destination is already occupied: /carry/web-project-knowledge.bundle.json",
"destination": "/carry/web-project-knowledge.bundle.json",
"repair": "Choose a new --to path. Bundle export never replaces an existing filesystem entry."
}
}
Non-portable record refusal:
{
"ok": false,
"error": {
"code": "knowledge_bundle_non_portable_record",
"message": "Project record \"deploy-routing\" field \"records[0].manifest.applicability.markers[0]\" contains an absolute machine path and is not portable.",
"record": "deploy-routing",
"field": "records[0].manifest.applicability.markers[0]",
"repair": "Remove the absolute machine path from record \"deploy-routing\" and record portable, root-relative guidance before exporting again."
}
}
Project-knowledge bundle import
Import is explicit at the receiving end:
rasen knowledge bundle import ./web-project-knowledge.bundle.json \
--project 3f0b0a2c-… \
[--dry-run] \
[--json]
The positional <bundle> is the file to read. --project is required and
accepts the same permanent project identity or registered root as export.
--dry-run runs the complete reader, project-identity check, identifier
validation, and target comparison, but creates no lock, directory, file, or
cleanup debris.
Every record is classified deterministically:
- added — the canonical identifier is absent;
- already present — canonical content and active/retired state agree; the existing files remain byte-identical;
- conflicting — content or lifecycle differs, or the target is occupied or unreadable.
Identity is the record identifier, not its knowledge key. A retired record against an active one conflicts. Import reports every conflict in one pass. Any conflict refuses the whole apply: no clean record from that bundle is written, no local record is overwritten, retired, or removed, and unrelated local knowledge is untouched. Apply rechecks the same plan under the existing per-project catalog lock, stages and verifies the complete new set, and publishes add-only. A write, verification, or publication failure rolls back only transaction-owned additions.
Human success names the project, bundle identity and path, counts, each classification, and warnings. A clean JSON import has the same facts:
{
"ok": true,
"state": "imported",
"refused": false,
"changed": true,
"project": "3f0b0a2c-…",
"bundle": {
"id": "7c18…",
"path": "/carry/web-project-knowledge.bundle.json",
"baseProjectCommit": "a17e…"
},
"added": [
{
"id": "portable-routing",
"knowledgeKey": "portable-routing-key",
"status": "active",
"contentDigest": "sha256:…"
}
],
"alreadyPresent": [],
"conflicts": [],
"warnings": [
{
"code": "base_project_commit_provenance",
"baseProjectCommit": "a17e…",
"message": "Warning: baseProjectCommit a17e… is provenance only and did not gate this import."
}
]
}
A complete conflict preview is still a successful preview document, with
"state": "previewed", "refused": true, "changed": false, every conflict,
and every record that would otherwise be added or was already present. Apply
of that unchanged input returns a refusal carrying the same plan:
{
"ok": false,
"error": {
"code": "knowledge_bundle_import_conflict",
"message": "1 conflict(s) stop the whole import; nothing was imported.",
"changed": false,
"plan": {
"project": "3f0b0a2c-…",
"added": [{ "id": "portable-clean-record" }],
"alreadyPresent": [],
"conflicts": [
{
"id": "portable-routing",
"reason": "content-differs",
"bundle": { "status": "active", "contentDigest": "sha256:…" },
"local": { "kind": "managed", "status": "active", "contentDigest": "sha256:…" }
}
]
},
"repair": "Resolve every named local conflict, then preview or import the same bundle again."
}
}
Malformed, newer-version, tampered, wrong-project, machine-path, and invalid-ID bundles are refused before catalog mutation. A wrong-project refusal names both identities. Records land as version-2 project-owned manifests naming the resolved permanent project identity, with no Store/publication source and no receiving-machine evidence. A bundle read from a cloned Store therefore remains project knowledge; the Store's catalog, metadata, membership, Git index, HEAD, and remote are outside the importer and unchanged.
baseProjectCommit is provenance, not a gate. This release adds explicit
portable project-knowledge import and the separately declared, confirmed
machine-preparation action. It still does not provide doctor/readiness
integration, interactive conflict reconciliation, automatic synchronization,
or portable run checkpoints.
effective reports one of three states: ready, degraded (a relevant Store could not be reached, so removals were deferred), or blocked (Stores disagree and no project record settles it, so nothing was written). Each conflict names every participant by permanent identity, and each unreachable Store carries its own repair.
{
"ok": true,
"status": "degraded",
"project": { "type": "project", "id": "3f0b0a2c-…", "root": "/work/web" },
"roots": {
"canonicalOwnerRoot": "/home/me/.rasen/project-knowledge/3f0b0a2c-…",
"evaluationRoot": "/work/web"
},
"skills": [
{
"id": "go-sql-transaction-locking",
"effectiveScope": "store",
"knowledgeKey": "go-sql-tx-locking",
"sources": [
{ "owner": { "type": "store", "uid": "9f0c1e2a-…", "id": "team" }, "id": "go-sql-transaction-locking" },
{ "owner": { "type": "store", "uid": "c41d77b8-…", "id": "platform" }, "id": "go-sql-transaction-locking" }
],
"canonicalContentDigest": "sha256:…",
"resolutionDigest": "sha256:…"
}
],
"unavailableStores": [
{
"store": { "type": "store", "uid": "5b2e90aa-…", "id": "elsewhere" },
"relevant": true,
"relevance": ["declared", "previous-source"],
"diagnostic": "store elsewhere is not registered on this machine",
"repair": ["rasen bootstrap"]
}
],
"conflicts": []
}
A conflict, and the ownership record the same run would have written:
{
"conflicts": [
{
"id": "go-sql-transaction-locking",
"kind": "effective",
"participants": [
{ "source": { "owner": { "type": "store", "uid": "9f0c1e2a-…", "id": "team" }, "id": "go-sql-transaction-locking" },
"knowledgeKey": "go-sql-tx-locking", "canonicalContentDigest": "sha256:…", "label": "store:team (9f0c1e2a-…)" },
{ "source": { "owner": { "type": "store", "uid": "c41d77b8-…", "id": "platform" }, "id": "go-sql-transaction-locking" },
"knowledgeKey": "go-sql-tx-locking", "canonicalContentDigest": "sha256:…", "label": "store:platform (c41d77b8-…)" }
],
"guidance": "Align the canonical store records exactly, rename one learned skill, or retire the inapplicable revision."
}
]
}
{
"version": 2,
"stores": {
"9f0c1e2a-…": { "lastMembership": "member", "id": "team" }
},
"tools": {
"claude": {
"learned": {
"go-sql-transaction-locking": {
"effectiveScope": "store",
"sources": [{ "owner": { "type": "store", "uid": "9f0c1e2a-…", "id": "team" }, "id": "go-sql-transaction-locking" }],
"canonicalContentDigest": "sha256:…",
"resolutionDigest": "sha256:…",
"resolutionSchemaVersion": 2,
"file": { "scope": "project", "path": ".claude/skills/go-sql-transaction-locking/SKILL.md", "sha256": "sha256:…" }
}
}
}
}
}
migrate runs two independent steps and reports each: the per-clone catalog move and the ownership re-key. Neither ever chooses between things that disagree — divergent catalogs are reported with every location named and nothing is deleted, and a display name that maps to more than one Store (or to none) blocks the re-key rather than guessing which Store owns a real file.
Knowledge-owner selection and planning-root selection are independent. A pointer project may report owner=project:web while its change planning root is store:team; a direct store launch never guesses one member project. Human and JSON output report both typed identities, and a store is reported by its permanent identity with its display name alongside.
Project mutations are authorized by an active codify profile. A store publication requires exact managed source records from at least two distinct projects the store's own membership records name as knowledge members, plus an approval naming that store. A global create or promotion requires the same independent, verified sources plus explicit approval (interactive prompt or --approve-global). A refused publication reports the evidence held and the evidence missing with a copy-pasteable next command, and writes nothing.
A store mutation writes into the store's repository and then tells you which files to commit — Rasen stages, commits, and pushes nothing. See Retention and learned skills for the scope, promotion, membership, applicability, ownership, and budget rules.
Workflow Commands
These commands support the artifact workflow. They're useful for both humans checking progress and agents determining next steps.
rasen new change
Create a change directory and optional checked-in metadata in the resolved Rasen root.
rasen new change <name> [options]
Options:
| Option | Description |
|---|---|
--description <text> |
Description to add to README.md |
--goal <text> |
Optional goal metadata to store with the change |
--schema <name> |
Workflow schema to use |
--store <id> |
Store id to use as the Rasen root (a store is a standalone rasen repo you've registered) |
--json |
Output JSON |
Examples:
rasen new change add-billing-api
rasen new change add-billing-api --store team-context --json
rasen status
Display artifact completion status for a change.
rasen status [options]
Options:
| Option | Description |
|---|---|
--change <id> |
Change name (prompts if omitted) |
--schema <name> |
Schema override (auto-detected from change's config) |
--json |
Output as JSON |
Examples:
# Interactive status check
rasen status
# Status for specific change
rasen status --change add-dark-mode
# JSON for agent use
rasen status --change add-dark-mode --json
Output (text):
Change: add-dark-mode
Schema: spec-driven
Progress: 2/4 artifacts complete
[x] proposal
[ ] design
[x] specs
[-] tasks (blocked by: design)
Output (JSON):
{
"changeName": "add-dark-mode",
"schemaName": "spec-driven",
"isComplete": false,
"applyRequires": ["tasks"],
"artifacts": [
{"id": "proposal", "outputPath": "proposal.md", "status": "done"},
{"id": "design", "outputPath": "design.md", "status": "ready"},
{"id": "specs", "outputPath": "specs/**/*.md", "status": "done"},
{"id": "tasks", "outputPath": "tasks.md", "status": "blocked", "missingDeps": ["design"]}
],
"actionContext": {
"mode": "repo-local",
"sourceOfTruth": "repo",
"version": 1,
"planningWriteRoots": ["/repo/rasen/specs", "/repo/rasen/changes"],
"codeWriteRoots": ["/repo"],
"readRoots": ["/repo"],
"allowedEditRoots": ["/repo"],
"requiresAffectedAreaSelection": false,
"constraints": ["Repo-local change artifacts and implementation edits are scoped to this project.", "..."]
}
}
actionContext states separately where planning artifacts may be written,
where code may be written, and what may only be read. version says which
contract you are reading:
version: 1also carriesallowedEditRoots, the compatibility view for consumers that know only the older single-list form. It is present only when the newer capability projects into it without granting anything the older form would not have granted — the projection can narrow, never widen.version: 2is reported when that projection is impossible — a session that plans in a Store while working on a project checkout needs two roots, which the older form cannot express.allowedEditRootsis then absent, so a consumer expecting only the older form stops instead of inheriting a root it never asked for.
Inside a Store session that works on a project checkout (standalone mode; Store v2 uses per-project partitions — see the Store user guide):
{
"actionContext": {
"version": 2,
"planningWriteRoots": ["/stores/team/rasen/specs", "/stores/team/rasen/changes"],
"codeWriteRoots": ["/projects/app"],
"readRoots": ["/stores/team", "/projects/app"],
"requiresAffectedAreaSelection": false,
"constraints": ["Planning artifacts are written in the planning root; code changes are confined to the selected checkout. ...", "..."]
}
}
A planning-only Store session reports "codeWriteRoots": [] — empty as a
stated fact, not as a discouragement. Making a root visible to the agent
process (--add-dir) never grants permission to write it.
rasen instructions
Get enriched instructions for creating an artifact or applying tasks. Used by AI agents to understand what to create next.
rasen instructions [artifact] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
artifact |
No | Artifact ID: proposal, specs, design, tasks, or apply |
Options:
| Option | Description |
|---|---|
--change <id> |
Change name (required in non-interactive mode) |
--schema <name> |
Schema override |
--json |
Output as JSON |
Special case: Use apply as the artifact to get task implementation instructions.
Examples:
# Get instructions for next artifact
rasen instructions --change add-dark-mode
# Get specific artifact instructions
rasen instructions design --change add-dark-mode
# Get apply/implementation instructions
rasen instructions apply --change add-dark-mode
# JSON for agent consumption
rasen instructions design --change add-dark-mode --json
Output includes:
- Template content for the artifact
- Project context from config
- Content from dependency artifacts
- Per-artifact rules from config
rasen templates
Show resolved template paths for all artifacts in a schema.
rasen templates [options]
Options:
| Option | Description |
|---|---|
--schema <name> |
Schema to inspect (default: spec-driven) |
--json |
Output as JSON |
Examples:
# Show template paths for default schema
rasen templates
# Show templates for custom schema
rasen templates --schema my-workflow
# JSON for programmatic use
rasen templates --json
Output (text):
Schema: spec-driven
Templates:
proposal → ~/.rasen/schemas/spec-driven/templates/proposal.md
specs → ~/.rasen/schemas/spec-driven/templates/specs.md
design → ~/.rasen/schemas/spec-driven/templates/design.md
tasks → ~/.rasen/schemas/spec-driven/templates/tasks.md
rasen schemas
List available workflow schemas with their descriptions and artifact flows.
rasen schemas [options]
Options:
| Option | Description |
|---|---|
--json |
Output as JSON |
Example:
rasen schemas
Output:
Available schemas:
spec-driven (package)
The default spec-driven development workflow
Flow: proposal → specs → design → tasks
my-custom (project)
Custom workflow for this project
Flow: research → proposal → tasks
Schema Commands
Commands for creating and managing custom workflow schemas.
rasen schema init
Create a new project-local schema.
rasen schema init <name> [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
name |
Yes | Schema name (kebab-case) |
Options:
| Option | Description |
|---|---|
--description <text> |
Schema description |
--artifacts <list> |
Comma-separated artifact IDs (default: proposal,specs,design,tasks) |
--default |
Set as project default schema |
--no-default |
Don't prompt to set as default |
--force |
Overwrite existing schema |
--json |
Output as JSON |
Examples:
# Interactive schema creation
rasen schema init research-first
# Non-interactive with specific artifacts
rasen schema init rapid \
--description "Rapid iteration workflow" \
--artifacts "proposal,tasks" \
--default
What it creates:
rasen/schemas/<name>/
├── schema.yaml # Schema definition
└── templates/
├── proposal.md # Template for each artifact
├── specs.md
├── design.md
└── tasks.md
rasen schema fork
Copy an existing schema to your project for customization.
rasen schema fork <source> [name] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
source |
Yes | Schema to copy |
name |
No | New schema name (default: <source>-custom) |
Options:
| Option | Description |
|---|---|
--force |
Overwrite existing destination |
--json |
Output as JSON |
Example:
# Fork the built-in spec-driven schema
rasen schema fork spec-driven my-workflow
rasen schema validate
Validate a schema's structure and templates.
rasen schema validate [name] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
name |
No | Schema to validate (validates all if omitted) |
Options:
| Option | Description |
|---|---|
--verbose |
Show detailed validation steps |
--json |
Output as JSON |
Example:
# Validate a specific schema
rasen schema validate my-workflow
# Validate all schemas
rasen schema validate
rasen schema which
Show where a schema resolves from (useful for debugging precedence).
rasen schema which [name] [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
name |
No | Schema name |
Options:
| Option | Description |
|---|---|
--all |
List all schemas with their sources |
--json |
Output as JSON |
Example:
# Check where a schema comes from
rasen schema which spec-driven
Output:
spec-driven resolves from: package
Source: /usr/local/lib/node_modules/@atelierai/rasen/schemas/spec-driven
Schema precedence:
- Project:
rasen/schemas/<name>/ - User:
~/.rasen/schemas/<name>/(or$RASEN_HOME/schemas/<name>/when set) - Package: Built-in schemas
Configuration Commands
rasen profile
Edit the current workflow selection or manage reusable named profile snapshots. Profile changes update global configuration; run rasen update in each project to install the selected workflows.
rasen profile
rasen profile new [name]
rasen profile use [name]
rasen profile update [name]
rasen profile list [--json]
rasen profile delete [name] [--yes]
rasen profile import <path> [--as <name>] [--force]
rasen profile export <path> [--profile <name>] [--thin] [--force]
| Subcommand | Description |
|---|---|
| (none) | Edit the current delivery mode and workflow selection interactively |
new [name] |
Create, save, and use a named profile; prompts for the name when omitted |
use [name] |
Use a built-in or saved profile; opens a picker when omitted |
update [name] |
Edit a saved profile definition in place; opens the picker seeded from the stored snapshot and saves back to the same file. Never changes the current user-wide selection or any project — projects locked to the profile pick the change up on their next rasen update |
list |
List built-in and saved profiles; add --json for structured output |
delete [name] |
Delete a saved profile; add --yes to skip confirmation |
import <path> |
Import a self-contained .rasenpkg, YAML, or JSON profile; package name is authoritative unless --as is supplied, and --force replaces only the profile snapshot |
export <path> |
Export current settings or the profile selected by --profile; profiles with user workflows default to self-contained .rasenpkg, while YAML/JSON requires explicit --thin |
Named profiles are saved snapshots. Using one copies its delivery and workflows into global configuration, where profile remains the effective classification (full, core, or custom) rather than the saved profile name. The saved name is retained by its file in the machine-global profiles directory.
Locked profiles (project scope). A project's rasen/config.yaml may carry profile: <full|core|saved-name> — the locked profile, written by rasen init --profile <value> or rasen config set profile <value> --scope project. When present, rasen update, extend-mode rasen init, drift detection, and the management UI resolve that project's workflows from the locked profile instead of the user-wide one. Precedence within a project: the workflows override in config.yaml (highest, with a warning naming the shadowed lock) → the profile lock → the user-wide profile. A lock that cannot be resolved on this machine (deleted or never-imported named profile, or custom, which cannot be locked) prints a warning and falls back to the user-wide profile — commands never fail because of a broken lock. Remove a lock with rasen config unset profile --scope project.
Self-contained profile packages embed selected user workflows and their user-workflow dependency closure. Built-in workflows remain references. Import reuses an installed workflow only when its digest is identical; a different digest is an error even with --force. Thin YAML/JSON import requires every referenced user workflow to be installed already and writes nothing when membership validation fails.
When new prompts for a name, invalid, reserved, and existing names show an inline error so another name can be entered. An invalid name supplied directly as new <name> fails without opening the remaining prompts.
In the workflow checklist, press Space to toggle one workflow, A to select all workflows or clear all when every workflow is already selected, and Enter to confirm.
Profile prompts, CLI help, the interactive config editor, and shell-completion descriptions and management messages are available in English, Japanese, and Simplified Chinese. The selected language is stored using the canonical language: "auto" | "en" | "ja" | "zh-cn" values in the machine-global JSON config. Set it with rasen config set language en, rasen config set language ja, or rasen config set language zh-cn.
The workflow picker shows the stable public workflow id before the localized name, with the separator aligned across rows (for example, propose - 変更を提案). Tool-specific slash punctuation is intentionally omitted because assistants may expose the same workflow as /rasen-propose, /rasen-propose, or a skill.
With language: "auto" (the default), Unix-like systems check LC_ALL, LC_MESSAGES, and LANG in that order. A value that resolves to a supported locale decides the language; C or POSIX (with or without an encoding suffix) explicitly requests unlocalized output and resolves to English; a well-formed but unsupported language such as fr_FR.UTF-8 falls back to English; a value that carries no language information (for example UTF-8) is skipped so the next variable can decide. On macOS, when no variable determines a language — a GUI-launched process, or a terminal that only exports LC_CTYPE=UTF-8 — the CLI reads the operating system's configured locale (defaults read -g AppleLocale, silently and at most once per process) before falling back to the runtime's system locale. Windows uses the system locale reported by Node.js. The aliases zh-CN, zh_CN.UTF-8, zh-SG, zh-Hans, and bare zh resolve to zh-cn. Traditional Chinese locales zh-TW, zh-HK, zh-MO, and zh-Hant are unsupported and fall back to English, as do other unsupported automatic locales. RASEN_LANG=en, RASEN_LANG=ja, or RASEN_LANG=zh-cn temporarily overrides the saved setting. Reinstall or regenerate shell completions after changing the saved language so generated descriptions are refreshed.
Translation catalogs are maintained as src/locales/en.json, src/locales/ja.json, and src/locales/zh-cn.json. The build copies them to dist/locales/, which is included in the published package. All three catalogs use the same keys and placeholders.
rasen config profile [full|core] remains available as a compatibility entry point, but rasen profile is the canonical command.
rasen workflow
Manage installable workflows in the machine-wide user library. These commands operate on workflow definitions, not artifact schemas or orchestration pipelines.
rasen workflow list [--unused] [--all] [--json]
rasen workflow show <id> [--json]
rasen workflow which <id> [--json]
rasen workflow init <id> --output <path> [--json]
rasen workflow validate <id-or-path> [--json]
rasen workflow import <path> [--json]
rasen workflow export <id> <path> [--force] [--json]
rasen workflow delete <id> [--yes] [--json]
| Subcommand | Description |
|---|---|
list |
List valid built-in/user definitions plus invalid user entries, grouped by kind; --unused is advisory and only considers detectable consumers; --all also reveals the internal group |
show <id> |
Show identity, skill metadata, the declared display title, dependencies, files, digest, and known usage |
which <id> |
Show whether an ID resolves from the built-in catalog or a user directory |
init <id> |
Create a minimal draft in the required empty --output directory without installing it |
validate <id-or-path> |
Statically validate an installed ID, unpacked draft, or strict .rasenpkg without executing scripts or writing the registry |
import <path> |
Validate, stage, revalidate, and atomically install an unpacked workflow or kind: workflow package |
export <id> <path> |
Export a user workflow and its required user-workflow closure as deterministic .rasenpkg; built-ins cannot be exported |
delete <id> |
Delete an unreferenced user workflow after usage preflight and confirmation; built-ins cannot be deleted |
Every JSON success payload includes status: []. Failures emit one JSON document whose status entries carry stable severity, code, and message fields. For example:
{
"workflow": null,
"usage": [],
"status": [
{
"severity": "error",
"code": "workflow_not_found",
"message": "Workflow \"missing\" was not found"
}
]
}
delete scans global selection, saved profiles, reverse dependencies, user/current-project pipelines, and the current project's managed-artifact ledger. It cannot prove that no unknown project elsewhere references the workflow, so successful deletion still prints that limitation. See Installable workflows and .rasenpkg for the manifest, package, digest, path, and resource-limit contracts.
Kind classification: every workflow definition carries a kind — task (an inner-loop operation invoked directly), driver (an outer-loop engine that consumes pipelines, e.g. auto-command/goal-command), or internal (a sub-unit invoked only by a driver, e.g. the goal-plan/goal-iterate/goal-report trio). The human list table groups entries into task and driver sections and hides internal unless --all is passed. --json always lists every workflow, ungrouped, with its kind — machine consumers see the full catalog regardless of --all. A user workflow's workflow.yaml defaults to kind: task and may optionally declare kind: internal; driver is reserved for built-in engines. kind is presentation metadata only — it never enters a workflow's digest, so classifying or reclassifying a workflow never triggers drift-healing.
Display title: a user workflow's workflow.yaml may declare a skill: block whose name is the workflow's human-readable display title. Pickers such as rasen profile show the title verbatim in the author's original language (never translated) while the stored value stays the workflow id; list --json and show expose it as a stable title field (null when absent). The human list table keeps machine values only. See Installable workflows and .rasenpkg for the block's contract.
rasen pipeline
Inspect, package, install, and remove orchestration pipelines — the outer-loop DAGs that sequence workflows (see Concepts for the schema/workflow/pipeline model). Pipelines resolve from three layers, highest precedence first: project (rasen/pipelines/<name>/pipeline.yaml), user (installed via import, machine-global), and package (built-in, shipped with rasen).
rasen pipeline list [--json]
rasen pipeline show <name> [--for-execution] [--planner|--implementer|--reviewer|--fixer|--shipper <runtime>] [--json]
rasen pipeline agents <name> [--planner|--implementer|--reviewer|--fixer|--shipper <runtime>] [--json]
rasen pipeline classify <task> [--json]
rasen pipeline resume <change> [--json]
rasen pipeline init <name> --output <path> [--json]
rasen pipeline save <name> --from <file> [--force] [--json]
rasen pipeline validate <name-or-path> [--json]
rasen pipeline import <path> [--force] [--json]
rasen pipeline export <name> <path> [--force] [--json]
rasen pipeline delete <name> [--yes] [--force] [--json]
All eleven subcommands accept --store <id> / --project <id>, resolving their root exactly like rasen validate.
Pipeline help and Rasen-owned human output for all eleven subcommands are available in English, Japanese, and Simplified Chinese. Localization changes presentation only: pipeline and stage IDs, role/runtime/source values, paths, JSON fields and raw descriptions, classifier keywords and results, and user-authored names and descriptions remain locale-neutral. Package-owned built-in descriptions are localized in human views while their JSON values remain raw.
| Subcommand | Description |
|---|---|
list |
List available pipelines (project > user > package) with description and stage ids |
show <name> |
Show a pipeline's stage DAG, build order, and resolved per-stage runtime/handoff/reuse config; --for-execution also validates active-profile skills |
agents <name> |
Show, or set (writing a project-local override), per-role Claude/Codex runtimes |
classify <task> |
Suggest a pipeline for a task string via an advisory keyword heuristic |
resume <change> |
Show a change's (or portfolio's) next/remaining stages from its run-state. Reports three distinguishable states: no file (hasRunState: false, plus the deterministic runStateDir state would be created at), a located-but-unparseable file (invalidRunState: true with path and reason), and a valid file naming no pipeline (hasRunState: true, pipeline: null, no next stage — a change holding retention identity only) |
init <name> |
Create a minimal pipeline.yaml draft in the required empty --output directory without installing it |
save <name> |
Validate a JSON or YAML definition from --from, then install canonical normalized YAML in the user layer; --force may replace an existing user pipeline, but never a built-in |
validate <name-or-path> |
Structurally validate an installed pipeline name, a draft directory, or a kind: pipeline .rasenpkg — parse, duplicate/cycle/parallel-group/decompose-stage checks; does not require referenced skills to already be installed |
import <path> |
Validate, stage, digest-reverify, and atomically install every pipeline in a kind: pipeline .rasenpkg into the user layer; --force allows overwriting an already-installed pipeline of the same name |
export <name> <path> |
Package an installed user pipeline as a deterministic .rasenpkg; built-in and project-local pipelines cannot be exported |
delete <name> |
Delete an unreferenced user pipeline after a refcount check; built-in pipelines cannot be deleted |
Pipeline definition content version. The normalized public definition always carries the top-level integer version: 1. Historical definitions with no version remain readable and normalize to v1; any explicit unsupported or malformed value is refused with an actionable issue at /version so the user can upgrade to a compatible Rasen release. show and the management detail API expose the normalized v1 definition. init and save emit canonical v1 YAML. export canonicalizes only the packaged pipeline.yaml, preserves ancillary files, and does not rewrite the installed source merely because it was read or exported. The package manifest's formatVersion is a separate .rasenpkg container version.
Pipeline v1 keeps the existing flat requires DAG and the current stage.loop.kind: review-cycle and stage.loop.kind: goal declarations. They remain readable today and are valid source inputs for a future compiled Composite run plan. For now, the LEAD orchestration playbook interprets both loop kinds. Canvas views and edits Pipeline definitions; it is not a programmatic Pipeline runner and does not introduce nested execution behavior.
.rasenpkg files carry a kind discriminant — workflow, profile, or pipeline — sharing one package format. A kind: pipeline package's digest, transactional install (temp stage → atomic rename, all-or-nothing across every packaged pipeline), and file-limit rules mirror the kind: workflow contract in Installable workflows and .rasenpkg. Every package also carries an optional minRasenVersion, stamped from the packing CLI's own version: an older CLI importing a package that requires a newer one gets a clear upgrade message instead of an opaque schema error. This preflight only helps CLIs from this point forward — an already-shipped CLI predating this field still rejects an unrecognized package kind opaquely; there is no way to retrofit that.
delete's refcount guard refuses to delete a pipeline referenced by any installed workflow's requires.pipelines or by another pipeline's decompose stage childPipeline (explicit or the small-feature default), naming every referrer; --force bypasses the guard (not the built-in-pipeline prohibition) and warns about the referrers left dangling.
Pipeline stage skill: fields in the built-in pipelines use the workflow directory-name form (rasen-propose, rasen-review); validate and package import also accept the retired skill-name colon form (rasen:review) for backward compatibility, and do not require the skill to be installed at import time — a missing skill is caught at execution time instead.
rasen retain
Prepare a change for a retention run — the Rasen-owned transition from "standalone retention resolved a mode" to "project knowledge operations have a frozen identity".
rasen retain prepare <change> [--store <id>|--project <id>] [--owner-store <id>|--owner-project <id>] [--json]
A change that never ran through a classified pipeline has no auto-run.json, so rasen pipeline resume reports no run-state directory and every --run-state-dir-bearing knowledge command has nothing to load. prepare closes that gap in one operation:
- reports the effective retention mode — the same resolution that authorizes a project-scoped
rasen knowledge apply, so it answers even when noretentionkey was ever stored (unlikerasen config get retention, which prints nothing for an unset key); - freezes durable knowledge identity when the change carries none, recording
{type:'project', projectId, id?}/{type:'store', uid, id?}refs and no absolute planning or owner directory, so the record stays valid on another machine or checkout; - reuses a
knowledgeContextalready recorded at any version verbatim — reported unchanged, never upgraded in place, so repeating preparation is a no-op on disk; - reports the
runStateDirto pass as--run-state-diron every later project/store knowledge command.
It writes run-state crash-safely (temp file plus rename), and it never replaces a record it did not create: a record that already exists — including one that appeared while preparation was resolving identity — is merged into, with knowledgeContext added and no other value changed, so the LEAD's own hand-written progress and handoff entries survive. The document is re-serialized rather than patched in place, so byte-level formatting is not preserved; a repeated key was already collapsed to its last value by any reader, which is the ambiguity pipeline resume reports separately.
It writes only for codify. Freezing identity is a write, and only the codify branch reads what it freezes: report writes a retrospective, and off changes no learning state at all. When neither the effective mode nor a mode already frozen in run-state is codify, preparation resolves nothing and writes nothing — it reports the mode, the pipeline, and the directory durable state would live at, with contextSource: "skipped" and no knowledgeContext. A change that never ran a pipeline is therefore not left holding an auto-run.json no run produced, or an identity frozen permanently at the version of the day it was frozen for a branch that never reads it. Either mode being codify opens the write: a worker dispatched for a canonical retain stage uses the mode the LEAD froze while a standalone run uses the effective one, and preparation cannot tell those two callers apart.
Two independent selectors. --store/--project select the planning root, exactly like rasen pipeline resume; --owner-store/--owner-project select the knowledge owner independently, exactly like the rasen knowledge group. Each pair is mutually exclusive within itself.
Fails closed before any candidate exists. Ambiguous, missing, renamed, or stale ownership (knowledge_owner_*), an owner selector disagreeing with an already-recorded identity (knowledge_selector_conflict), an unreadable run-state (retention_run_state_invalid), and a change read from one planning root while identity resolves to another (retention_planning_root_mismatch) all refuse without writing. The ownership and planning-root refusals belong to the resolution path: a preparation that records nothing because no mode it reports is codify resolves no owner and so reports none of them. An unreadable run-state still refuses, because the frozen mode cannot be read from it.
{
"ok": true,
"change": "add-thing",
"retention": "codify",
"runStateDir": "/abs/path/.rasen/changes/add-thing/ephemera",
"runStatePath": "/abs/path/.rasen/changes/add-thing/ephemera/auto-run.json",
"pipeline": null,
"contextSource": "prepared",
"knowledgeContext": {
"version": 3,
"planningRoot": { "type": "project", "projectId": "…" },
"owner": { "type": "project", "projectId": "…" }
},
"owner": "project:…",
"planningRoot": "project:…"
}
contextSource is prepared when this call froze the identity, recorded when it reused one already on file, and skipped when no mode it reports is codify — that payload carries no knowledgeContext, owner, or planningRoot, because nothing was resolved and nothing was written. frozenRetention appears only when run-state carries a mode the LEAD froze for a pipeline retain stage; retention always reports the effective mode.
rasen config
View and modify global or project rasen configuration. Every subcommand accepts --scope <global|project> (default global); --scope project reads and writes the current project's rasen/config.yaml instead of the global config file. Running rasen config with no subcommand opens an interactive full-view editor (in a TTY) showing every configurable key, its effective value, and which layer produced it (default, global, project, or env-override); outside a TTY it prints that same effective view non-interactively and exits.
rasen config <subcommand> [options]
Subcommands:
| Subcommand | Description |
|---|---|
path |
Show config file location |
list |
Show all current settings |
get <key> |
Get a specific value |
set <key> <value> |
Set a value (validated against the config-key registry) |
unset <key> |
Remove a key |
reset |
Reset to defaults (global scope only) |
edit |
Open in $EDITOR (global scope only) |
profile [preset] |
Compatibility entry point for rasen profile or rasen profile use <preset> |
Configurable keys (see rasen config with no arguments for the full list with current values):
| Key | Scope | Description |
|---|---|---|
profile |
global, project | Workflow profile. Global: the user-wide profile (full/core/custom, use rasen profile to edit). Project: the locked profile (full, core, or a saved profile name — never custom) |
workflows |
global, project | Workflow selection. Global: edit via rasen profile. Project: a per-space override that replaces the user-wide profile (and shadows a profile lock) for that space only |
language |
global | CLI display language: auto, en, ja, or zh-cn |
featureFlags.<name> |
global | Feature flag toggle |
proactive |
global | Whether agents proactively suggest next steps |
repoMode |
global | solo or collaborative |
telemetry.enabled |
global | Telemetry on/off (environment opt-outs always win) |
handoff.threshold |
global, project | Context-handoff threshold; project wins over global. Dual-form: a fraction in (0, 1], or the absolute { remainingTokens: N } headroom form (a positive integer token count) |
schema |
project | The workflow schema this project uses |
autopilot.gates |
project | Default autopilot gate policy (on/off) |
autopilot.selection |
project | Default autopilot pipeline-selection policy |
archive.timing |
project | When archive bookkeeping runs (on-merge / in-ship). archive.destination is retired: it is no longer settable, still parses with a deprecation warning, and selects nothing — archives always land in the planning root |
Examples:
# Show config file path
rasen config path
# List all settings
rasen config list
# Get a specific value
rasen config get telemetry.enabled
# Set a value
rasen config set telemetry.enabled false
# Persist Japanese CLI prompts and help
rasen config set language ja
# Persist Simplified Chinese CLI prompts and help
rasen config set language zh-cn
# Set a string value explicitly
rasen config set featureFlags.myFlag "custom" --string
# Set a dual-form threshold: a fraction, or the absolute { remainingTokens: N } headroom form
rasen config set handoff.threshold 0.6
rasen config set --scope global handoff.threshold '{"remainingTokens": 60000}'
# Remove a custom setting
rasen config unset handoff.threshold
# Project-scope config (writes rasen/config.yaml, preserving comments)
rasen config set --scope project autopilot.gates off
rasen config get --scope project autopilot.gates
rasen config list --scope project
# Reset all configuration
rasen config reset --all --yes
# Edit config in your editor
rasen config edit
# Open the interactive full-view editor
rasen config
# Configure profile with action-based wizard
rasen profile
# Fast preset: switch workflows to core (keeps delivery mode)
rasen profile use core
rasen profile starts with a current-state summary, then lets you choose:
- Change delivery + workflows
- Change delivery only
- Change workflows only
- Keep current settings (exit)
If you keep current settings, no changes are written and no update prompt is shown.
If there are no config changes but the current project files are out of sync with your global profile/delivery, rasen will show a warning and suggest rasen update.
Pressing Ctrl+C also cancels the flow cleanly (no stack trace) and exits with code 130.
In the workflow checklist, [x] means the workflow is selected in global config. Press A to select all, or press it again when everything is selected to clear all. To apply those selections to project files, run rasen update (or choose Apply changes to this project now? when prompted inside a project).
Interactive examples:
# Delivery-only update
rasen profile
# choose: Change delivery only
# choose delivery: Skills only
# Workflows-only update
rasen profile
# choose: Change workflows only
# toggle workflows in the checklist, then confirm
Utility Commands
rasen feedback
Submit feedback about rasen. Creates a GitHub issue.
rasen feedback <message> [options]
Arguments:
| Argument | Required | Description |
|---|---|---|
message |
Yes | Feedback message |
Options:
| Option | Description |
|---|---|
--body <text> |
Detailed description |
Requirements: GitHub CLI (gh) must be installed and authenticated.
Example:
rasen feedback "Add support for custom artifact types" \
--body "I'd like to define my own artifact types beyond the built-in ones."
rasen completion
Manage shell completions for the rasen CLI.
rasen completion <subcommand> [shell]
Subcommands:
| Subcommand | Description |
|---|---|
generate [shell] |
Output completion script to stdout |
install [shell] |
Install completion for your shell |
uninstall [shell] |
Remove installed completions |
Supported shells: bash, zsh, fish, powershell
Examples:
# Install completions (auto-detects shell)
rasen completion install
# Install for specific shell
rasen completion install zsh
# Generate script for manual installation
rasen completion generate bash > ~/.bash_completion.d/rasen
# Uninstall
rasen completion uninstall
Exit Codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Error (validation failure, missing files, etc.) |
Environment Variables
| Variable | Description |
|---|---|
RASEN_TELEMETRY |
Set to 0 to disable telemetry |
DO_NOT_TRACK |
Set to 1 to disable telemetry (standard DNT signal) |
RASEN_CONCURRENCY |
Default concurrency for bulk validation (default: 6) |
RASEN_LANG |
Temporarily override the saved CLI language (en, ja, or zh-cn) |
RASEN_SESSION_CONTEXT |
Absolute path to the session context file the supervisor wrote (set for you inside a supervised session; see Session runtime context) |
EDITOR or VISUAL |
Editor for rasen config edit |
NO_COLOR |
Disable color output when set |
Related Documentation
- Commands - AI slash commands (
/rasen-propose,/rasen-apply-change, etc.) - Workflows - Common patterns and when to use each command
- Customization - Create custom schemas and templates
- Getting Started - First-time setup guide