mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-10 14:38:24 +08:00
Merge remote-tracking branch 'origin/main' into garrytan/plan-devex-review
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,5 +1,17 @@
|
||||
# Changelog
|
||||
|
||||
## [0.15.2.1] - 2026-04-02 — Setup Runs Migrations
|
||||
|
||||
`git pull && ./setup` now applies version migrations automatically. Previously, migrations only ran during `/gstack-upgrade`, so users who updated via git pull never got state fixes (like the skill directory restructure from v0.15.1.0). Now `./setup` tracks the last version it ran at and applies any pending migrations on every run.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Setup runs pending migrations.** `./setup` now checks `~/.gstack/.last-setup-version` and runs any migration scripts newer than that version. No more broken skill directories after `git pull`.
|
||||
- **Space-safe migration loop.** Uses `while read` instead of `for` loop to handle paths with spaces correctly.
|
||||
- **Fresh installs skip migrations.** New installs write the version marker without running historical migrations that don't apply to them.
|
||||
- **Future migration guard.** Migrations for versions newer than the current VERSION are skipped, preventing premature execution from development branches.
|
||||
- **Missing VERSION guard.** If the VERSION file is absent, the version marker isn't written, preventing permanent migration poisoning.
|
||||
|
||||
## [0.15.2.0] - 2026-04-02 — Voice-Friendly Skill Triggers
|
||||
|
||||
Say "run a security check" instead of remembering `/cso`. Skills now have voice-friendly trigger phrases that work with AquaVoice, Whisper, and other speech-to-text tools. No more fighting with acronyms that get transcribed wrong ("CSO" -> "CEO" -> wrong skill).
|
||||
|
||||
290
docs/designs/SLATE_HOST.md
Normal file
290
docs/designs/SLATE_HOST.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# 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`, `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
|
||||
- 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
|
||||
29
setup
29
setup
@@ -703,7 +703,34 @@ if [ "$INSTALL_CODEX" -eq 1 ]; then
|
||||
create_agents_sidecar "$SOURCE_GSTACK_DIR"
|
||||
fi
|
||||
|
||||
# 8. First-time welcome + legacy cleanup
|
||||
# 8. Run pending version migrations
|
||||
# Migrations handle state fixes that ./setup alone can't cover (stale config,
|
||||
# orphaned files, directory structure changes). Each migration is idempotent.
|
||||
MIGRATIONS_DIR="$SOURCE_GSTACK_DIR/gstack-upgrade/migrations"
|
||||
CURRENT_VERSION=$(cat "$SOURCE_GSTACK_DIR/VERSION" 2>/dev/null || echo "unknown")
|
||||
LAST_SETUP_VERSION=$(cat "$HOME/.gstack/.last-setup-version" 2>/dev/null || echo "0.0.0.0")
|
||||
if [ -d "$MIGRATIONS_DIR" ] && [ "$CURRENT_VERSION" != "unknown" ] && [ "$LAST_SETUP_VERSION" != "$CURRENT_VERSION" ]; then
|
||||
# Fresh install (no marker file) — skip migrations, just write marker
|
||||
if [ ! -f "$HOME/.gstack/.last-setup-version" ]; then
|
||||
: # fall through to marker write below
|
||||
else
|
||||
find "$MIGRATIONS_DIR" -maxdepth 1 -name 'v*.sh' -type f 2>/dev/null | sort -V | while IFS= read -r migration; do
|
||||
m_ver="$(basename "$migration" .sh | sed 's/^v//')"
|
||||
# Run if migration is newer than last setup version AND not newer than current version
|
||||
if [ "$(printf '%s\n%s' "$LAST_SETUP_VERSION" "$m_ver" | sort -V | head -1)" = "$LAST_SETUP_VERSION" ] && [ "$LAST_SETUP_VERSION" != "$m_ver" ] \
|
||||
&& [ "$(printf '%s\n%s' "$m_ver" "$CURRENT_VERSION" | sort -V | tail -1)" = "$CURRENT_VERSION" ]; then
|
||||
echo " running migration $m_ver..."
|
||||
bash "$migration" || echo " warning: migration $m_ver had errors (non-fatal)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
mkdir -p "$HOME/.gstack"
|
||||
if [ "$CURRENT_VERSION" != "unknown" ]; then
|
||||
echo "$CURRENT_VERSION" > "$HOME/.gstack/.last-setup-version"
|
||||
fi
|
||||
|
||||
# 9. First-time welcome + legacy cleanup
|
||||
if [ ! -f "$HOME/.gstack/.welcome-seen" ]; then
|
||||
echo " Welcome! Run /gstack-upgrade anytime to stay current."
|
||||
touch "$HOME/.gstack/.welcome-seen"
|
||||
|
||||
Reference in New Issue
Block a user