Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
| SDD | `sdd` | Spec-Driven Development workflow (10 phases, including `sdd-onboard`) — the agent handles SDD organically when the task warrants it, or when you ask; you don't need to learn the commands |
| Skills | `skills` | Curated coding skill library |
| Context7 | `context7` | MCP server for live framework/library documentation |
| Headroom | `headroom` | MCP context compression server — compresses verbose tool outputs and LLM responses to stay within context window limits via three tools: `compress`, `retrieve`, and `stats`. Installed via pip (`headroom-ai[all]`). See [headroom docs](headroom.md) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify actual MCP tool names.

The description lists the tools as `compress`, `retrieve`, and `stats`, but the actual MCP tool names exposed by Headroom are headroom_compress, headroom_retrieve, and headroom_stats (as shown in docs/headroom.md). Users referencing this table may try to invoke unprefixed names that won't exist.

- | Headroom | `headroom` | MCP context compression server — compresses verbose tool outputs and LLM responses to stay within context window limits via three tools: `compress`, `retrieve`, and `stats`. Installed via pip (`headroom-ai[all]`). See [headroom docs](headroom.md) |
+ | Headroom | `headroom` | MCP context compression server — compresses verbose tool outputs and LLM responses to stay within context window limits via three tools: `headroom_compress`, `headroom_retrieve`, and `headroom_stats`. Installed via pip (`headroom-ai[all]`). See [headroom docs](headroom.md) |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Headroom | `headroom` | MCP context compression server — compresses verbose tool outputs and LLM responses to stay within context window limits via three tools: `compress`, `retrieve`, and `stats`. Installed via pip (`headroom-ai[all]`). See [headroom docs](headroom.md) |
| Headroom | `headroom` | MCP context compression server — compresses verbose tool outputs and LLM responses to stay within context window limits via three tools: `headroom_compress`, `headroom_retrieve`, and `headroom_stats`. Installed via pip (`headroom-ai[all]`). See [headroom docs](headroom.md) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/components.md` at line 15, The Headroom entry in the components table
lists the wrong MCP tool names, so update the description to match the actual
symbols exposed by Headroom: headroom_compress, headroom_retrieve, and
headroom_stats. Keep the rest of the Headroom summary intact, but replace the
unprefixed tool list in the docs/components.md table so users see the correct
callable names consistent with docs/headroom.md.

| Persona | `persona` | Managed Gentleman/neutral persona injection, or unmanaged custom persona mode |
| Permissions | `permissions` | Security-first defaults and guardrails. Applied to Claude Code and OpenCode (the two adapters with permissions overlay support). Default sensitive-paths deny list: `~/.ssh/*`, `~/.ssh/**/*`, `**/*.pem`, `**/*.key`, `**/.env*`, `~/.credentials/*`, `~/.aws/credentials`, `~/.config/gh/hosts.yml`, `~/Library/Keychains/*`, `**/secrets/*`, `**/*.p12`, `**/*.pfx` |
| GGA | `gga` | Gentleman Guardian Angel — AI provider switcher |
Expand Down Expand Up @@ -80,8 +81,8 @@ For framework-specific skills (React 19, Angular, TypeScript, Tailwind 4, Zod 4,

| Preset | ID | What's Included |
|--------|-----|-----------------|
| Dev Stack + Polish | `full-gentleman` | All components (Engram + SDD + Skills + Context7 + GGA + Permissions + Theme) + all skills |
| Dev Stack | `ecosystem-only` | Core components (Engram + SDD + Skills + Context7 + GGA) + all skills |
| Dev Stack + Polish | `full-gentleman` | All components (Engram + SDD + Skills + Context7 + Headroom + GGA + Permissions + Theme) + all skills |
| Dev Stack | `ecosystem-only` | Core components (Engram + SDD + Skills + Context7 + Headroom + GGA) + all skills |
| Memory Only | `minimal` | Engram + SDD skills only |
| Custom | `custom` | You choose components and skills manually while keeping any existing persona/settings unmanaged |

Expand Down
116 changes: 116 additions & 0 deletions docs/headroom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Headroom — Context Compression MCP Server

← [Back to README](../README.md) · [Components & Presets](components.md)

---

Headroom is an MCP server that compresses verbose content — tool outputs, LLM responses, file contents — to help you stay within context window limits without losing information you might need later.

## The Problem

AI coding agents produce enormous tool outputs: directory listings, file reads, git diffs, search results, linter output, test logs. Each one eats into your context window. When the window fills, the agent starts forgetting earlier instructions, decisions, or code context.

Engram solves the **cross-session** memory problem. Headroom solves the **within-session** context pressure problem. They are complementary.

## How It Works

Headroom exposes three MCP tools that the agent can call on demand:

| Tool | What it does |
|------|-------------|
| `headroom_compress` | Compresses a text block and returns a short reference handle |
| `headroom_retrieve` | Decompresses a reference handle back to full text |
| `headroom_stats` | Reports compression ratios and total savings for the session |

The agent decides when to compress — typically when it notices the context is getting full or when a tool output is unusually large. Compressed content is stored locally; nothing leaves your machine.

## Installation

Headroom is installed automatically by `gentle-ai` when you select it as a component:

### Via TUI

```
gentle-ai install
```

Select `Headroom` in the components screen (included by default in the `full-gentleman` and `ecosystem-only` presets).

### Via CLI

```bash
gentle-ai install --component headroom
```

The installer will:
1. Check if `headroom` is already on your PATH
2. If not, find `pip` or `pip3` and run `pip install "headroom-ai[all]"`
3. Inject MCP configuration into all your configured agents

### Manual Installation

If you prefer to install Headroom yourself:

```bash
pip install "headroom-ai[all]"
```

Then run `gentle-ai install --component headroom` to configure your agents, or `gentle-ai sync` if Headroom is already in your selection.

## What Gets Configured

For each agent you have installed, gentle-ai writes the correct MCP config:

| Agent | Config File | Format |
|-------|-------------|--------|
| Claude Code | `~/.claude/mcp/headroom.json` | Separate file |
| OpenCode / Kilo Code | `opencode.json` `mcp.headroom` | Merge overlay |
| Cursor | `~/.cursor/mcp.json` `mcpServers.headroom` | Merge |
| VS Code Copilot | `Code/User/mcp.json` `servers.headroom` | Merge |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Incomplete file path for VS Code Copilot.

Code/User/mcp.json is not a valid absolute path. Users won't know where to find this. Specify the full platform-dependent path (e.g., ~/Library/Application Support/Code/User/mcp.json on macOS, ~/.config/Code/User/mcp.json on Linux).

- | VS Code Copilot | `Code/User/mcp.json` `servers.headroom` | Merge |
+ | VS Code Copilot | `~/Library/Application Support/Code/User/mcp.json` `servers.headroom` | Merge |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/headroom.md` at line 69, The VS Code Copilot entry in the headroom docs
uses an incomplete relative path for the MCP config, so update the documentation
entry to show the full platform-specific absolute path instead of
Code/User/mcp.json. Adjust the row in the table under the VS Code Copilot
reference so it clearly points users to the correct mcp.json location for macOS
and Linux, using the same servers.headroom reference.

| Windsurf | `~/.codeium/windsurf/mcp_config.json` | Merge |
| Codex | `~/.codex/config.toml` `[mcp_servers.headroom]` | TOML block |
| Gemini CLI | `~/.gemini/settings.json` `mcpServers.headroom` | Merge |
| Antigravity | `~/.gemini/antigravity/mcp_config.json` | Merge |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Antigravity's expected config path
rg -n "antigravity" --glob '*.go' | head -20

Repository: Gentleman-Programming/gentle-ai

Length of output: 169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Files mentioning antigravity =='
rg -n -i "antigravity" . || true

printf '\n%s\n' '== Files mentioning gemini and mcp_config =='
rg -n -i "gemini|mcp_config" docs . || true

printf '\n%s\n' '== docs/headroom.md context =='
cat -n docs/headroom.md | sed -n '60,85p'

Repository: Gentleman-Programming/gentle-ai

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== docs/headroom.md =='
cat -n docs/headroom.md | sed -n '68,78p'

echo
echo '== docs/platforms.md =='
cat -n docs/platforms.md | sed -n '38,52p'

echo
echo '== antigravity support spec =='
cat -n openspec/specs/antigravity-support/spec.md | sed -n '1,50p'

echo
echo '== headroom component design/task references =='
rg -n "antigravity|mcp_config.json|antigravity-cli" openspec/changes/archive/2026-06-27-headroom-mcp-component openspec/changes/antigravity-2.0-compatibility -g '*.md'

Repository: Gentleman-Programming/gentle-ai

Length of output: 10737


Update Antigravity’s MCP path to ~/.gemini/antigravity-cli/mcp_config.json. docs/headroom.md:73 still points at ~/.gemini/antigravity/mcp_config.json, which doesn’t match the supported Antigravity config root.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/headroom.md` at line 73, Update the Antigravity MCP path reference in
docs/headroom.md so it points to the supported config root under
~/.gemini/antigravity-cli/mcp_config.json instead of the old
~/.gemini/antigravity/mcp_config.json value. Make this change in the Antigravity
row of the headroom table, keeping the rest of the documentation entry
unchanged.

| Kimi Code | `~/.kimi/settings.json` | Merge |
| Qwen Code | `~/.qwen/settings.json` | Merge |
| Kiro IDE | `~/.kiro/settings/mcp.json` | Merge |
| OpenClaw | `~/.openclaw/openclaw.json` | Merge |
| Trae | App support dir `mcp.json` | Merge |
| Pi | `.pi/settings.json` | Merge |
| Hermes | `~/.hermes/config.yaml` | YAML block |

All entries point to a local `headroom` process with `args: ["mcp", "serve"]`.

## Uninstalling

```bash
gentle-ai uninstall --component headroom
```

This removes all MCP config entries from your agents. To also remove the pip package:

```bash
pip uninstall headroom-ai
```

## Usage Tips

- Headroom is most useful during long coding sessions where the agent performs many read/search/write operations
- The agent calls `headroom_compress` automatically when it detects large outputs — you don't need to interact with it directly
- Check savings with `headroom_stats` if you're curious how much context you've recovered
- If the agent seems to forget earlier context, try asking: "check your context usage with headroom_stats and compress anything you don't need right now"

## Comparison

| | Engram | Headroom |
|--|--------|----------|
| What | Cross-session persistent memory | Within-session context compression |
| When | Between coding sessions | During a long session |
| How | FTS5 search + save/recall | Compress/decompress large text blocks |
| Analogy | Your project notebook | Vacuum packing bulky items in your current workspace |

## References

- [Headroom GitHub](https://github.com/headroomlabs-ai/headroom)
- [Headroom PyPI](https://pypi.org/project/headroom-ai/)
- [Components & Presets Reference](components.md)
1 change: 1 addition & 0 deletions internal/catalog/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var mvpComponents = []Component{
{ID: model.ComponentTheme, Name: "Theme", Description: "Gentleman Kanagawa theme overlay"},
{ID: model.ComponentClaudeTheme, Name: "Claude Gentleman Theme", Description: "Claude Code Gentleman custom theme"},
{ID: model.ComponentOpenCodeGentleLogo, Name: "OpenCode Gentle Logo", Description: "OpenCode home logo TUI plugin with Braille rose"},
{ID: model.ComponentHeadroom, Name: "Headroom", Description: "AI memory compression and retrieval for tool-use chains"},
}

func MVPComponents() []Component {
Expand Down
2 changes: 1 addition & 1 deletion internal/cli/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type DoctorReport struct {
Checks []CheckResult
}

var knownTools = []string{"gentle-ai", "engram", "gga", "claude", "opencode"}
var knownTools = []string{"gentle-ai", "engram", "gga", "claude", "opencode", "headroom"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't fail doctor for an optional tool.

checkToolBinaries() iterates every knownTools entry unconditionally, and checkOneTool() marks a missing binary as fail. Adding headroom here means doctor will now report a failing installation for users who never enabled the optional Headroom component. Gate this check on selected/installed components, or downgrade it to a non-failing informational check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/doctor.go` at line 41, The doctor check is treating the optional
Headroom binary like a required tool because `knownTools` feeds
`checkToolBinaries()` unconditionally and `checkOneTool()` turns missing
binaries into a failure. Update the logic around `knownTools`,
`checkToolBinaries()`, and `checkOneTool()` so `headroom` is only checked when
that component is selected/installed, or otherwise report it as informational
instead of failing `doctor`.


const (
engramHealthEnvVar = "ENGRAM_BASE_URL"
Expand Down
1 change: 1 addition & 0 deletions internal/cli/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func TestNormalizeInstallFlagsDefaults(t *testing.T) {
model.ComponentContext7,
model.ComponentPermission,
model.ComponentGGA,
model.ComponentHeadroom,
model.ComponentClaudeTheme,
model.ComponentOpenCodeGentleLogo,
model.ComponentPersona,
Expand Down
41 changes: 40 additions & 1 deletion internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/gentleman-programming/gentle-ai/internal/planner"
"github.com/gentleman-programming/gentle-ai/internal/state"
"github.com/gentleman-programming/gentle-ai/internal/system"
"github.com/gentleman-programming/gentle-ai/internal/versions"
"github.com/gentleman-programming/gentle-ai/internal/verify"
)

Expand Down Expand Up @@ -866,11 +867,32 @@ func (s componentApplyStep) Run() error {
return nil
case model.ComponentContext7:
for _, adapter := range adapters {
if _, err := mcp.Inject(s.homeDir, adapter); err != nil {
if _, err := mcp.Inject(s.homeDir, adapter, model.ComponentContext7); err != nil {
return fmt.Errorf("inject context7 for %q: %w", adapter.Agent(), err)
}
}
return nil
case model.ComponentHeadroom:
if _, err := cmdLookPath("headroom"); err != nil {
// headroom not on PATH — install the pip package.
pipCmd := "pip"
if _, pipErr := cmdLookPath(pipCmd); pipErr != nil {
pipCmd = "pip3"
if _, pip3Err := cmdLookPath(pipCmd); pip3Err != nil {
return fmt.Errorf("headroom: %[1]w and pip/pip3 not found — install headroom-ai[all] via pip or add headroom to PATH before running install: %[1]w", err)
}
}
fmt.Fprintf(os.Stderr, "INFO: headroom binary not found on PATH — installing headroom-ai[all]==%s via %s\n", versions.HeadroomMCP, pipCmd)
if installErr := runCommand(pipCmd, "install", fmt.Sprintf("headroom-ai[all]==%s", versions.HeadroomMCP)); installErr != nil {
return fmt.Errorf("install headroom via %s: %w", pipCmd, installErr)
}
}
for _, adapter := range adapters {
if _, err := mcp.Inject(s.homeDir, adapter, model.ComponentHeadroom); err != nil {
return fmt.Errorf("inject headroom for %q: %w", adapter.Agent(), err)
}
}
return nil
case model.ComponentPersona:
for _, adapter := range adapters {
targetDir := componentInjectionDirScoped(s.homeDir, s.workspaceDir, s.scope, adapter)
Expand Down Expand Up @@ -1292,6 +1314,23 @@ func componentPathsWithWorkspaceScoped(homeDir, workspaceDir string, scope Insta
paths = append(paths, p)
}
}
case model.ComponentHeadroom:
switch adapter.MCPStrategy() {
case model.StrategySeparateMCPFiles:
paths = append(paths, adapter.MCPConfigPath(homeDir, "headroom"))
case model.StrategyMergeIntoSettings:
if p := adapter.SettingsPath(homeDir); p != "" {
paths = append(paths, p)
}
case model.StrategyMCPConfigFile:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
case model.StrategyTOMLFile:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
}
Comment on lines +1317 to +1333

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Track the YAML strategy file for Headroom too.

Hermes uses StrategyMergeIntoYAML, and the new Headroom injector writes ~/.hermes/config.yaml. This path list skips that strategy, so install/sync bookkeeping misses the actual user config write. That leaves the change outside the backup/restore set. As per path instructions, internal/components/**/*.go: "Any write to user config must have a backup/restore path."

