Skip to content

Commit b67b285

Browse files
feat(agents): add Goose AI agent support (#2015)
* feat(integrations): add YamlIntegration base class for YAML recipe agents Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * feat(integrations): add Goose integration subpackage with YAML recipe support Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * feat(integrations): register GooseIntegration in the integration registry Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * feat(agents): add YAML format support to CommandRegistrar for extension/preset commands Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * feat(scripts): add goose agent type to bash update-agent-context script Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * feat(scripts): add goose agent type to PowerShell update-agent-context script Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * docs(agents): add Goose to supported agents table and integration notes Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * docs(readme): add Goose to supported agents table Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * test(integrations): add YamlIntegrationTests base mixin for YAML agent testing Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * test(integrations): add Goose integration tests Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * test(consistency): add Goose consistency checks for config, registrar, and scripts Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * docs(agents): move Goose to YAML Format section in Command File Formats Goose uses YAML recipes, not Markdown. Remove it from the Markdown Format list and add a dedicated YAML Format subsection with a representative recipe example showing prompt: | and {{args}} placeholders. * refactor(agents): delegate render_yaml_command to YamlIntegration Remove the duplicate header dict, yaml.safe_dump call, body indentation, and _human_title logic from CommandRegistrar.render_yaml_command(). Delegate to YamlIntegration._render_yaml() and _human_title() so YAML recipe output stays consistent across the init-time generation and command-registration code paths. * fix(agents): guard alias output path against directory traversal Validate that alias_file resolves within commands_dir before writing. Uses the same resolve().relative_to() pattern already established in extensions.py for ZIP path containment checks. * docs(agents): add Goose to Multi-Agent Support comment list in update-agent-context.sh * fix(agents): add goose to print_summary Usage line in bash context script The print_summary() function listed all supported agents in its Usage output but omitted goose, making it inconsistent with the header docs and the error message in update_specific_agent(). Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(agents): add goose to Print-Summary Usage line in PowerShell context script The Print-Summary function listed all supported agents in its Usage output but omitted goose, making it inconsistent with the ValidateSet and the header documentation. Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(agents): normalize description and title types in YamlIntegration.setup() YAML frontmatter can contain non-string types (null, list, int). Add isinstance checks matching TomlIntegration._extract_description() to ensure Goose recipes always receive valid string fields. Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(agents): validate shared script exists before exec in Goose bash wrapper Add Forge-style check that the shared update-agent-context.sh is present and executable, producing a clear error instead of a cryptic shell exec failure when the shared script is missing. Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(agents): validate shared script exists before invoke in Goose PowerShell wrapper Add Forge-style Test-Path check that the shared update-agent-context.ps1 exists, producing a clear error instead of a cryptic PowerShell failure when the shared script is missing. Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(agents): normalize title and description types in render_yaml_command() Extension/preset frontmatter can contain non-string types. Add isinstance checks matching the normalization in YamlIntegration.setup() so both code paths produce valid Goose recipe fields. Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(agents): replace $ARGUMENTS with arg_placeholder in process_template() Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * test(agents): assert $ARGUMENTS absent from generated YAML recipes Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * test(agents): assert $ARGUMENTS absent from generated TOML commands Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> * fix(tests): rewrite docstring to avoid embedded triple-quote in TOML test Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com> --------- Signed-off-by: Furkan Köykıran <furkankoykiran@gmail.com>
1 parent 52ed84d commit b67b285

File tree

14 files changed

+1228
-142
lines changed

14 files changed

+1228
-142
lines changed

AGENTS.md

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Each AI agent is a self-contained **integration subpackage** under `src/specify_
1717
```
1818
src/specify_cli/integrations/
1919
├── __init__.py # INTEGRATION_REGISTRY + _register_builtins()
20-
├── base.py # IntegrationBase, MarkdownIntegration, TomlIntegration, SkillsIntegration
20+
├── base.py # IntegrationBase, MarkdownIntegration, TomlIntegration, YamlIntegration, SkillsIntegration
2121
├── manifest.py # IntegrationManifest (file tracking)
2222
├── claude/ # Example: SkillsIntegration subclass
2323
│ ├── __init__.py # ClaudeIntegration class
@@ -48,6 +48,7 @@ The registry is the **single source of truth for Python integration metadata**.
4848
|---|---|
4949
| Standard markdown commands (`.md`) | `MarkdownIntegration` |
5050
| TOML-format commands (`.toml`) | `TomlIntegration` |
51+
| YAML recipe files (`.yaml`) | `YamlIntegration` |
5152
| Skill directories (`speckit-<name>/SKILL.md`) | `SkillsIntegration` |
5253
| Fully custom output (companion files, settings merge, etc.) | `IntegrationBase` directly |
5354

@@ -343,16 +344,82 @@ Command content with {SCRIPT} and {{args}} placeholders.
343344
"""
344345
```
345346

347+
### YAML Format
348+
349+
Used by: Goose
350+
351+
```yaml
352+
version: 1.0.0
353+
title: "Command Title"
354+
description: "Command description"
355+
author:
356+
contact: spec-kit
357+
extensions:
358+
- type: builtin
359+
name: developer
360+
activities:
361+
- Spec-Driven Development
362+
prompt: |
363+
Command content with {SCRIPT} and {{args}} placeholders.
364+
```
365+
346366
## Argument Patterns
347367
348368
Different agents use different argument placeholders. The placeholder used in command files is always taken from `registrar_config["args"]` for each integration — check there first when in doubt:
349369

350370
- **Markdown/prompt-based**: `$ARGUMENTS` (default for most markdown agents)
351371
- **TOML-based**: `{{args}}` (e.g., Gemini)
372+
- **YAML-based**: `{{args}}` (e.g., Goose)
352373
- **Custom**: some agents override the default (e.g., Forge uses `{{parameters}}`)
353374
- **Script placeholders**: `{SCRIPT}` (replaced with actual script path)
354375
- **Agent placeholders**: `__AGENT__` (replaced with agent name)
355376

377+
## Special Processing Requirements
378+
379+
Some agents require custom processing beyond the standard template transformations:
380+
381+
### Copilot Integration
382+
383+
GitHub Copilot has unique requirements:
384+
- Commands use `.agent.md` extension (not `.md`)
385+
- Each command gets a companion `.prompt.md` file in `.github/prompts/`
386+
- Installs `.vscode/settings.json` with prompt file recommendations
387+
- Context file lives at `.github/copilot-instructions.md`
388+
389+
Implementation: Extends `IntegrationBase` with custom `setup()` method that:
390+
1. Processes templates with `process_template()`
391+
2. Generates companion `.prompt.md` files
392+
3. Merges VS Code settings
393+
394+
### Forge Integration
395+
396+
Forge has special frontmatter and argument requirements:
397+
- Uses `{{parameters}}` instead of `$ARGUMENTS`
398+
- Strips `handoffs` frontmatter key (Forge-specific collaboration feature)
399+
- Injects `name` field into frontmatter when missing
400+
401+
Implementation: Extends `MarkdownIntegration` with custom `setup()` method that:
402+
1. Inherits standard template processing from `MarkdownIntegration`
403+
2. Adds extra `$ARGUMENTS` → `{{parameters}}` replacement after template processing
404+
3. Applies Forge-specific transformations via `_apply_forge_transformations()`
405+
4. Strips `handoffs` frontmatter key
406+
5. Injects missing `name` fields
407+
6. Ensures the shared `update-agent-context.*` scripts include a `forge` case that maps context updates to `AGENTS.md` and lists `forge` in their usage/help text
408+
409+
### Goose Integration
410+
411+
Goose is a YAML-format agent using Block's recipe system:
412+
- Uses `.goose/recipes/` directory for YAML recipe files
413+
- Uses `{{args}}` argument placeholder
414+
- Produces YAML with `prompt: |` block scalar for command content
415+
416+
Implementation: Extends `YamlIntegration` (parallel to `TomlIntegration`):
417+
1. Processes templates through the standard placeholder pipeline
418+
2. Extracts title and description from frontmatter
419+
3. Renders output as Goose recipe YAML (version, title, description, author, extensions, activities, prompt)
420+
4. Uses `yaml.safe_dump()` for header fields to ensure proper escaping
421+
5. Context updates map to `AGENTS.md` (shared with opencode/codex/pi/forge)
422+
356423
## Common Pitfalls
357424

358425
1. **Using shorthand keys for CLI-based integrations**: For CLI-based integrations (`requires_cli: True`), the `key` must match the executable name (e.g., `"cursor-agent"` not `"cursor"`). `shutil.which(key)` is used for CLI tool checks — mismatches require special-case mappings. IDE-based integrations (`requires_cli: False`) are not subject to this constraint.

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ Community projects that extend, visualize, or build on Spec Kit:
310310
| [Cursor](https://cursor.sh/) || |
311311
| [Forge](https://forgecode.dev/) || CLI tool: `forge` |
312312
| [Gemini CLI](https://github.com/google-gemini/gemini-cli) || |
313+
| [Goose](https://block.github.io/goose/) || Uses YAML recipe format in `.goose/recipes/` with slash command support |
313314
| [GitHub Copilot](https://code.visualstudio.com/) || |
314315
| [IBM Bob](https://www.ibm.com/products/bob) || IDE-based agent with slash command support |
315316
| [Jules](https://jules.google.com/) || |
@@ -654,7 +655,7 @@ specify init . --force --ai claude
654655
specify init --here --force --ai claude
655656
```
656657

657-
The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, Tabnine CLI, Kiro CLI, Pi, Forge, or Mistral Vibe installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:
658+
The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, Tabnine CLI, Kiro CLI, Pi, Forge, Goose, or Mistral Vibe installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:
658659

659660
```bash
660661
specify init <project_name> --ai claude --ignore-agent-tools

scripts/bash/update-agent-context.sh

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@
3030
#
3131
# 5. Multi-Agent Support
3232
# - Handles agent-specific file paths and naming conventions
33-
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Junie, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Tabnine CLI, Kiro CLI, Mistral Vibe, Kimi Code, Pi Coding Agent, iFlow CLI, Forge, Antigravity or Generic
33+
# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Junie, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Tabnine CLI, Kiro CLI, Mistral Vibe, Kimi Code, Pi Coding Agent, iFlow CLI, Forge, Goose, Antigravity or Generic
3434
# - Can update single agents or all existing agent files
3535
# - Creates default Claude file if no agent files exist
3636
#
3737
# Usage: ./update-agent-context.sh [agent_type]
38-
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic
38+
# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|goose|generic
3939
# Leave empty to update all existing agent files
4040

4141
set -e
@@ -74,7 +74,7 @@ AUGGIE_FILE="$REPO_ROOT/.augment/rules/specify-rules.md"
7474
ROO_FILE="$REPO_ROOT/.roo/rules/specify-rules.md"
7575
CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md"
7676
QODER_FILE="$REPO_ROOT/QODER.md"
77-
# Amp, Kiro CLI, IBM Bob, Pi, and Forge all share AGENTS.md — use AGENTS_FILE to avoid
77+
# Amp, Kiro CLI, IBM Bob, Pi, Forge, and Goose all share AGENTS.md — use AGENTS_FILE to avoid
7878
# updating the same file multiple times.
7979
AMP_FILE="$AGENTS_FILE"
8080
SHAI_FILE="$REPO_ROOT/SHAI.md"
@@ -710,12 +710,15 @@ update_specific_agent() {
710710
forge)
711711
update_agent_file "$AGENTS_FILE" "Forge" || return 1
712712
;;
713+
goose)
714+
update_agent_file "$AGENTS_FILE" "Goose" || return 1
715+
;;
713716
generic)
714717
log_info "Generic agent: no predefined context file. Use the agent-specific update script for your agent."
715718
;;
716719
*)
717720
log_error "Unknown agent type '$agent_type'"
718-
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic"
721+
log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|goose|generic"
719722
exit 1
720723
;;
721724
esac
@@ -759,7 +762,7 @@ update_all_existing_agents() {
759762
_update_if_new "$COPILOT_FILE" "GitHub Copilot" || _all_ok=false
760763
_update_if_new "$CURSOR_FILE" "Cursor IDE" || _all_ok=false
761764
_update_if_new "$QWEN_FILE" "Qwen Code" || _all_ok=false
762-
_update_if_new "$AGENTS_FILE" "Codex/opencode/Amp/Kiro/Bob/Pi/Forge" || _all_ok=false
765+
_update_if_new "$AGENTS_FILE" "Codex/opencode/Amp/Kiro/Bob/Pi/Forge/Goose" || _all_ok=false
763766
_update_if_new "$WINDSURF_FILE" "Windsurf" || _all_ok=false
764767
_update_if_new "$JUNIE_FILE" "Junie" || _all_ok=false
765768
_update_if_new "$KILOCODE_FILE" "Kilo Code" || _all_ok=false
@@ -800,7 +803,7 @@ print_summary() {
800803
fi
801804

802805
echo
803-
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic]"
806+
log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|goose|generic]"
804807
}
805808

806809
#==============================================================================

scripts/powershell/update-agent-context.ps1

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Mirrors the behavior of scripts/bash/update-agent-context.sh:
99
2. Plan Data Extraction
1010
3. Agent File Management (create from template or update existing)
1111
4. Content Generation (technology stack, recent changes, timestamp)
12-
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, junie, kilocode, auggie, roo, codebuddy, amp, shai, tabnine, kiro-cli, agy, bob, vibe, qodercli, kimi, trae, pi, iflow, forge, generic)
12+
5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, junie, kilocode, auggie, roo, codebuddy, amp, shai, tabnine, kiro-cli, agy, bob, vibe, qodercli, kimi, trae, pi, iflow, forge, goose, generic)
1313
1414
.PARAMETER AgentType
1515
Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist).
@@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1
2525
#>
2626
param(
2727
[Parameter(Position=0)]
28-
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','junie','kilocode','auggie','roo','codebuddy','amp','shai','tabnine','kiro-cli','agy','bob','vibe','qodercli','kimi','trae','pi','iflow','forge','generic')]
28+
[ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','junie','kilocode','auggie','roo','codebuddy','amp','shai','tabnine','kiro-cli','agy','bob','vibe','qodercli','kimi','trae','pi','iflow','forge','goose','generic')]
2929
[string]$AgentType
3030
)
3131

@@ -68,6 +68,7 @@ $KIMI_FILE = Join-Path $REPO_ROOT 'KIMI.md'
6868
$TRAE_FILE = Join-Path $REPO_ROOT '.trae/rules/project_rules.md'
6969
$IFLOW_FILE = Join-Path $REPO_ROOT 'IFLOW.md'
7070
$FORGE_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
71+
$GOOSE_FILE = Join-Path $REPO_ROOT 'AGENTS.md'
7172

7273
$TEMPLATE_FILE = Join-Path $REPO_ROOT '.specify/templates/agent-file-template.md'
7374

@@ -417,8 +418,9 @@ function Update-SpecificAgent {
417418
'pi' { Update-AgentFile -TargetFile $AGENTS_FILE -AgentName 'Pi Coding Agent' }
418419
'iflow' { Update-AgentFile -TargetFile $IFLOW_FILE -AgentName 'iFlow CLI' }
419420
'forge' { Update-AgentFile -TargetFile $FORGE_FILE -AgentName 'Forge' }
421+
'goose' { Update-AgentFile -TargetFile $GOOSE_FILE -AgentName 'Goose' }
420422
'generic' { Write-Info 'Generic agent: no predefined context file. Use the agent-specific update script for your agent.' }
421-
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic'; return $false }
423+
default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|goose|generic'; return $false }
422424
}
423425
}
424426

@@ -460,7 +462,7 @@ function Update-AllExistingAgents {
460462
if (-not (Update-IfNew -FilePath $COPILOT_FILE -AgentName 'GitHub Copilot')) { $ok = $false }
461463
if (-not (Update-IfNew -FilePath $CURSOR_FILE -AgentName 'Cursor IDE')) { $ok = $false }
462464
if (-not (Update-IfNew -FilePath $QWEN_FILE -AgentName 'Qwen Code')) { $ok = $false }
463-
if (-not (Update-IfNew -FilePath $AGENTS_FILE -AgentName 'Codex/opencode/Amp/Kiro/Bob/Pi/Forge')) { $ok = $false }
465+
if (-not (Update-IfNew -FilePath $AGENTS_FILE -AgentName 'Codex/opencode/Amp/Kiro/Bob/Pi/Forge/Goose')) { $ok = $false }
464466
if (-not (Update-IfNew -FilePath $WINDSURF_FILE -AgentName 'Windsurf')) { $ok = $false }
465467
if (-not (Update-IfNew -FilePath $JUNIE_FILE -AgentName 'Junie')) { $ok = $false }
466468
if (-not (Update-IfNew -FilePath $KILOCODE_FILE -AgentName 'Kilo Code')) { $ok = $false }
@@ -490,7 +492,7 @@ function Print-Summary {
490492
if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" }
491493
if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" }
492494
Write-Host ''
493-
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|generic]'
495+
Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|junie|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|kimi|trae|pi|iflow|forge|goose|generic]'
494496
}
495497

496498
function Main {

0 commit comments

Comments
 (0)