-
Notifications
You must be signed in to change notification settings - Fork 7.1k
feat(agents): add Goose AI agent support #2015
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
29fa832
79bdc39
b8fc3d9
57da3d4
5b4d283
2c7c3ba
0edef9d
fdf9e15
7ff5780
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -116,6 +116,27 @@ generate_commands() { | |||||||||||||||||||||||||||||
| echo "$body" > "$output_dir/speckit.$name.$ext" ;; | ||||||||||||||||||||||||||||||
| agent.md) | ||||||||||||||||||||||||||||||
| echo "$body" > "$output_dir/speckit.$name.$ext" ;; | ||||||||||||||||||||||||||||||
| yaml) | ||||||||||||||||||||||||||||||
| # Generate Goose recipe format YAML | ||||||||||||||||||||||||||||||
| local title instructions | ||||||||||||||||||||||||||||||
| title=$(echo "$name" | sed 's/\b\(.\)/\u/g/g') # Convert to title case | ||||||||||||||||||||||||||||||
| instructions=$(printf '%s\n' "$body" | sed 's/"/\\"/g' | tr '\n' '\\n') | ||||||||||||||||||||||||||||||
| cat > "$output_dir/speckit.$name.$ext" <<YAML_EOF | ||||||||||||||||||||||||||||||
| version: 1.0.0 | ||||||||||||||||||||||||||||||
| title: "$title" | ||||||||||||||||||||||||||||||
| description: "$description" | ||||||||||||||||||||||||||||||
| instructions: "$description" | ||||||||||||||||||||||||||||||
|
Comment on lines
+122
to
+128
|
||||||||||||||||||||||||||||||
| title=$(echo "$name" | sed 's/\b\(.\)/\u/g/g') # Convert to title case | |
| instructions=$(printf '%s\n' "$body" | sed 's/"/\\"/g' | tr '\n' '\\n') | |
| cat > "$output_dir/speckit.$name.$ext" <<YAML_EOF | |
| version: 1.0.0 | |
| title: "$title" | |
| description: "$description" | |
| instructions: "$description" | |
| title=$(echo "$name" | sed 's/\b\(.\)/\u\1/g') # Convert to title case | |
| instructions=$(printf '%s\n' "$body" | sed 's/"/\\"/g' | tr '\n' '\\n') | |
| cat > "$output_dir/speckit.$name.$ext" <<YAML_EOF | |
| version: 1.0.0 | |
| title: "$title" | |
| description: "$description" | |
| instructions: "$instructions" |
Copilot
AI
Mar 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The YAML recipe generation here produces invalid YAML for the prompt: | block because only the first line of $body is indented (subsequent lines from $body start at column 0). YAML block scalars require every content line to be indented at least one level, so these files won’t parse. Consider indenting every line of $body (e.g., prefix each line with two spaces) before writing it under prompt: |.
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -162,6 +162,12 @@ class CommandRegistrar: | |||||||||||||
| "format": "markdown", | ||||||||||||||
| "args": "$ARGUMENTS", | ||||||||||||||
| "extension": ".md" | ||||||||||||||
| }, | ||||||||||||||
| "goose": { | ||||||||||||||
| "dir": ".goose/recipes", | ||||||||||||||
| "format": "yaml", | ||||||||||||||
| "args": "{{args}}", | ||||||||||||||
| "extension": ".yaml" | ||||||||||||||
|
Comment on lines
+168
to
+170
|
||||||||||||||
| "format": "yaml", | |
| "args": "{{args}}", | |
| "extension": ".yaml" | |
| "format": "markdown", | |
| "args": "{{args}}", | |
| "extension": ".md" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -467,3 +467,68 @@ def test_iflow_in_agent_context_scripts(self): | |||||||||||||||||||||||||||||
| def test_ai_help_includes_iflow(self): | ||||||||||||||||||||||||||||||
| """CLI help text for --ai should include iflow.""" | ||||||||||||||||||||||||||||||
| assert "iflow" in AI_ASSISTANT_HELP | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # --- Goose consistency checks --- | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_goose_in_agent_config(self): | ||||||||||||||||||||||||||||||
| """AGENT_CONFIG should include goose with correct folder and commands_subdir.""" | ||||||||||||||||||||||||||||||
| assert "goose" in AGENT_CONFIG | ||||||||||||||||||||||||||||||
| assert AGENT_CONFIG["goose"]["folder"] == ".goose/" | ||||||||||||||||||||||||||||||
| assert AGENT_CONFIG["goose"]["commands_subdir"] == "recipes" | ||||||||||||||||||||||||||||||
| assert AGENT_CONFIG["goose"]["requires_cli"] is True | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_goose_in_extension_registrar(self): | ||||||||||||||||||||||||||||||
| """Extension command registrar should include goose targeting .goose/recipes.""" | ||||||||||||||||||||||||||||||
| cfg = CommandRegistrar.AGENT_CONFIGS | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| assert "goose" in cfg | ||||||||||||||||||||||||||||||
| assert cfg["goose"]["dir"] == ".goose/recipes" | ||||||||||||||||||||||||||||||
| assert cfg["goose"]["format"] == "yaml" | ||||||||||||||||||||||||||||||
| assert cfg["goose"]["args"] == "{{args}}" | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_goose_in_release_agent_lists(self): | ||||||||||||||||||||||||||||||
| """Bash and PowerShell release scripts should include goose in agent lists.""" | ||||||||||||||||||||||||||||||
| sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8") | ||||||||||||||||||||||||||||||
| ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| sh_match = re.search(r"ALL_AGENTS=\(([^)]*)\)", sh_text) | ||||||||||||||||||||||||||||||
| assert sh_match is not None | ||||||||||||||||||||||||||||||
| sh_agents = sh_match.group(1).split() | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ps_match = re.search(r"\$AllAgents = @\(([^)]*)\)", ps_text) | ||||||||||||||||||||||||||||||
| assert ps_match is not None | ||||||||||||||||||||||||||||||
| ps_agents = re.findall(r"'([^']+)'", ps_match.group(1)) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| assert "goose" in sh_agents | ||||||||||||||||||||||||||||||
| assert "goose" in ps_agents | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_goose_in_release_scripts_build_variant(self): | ||||||||||||||||||||||||||||||
| """Release scripts should generate YAML recipes for goose in .goose/recipes.""" | ||||||||||||||||||||||||||||||
| sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8") | ||||||||||||||||||||||||||||||
| ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| assert ".goose/recipes" in sh_text | ||||||||||||||||||||||||||||||
| assert ".goose/recipes" in ps_text | ||||||||||||||||||||||||||||||
| assert re.search(r"'goose'\s*\{.*?\.goose/recipes", ps_text, re.S) is not None | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def test_goose_in_github_release_output(self): | ||||||||||||||||||||||||||||||
| """GitHub release script should include goose template packages.""" | ||||||||||||||||||||||||||||||
| gh_release_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-github-release.sh").read_text(encoding="utf-8") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| assert "spec-kit-template-goose-sh-" in gh_release_text | ||||||||||||||||||||||||||||||
| assert "spec-kit-template-goose-ps-" in gh_release_text | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+515
to
+520
|
||||||||||||||||||||||||||||||
| """GitHub release script should include goose template packages.""" | |
| gh_release_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-github-release.sh").read_text(encoding="utf-8") | |
| assert "spec-kit-template-goose-sh-" in gh_release_text | |
| assert "spec-kit-template-goose-ps-" in gh_release_text | |
| """GitHub release script should include goose template packages. | |
| NOTE: The create-github-release.sh script does not yet upload the | |
| spec-kit-template-goose-* artifacts. Once it does, reintroduce | |
| explicit assertions for those artifact names here. | |
| """ | |
| gh_release_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-github-release.sh").read_text(encoding="utf-8") | |
| # Intentionally no assertions on specific goose template artifact names | |
| # until create-github-release.sh is updated to publish them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The generated YAML recipe will be invalid for
prompt: |because$bodyis inserted without indenting each line (only the first line is prefixed by the single space in the here-string). YAML block scalars require consistent indentation for all lines in the scalar. Also,$escapedBodyis computed but unused, andinstructions:is set to$descriptiondespite having separate variables—please either map these fields correctly or remove the unused variables.