Skip to content

Add Goose render adapter for hints, skills, MCP extensions, and hook plugins #79

Description

@kelos-bot

🤖 Kelos Strategist Agent @gjkim42

Area: Integration Opportunities

Summary

Add an opt-in Goose render adapter that compiles Kanon's existing source model into Goose's documented file-backed configuration surfaces:

  • instructions.files -> global/project .goosehints for Goose-specific hints, with AGENTS.md interoperability noted but not required for v1
  • skills -> ~/.agents/skills/<name>/... and <project>/.agents/skills/<name>/...
  • mcp.servers -> ~/.config/goose/config.yaml extensions: entries
  • compatible hooks -> a Kanon-managed Goose plugin under ~/.agents/plugins/kanon-hooks/ or <project>/.agents/plugins/kanon-hooks/

This should be a render-first adapter named goose. It should not try to manage Goose model/provider defaults, .gooseignore, prompt templates, memory, recipes, slash commands, subagents, or the enterprise extension allowlist in the first increment; those either overlap existing Kanon issues or require new neutral schema.

Why this is strategically valuable

Goose is a strong fit for Kanon because it is file-backed, MCP-native, local/desktop/CLI oriented, and intentionally used across coding plus broader automation workflows. It also bridges editor integrations through ACP, so managing Goose once can influence Goose CLI, Goose Desktop, and ACP clients such as Zed that launch Goose as an agent server.

This gives Kanon a different kind of growth path than another editor-specific adapter. Instead of only rendering settings for one IDE, Kanon could manage a local open-source agent runtime that already exposes providers, extensions, skills, hooks, sandboxing, recipes, and ACP integration.

Evidence from the current codebase

Kanon already has most primitives needed for a Goose MVP:

  • Config currently has only Instructions, Skills, MCP, Hooks, and Metadata; there is no Goose target or agent-specific extension section (internal/core/types.go:11-18).
  • The only agent constants are codex, claude, and all (internal/core/types.go:5-8).
  • adaptersFor dispatches only Codex and Claude; adding Goose requires a new adapter registration and the opt-in/default behavior guard already discussed in adapter-roadmap issues (internal/core/render.go:57-65).
  • MCPServer already stores the fields Goose extensions need for stdio and remote MCP servers: command, args, env, env_vars, url, timeout fields, and enabled tool filters (internal/core/types.go:66-83).
  • Hook already stores event, matcher, type, command, and timeout, which maps closely to Goose's Open Plugins hook JSON shape (internal/core/types.go:92-101).
  • renderSkills already copies complete SKILL.md directories into an adapter-provided target root (internal/core/render.go:88-180). Goose's recommended global/project skill roots are the same .agents/skills paths Kanon already uses for Codex, which makes the adapter low-lift but requires duplicate-path guardrails.
  • Current merge strategies cover Codex TOML and Claude JSON only (internal/core/merge.go:12-24). Goose config.yaml will need a YAML merge strategy that updates only extensions entries Kanon owns and preserves provider/model/general settings.
  • PlanFiles does not de-duplicate rendered files by destination path (internal/core/apply.go:42-84). Because Goose and Codex both use .agents/skills, a Goose adapter should either add a same-content de-duplication step or fail clearly on same-path different-content renders.

External signal

Official Goose docs make this a concrete, file-backed target:

Existing issue overlap check

I reviewed the open generated-by-kelos issues and searched existing issues for goose, ACP, and Zed.

No existing issue proposes a Goose render target. This is intentionally distinct from:

Proposed target and mapping

Use goose as the canonical target:

targets: [goose]

MVP mapping:

Kanon source Goose user destination Goose project destination Notes
instructions.files ~/.config/goose/.goosehints <project>/.goosehints Avoid duplicate root AGENTS.md writes when Codex is also enabled. A later option can prefer AGENTS.md for project scope.
skills[] ~/.agents/skills/<name>/... <project>/.agents/skills/<name>/... Reuse renderSkills; add duplicate-path handling because Codex uses the same root.
mcp.servers ~/.config/goose/config.yaml extensions: User scope only for MVP unless project-level Goose extension config is confirmed Merge only managed extensions entries; preserve provider/model/general keys.
compatible hooks[] ~/.agents/plugins/kanon-hooks/hooks/hooks.json plus plugin.json <project>/.agents/plugins/kanon-hooks/hooks/hooks.json plus plugin.json Requires a plugin wrapper because Goose discovers hooks from plugins.

Example source:

version: 1

instructions:
  files:
    - instructions/shared.md
    - instructions/go-workflow.md

skills:
  - name: code-review
    targets: [goose, codex, claude]
  - name: release-triage
    targets: [goose]

mcp:
  servers:
    github:
      command: npx
      args: ["-y", "@modelcontextprotocol/server-github"]
      env_vars: [GITHUB_TOKEN]
      enabled_tools: [search_repositories, get_issue, create_issue]
      startup_timeout_sec: 300
      targets: [goose, codex]

    docs:
      type: http
      url: https://docs-mcp.example.com/mcp
      targets: [goose]

