Workspaces
A workspace is where an agent works. It is the provider’s working directory, the context it starts with, and the default home for files that belong to that workspace. It is not a sandbox: an explicit task or workspace rule may name another destination.
Enso is opinionated about this. The layout below is fixed, Enso creates it, enso workspace audit proves it is still intact, and --fix repairs it. The point is that every
path the agent is told about in AGENTS.md actually exists, and that skills are wired up
rather than assembled by hand.
Quick start
enso workspace create research
enso config set bindings.slack:C0123456789 research
enso workspace audit research
Use your actual Slack channel ID and invite the bot there. The binding applies to the next message without a restart. Telegram and private-message keys are described in Bindings.
Layout
~/.enso/workspaces/<name>/
├── AGENTS.md # purpose, scope, terms, approval rules for this workspace
├── CLAUDE.md # symlink -> AGENTS.md
├── skills/ # skills unique to this workspace
├── knowledge/ # durable reference material the agent should keep
├── drafts/ # generated or editable output
├── uploads/ # chat attachments, one directory per turn
├── .claude/skills # symlink -> ../skills, read by Claude Code and Grok
├── .agents/skills # symlink -> ../skills, read by Codex, Grok, Antigravity, and OpenCode
└── .claude/settings.json, .codex/config.toml, .grok/config.toml, opencode.json
# optional: each CLI's own policy file; required when restricted
| Directory | What belongs there |
|---|---|
knowledge/ |
Things that stay true. Reference notes, extracted facts, research the agent should be able to find next week. |
drafts/ |
Generated or editable work: posts, reports, and scratch analysis. Review before deleting. |
uploads/ |
Chat attachments. Enso writes here; nothing else should. |
skills/ |
Skills only this workspace needs. |
| Policy files | Each CLI’s own project-level permission file, in its own format. Optional, unless the workspace is restricted. |
Names are lowercase kebab-case (meteor, blog-research), at most 64 characters. The name
is the directory name, and there is no other valid location.
The knowledge feature adds shared reference at
~/.enso/knowledge/ (or $ENSO_HOME/knowledge/ for a custom home) and automatically
discovers each workspace’s knowledge/ for the reader. Use workspace knowledge for its
own context and shared knowledge for material used across workspaces. Both support nested
folders and links; see Knowledge for the format and migration guide.
Memory records a conversation’s workspace alongside its
dated exchanges in enso.db. Agents can filter recall to that workspace; these records
do not create files in its knowledge/ directory. Workspace filters organize context,
not access permissions.
AGENTS.md is the file you write; CLAUDE.md is always a symlink to it so both CLI
families read one document. Skills follow the same rule: skills/ is the directory you
write, and the two dot-directories are symlinks to it that the provider CLIs discover. One
source of truth, generated links everywhere else. Keep AGENTS.md short — what the
workspace is for, what ambiguous terms mean, and any rule that must be visible on every
single turn. Detail belongs in knowledge/, referenced by path. See
Customizing.
enso init prepares this layout for default, filling missing instructions and links
without changing existing files. It reports conflicting files, directories, and links
instead of replacing them. This also makes an interrupted initial scaffold safe to rerun;
see Non-interactive setup. The ordinary workspace audit
retains its separately documented repair behavior.
Uploads
Chat attachments download into <workspace>/uploads/<id>/, one directory per turn, for
both Slack and Telegram. The agent is given the local paths in its prompt.
A Slack attachment is named by Enso: an opaque token, then a short readable tail taken from the original name, so nothing a sender chooses decides a path. Slack file metadata is otherwise untrusted too, so a file is fetched only from Slack’s own file-download endpoint and one that names anywhere else is skipped without a request. A Telegram attachment keeps its own filename, sanitized.
These are retained on purpose — an agent may need to come back to a file the next day — and
Enso never deletes them. enso workspace audit reports the total size so you can decide
when to clear it out.
Skills
Skills resolve across three scopes.
| Scope | Location | Who owns it |
|---|---|---|
| Workspace | <workspace>/skills/<name>/SKILL.md |
You, for this workspace only |
| Enso | ~/.enso/skills/<name>/SKILL.md |
You, bundled skills, and installed official optional skills |
| User | Your CLI’s own user directory, see below | You, entirely outside Enso |
Enso ships its core skills into ~/.enso/skills/; see the
bundled skill list. enso init and enso setup write a
missing bundled skill and preserve existing copies. Managed upgrades refresh only bundled
files that match their recorded baseline; edits and tracked deletions are preserved.
enso and the enso- prefix are reserved for what Enso installs, skills and jobs alike,
so the audit warns about a name in that namespace it did not put there. See
Customizing for the owning rules.
Skill names must be unique across the workspace and enso scopes. A workspace does not override an enso-wide skill by reusing its name; the audit reports that as an error, because the provider CLIs disagree about which copy would win (see the table below). A name that collides with one of your user-level skills is a warning.
How they reach the agent
Every provider CLI discovers project skills by walking from its working directory up to the
nearest Git root, following symlinks on the way. The working directory is the workspace and
the Git root is the Enso home (enso init and enso setup prepare it), so two symlinks
in each place put both Enso scopes in reach:
~/.enso/.claude/skills -> ../skills
~/.enso/.agents/skills -> ../skills
~/.enso/workspaces/<name>/.claude/skills -> ../skills
~/.enso/workspaces/<name>/.agents/skills -> ../skills
These links are static. Add or edit a skill in <workspace>/skills/ or ~/.enso/skills/
and the next turn can load it; no Enso restart or link regeneration is needed. The provider
CLI discovers the files when Enso launches it for a turn. enso workspace audit --fix
creates a missing link and repairs one that points elsewhere.
The home-level AGENTS.md reaches the agent the same way: Codex, Grok, Antigravity, and
OpenCode walk up to the Git root for AGENTS.md, and Claude Code walks up for CLAUDE.md.
That is why the home must be a Git root, and the audit checks that it still is.
The runtime discovery reference records version-specific provider behavior. Enso requires unique names instead of relying on different providers to resolve collisions the same way.
Antigravity is the odd one out: its working directory is the folder registered for a
project in ~/.gemini/config/projects/, not the shell’s cwd. Started without a project it
discovers nothing — not the skills above, not any AGENTS.md. So on the first turn Enso
launches agy with --new-project, which registers the workspace, and pins every later
launch to that project id. Nothing is asked of you, and nothing in the workspace changes; see
Configuration.
OpenCode needs no registration and no extra workspace link: it reads the existing
.agents/skills links directly. Its native user-level skills are the skill/ and skills/
directories below each of its configuration roots, and both names below both roots are
scanned by Enso, alongside the Claude-compatible user directories OpenCode also loads:
| Root | Where it is |
|---|---|
| Global configuration | $XDG_CONFIG_HOME/opencode when that variable is set and non-empty, otherwise ~/.config/opencode |
| Additional configuration | The directory OPENCODE_CONFIG_DIR names, when it is set and non-empty |
OPENCODE_CONFIG_DIR adds a root rather than moving the global one, and it does not move
OpenCode’s global AGENTS.md, which stays at the global root. Two roots that resolve to the
same directory are scanned once.
User-level skills are found by each CLI on its own, and so are your user-level
instructions (~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, ~/.gemini/AGENTS.md, and
AGENTS.md at OpenCode’s global root above, so ~/.config/opencode/AGENTS.md by
default). They apply inside Enso too. Enso scans the user-level
skill directories so the audit and the web viewer can show the full skill picture,
but it does not read user-level instruction files or write either kind of file. If another
tool manages them, Enso stays out of its way. In a listing a user-level directory counts as
a skill when it has a SKILL.md; Enso does not validate its contents, and a name present in
more than one of those directories is shown once.
None of the CLIs require the frontmatter name to match the directory name; all of them
use the directory name as the skill’s identity. Enso requires the match anyway, so that
what you see in a listing is the directory you edit.
Auditing
enso workspace audit # the home and every workspace
enso workspace audit meteor # the home and one workspace
enso workspace audit --fix # repair what can be repaired, then report the rest
enso workspace audit --json # machine-readable, for scripts and the viewer
The audit checks, each finding carrying the check id shown:
| Check | Id | Severity | --fix behaviour |
|---|---|---|---|
| Required directories exist inside an existing workspace | directory |
error | Creates them |
CLAUDE.md is a symlink to AGENTS.md |
link |
error | Creates or repoints the link |
.claude/skills and .agents/skills are symlinks to ../skills |
link |
error | Creates or repoints the link |
| The home is a Git root, and no workspace is one | git-root |
error | Runs git init in the home |
The home has AGENTS.md, skills/, and the same three links |
agents-md, directory, link |
error | Creates skills/ and the links |
AGENTS.md exists |
agents-md |
error | Reports only |
AGENTS.md is not still the untouched template |
agents-md |
warning | Reports only |
Every skill directory has a SKILL.md whose name matches the directory |
skill |
error | Reports only |
| No skill name appears in both the workspace and enso scope | skill-collision |
error | Reports only |
| No skill name collides with a user-level skill | skill-collision |
warning | Reports only |
No enso-* skill or job exists that Enso did not install |
reserved |
warning | Reports only |
| The workspace is bound, or named by a job | orphan |
warning | Reports only |
| A restricted workspace passes its chat provider’s policy prerequisite check | policy |
error | Reports only |
Unexpected entries at a workspace’s top level, or under workspaces/ |
unexpected |
warning | Reports only |
uploads/ size |
— | — | Reported as a number |
A passing policy check covers only the chat provider’s launch prerequisites. It
neither tests a denial nor checks providers used only by jobs or beats. Follow the
verification and troubleshooting guide
to check the permissions you intend.
With the knowledge feature, the home directory check
also requires knowledge/; --fix creates it when missing without changing its contents.
Use enso knowledge audit separately to check the pages’ metadata and links.
--fix only ever creates and repairs. It never deletes a file, never edits AGENTS.md, and
never touches anything under knowledge/, drafts/, or uploads/. A real file or
directory sitting where a link belongs is reported and left for you to move aside. Fixes
run first and the report shows what remains, so a second --fix finds nothing to do.
The command exits 1 while any error remains and 0 otherwise; warnings never fail an
audit. An orphan workspace — one nothing is bound to and no job names — is a warning, not
an error. So is an unexpected top-level entry: Enso tells you it is there and leaves it
alone. Files the CLIs themselves drop inside .claude/, such as Claude Code’s
.cc-writes/, are expected and not reported, and neither is .DS_Store.
--fix repairs directories inside an existing workspace; it does not recreate an entire
missing workspace. Use enso workspace create NAME to scaffold one.
A workspace with its own .git is an error that --fix does not touch. The CLIs stop
their walk at the nearest Git root, so a repository inside a workspace hides the home’s
AGENTS.md and skills from the agent. Keep the repository elsewhere.
--json prints one object: ok, the home, and one entry per workspace.
{
"ok": false,
"home": {"path": "/Users/you/.enso", "status": "ok", "findings": [], "fixed": []},
"workspaces": [
{
"name": "meteor",
"path": "/Users/you/.enso/workspaces/meteor",
"status": "error",
"bindings": ["slack:C0123456789"],
"jobs": ["meteor-forum-watch"],
"uploads_bytes": 1048576,
"findings": [
{"check": "directory", "severity": "error", "message": "drafts/ is missing", "fixable": true}
],
"fixed": []
}
]
}
status is the worst severity present (error, warning, or ok), fixed lists what
--fix did on this run, and fixable says whether --fix would repair a finding. The
same report backs enso workspace list’s audit column, the warning enso serve logs at
start, and the web viewer.
enso doctor runs the workspace audit alongside a config check, the provider paths, and
the service status, for one strict answer to “is this machine healthy”. It exits 1 on an
audit error even when serve can continue as described below; see CLI.
Creating and retiring
enso workspace create meteor
Scaffolds the full layout, seeds AGENTS.md from the template, and creates the skill
links. Then bind a conversation to it in config.json; the next message in that
conversation lands in the new workspace, with no restart. See
Configuration.
enso workspace list shows every workspace with the bindings pointing at it, the jobs
naming it, and its audit summary.
To retire one, remove or repoint its bindings and jobs, and repoint any projects that name
it while preserving their tasks. Inspect active and paused beats with
enso heartbeat list --workspace NAME, paging through results if needed; move ongoing beats
to another workspace or explicitly close work that is ending. Pausing a beat keeps its
workspace reference. Keep the directory while any of those dependents still needs it.
Let running turns, jobs, and beats finish before moving files, and remove the retired
workspace’s configuration override when present. Run enso config check before and after
archiving or deleting the directory; deletion needs the user’s authorization. These changes
need no Enso restart. Configuration validation catches missing binding and project
directories, but it does not inspect heartbeat state, so the beat review is a separate step.
Enso does not delete a workspace for you.
When a workspace is malformed
A bound workspace that fails its audit is a warning at service start, not a fatal error. It
is logged, the viewer shows it, and turns still run. A missing drafts/ should not take
your chat bridge down. The same goes for a workspace a job names, and for the home itself:
enso serve logs one line per failing root, naming the errors, and points at
enso workspace audit.
The exception is a bound workspace directory that does not exist at all: config check
treats that as a problem and enso serve refuses to start. If a bound directory disappears
while the service is running, the next read of config.json fails the same check: the
service logs that once and keeps the last valid configuration, a turn bound to the missing
directory fails when its provider cannot start there, and other conversations and jobs
continue. A missing directory named only
by a job is a job validation problem: that job cannot run, while the service and other jobs
can continue.