💡 Suggested fix
 		case model.ComponentHeadroom:
 			switch adapter.MCPStrategy() {
 			case model.StrategySeparateMCPFiles:
 				paths = append(paths, adapter.MCPConfigPath(homeDir, "headroom"))
 			case model.StrategyMergeIntoSettings:
 				if p := adapter.SettingsPath(homeDir); p != "" {
 					paths = append(paths, p)
 				}
 			case model.StrategyMCPConfigFile:
 				if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
 					paths = append(paths, p)
 				}
 			case model.StrategyTOMLFile:
 				if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
 					paths = append(paths, p)
 				}
+			case model.StrategyMergeIntoYAML:
+				if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
+					paths = append(paths, p)
+				}
 			}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case model.ComponentHeadroom:
switch adapter.MCPStrategy() {
case model.StrategySeparateMCPFiles:
paths = append(paths, adapter.MCPConfigPath(homeDir, "headroom"))
case model.StrategyMergeIntoSettings:
if p := adapter.SettingsPath(homeDir); p != "" {
paths = append(paths, p)
}
case model.StrategyMCPConfigFile:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
case model.StrategyTOMLFile:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
}
case model.ComponentHeadroom:
switch adapter.MCPStrategy() {
case model.StrategySeparateMCPFiles:
paths = append(paths, adapter.MCPConfigPath(homeDir, "headroom"))
case model.StrategyMergeIntoSettings:
if p := adapter.SettingsPath(homeDir); p != "" {
paths = append(paths, p)
}
case model.StrategyMCPConfigFile:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
case model.StrategyTOMLFile:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
case model.StrategyMergeIntoYAML:
if p := adapter.MCPConfigPath(homeDir, "headroom"); p != "" {
paths = append(paths, p)
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/run.go` around lines 1317 - 1333, The Headroom path collection
in the ComponentHeadroom branch is missing the YAML-backed config write used by
StrategyMergeIntoYAML, so install/sync bookkeeping does not include the actual
user config file. Update the adapter.MCPStrategy() handling in this section to
also append the YAML config path for Headroom when the adapter uses
StrategyMergeIntoYAML, alongside the existing StrategyMergeIntoSettings,
StrategyMCPConfigFile, and StrategyTOMLFile cases, so backup/restore tracking
covers ~/.hermes/config.yaml.

Source: Path instructions

case model.ComponentPersona:
if selection.Persona == model.PersonaCustom {
break
Expand Down
13 changes: 12 additions & 1 deletion internal/cli/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ func BuildSyncSelection(flags SyncFlags, agentIDs []model.AgentID) model.Selecti
model.ComponentSDD,
model.ComponentEngram,
model.ComponentContext7,
model.ComponentHeadroom,
model.ComponentGGA,
model.ComponentSkills,
}
Expand Down Expand Up @@ -629,14 +630,24 @@ func (s componentSyncStep) Run() error {

case model.ComponentContext7:
for _, adapter := range adapters {
res, err := mcp.Inject(s.homeDir, adapter)
res, err := mcp.Inject(s.homeDir, adapter, model.ComponentContext7)
if err != nil {
return fmt.Errorf("sync context7 for %q: %w", adapter.Agent(), err)
}
s.countChanged(boolToInt(res.Changed), res.Files...)
}
return nil

case model.ComponentHeadroom:
for _, adapter := range adapters {
res, err := mcp.Inject(s.homeDir, adapter, model.ComponentHeadroom)
if err != nil {
return fmt.Errorf("sync headroom for %q: %w", adapter.Agent(), err)
}
s.countChanged(boolToInt(res.Changed), res.Files...)
}
return nil

case model.ComponentSDD:
profileStrategy := sdd.ResolveProfileStrategy(s.homeDir, s.selection.SDDProfileStrategy)

Expand Down
3 changes: 2 additions & 1 deletion internal/cli/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func componentsForPreset(preset model.PresetID, persona model.PersonaID) []model
case model.PresetMinimal:
components = []model.ComponentID{model.ComponentEngram}
case model.PresetEcosystemOnly:
components = []model.ComponentID{model.ComponentEngram, model.ComponentSDD, model.ComponentSkills, model.ComponentContext7, model.ComponentGGA}
components = []model.ComponentID{model.ComponentEngram, model.ComponentSDD, model.ComponentSkills, model.ComponentContext7, model.ComponentGGA, model.ComponentHeadroom}
case model.PresetCustom:
return nil
default: // full-gentleman
Expand All @@ -172,6 +172,7 @@ func componentsForPreset(preset model.PresetID, persona model.PersonaID) []model
model.ComponentContext7,
model.ComponentPermission,
model.ComponentGGA,
model.ComponentHeadroom,
model.ComponentClaudeTheme,
model.ComponentOpenCodeGentleLogo,
}
Expand Down
66 changes: 66 additions & 0 deletions internal/components/mcp/headroom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package mcp

var defaultHeadroomServerJSON = []byte("{\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ]\n}\n")

var defaultHeadroomOverlayJSON = []byte("{\n \"mcpServers\": {\n \"headroom\": {\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ]\n }\n }\n}\n")

// openCodeHeadroomOverlayJSON is the opencode.json overlay using the new MCP format.
// Headroom is a local stdio MCP server.
// The headroom entry must replace atomically so legacy local keys do not survive
// deep merge into OpenCode/KiloCode's strict MCP schema.
var openCodeHeadroomOverlayJSON = []byte("{\n \"mcp\": {\n \"headroom\": {\n \"__replace__\": {\n \"type\": \"local\",\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ],\n \"enabled\": true\n }\n }\n }\n}\n")

// openClawHeadroomOverlayJSON is the OpenClaw openclaw.json overlay.
var openClawHeadroomOverlayJSON = []byte("{\n \"mcp\": {\n \"servers\": {\n \"headroom\": {\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ]\n }\n }\n }\n}\n")

// vsCodeHeadroomOverlayJSON is the VS Code mcp.json overlay using the "servers" key.
var vsCodeHeadroomOverlayJSON = []byte("{\n \"servers\": {\n \"headroom\": {\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ]\n }\n }\n}\n")

// antigravityHeadroomOverlayJSON is the Antigravity mcp_config.json overlay.
// Uses mcpServers key with command/args for local stdio.
var antigravityHeadroomOverlayJSON = []byte("{\n \"mcpServers\": {\n \"headroom\": {\n \"__replace__\": {\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ]\n }\n }\n }\n}\n")

// kimiHeadroomOverlayJSON follows Kimi's documented mcp.json format for local servers.
var kimiHeadroomOverlayJSON = []byte("{\n \"mcpServers\": {\n \"headroom\": {\n \"__replace__\": {\n \"command\": \"headroom\",\n \"args\": [\n \"mcp\",\n \"serve\"\n ]\n }\n }\n }\n}\n")

func DefaultHeadroomServerJSON() []byte {
content := make([]byte, len(defaultHeadroomServerJSON))
copy(content, defaultHeadroomServerJSON)
return content
}

func DefaultHeadroomOverlayJSON() []byte {
content := make([]byte, len(defaultHeadroomOverlayJSON))
copy(content, defaultHeadroomOverlayJSON)
return content
}

func OpenCodeHeadroomOverlayJSON() []byte {
content := make([]byte, len(openCodeHeadroomOverlayJSON))
copy(content, openCodeHeadroomOverlayJSON)
return content
}

func OpenClawHeadroomOverlayJSON() []byte {
content := make([]byte, len(openClawHeadroomOverlayJSON))
copy(content, openClawHeadroomOverlayJSON)
return content
}

func VSCodeHeadroomOverlayJSON() []byte {
content := make([]byte, len(vsCodeHeadroomOverlayJSON))
copy(content, vsCodeHeadroomOverlayJSON)
return content
}

func AntigravityHeadroomOverlayJSON() []byte {
content := make([]byte, len(antigravityHeadroomOverlayJSON))
copy(content, antigravityHeadroomOverlayJSON)
return content
}

func KimiHeadroomOverlayJSON() []byte {
content := make([]byte, len(kimiHeadroomOverlayJSON))
copy(content, kimiHeadroomOverlayJSON)
return content
}
Loading