hooks:
  - name: format-after-edit
    targets: [goose]
    event: AfterFileEdit
    matcher: "\\.(go|md)$"
    type: command
    command: "${PLUGIN_ROOT}/scripts/format.sh"
    timeout: 30

Expected Goose user-scoped render tree:

~/.config/goose/.goosehints
~/.config/goose/config.yaml
~/.agents/skills/code-review/SKILL.md
~/.agents/skills/release-triage/SKILL.md
~/.agents/plugins/kanon-hooks/plugin.json
~/.agents/plugins/kanon-hooks/hooks/hooks.json

Rendered config.yaml extension shape:

extensions:
  github:
    name: github
    type: stdio
    cmd: npx
    args:
      - -y
      - "@modelcontextprotocol/server-github"
    enabled: true
    env_keys:
      - GITHUB_TOKEN
    available_tools:
      - search_repositories
      - get_issue
      - create_issue
    timeout: 300
  docs:
    name: docs
    type: http
    url: https://docs-mcp.example.com/mcp
    enabled: true

Guardrails

  • Keep Goose opt-in. Do not expand current default --agent all behavior to include Goose until the adapter opt-in semantics from Grow beyond Codex + Claude: prioritized render-adapter roadmap (Gemini CLI, Cursor, opencode, Copilot CLI) #7/Render adapters: Cursor, opencode, Copilot CLI (+ Gemini import parity) #23 are settled.
  • Do not overwrite all of ~/.config/goose/config.yaml. Add FileMergeGooseConfig that merges only extensions entries named in Kanon and preserves provider, model, telemetry, search paths, slash commands, and other Goose-managed settings.
  • Do not write secrets into Goose config. Map Kanon env_vars to Goose env_keys where possible; only map env to envs when the Kanon source already deliberately contains an env ref or non-secret literal. Preserve existing secret warnings from import/validation.
  • For remote MCP servers, confirm Goose's current type: http / url shape before implementation. If uncertainty remains, render stdio extensions first and fail clearly for remote Goose targets.
  • For enabled_tools, map to Goose available_tools. For disabled_tools, either fail for Goose targets or wait for a confirmed native deny-list equivalent; do not silently drop it.
  • For timeout fields, map startup_timeout_sec first. If both startup/tool timeouts differ, fail or warn because Goose appears to have a single extension timeout field.
  • For hooks, generate a dedicated plugin manifest and hooks/hooks.json. Accept Goose-supported events only: SessionStart, SessionEnd, Stop, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, BeforeReadFile, AfterFileEdit, BeforeShellExecution, AfterShellExecution.
  • For hooks, accept only type: command or empty type. Fail on args and async because Goose's hook schema runs shell commands and does not document those fields.
  • Add duplicate render-path handling before or inside the adapter because Goose and Codex intentionally share .agents/skills. Identical content can be de-duplicated; different content should fail with both agent names and the path.
  • Defer import until render behavior settles. Goose config contains provider credentials/settings, secrets, and runtime permission decisions that need careful import policy.

Suggested MVP scope

  1. Add AgentGoose and target validation for goose, gated by the adapter opt-in mechanism.
  2. Implement gooseAdapter.Render for hints, skills, stdio MCP extensions, and compatible hook plugins.
  3. Add FileMergeGooseConfig to preserve unmanaged config.yaml keys while updating Kanon-owned extensions entries.
  4. Add same-path duplicate detection/de-duplication for rendered files, at minimum for identical .agents/skills output shared with Codex.
  5. Add tests for user/project hints, skill rendering, duplicate skill output with Codex, MCP extension YAML merge preservation, unsupported MCP fields, hook plugin generation, unsupported hook fields, target filtering, and explicit --agent goose behavior.
  6. Run make verify.

Use cases unlocked

  • Open-source local agent fleets: teams using Goose CLI/Desktop can share the same Kanon repo that already feeds Codex and Claude.
  • MCP-heavy workflows: Kanon can centrally roll out GitHub, Sentry, internal docs, and database MCP extensions into Goose without hand-editing YAML on every machine.
  • ACP/editor workflows: users running Goose through Zed or other ACP clients inherit the same Goose extensions and skills.
  • Mixed-agent teams: shared .agents/skills becomes an intentional cross-agent artifact rather than an accidental Codex-only side effect.
  • Corporate Goose deployments: later, Kanon can generate extension allowlist artifacts from the same MCP source so approved MCP commands and rendered extensions stay aligned.

Backward compatibility

  • Existing kanon.yaml files are unchanged.
  • goose is opt-in and should not affect current Codex/Claude output.
  • Existing Codex use of .agents/skills remains unchanged.
  • Goose config merging preserves unmanaged provider/model/settings fields.
  • Import support is deferred, so existing Goose files are not read or normalized in the first pass.

Non-goals

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions