# Slate Host Integration — Research & Design Doc

**Date:** 2026-04-02
**Branch:** garrytan/slate-agent-support
**Status:** Research complete, blocked on host config refactor
**Supersedes:** None

## What is Slate

Slate is a proprietary coding agent CLI from Random Labs.
Install: `npm i -g @randomlabs/slate` or `brew install anthropic/tap/slate`.
License: Proprietary. 85MB compiled Bun binary (arm64/x64, darwin/linux/windows).
npm package: `@randomlabs/slate@1.0.25` (thin 8.8KB launcher + platform-specific optional deps).

Multi-model: dynamically selects Claude Sonnet/Opus/Haiku, plus other models.
Built for "swarm orchestration" with extended multi-hour sessions.

## Slate is an OpenCode fork

**Confirmed via binary strings analysis** of the 85MB Mach-O arm64 binary:

- Internal name: `name: "opencode"` (literal string in binary)
- All `OPENCODE_*` env vars present alongside `SLATE_*` equivalents
- Shares OpenCode's tool/skill architecture, LSP integration, terminal management
- Own branding, API endpoints (`api.randomlabs.ai`, `agent-worker-prod.randomlabs.workers.dev`), and config paths

This matters for integration: OpenCode conventions mostly apply, but Slate adds
its own paths and env vars on top.

## Skill Discovery (confirmed from binary)

Slate scans ALL four directory families for skills. Error messages in binary confirm:

```
"failed .slate directory scan for skills"
"failed .claude directory scan for skills"
"failed .agents directory scan for skills"
"failed .opencode directory scan for skills"
```

**Discovery paths (priority order from Slate docs):**

1. `.slate/skills/<name>/SKILL.md` — project-level, highest priority
2. `~/.slate/skills/<name>/SKILL.md` — global
3. `.opencode/skills/`, `.agents/skills/` — compatibility fallback
4. `.claude/skills/` — Claude Code compatibility fallback (lowest)
5. Custom paths via `slate.json`

**Glob patterns:** `**/SKILL.md` and `{skill,skills}/**/SKILL.md`

**Commands:** Same directory structure but under `commands/` subdirs:
`/.slate/commands/`, `/.claude/commands/`, `/.agents/commands/`, `/.opencode/commands/`

**Skill frontmatter:** YAML with `name` and `description` fields (per Slate docs).
No documented length limits on either field.

## Project Instructions

Slate reads both `CLAUDE.md` and `AGENTS.md` for project instructions.
Both literal strings confirmed in binary. No changes needed to existing
gstack projects... CLAUDE.md works as-is.

## Configuration

**Config file:** `slate.json` / `slate.jsonc` (NOT opencode.json)

**Config options (from Slate docs):**
- `privacy` (boolean) — disables telemetry/logging
- Permissions: `allow`, `ask`, `deny` per tool (`read`, `edit`, `bash`, `grep`, `webfetch`, `websearch`, `*`)
- Model slots: `models.main`, `models.subagent`, `models.search`, `models.reasoning`
- MCP servers: local or remote with custom commands and headers
- Custom commands: `/commands` with templates

The setup script should NOT create `slate.json`. Users configure their own permissions.

## CLI Flags (Headless Mode)

```
--stream-json / --output-format stream-json  — JSONL output, "compatible with Anthropic Claude Code SDK"
--dangerously-skip-permissions               — bypass all permission checks (CI/automation)
--input-format stream-json                   — programmatic input
-q                                           — non-interactive mode
-w <dir>                                     — workspace directory
--output-format text                         — plain text output (default)
```

**Stream-JSON format:** Slate docs claim "compatible with Anthropic Claude Code SDK."
Not yet empirically verified. Given OpenCode heritage, likely matches Claude Code's
NDJSON event schema (type: "assistant", type: "tool_result", type: "result").

**Need to verify:** Run `slate -q "hello" --stream-json` with valid credits and
capture actual JSONL events before building the session runner parser.

## Environment Variables (from binary strings)

