Roscoe keeps two parallel directories on disk. A global one lives in your
home folder, for personal use. A project one lives inside each repo, for
things your team should share. Workflows and roscoe.yaml both live in this
two-tier structure. The database is global-only. Know which directory holds
what, and which one wins on collision, and you'll never wonder "why did my
workflow disappear?"
This page is the conceptual overview. Once you've read it, the configuration deep-dives (workflow sources, roscoe.yaml) make a lot more sense.
The two directories#
| Directory | What lives there | Scope |
|---|---|---|
~/.roscoe/ |
Personal config, your scratch workflows, the DB | Per-user, cross-project |
<repo>/.roscoe/ |
Team-shared workflows, repo-specific overrides | Per-repo, committed (or not) |
Both are scaffolded by roscoe init when run inside a git repo; outside a
repo, only ~/.roscoe/ is created. How the repo root is detected depends
on which surface you're calling from:
- CLI (
roscoe run,list,history, …): always derives from the current working directory's git root. Theroscoe serveUI's "active project" doesn't influence one-shot CLI commands. - MCP (Model Context Protocol): each tool call passes either a
cwdargument, resolved to that call's git root, orglobal: trueto use the global store. See MCP project scoping. - Web UI /
roscoe serve: uses the registry's active project, set via the project switcher. Add a project with the switcher's + button (orroscoe register). Remove one with its row's trash icon (orroscoe unregister). That only drops the registry entry: no files are deleted. Remove the active project and the workspace falls back to Global, a first-class workspace with no active project. It scopes the UI to the global store, and it's where cwd-less runs from the Claude desktop app live. ROSCOE_REPO_HOMEis a last-resort fallback for environments without a cwd-derived root (e.g. running CLI commands outside any git repo).
The home dir can be moved with ROSCOE_HOME.
What goes where#
Workflows#
| File | Source |
|---|---|
~/.roscoe/workflows/*.yaml |
Global |
<repo>/.roscoe/workflows/*.yaml |
Project |
Both are scanned on every list, lookup, and run. Repo wins on
collision: when the same workflow id exists in both, the project copy is
what runs. The global one is listed as [shadowed by repo]. Override
per-run with roscoe run <id> --source global|repo.
roscoe init seeds the demo workflows that ship with Roscoe, demo-1-the-panel
through demo-9-check-one-claim, into Global only. Project starts empty
so the team can fill it deliberately.
Full details: workflow sources.
Configuration (roscoe.yaml)#
| File | Role |
|---|---|
~/.roscoe/roscoe.yaml |
Your personal defaults |
<repo>/.roscoe/roscoe.yaml |
Team-pinned overrides committed to the repo |
These layer: project values override global on a per-section basis. The
models.available list replaces wholesale, so a repo can pin its team to a
specific model set. ports and defaults shallow-merge field by field.
Use the project file to pin things the whole team should share:
- a fixed default model
- a fixed port for
roscoe serve, so everyone agrees on the URL - shared
maxRetries/timeoutMsdefaults
Leave personal preferences in the global file.
Full schema: roscoe.yaml.
The database#
| File | Source |
|---|---|
~/.roscoe/roscoe.db |
Global only, always |
Run history is per-user, not per-repo. The workflow_runs.workflow_source
column records which source ('global' or 'repo') a given run executed
against. That way you can audit history across repos without losing track
of where each run came from.
If the DB ever gets corrupted, see database recovery.
When to put a workflow in project vs global#
Reach for the project directory when:
- The workflow is part of your team's shared process for this codebase (for example, an approval flow gated on shared rules).
- You want the same behaviour for every contributor, including CI.
- The workflow references repo-specific paths or scripts.
Reach for the global directory when:
- It's a personal experiment or scratch workflow.
- You want it available everywhere on your machine, regardless of which repo you're in.
- It's a starter / template you'll copy into a project later.
Move workflows between sources from the web UI's kebab menu
(Move to Project / Move to Global) or via the
move_workflow MCP tool.
Committing the project directory#
<repo>/.roscoe/ is yours to commit: it holds only shareable source (the
database lives in the global home, ~/.roscoe/, never here). roscoe init
leaves your repo's root .gitignore alone. It drops a .roscoe/.gitignore
instead. That file already excludes the regenerated, version-pinned schema
files (roscoe-config.schema.json, workflow.schema.json) and a stray
roscoe.db. So to share workflows with the team, you don't need to edit any
ignore rules. Commit:
.roscoe/workflows/*.workflow.yaml— the workflows you want shared. Anything you'd rather keep personal stays in the global directory..roscoe/roscoe.yaml— project config (models, defaults). Keep machine-specific bits like port overrides in the environment (e.g.ROSCOE_PORT), not here..roscoe/.gitignoreitself — so teammates inherit the same rules.
If you'd rather commit the schema files (e.g. for editor autocomplete on a
fresh clone without running roscoe init), delete their lines from
.roscoe/.gitignore. init won't re-add them.
See also#
- Workflow sources — full resolution
rules,
--sourceoverrides, MCPsourceparameter. - roscoe.yaml — config schema and merge semantics.
- Environment variables —
ROSCOE_HOME,ROSCOE_REPO_HOME.