A thin command-line front end for the workload-router. Spawns the harness CLI
(claude, codex, opencode) configured by a selected persona from the
project-local layer, configured source directories, or the small internal
built-in system catalog.
agentworkforce create [--save-in-directory=<target>] [--save-default] [--install-in-repo] [--no-launch-metadata]
agentworkforce agent [--install-in-repo] [--no-launch-metadata] <persona>[@<tier>]
agentworkforce list [flags]
agentworkforce show <persona>[@<tier>]
agentworkforce install [flags] <pkg|path>
agentworkforce sources <list|add|remove>
agentworkforce harness check
agentworkforce destroy <persona-or-agent-id> [--workspace <id>] [--cloud-url <url>] [--no-prompt]
agentworkforce --version
create— openspersona-maker@bestfor creating a new persona. It resolves a target persona directory with the same source-cascade constructs used bylist,show, andagent, then passesTARGET_DIRandCREATE_MODEinto the persona as prompt-visible inputs.agent— drops you into an interactive session with the harness.list— print the persona catalog as a table (or JSON). See## Listbelow for every flag.show— print the resolved spec for one persona.install— copy persona JSON files from an npm or local persona pack into the current project's fixed cwd source directory.sources— list, add, or remove persona source directories.harness check— probe which harnesses (claude,codex,opencode) are installed. See## Harness checkbelow.destroy— tear down a deployed cloud agent: cancels all relaycron schedules and marks the agent as destroyed. Accepts either a persona JSON path (slug resolved via the workspace's agents index) or a literal agent UUID. Exits0on success,2when the agent is unknown or already destroyed,1for any other failure.--version— print the installed package version.
Install the top-level agentworkforce package. It provides the
agentworkforce bin and depends on this internal CLI package.
From npm:
npm i -g agentworkforceFrom the repo checkout:
corepack pnpm -r build
corepack pnpm --filter agentworkforce link --globalagentworkforce agent <persona>[@<tier>]
<persona>— matches, in order:- A cwd-local id (files in
<cwd>/.agentworkforce/workforce/personas/*.json) - A configured persona source dir, in order. The default is
~/.agentworkforce/workforce/personas/*.json. - An internal library persona — by intent first, then by id. The built-in
library is system-only; optional personas such as
code-reviewercome from installed persona packs.
- A cwd-local id (files in
<tier>—best|best-value|minimum. Defaults tobest-value.
Unknown persona prints the full catalog with each entry's origin.
agentworkforce create [--save-in-directory=<target>] [--save-default] [--install-in-repo] [--no-launch-metadata]
create is the persona-authoring entry point. It runs persona-maker@best
through the same interactive launch path as agent, including skill
materialization, sandbox mount behavior, env/MCP resolution, and harness argv
translation. The only extra work create does is resolve a target and pass it as
persona inputs:
| Input | Meaning |
|---|---|
TARGET_DIR |
Absolute directory where the new <id>.json persona file should be written. |
CREATE_MODE |
local writes only JSON; built-in is reserved for internal/system personas and also updates catalog/routing/test/docs integration. |
Targets:
| Target | Resolves to | Create mode |
|---|---|---|
cwd |
<cwd>/.agentworkforce/workforce/personas |
local |
user |
~/.agentworkforce/workforce/personas (or AGENT_WORKFORCE_CONFIG_DIR) |
local |
dir:n |
the nth configured persona source from sources list |
local |
library |
<repo>/personas |
built-in |
| path | explicit directory path | local |
Default target resolution:
--save-in-directory=<target>wins.- Else use
defaultCreateTargetfrom~/.agentworkforce/workforce/config.json. - Else use
cwd(<cwd>/.agentworkforce/workforce/personas).
The cwd persona directory is created (mkdir -p) when it does not exist, so a
fresh project can run agentworkforce create without any setup. To author
personas anywhere else, pass --save-in-directory=<target> on the command line
(the value can also be passed as --save-in-directory <target>), or use
--save-default once to persist a different default in the source config.
Examples:
# Create in <cwd>/.agentworkforce/workforce/personas (created if missing)
agentworkforce create
# Force the user persona directory
agentworkforce create --save-in-directory=user
# Create in a configured persona source
agentworkforce create --save-in-directory=dir:1
# Create in an explicit checked-out persona directory and make that the default
agentworkforce create --save-in-directory=../team-personas/personas --save-default
# Create an internal/system built-in persona in this repo's /personas catalog
agentworkforce create --save-in-directory=libraryagentworkforce create
# Interactive code reviewer
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
agentworkforce agent code-reviewer@best-value
# Interactive against a local override
agentworkforce agent my-reviewer@bestagentworkforce install <pkg|path> [--persona <id> ...] [--overwrite]
install is a shadcn-style copy utility for persona JSON. It copies persona
files into the current project's fixed cwd layer:
<cwd>/.agentworkforce/workforce/personas/.
Once copied, files are project-owned. Edit them directly and commit them to git. The CLI does not create an install ledger, lockfile, manifest, update command, uninstall command, diff command, or central AgentWorkforce registry.
Npm package specs are resolved with npm pack, so npm auth, npm config,
private packages, tags, and versions work the same way they do for npm:
agentworkforce install @agentworkforce/personas-core
agentworkforce install @agentworkforce/personas-core@0.8.0 --persona code-reviewer
agentworkforce install @agentrelay/personas
agentworkforce install @agentrelay/personas@1.2.3
agentworkforce install @agentrelay/personas@latestLocal path installs read directly from the directory:
agentworkforce install ./local-personas
agentworkforce install /absolute/path/to/local-personasBy default, every *.json file in the pack's persona directory is copied.
Use repeated --persona <id> flags to install a subset by persona id:
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
agentworkforce install @agentrelay/personas --persona relay-orchestratorIf any requested id is missing, the command exits non-zero before copying anything.
Target filenames are flattened into the cwd persona directory:
package/personas/nested/code-reviewer.json
-> .agentworkforce/workforce/personas/code-reviewer.json
If the target file already exists, the installer reports a conflict, skips
that file, and exits non-zero. Non-conflicting files from the same run may
still be copied. Pass --overwrite to replace existing files unconditionally:
agentworkforce install @agentrelay/personas --overwriteFilename collisions across packages use the same rule. The install layer does not namespace files by package; avoid shipping two pack files with the same basename if they are expected to be installed together.
A pack can contain multiple personas:
@acme/personas
├── package.json
└── personas/
├── reviewer.json
└── release-runner.json
package.json may declare the persona directory:
{
"name": "@acme/personas",
"version": "1.2.3",
"files": ["personas"],
"keywords": ["agentworkforce-personas"],
"agentworkforce": {
"personas": "personas"
}
}Resolution rules:
- Read
package.json.agentworkforce.personasif present. - Otherwise use a top-level
personas/directory. - Recursively copy every
*.jsonfile from that directory, flattening to<cwd>/.agentworkforce/workforce/personas/<basename>.json.
Local path installs use the same metadata rules.
Use install when this project should receive editable copies:
agentworkforce install @acme/personas
git add .agentworkforce/workforce/personasUse sources add when you want the cascade to point at a live directory
without copying:
agentworkforce sources add ~/src/acme-personas/personasBoth feed the same cascade. install writes to the fixed cwd layer, while
sources changes the configured source directories in
~/.agentworkforce/workforce/config.json.
mkdir -p acme-personas/personas
cd acme-personas
npm init -y
npm pkg set name=@acme/personas version=1.0.0
npm pkg set 'files[0]=personas' 'keywords[0]=agentworkforce-personas'
npm pkg set agentworkforce.personas=personas
$EDITOR personas/reviewer.json
npm publish --access publicThen install it in a project:
cd ../my-project
agentworkforce install @acme/personas --persona reviewer
agentworkforce list --filter-tag review
agentworkforce agent reviewer@best-valueagentworkforce list [flags]
Prints the merged persona catalog — everything the agent subcommand would
accept as a selector — including cascade source (library, user, cwd,
or dir:<n>),
harness, model, description, and tier ("rating"). One row per persona-tier
combination; by default only the recommended tier per intent is shown
(as declared in
packages/workload-router/routing-profiles/default.json).
| Flag | Default | Effect |
|---|---|---|
--all |
off | Show every tier of every persona. Alias: --no-recommended. |
--recommended |
on | Only show the recommended tier per intent. Implicit default; mostly useful for undoing --all earlier in a wrapper script. |
--filter-rating <tier> |
— | Restrict to a single tier (best | best-value | minimum). Implicitly turns off the recommended-only default, so filtering by best shows every persona's best row even when that's not the recommended tier. |
--filter-harness <harness> |
— | Restrict to a single harness (claude | codex | opencode). Composable with --filter-rating and --all. |
--no-display-description |
off | Hide the DESCRIPTION column. --display-description re-enables it. |
--json |
off | Emit { "personas": [...] } with one object per row. Same field set as the table, useful for scripting. |
-h, --help |
— | Print a one-line usage string and exit. |
Invalid values for --filter-rating / --filter-harness fail fast and list
the allowed values.
# Default: one row per persona, recommended tier only
agentworkforce list
# See every tier
agentworkforce list --all
# Only the top tier across the catalog — independent of recommendations
agentworkforce list --filter-rating best
# All claude-harness personas (any tier)
agentworkforce list --all --filter-harness claude
# Compact table for a narrow terminal
agentworkforce list --no-display-description
# Machine-readable
agentworkforce list --json --filter-harness claudeagentworkforce sources list [--json]
agentworkforce sources add <dir> [--position <n>]
agentworkforce sources remove <dir|config-position>
The fixed project source is always first:
<cwd>/.agentworkforce/workforce/personas/*.json.
After that, the CLI reads an ordered list of configurable persona directories
from ~/.agentworkforce/workforce/config.json. If no config exists, the list
defaults to ~/.agentworkforce/workforce/personas. This makes installed
personas work as plain JSON files in the default user location, or from any
checked-out repo you add as a source directory.
The same config may also carry defaultCreateTarget, used by agentworkforce create
to override its default of cwd:
{
"personaDirs": ["~/src/company-personas/personas"],
"defaultCreateTarget": "dir:1"
}Valid defaultCreateTarget values are the same values accepted by
create --save-in-directory=<target>: cwd, user, dir:n, library, or an
explicit path. When this key is unset, agentworkforce create writes to
<cwd>/.agentworkforce/workforce/personas (creating it if missing). Use
agentworkforce create --save-in-directory=<target> --save-default to write
the override without editing JSON by hand.
sources add appends by default. --position <n> inserts at the 1-based
position among configurable directories, so --position 1 gives that directory
the highest priority after the fixed cwd source. sources remove accepts either
that configurable position or an exact path.
Examples:
# Show the full source cascade, including fixed cwd and library entries
agentworkforce sources list
# Install personas from another checkout, below the default user persona dir
agentworkforce sources add ~/src/company-personas/personas
# Give a checked-out persona repo priority over the default user dir
agentworkforce sources add ~/src/company-personas/personas --position 1
# Remove the first configurable persona source
agentworkforce sources remove 1agentworkforce harness check
Probes your PATH for each supported harness binary (claude, codex,
opencode) and prints a table with status (ok / missing), resolved
version, and the resolved path (or the error, for missing ones). Exit
code is always 0 — this command is diagnostic, not a gate.
A persona is a JSON object describing what harness runs, which model, with what system prompt, what skills to install, what env vars to inject, and which MCP servers to attach. Full library shape:
The repo built-in catalog is intentionally system-only and currently contains
persona-maker. Optional reusable personas are installed from packs such as
@agentworkforce/personas-core or @agentrelay/personas.
Local persona files layer on top of the library. Resolution precedence (highest wins):
<cwd>/.agentworkforce/workforce/personas/*.json— cwd- Configurable persona source dirs, in order. Default:
~/.agentworkforce/workforce/personas/*.json— user - Internal built-in system personas in
/personas/— library
Local files are partial overlays: only the fields you set replace the inherited value. Everything else cascades through from below.
Set AGENT_WORKFORCE_HOME to move the ~/.agentworkforce/workforce config
root. The legacy AGENT_WORKFORCE_CONFIG_DIR env var is still honored as a
direct override for the default user persona directory.
Install the core pack first so code-reviewer exists in a lower layer:
agentworkforce install @agentworkforce/personas-core --persona code-reviewer~/.agentworkforce/workforce/personas/my-reviewer.json:
{
"id": "my-reviewer",
"extends": "code-reviewer",
"systemPrompt": "Review this repository's API compatibility and migration risks. Lead with blockers."
}That inherits every field from the installed code-reviewer persona, then
layers your local prompt on top.
If your file's id matches a persona in a lower layer and you omit extends,
the loader implicitly inherits from that same-id base:
<cwd>/.agentworkforce/workforce/personas/code-reviewer.json:
{
"id": "code-reviewer",
"systemPrompt": "Review with this repository's compatibility checklist first."
}Resolving code-reviewer now hits this cwd override first; it inherits the
rest from the installed lower-layer code-reviewer.
A cwd file can extend a user or configured-dir file, which can extend an installed pack persona or the internal library:
~/.agentworkforce/workforce/personas/reviewer-base.json:
{ "id": "reviewer-base", "extends": "code-reviewer", "systemPrompt": "Review with org-wide API compatibility rules." }
<cwd>/.agentworkforce/workforce/personas/reviewer-prod.json:
{ "id": "reviewer-prod", "extends": "reviewer-base", "systemPrompt": "Add this service's migration checklist." }
Resolving reviewer-prod:
- Start with installed
code-reviewer(tiers, skills, prompt, ...) - Layer user
reviewer-baseon top - Layer cwd
reviewer-prodon top
extends is resolved strictly against lower layers — cwd extends configured
dirs or library, configured dirs extend lower configured dirs or library, and
library has no extends.
{
"id": "my-agent", // required
"extends": "code-reviewer", // optional; implicit same-id if omitted
"description": "…", // replaces base description
"skills": [ … ], // replaces entire skills array
"inputs": { // prompt-visible runtime inputs; union by key
"TARGET_DIR": {
"description": "Where to write output",
"default": "./out",
"env": "MY_AGENT_TARGET_DIR"
}
},
"env": { … }, // union, local wins per key
"mcpServers": { … }, // union by server name, local wins per key
"mount": { // Relayfile file scope; pattern arrays append
"ignoredPatterns": ["…"],
"readonlyPatterns": ["…"]
},
"permissions": { // allow/deny union (dedup), mode replaces
"allow": ["…"], "deny": ["…"], "mode": "default"
},
"systemPrompt": "…", // replaces systemPrompt on every inherited tier
"tiers": { // per-tier partial override
"best": { "model": "claude-sonnet-4-6" }
// other tiers inherited untouched
}
}Per-tier partial merge. If you set tiers.best.model, only model
changes — systemPrompt, harness, and harnessSettings still come from the
base. Use top-level systemPrompt if you want to replace the prompt
uniformly across all tiers.
To define a standalone local persona that does not inherit from a lower layer,
include intent and a complete tiers object for best, best-value, and
minimum. This is the shape persona packs usually ship before install
copies them into the cwd layer.
Personas are plain JSON files. Use agentworkforce create when you want the
persona-maker to draft one with the repo's conventions, or write the JSON by
hand when the shape is simple.
Local personas live in the source cascade and do not require built-in catalog integration:
agentworkforce create --save-in-directory=user
agentworkforce create --save-in-directory=cwd
agentworkforce create --save-in-directory=dir:1The persona maker receives TARGET_DIR and CREATE_MODE=local, so it
should write only:
<target-dir>/<id>.json
Minimal local persona:
{
"id": "my-reviewer",
"extends": "code-reviewer",
"description": "Reviews this project with local conventions.",
"systemPrompt": "Focus on this repository's API compatibility rules and summarize only blocking issues."
}Built-in personas live in the repo's /personas catalog, are reserved for
required internal/system surface, and require workload-router integration:
agentworkforce create --save-in-directory=libraryThe persona maker receives CREATE_MODE=built-in, so it should write
personas/<id>.json, update the internal catalog registration/routing/tests/docs
as needed, regenerate src/generated/personas.ts, and run the repo check.
Optional generic or domain personas should be published through persona packs
instead.
Use this when you are not extending an existing persona:
{
"id": "release-checker",
"intent": "release-check",
"tags": ["release", "review"],
"description": "Checks release readiness and reports blockers.",
"skills": [
{
"id": "prpm/npm-trusted-publishing",
"source": "https://prpm.dev/packages/@prpm/npm-trusted-publishing",
"description": "Trusted publishing and provenance setup guidance."
}
],
"inputs": {
"PACKAGE_NAME": {
"description": "Package to inspect.",
"env": "PACKAGE_NAME",
"default": "."
}
},
"env": {
"NPM_TOKEN": "$NPM_TOKEN"
},
"permissions": {
"allow": ["Bash(npm *)"],
"deny": ["Bash(npm publish *)"],
"mode": "default"
},
"mount": {
"readonlyPatterns": ["*"]
},
"tiers": {
"best": {
"harness": "codex",
"model": "openai-codex/gpt-5.3-codex",
"systemPrompt": "Check release readiness for $PACKAGE_NAME. Produce blockers first, then evidence.",
"harnessSettings": { "reasoning": "high", "timeoutSeconds": 1200 }
},
"best-value": {
"harness": "opencode",
"model": "opencode/gpt-5-nano",
"systemPrompt": "Check release readiness for $PACKAGE_NAME. Produce blockers first, then evidence.",
"harnessSettings": { "reasoning": "medium", "timeoutSeconds": 900 }
},
"minimum": {
"harness": "opencode",
"model": "opencode/minimax-m2.5-free",
"systemPrompt": "Check release readiness for $PACKAGE_NAME. Produce only blocking issues and exact evidence.",
"harnessSettings": { "reasoning": "low", "timeoutSeconds": 600 }
}
}
}inputs declare non-secret values that a launcher can pass into a persona at
runtime. They are useful for output paths, package names, modes, and other
prompt-visible context that should not be hard-coded into the persona.
Input names must be env-style uppercase keys: TARGET_DIR, PACKAGE_NAME,
CREATE_MODE. In systemPrompt, use $NAME or ${NAME}; both forms are
replaced before the harness starts.
{
"inputs": {
"TARGET_DIR": {
"description": "Directory where generated files should be written.",
"env": "MY_TARGET_DIR",
"default": "./out"
},
"CREATE_MODE": "local"
},
"tiers": {
"best": {
"systemPrompt": "Write the persona to $TARGET_DIR. Mode: ${CREATE_MODE}."
}
}
}Each input may be either a string shorthand or an object:
| Shape | Meaning |
|---|---|
"NAME": "value" |
Shorthand for { "default": "value" }. |
description |
Human-readable explanation for show, docs, and catalog UIs. |
env |
Env var to read when the caller did not provide an explicit value. Defaults to the input key. |
default |
Literal fallback when no explicit value or env var exists. |
Resolution order is strict:
- Explicit launcher value, such as the values passed by
agentworkforce create. process.env[env], orprocess.env[NAME]whenenvis omitted.default.- If none exist, launch fails before the harness starts.
Resolved inputs are substituted into the system prompt and injected into the
child process env under the input key. They are not secrets: resolved values
can appear in prompts, process env, logs, and agent output. Use env references
instead for API keys and tokens.
Local persona overlays merge inputs by key, so a user or project override can
add one input without replacing all inherited inputs.
Any env value or mcpServers.*.{headers,env,args,url,command} value can be
either a literal string or an env reference. Two forms:
| Form | Meaning | Example |
|---|---|---|
"$VAR" |
Whole-string reference — the entire value is the env var. | "POSTHOG_API_KEY": "$POSTHOG_API_KEY" |
"prefix ${VAR}" |
Braced interpolation — each ${VAR} is replaced in place, anywhere in the string. |
"Authorization": "Bearer ${POSTHOG_API_KEY}" |
-
Both forms resolve against the shell
process.envat spawn time (not load time). -
Unbraced
$VARmid-string stays literal —"prefix-$FOO"is NOT interpolated. Use${FOO}if you want interpolation there. This prevents a stray$in a JSON string from accidentally getting eaten. -
An unset or empty referenced var is a warning, not a fatal error. The CLI drops the referring entry and proceeds. So a persona that references
$POSTHOG_API_KEYin bothenvand anAuthorizationheader will, if the var isn't set, launch without either — and the agent can still authenticate interactively (e.g. via Claude Code's MCP OAuth flow). Example warning:warning: env.POSTHOG_API_KEY dropped (env var POSTHOG_API_KEY is not set). warning: mcpServers.posthog.headers.Authorization dropped (env var POSTHOG_API_KEY is not set). (referenced env vars were not set — proceeding without those values; if the agent relies on them it may need to authenticate interactively, e.g. via OAuth.) -
An MCP server whose structural field (
url,command, or anyarg) references a missing var is dropped entirely, since the server couldn't be launched without that value. The warning names the server and the refs that were unset.
Secrets therefore stay in your shell/keychain, not in files on disk — local persona JSON remains commit-safe as long as you only use references.
Interactive claude and opencode sessions run inside a Relayfile mount by
default. File visibility and writability are controlled by the persona's
mount block plus project-level dotfiles:
{
"mount": {
"ignoredPatterns": ["secrets/**", ".env*"],
"readonlyPatterns": [
"*",
"!docs/",
"!docs/**"
]
}
}ignoredPatternsare omitted from the mount entirely.readonlyPatternsare copied into the mount but edits do not sync back.- Patterns use gitignore semantics, so later
!negations can reopen paths. - Persona patterns append to inherited persona patterns. At launch, the CLI
also merges project-root
.agentignore,.agentreadonly,.<personaId>.agentignore, and.<personaId>.agentreadonly.
A persona can declare which tool calls the harness should auto-approve, block,
or gate via a permission mode. Skip the approval prompts for trusted tools
(e.g. a persona's own MCP server); keep them on for anything you want to
eyeball. File visibility and writability are not defined here; use Relayfile
mount rules (.agentignore / .agentreadonly) for that.
{
"permissions": {
"allow": ["mcp__posthog", "Bash(git *)"], // auto-approve
"deny": ["Bash(rm -rf *)"], // always block
"mode": "default" // default | acceptEdits | bypassPermissions | plan
}
}- Tool patterns are passed through verbatim; use the harness's native
grammar. For Claude Code:
Bash(<pattern>),mcp__<server>(all tools from that server),mcp__<server>__<tool>(specific tool). - Harness support today: only
claudeis wired forpermissions(flags:--allowedTools,--disallowedTools,--permission-mode). codex and opencode emit a warning and fall back to their defaults whenpermissionsis set. - Cascade merge:
allowanddenyare unions across layers (deduped on merge);modeis replaced by the topmost layer that sets it. So the library can declare the minimum-viable allow list, a user or configured persona source can layer shared denies, and cwd can add per-project patterns — they all compose.
Codex-backed tiers can set Codex launch policy inside harnessSettings:
{
"tiers": {
"best": {
"harness": "codex",
"model": "openai-codex/gpt-5.3-codex",
"systemPrompt": "…",
"harnessSettings": {
"reasoning": "high",
"timeoutSeconds": 1200,
"sandboxMode": "workspace-write",
"approvalPolicy": "on-request",
"workspaceWriteNetworkAccess": true,
"webSearch": true
}
}
}
}sandboxMode maps to Codex --sandbox (read-only, workspace-write, or
danger-full-access), approvalPolicy maps to --ask-for-approval,
workspaceWriteNetworkAccess maps to
-c sandbox_workspace_write.network_access=<bool>, and webSearch maps to
--search. Prefer workspace-write plus workspaceWriteNetworkAccess for
package or registry discovery so filesystem writes stay sandboxed.
If an installed lower-layer persona declares broad permissions, a local override can add narrower tool patterns for project workflows:
{
"id": "my-analytics",
"extends": "analytics-reader",
"permissions": {
"allow": [
"mcp__posthog__projects-get",
"mcp__posthog__insights-list",
"mcp__posthog__events-query"
]
}
}Because allow is a union, any broad allow pattern from the base persona would
still be in the merged list. If you need to shrink an allow list, create a
standalone local persona or update the lower-layer pack persona; overlays only
append.
The mcpServers block mirrors Claude Code's --mcp-config JSON shape
verbatim. Three transport types:
// Remote HTTP / streamable-http
{ "type": "http", "url": "https://…", "headers": { "Authorization": "Bearer ${TOKEN}" } }
// Remote SSE (deprecated by most servers but still supported)
{ "type": "sse", "url": "https://…", "headers": { … } }
// Stdio — long-running local MCP server
{ "type": "stdio", "command": "npx", "args": ["-y", "…"], "env": { "API_KEY": "$API_KEY" } }| Harness | Interactive MCP | One-shot MCP |
|---|---|---|
| claude | yes (via --mcp-config + --strict-mcp-config) |
not yet — SDK workflow path doesn't thread MCP |
| codex | yes (via --config mcp_servers.<name>...) |
not yet — SDK workflow path doesn't thread MCP |
| opencode | not yet — warns and proceeds without MCP | not yet |
For a persona that needs MCP today, pick claude or codex as the harness
for that tier.
For the claude harness the CLI always spawns with --strict-mcp-config,
paired with an explicit --mcp-config payload (the persona's mcpServers, or
{"mcpServers":{}} if none). That means only the servers declared on the
persona are loaded — your user-level ~/.claude.json MCPs and any
project-level MCP sources are ignored inside the session. This keeps each
persona session self-contained and prevents cross-contamination with the
agents you normally run. If you need one of your personal MCPs inside a
persona session, add it to the persona's mcpServers block.
agentworkforce agent [--install-in-repo] [--no-launch-metadata] <persona>[@<tier>]By default, claude and opencode sessions run inside a sandbox mount — see
Sandbox mount below. --install-in-repo opts out.
- Resolves the persona, walks the cascade, resolves
$VARrefs. - Stages skills outside the repo by default (claude interactive only —
see Skill staging below). For codex / opencode, or when
--install-in-repois passed, falls back to the legacy repo-relative install path (.claude/skills/,.agents/skills/,.skills/). - Runs skill install (
prpm install …) if the persona declares any skills, using the computed target (stage dir or repo). - Execs the harness binary with stdio inherited:
claude:claude --model <model> --append-system-prompt <prompt> --mcp-config '<json>' --strict-mcp-config [--plugin-dir <stage>]. The--plugin-dirflag is appended when the session uses out-of-repo staging so Claude Code loads exactly the staged skills — and nothing the repo happens to carry. Both MCP flags are always passed so the session only sees the persona's declared MCP servers (see MCP isolation above).codex:codex -m <model>with the system prompt as the initial positional[PROMPT]. (codex has no--system-promptflag today.)opencode:opencode --model <model>with the system prompt as the initial argument.
- Runs the skill cleanup command on exit, regardless of exit status. In
stage-dir mode this is a single
rm -rf <stage-dir>. - Records launch metadata for the session and refreshes harness session logs while the child runs, then once more after exit. The harness is still launched directly.
- Propagates the harness's exit code.
Signals (SIGINT, SIGTERM) are forwarded to the child.
Persona launches record metadata by default when the installed backend supports
launcher metadata. AgentWorkforce records:
agentworkforce=1, persona=<id>, personaTier=<tier>,
personaVersion=<sha256>, and personaSource=<cwd|user|dir:n|library>.
personaVersion is the SHA-256 of the fully resolved persona spec after
cascade/extends merge and before prompt input substitution.
Opt out for one launch:
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
agentworkforce agent --no-launch-metadata code-reviewer@bestOpt out through the environment:
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
AGENTWORKFORCE_LAUNCH_METADATA=0 agentworkforce agent code-reviewer@bestBy default, interactive claude sessions stage skills under the user's home
directory instead of the current repo. Nothing gets written to .claude/ in
the working tree, and the session only sees the skills the persona declares
— never whatever skills the repo happens to carry on disk.
Layout:
~/.agentworkforce/workforce/
└── sessions/<personaId>-<timestamp>-<rand>/
└── claude/
└── plugin/ ← passed as --plugin-dir
├── .claude-plugin/plugin.json ← generated scaffold
├── skills → .claude/skills ← relative symlink
└── .claude/skills/<name>/SKILL.md ← prpm install output
- Stage dir —
~/.agentworkforce/workforce/sessions/<id>/claude/plugin/.<id>is<personaId>-<base36-timestamp>-<hex-random>so parallel sessions never collide. - Plugin wrapper — the CLI writes a minimal
.claude-plugin/plugin.json({"name":"agent-workforce-session","version":"0.0.0",…}) and a relative symlinkskills → .claude/skillsso Claude Code's plugin layout (skills/<name>/SKILL.md) resolves to prpm's actual output (.claude/skills/<name>/SKILL.md) without moving any files. - prpm install — still runs
npx -y prpm install <ref> --as claude, but insidecd <stage-dir>so the harness-conventional.claude/skills/lands in the stage dir, not the repo. - Cleanup — on exit, two cleanup scopes run. The workload-router removes
<stage-dir>(e.g..../sessions/<id>/claude/plugin/) via its generatedrm -rfcommand. The CLI additionally removes the enclosing session root (.../sessions/<id>/) so the mount dir and any empty parents don't accumulate under~/.agentworkforce/workforce/sessions/. The provider lockfile (prpm.lock) is inside the stage dir and goes with it — no repeat-run resolution cache today. Restart cost is one prpm install per session.
Opt-out — --install-in-repo:
Pass --install-in-repo to fall back to the legacy behavior (skills land in
the repo's .claude/skills/ directory, cleaned on exit):
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
agentworkforce agent --install-in-repo code-reviewer@bestUseful when you want to inspect the installed skills on disk, or when the
stage dir conflicts with something else (network filesystem, read-only
$HOME, etc.).
Caveats for V1:
- Claude harness only. codex and opencode continue to install into their
conventional repo-relative directories. The SDK throws if
installRootis passed with a non-claude harness. - No cache layer yet. Every interactive session runs a fresh prpm install
into a new stage dir. A
~/.agentworkforce/workforce/cache/content-addressed cache is planned but not wired up.
By default, claude and opencode interactive sessions run inside a
@relayfile/local-mount
mount that hides repo-level harness configuration from the session, applies
the persona mount block plus Relayfile .agentignore / .agentreadonly
rules, and routes skill-install writes into the sandbox — so the model sees
persona context + user-level context, and only the project files the mount
exposes. Codex sessions never mount (no harness-side support).
--install-in-repo opts out and runs against the real cwd.
The CLI reads these files from the project root before creating the mount:
| File | Effect |
|---|---|
.agentignore |
Hide matching files for every persona. |
.agentreadonly |
Copy matching files into the mount as read-only and skip syncing their edits back. |
.<personaId>.agentignore |
Hide matching files only for that persona. |
.<personaId>.agentreadonly |
Make matching files read-only only for that persona. |
What's hidden (gitignore semantics, at any depth):
For claude:
| Pattern | Rationale |
|---|---|
CLAUDE.md |
Repo-level project memory |
CLAUDE.local.md |
Developer-local project memory |
.claude |
Repo Claude Code config dir (settings, agents, skills, commands) |
.mcp.json |
Repo-declared MCP servers |
For opencode (skill-install pollution that would otherwise leak back to the repo):
| Pattern | Rationale |
|---|---|
.agents, .claude/skills, .factory/skills, .kiro/skills, skills |
skill.sh universal install root + per-harness symlink farms |
.opencode, .skills |
prpm --as <harness> output roots |
prpm.lock, skills-lock.json |
provider lockfiles |
What's preserved:
- User-level context under
~/.claude/—CLAUDE.md, skills, etc. still load. The mount scrubs the project, not the user. To exclude user-level context too, launch under a scratch$HOME. - Persona skills. For claude, the
--plugin-dirpassed to the harness resolves to an absolute path outside the mount, so staged skills from~/.agentworkforce/workforce/sessions/<id>/claude/plugin/load normally. For opencode, the install runs inside the mount so the writes land in the sandbox. - Keychain auth. The mount does not pass
--bare; it only hides files. Claude Code's macOS keychain login stays active. - Persona
mcpServers. Still passed via--mcp-config— unaffected by the mount. The repo's.mcp.jsonis hidden regardless. - Git.
.gitis included in the mount (one-way project→mount sync per@relayfile/local-mount0.6+'sincludeGit). Tracked paths matching the hidden patterns are flaggedskip-worktreesogit statusdoesn't report them as deleted, and the patterns are added to.git/info/excludeto suppress untracked-and-hidden files. Mount-side commits/refs are sandboxed and discarded on cleanup —git pushto persist work.
Both the skill install root and the sandbox mount live under a single
session directory. The session id (<personaId>-<base36-timestamp>-<hex>)
is generated once and both paths are derived from it:
~/.agentworkforce/workforce/
└── sessions/<personaId>-<timestamp>-<rand>/
├── claude/
│ └── plugin/ ← passed as --plugin-dir
│ ├── .claude-plugin/plugin.json
│ ├── skills → .claude/skills
│ └── .claude/skills/<name>/SKILL.md
└── mount/ ← session cwd
└── <mirrored project tree, minus the hidden patterns>
@relayfile/local-mount handles mount creation, process spawn,
SIGINT/SIGTERM forwarding, write syncback, and cleanup on exit. The
agentworkforce CLI just wires the paths and passes the persona's argv.
# Interactive persona session with the repo's CLAUDE.md, .claude/, and
# .mcp.json hidden — session sees the persona's staged skills plus your
# user-level ~/.claude/CLAUDE.md, nothing else from this repo.
agentworkforce install @agentworkforce/personas-core --persona code-reviewer
agentworkforce agent code-reviewer@bestOn exit: mount is synced back to the real repo, then torn down; skill
stage dir is cleaned up by the existing rm -rf cleanup command.
A persona's three tiers can use different harnesses.
If a persona uses MCP, use claude or codex tiers.
opencode still does not inject persona mcpServers at spawn time.
-
Unknown persona "X".— The CLI prints the full catalog. If your local file should be listed, check for a warning on the preceding line — parse errors and badextendsreferences are reported but non-fatal. -
warning: <field> dropped (env var X is not set)— Informational. The CLI skipped that value and is launching without it. Export the variable if you want the agent to have it up-front; otherwise the harness may handle auth interactively (e.g. Claude Code's MCP OAuth flow). -
Failed to spawn "claude": binary not found on PATH.— Install the harness CLI (claude,codex, oropencode) and ensure it's on your PATH. -
warning: persona declares mcpServers but the opencode harness is not yet wired …— Switch that tier'sharnesstoclaudeorcodex, or drop the MCP requirement. -
extends cycle detected through …— A local persona extends itself transitively. Break the chain or point one link at the library directly. -
Local file silently missing from the list — Scroll up for a
warning: [layer] file.json: …line. Common causes: invalid JSON,idmissing, orextendspointing at something that isn't in a lower layer.
{ "id": "posthog", "intent": "posthog", "description": "…", "skills": [], "env": { "POSTHOG_API_KEY": "$POSTHOG_API_KEY" }, "mcpServers": { "posthog": { "type": "http", "url": "https://mcp.posthog.com/mcp", "headers": { "Authorization": "Bearer ${POSTHOG_API_KEY}" } } }, "tiers": { "best": { "harness": "claude", "model": "claude-opus-4-6", "systemPrompt": "…", "harnessSettings": { "reasoning": "high", "timeoutSeconds": 900 } }, "best-value": { "harness": "claude", "model": "claude-sonnet-4-6", "systemPrompt": "…", "harnessSettings": { "reasoning": "medium", "timeoutSeconds": 600 } }, "minimum": { "harness": "claude", "model": "claude-haiku-4-5-20251001", "systemPrompt": "…", "harnessSettings": { "reasoning": "low", "timeoutSeconds": 300 } } } }