### Slate-specific
```
SLATE_API_KEY                              — API key
SLATE_AGENT                                — agent selection
SLATE_AUTO_SHARE                           — auto-share setting
SLATE_CLIENT                               — client identifier
SLATE_CONFIG                               — config override
SLATE_CONFIG_CONTENT                       — inline config
SLATE_CONFIG_DIR                           — config directory
SLATE_DANGEROUSLY_SKIP_PERMISSIONS         — bypass permissions
SLATE_DIR                                  — data directory override
SLATE_DISABLE_AUTOUPDATE                   — disable auto-update
SLATE_DISABLE_CLAUDE_CODE                  — disable Claude Code integration entirely
SLATE_DISABLE_CLAUDE_CODE_PROMPT           — disable Claude Code prompt loading
SLATE_DISABLE_CLAUDE_CODE_SKILLS           — disable .claude/skills/ loading
SLATE_DISABLE_DEFAULT_PLUGINS              — disable default plugins
SLATE_DISABLE_FILETIME_CHECK               — disable file time checks
SLATE_DISABLE_LSP_DOWNLOAD                 — disable LSP auto-download
SLATE_DISABLE_MODELS_FETCH                 — disable models config fetch
SLATE_DISABLE_PROJECT_CONFIG               — disable project-level config
SLATE_DISABLE_PRUNE                        — disable session pruning
SLATE_DISABLE_TERMINAL_TITLE               — disable terminal title updates
SLATE_ENABLE_EXA                           — enable Exa search
SLATE_ENABLE_EXPERIMENTAL_MODELS           — enable experimental models
SLATE_EXPERIMENTAL                         — enable experimental features
SLATE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS — bash timeout override
SLATE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT  — disable copy on select
SLATE_EXPERIMENTAL_DISABLE_FILEWATCHER     — disable file watcher
SLATE_EXPERIMENTAL_EXA                     — Exa search (alt flag)
SLATE_EXPERIMENTAL_FILEWATCHER             — enable file watcher
SLATE_EXPERIMENTAL_ICON_DISCOVERY          — icon discovery
SLATE_EXPERIMENTAL_LSP_TOOL               — LSP tool
SLATE_EXPERIMENTAL_LSP_TY                 — LSP type checking
SLATE_EXPERIMENTAL_MARKDOWN               — markdown mode
SLATE_EXPERIMENTAL_OUTPUT_TOKEN_MAX       — output token limit
SLATE_EXPERIMENTAL_OXFMT                  — oxfmt integration
SLATE_EXPERIMENTAL_PLAN_MODE              — plan mode
SLATE_FAKE_VCS                            — fake VCS for testing
SLATE_GIT_BASH_PATH                       — git bash path (Windows)
SLATE_MODELS_URL                          — models config URL
SLATE_PERMISSION                          — permission override
SLATE_SERVER_PASSWORD                     — server auth
SLATE_SERVER_USERNAME                     — server auth
SLATE_TELEMETRY_DISABLED                  — disable telemetry
SLATE_TEST_HOME                           — test home directory
SLATE_TOKEN_DIR                           — token storage directory
```

### OpenCode legacy (still functional)
```
OPENCODE_DISABLE_LSP_DOWNLOAD
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER
OPENCODE_EXPERIMENTAL_FILEWATCHER
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY
OPENCODE_EXPERIMENTAL_LSP_TY
OPENCODE_EXPERIMENTAL_OXFMT
OPENCODE_FAKE_VCS
OPENCODE_GIT_BASH_PATH
OPENCODE_LIBC
OPENCODE_TERMINAL
```

### Critical env vars for gstack integration

**`SLATE_DISABLE_CLAUDE_CODE_SKILLS`** — When set, `.claude/skills/` loading is disabled.
This makes publishing to `.slate/skills/` load-bearing, not just an optimization.
Without native `.slate/` publishing, gstack skills vanish when this flag is set.

**`SLATE_TEST_HOME`** — Useful for E2E tests. Can redirect Slate's home directory
to an isolated temp directory, similar to how Codex tests use a temp HOME.

**`SLATE_DANGEROUSLY_SKIP_PERMISSIONS`** — Required for headless E2E tests.

## Model References (from binary)

```
anthropic/claude-sonnet-4.6
anthropic/claude-opus-4
anthropic/claude-haiku-4
anthropic/slate              — Slate's own model routing
openai/gpt-5.3-codex
google/nano-banana
randomlabs/fast-default-alpha
```

## API Endpoints (from binary)

```
https://api.randomlabs.ai                          — main API
https://api.randomlabs.ai/exaproxy                 — Exa search proxy
https://agent-worker-prod.randomlabs.workers.dev   — production worker
https://agent-worker-dev.randomlabs.workers.dev    — dev worker
https://dashboard.randomlabs.ai                    — dashboard
https://docs.randomlabs.ai                         — documentation
https://randomlabs.ai/config.json                  — remote config
```

