Skip to content

Configuration

Three layers, lowest to highest precedence: config files → environment variables → CLI flags. The picker writes saved credentials and last-used provider/model to the user-level config file; you usually don't need to edit it by hand.

Command-line flags

FlagShortDescription
--provider <name>-pProvider name or alias (e.g. anthropic, claude, gemini, or)
--model <name>-mModel to use; accepts <provider>:<model>
--host <url>Override the provider's default host (e.g. remote Ollama, llama.cpp)
--token <token>-tAPI token (overrides env var and saved credential)
--planStart in plan mode
--auto-correctEnable LLM tool-call corrector (off by default)
--bash-dedupEnable Bash near-duplicate detector
--no-read-cacheDisable Read mtime/hash cache
--no-line-count-hintDrop the cloc/scc system-prompt hint
--no-subagentsDisable the Delegate tool
--no-hooksDisable user-supplied lifecycle hook commands
--turn-timeout <sec>Auto-abort agent after N seconds
--no-logDisable session JSONL logging
--strict-logExit non-zero if session logging fails (init or first write)
--no-clearDo not clear the screen on startup
--pickForce the startup picker even when a previous session is on file
--rotate <a:b,c:d>Default rotation chain (comma-separated <provider>:<model>); session-only unless --save-rotate
--save-rotatePersist --rotate to global config
--no-rotateDisable both key rotation and model rotation
--no-rotate-keysDisable key rotation (still rotate provider/model entries)
--no-rotate-modelsDisable model rotation (still rotate keys within the same model)
--debugEnable debug logging to stderr (alias for FACTORY_DEBUG=1)
--help-hShow help
--version-VPrint version and exit

Environment variables

Provider credentials. Set whichever ones you use; the picker also saves them to the config file the first time you enter them. Saved keys live in keys[<provider>] in the config file (each entry has an id, token, createdAt, optional label, and optional extras for things like Workers AI's accountId); legacy flat *Token fields are migrated to a single default-labelled entry on first launch and kept in place for downgrade safety.

See providers.md for the per-provider env-var table.

Behavioural overrides:

VariablePurpose
FACTORY_DEBUG=1Print startup checkpoints (picker, auth, provider, models, validation) to stderr
XDG_CONFIG_HOMEOverride config directory (defaults to ~/.config)
FACTORY_GITHUB_LOGIN_BASE_URLOverride GitHub OAuth host for Copilot auth
FACTORY_GITHUB_API_BASE_URLOverride GitHub API host for Copilot auth
FACTORY_OPENAI_SSE_IDLE_TIMEOUT_MSMax ms between SSE events before an OpenAI stream aborts with a 504. Default 30000. Raise for heavy reasoning.
FACTORY_OPENAI_RESPONSES_STORESet to false/0 to send store: false on Responses-API calls (disables previous_response_id chaining). Default true.

Config files

Configuration is read from, lowest to highest precedence:

  1. User-level~/.config/factory/config.json (or $XDG_CONFIG_HOME/factory/config.json).
  2. Project-level./.factory/config.json. Overrides user-level for keys it sets; other keys still come from user-level.

CLI flags and env vars override both.

The user-level config file is created with mode 0o600 on Unix; the parent directory with 0o700. On Windows, file ACLs are not enforced.

Example config.json:

json
{
  "provider": "anthropic",
  "model": "claude-sonnet-4-6",
  "agent": {
    "experimental": {
      "bashDedup": false,
      "readCache": true,
      "lineCountHint": true,
      "subagents": true,
      "skills": true,
      "hooks": true
    },
    "web": {
      "allowlist": ["docs.anthropic.com"]
    }
  },
  "permissions": {
    "allowAll": ["Read", "Glob", "Grep"],
    "bashRules": [
      { "pattern": "git status*", "decision": "allow" },
      { "pattern": "git push *", "decision": "deny", "note": "Push from a real terminal" }
    ]
  },
  "security": {
    "bashEnv": {
      "allow": ["MY_BUILD_FLAG"],
      "denyPrefixes": ["INTERNAL_"]
    },
    "paths": {
      "deny": ["~/work/private-repo/secrets"]
    }
  },
  "keys": {
    "anthropic": [{ "id": "default", "token": "sk-ant-...", "createdAt": "2025-04-01T00:00:00Z" }]
  }
}

The agent.experimental block toggles the experimental flags below. Toggle at runtime with /exp <name> on|off.

Permissions (permissions)

Tool-level permissions. The user is prompted before any tool call that isn't pre-allowed; this section pre-allows tools and lays down Bash rules so the prompts don't show up.

FieldTypePurpose
allowAllstring[]Tool names that bypass the per-call prompt. Names match the registered tool ids (Read, Glob, Grep, Bash, Edit, Write, WebFetch, plus any MCP tool names). Granting Bash here does not bypass the built-in forbidden patterns or your own bashRules — those still apply.
bashRulesBashRuleConfig[]Ordered list of glob patterns scoped to Bash. First match wins. Each rule has pattern (shell glob), decision (allow | deny | prompt), and an optional note shown in /permissions.

Bash rule evaluation:

  1. Built-in forbidden patterns (e.g. rm -rf /, fork bombs, curl ... | sh, force-push to protected branches) — hard deny, no prompt, cannot be overridden by allowAll or your own rules. The safety net stays on.
  2. Your bashRules — first matching rule wins.
  3. Bash in allowAll — runs without prompt.
  4. Otherwise — prompt.
json
"permissions": {
  "allowAll": ["Read", "Glob", "Grep"],
  "bashRules": [
    { "pattern": "git status*", "decision": "allow" },
    { "pattern": "npm test*",   "decision": "allow" },
    { "pattern": "git push *",  "decision": "deny", "note": "Push from a terminal, not the agent" }
  ]
}

For headless mode, see headless.md — anything not on allowAll is denied (no TTY to prompt) and the run exits with code 3. WebFetch has its own per-domain whitelist on top of this — see web-fetch.md.

Security (security)

Hardening for the Bash subprocess and the file-access tools (Read, Write, Edit). User entries extend the built-in policy — they cannot remove built-in denials.

security.bashEnv

Env-var allowlist for Bash subprocesses. Deny-by-default: only vars whose name is on the allowlist (or matches an allowed prefix) reach the spawned shell, so provider API keys, GitHub tokens, AWS credentials, etc., in your interactive shell don't leak into model-driven commands.

FieldPurpose
allowExact var names to forward in addition to the built-in safe set (PATH, HOME, LANG, SSH_AUTH_SOCK, …).
allowPrefixesPrefix patterns; e.g. "MY_" forwards every MY_* var. Built-ins include LC_, GIT_, XDG_.
denyExact names to scrub even if otherwise allowed. Wins over allow.
denyPrefixesPrefix variant of deny. Wins over allow.

Built-in deny entries (e.g. GIT_ASKPASS, GIT_SSH_COMMAND) cannot be removed.

security.paths

Path policy for Read/Write/Edit. The built-in deny list covers ~/.ssh, ~/.aws, ~/.gnupg, ~/.factory, /etc/shadow, /etc/sudoers, etc. Symlinks are resolved with realpath() before the check, so a symlink pointing at ~/.ssh/id_rsa is denied even if its name looks innocuous.

FieldPurpose
denyAdditional paths to deny. Tilde is expanded. Directory entries cover the directory and everything under it.

Project instructions

Create .factory/INSTRUCTIONS.md in your repository root for project-specific guidelines that get appended verbatim to the system prompt. factory also picks up agent-guidance files from other tools, in this order, concatenated under per-source ## From <path> headers:

  1. .factory/INSTRUCTIONS.md (canonical)
  2. AGENTS.md (cross-tool convention)
  3. CLAUDE.md (Claude Code)
  4. .cursorrules (Cursor)

Total size is capped at ~16 KB; sources past the cap are dropped with a truncation note. Only repo-root files are checked — nested instruction files are ignored.

Experimental flags

FlagDefaultDescription
bashDedupoffTracks recent Bash commands. When the model runs three near-duplicate commands (token-level Jaccard ≥ 0.5), injects a system nudge to prevent spinning.
readCacheonStamps Read operations with mtime + sha256. Repeat reads short-circuit with a reference to prior result, saving tokens.
lineCountHintonAdds system-prompt hint: prefer cloc/scc when available; avoid running multiple line-counting variants.
subagentsonRegisters the Delegate tool for spawning a read-only research subagent.
skillsonLoads markdown skill files from .factory/skills/ and conditionally injects them based on triggers.
hooksonRun user-supplied shell commands at lifecycle events. No-op when no hook commands are configured.

Toggle via CLI (--bash-dedup, --no-read-cache, --no-line-count-hint, --no-subagents, --no-skills, --no-hooks), via the config file under agent.experimental, or at runtime with /exp <name> on|off.

Released under the Apache-2.0 License.