refactor(hosts): defineHost() factory — 10 copy-paste host files become declarations

hosts/*.ts were ten copies of one file: runtimeRoot byte-identical in 9/10,
pathRewrites mechanically derivable from the host name for 7/10, the 11-entry
toolRewrites map byte-identical between openclaw and gbrain, and every asset
change a 10-file edit (cursor and slate had already fallen out of three other
hand-maintained lists). defineHost() owns the defaults; each host file now
declares only what makes it different (slate/cursor: 8 lines each). Shared
constants: CROSS_MODEL_RESOLVERS, GBRAIN_RESOLVERS, EXEC_STYLE_TOOL_REWRITES.
Genuinely-different things stayed explicit: codex/factory $GSTACK_ROOT
rewrites, hermes's tool vocabulary, claude's denylist+prefixable install,
opencode's wider runtimeRoot.

Proof: JSON.stringify(ALL_HOST_CONFIGS) dump-diff before/after EMPTY (and a
runtime walk confirmed no function-valued or undefined-keyed fields, so the
JSON diff is complete); gen:skill-docs --host all zero-diff; host-config +
gen-skill-docs + idempotency suites 485/485. Host files 595 -> 285 lines.
docs/ADDING_A_HOST.md teaches the factory pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-14 21:12:21 -07:00
co-authored by Claude Fable 5
parent 7c309f7817
commit 408ee77cde
12 changed files with 284 additions and 419 deletions
+6 -45
View File
@@ -1,20 +1,13 @@
import type { HostConfig } from '../scripts/host-config';
import { defineHost, CROSS_MODEL_RESOLVERS, EXEC_STYLE_TOOL_REWRITES } from './define-host';
/**
* GBrain host config.
* Compatible with GBrain >= v0.10.0 (doctor --fast --json, search CLI, entity enrichment).
* When updating, check INSTALL_FOR_AGENTS.md in the GBrain repo for breaking changes.
*/
const gbrain: HostConfig = {
const gbrain = defineHost({
name: 'gbrain',
displayName: 'GBrain',
cliCommand: 'gbrain',
cliAliases: [],
globalRoot: '.gbrain/skills/gstack',
localSkillRoot: '.gbrain/skills/gstack',
hostSubdir: '.gbrain',
usesEnvVars: true,
frontmatter: {
mode: 'allowlist',
@@ -28,51 +21,19 @@ const gbrain: HostConfig = {
includeSkills: [],
},
pathRewrites: [
{ from: '~/.claude/skills/gstack', to: '~/.gbrain/skills/gstack' },
{ from: '.claude/skills/gstack', to: '.gbrain/skills/gstack' },
{ from: '.claude/skills', to: '.gbrain/skills' },
extraPathRewrites: [
{ from: 'CLAUDE.md', to: 'AGENTS.md' },
],
toolRewrites: {
'use the Bash tool': 'use the exec tool',
'use the Write tool': 'use the write tool',
'use the Read tool': 'use the read tool',
'use the Edit tool': 'use the edit tool',
'use the Agent tool': 'use sessions_spawn',
'use the Grep tool': 'search for',
'use the Glob tool': 'find files matching',
'the Bash tool': 'the exec tool',
'the Read tool': 'the read tool',
'the Write tool': 'the write tool',
'the Edit tool': 'the edit tool',
},
toolRewrites: { ...EXEC_STYLE_TOOL_REWRITES },
// GBrain gets brain-aware resolvers. All other hosts suppress these.
suppressedResolvers: [
'DESIGN_OUTSIDE_VOICES',
'ADVERSARIAL_STEP',
'CODEX_SECOND_OPINION',
'CODEX_PLAN_REVIEW',
'REVIEW_ARMY',
...CROSS_MODEL_RESOLVERS,
// NOTE: GBRAIN_CONTEXT_LOAD and GBRAIN_SAVE_RESULTS are NOT suppressed here.
// GBrain is the only host that gets brain-first lookup and save-to-brain behavior.
],
runtimeRoot: {
globalSymlinks: ['bin', 'browse/dist', 'browse/bin', 'gstack-upgrade', 'ETHOS.md'],
globalFiles: {
'review': ['checklist.md', 'TODOS-format.md'],
},
},
install: {
prefixable: false,
linkingStrategy: 'symlink-generated',
},
coAuthorTrailer: 'Co-Authored-By: GBrain Agent <agent@gbrain.dev>',
learningsMode: 'basic',
};
});
export default gbrain;