Brew tap: `anthropic/tap/slate` (notable: under Anthropic's tap, not Random Labs)

## npm Package Structure

```
@randomlabs/slate (8.8 kB, thin launcher)
├── bin/slate           — Node.js launcher (finds platform binary in node_modules)
├── bin/slate1          — Bun launcher (same logic, import.meta.filename)
├── postinstall.mjs     — Verifies platform binary exists, symlinks if needed
└── package.json        — Declares optionalDependencies for all platforms

Platform packages (85MB each):
├── @randomlabs/slate-darwin-arm64
├── @randomlabs/slate-darwin-x64
├── @randomlabs/slate-linux-arm64
├── @randomlabs/slate-linux-x64
├── @randomlabs/slate-linux-x64-musl
├── @randomlabs/slate-linux-arm64-musl
├── @randomlabs/slate-linux-x64-baseline
├── @randomlabs/slate-linux-x64-baseline-musl
├── @randomlabs/slate-darwin-x64-baseline
├── @randomlabs/slate-windows-x64
└── @randomlabs/slate-windows-x64-baseline
```

Binary override: `SLATE_BIN_PATH` env var skips all discovery, runs the specified binary directly.

## What Already Works Today

gstack skills already work in Slate via the `.claude/skills/` fallback path.
No changes needed for basic functionality. Users who install gstack for Claude Code
and also use Slate will find their skills available in both agents.

## What First-Class Support Adds

1. **Reliability** — `.slate/skills/` is Slate's highest-priority path. Immune to
   `SLATE_DISABLE_CLAUDE_CODE_SKILLS`.
2. **Optimized frontmatter** — Strip Claude-specific fields (allowed-tools, hooks, version)
   that Slate doesn't use. Keep only `name` and `description`.
3. **Setup script** — Auto-detect `slate` binary, install skills to `~/.slate/skills/`.
4. **E2E tests** — Verify skills work when invoked by Slate directly.

## Blocked On: Host Config Refactor

Codex's outside voice review identified that adding Slate as a 4th host (after Claude,
Codex, Factory) is "host explosion for a path alias." The current architecture has:

- Hard-coded host names in `type Host = 'claude' | 'codex' | 'factory'`
- Per-host branches in `transformFrontmatter()` with near-duplicate logic
- Per-host config in `EXTERNAL_HOST_CONFIG` with similar patterns
- Per-host functions in the setup script (`create_codex_runtime_root`, `link_codex_skill_dirs`)
- Host names duplicated in `bin/gstack-platform-detect` (since deleted — host
  detection now lives in the `hosts/` registry, exported to shell via
  `scripts/host-config-export.ts`), `bin/gstack-uninstall`, `bin/dev-setup`

Adding Slate means copying all of these patterns again. A refactor to make hosts
data-driven (config objects instead of if/else branches) would make Slate integration
trivial AND make future hosts (any new OpenCode fork, any new agent) zero-effort.

### Missing from the plan (identified by Codex)

- `lib/worktree.ts` only copies `.agents/`, not `.slate/` — E2E tests in worktrees won't
  have Slate skills
- `bin/gstack-uninstall` doesn't know about `.slate/`
- `bin/dev-setup` doesn't wire `.slate/` for contributor dev mode
- `bin/gstack-platform-detect` doesn't detect Slate (obsolete: the bin was
  deleted; host detection is now the `hosts/` registry via
  `scripts/host-config-export.ts` — `hosts/slate.ts` is where Slate lives)
- E2E tests should set `SLATE_DISABLE_CLAUDE_CODE_SKILLS=1` to prove `.slate/` path
  actually works (not just falling back to `.claude/`)

## Session Runner Design (for later)

When the JSONL format is verified, the session runner should:

- Spawn: `slate -q "<prompt>" --stream-json --dangerously-skip-permissions -w <dir>`
- Parse: Claude Code SDK-compatible NDJSON (assumed, needs verification)
- Skills: Install to `.slate/skills/` in test fixture (not `.claude/skills/`)
- Auth: Use `SLATE_API_KEY` or existing `~/.slate/` credentials
- Isolation: Use `SLATE_TEST_HOME` for home directory isolation
- Timeout: 300s default (same as Codex)

```typescript
export interface SlateResult {
  output: string;
  toolCalls: string[];
  tokens: number;
  exitCode: number;
  durationMs: number;
  sessionId: string | null;
  rawLines: string[];
  stderr: string;
}
```

## Docs References

- Slate docs: https://docs.randomlabs.ai
- Quickstart: https://docs.randomlabs.ai/en/getting-started/quickstart
- Skills: https://docs.randomlabs.ai/en/using-slate/skills
- Configuration: https://docs.randomlabs.ai/en/using-slate/configuration
- Hotkeys: https://docs.randomlabs.ai/en/using-slate/hotkey_reference
