From 756935f7da8772a736f9d3c1a5d18512dec13e4d Mon Sep 17 00:00:00 2001 From: Greg Kitchen Date: Mon, 16 Feb 2026 13:38:09 -0600 Subject: [PATCH 01/11] feat(agents): add Kiro CLI and Kiro IDE support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two new agents for Kiro's CLI tool and IDE, bringing the total to 11 supported AI assistants. Kiro CLI generates plain markdown prompts at ~/.kiro/prompts/, while Kiro IDE generates custom subagent files with YAML frontmatter at ~/.kiro/agents/. Key changes: - KIRO and KIRO_IDE command formats with dedicated generators - Command cross-reference rewriting (/SDD-N-name → @name) - SDD ordering prefix stripping for clean filenames and invocation names - Tracking metadata via trailing HTML comments for cleanup detection - Documentation for /tools trust-all workaround (Kiro CLI permissions) --- README.md | 3 + docs/slash-command-generator.md | 17 ++- slash_commands/config.py | 20 +++ slash_commands/generators.py | 125 +++++++++++++++++ slash_commands/writer.py | 37 ++++- tests/test_config.py | 18 ++- tests/test_generators.py | 230 ++++++++++++++++++++++++++++++++ tests/test_writer.py | 116 ++++++++++++++++ 8 files changed, 558 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index cb9aff1..857321f 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,9 @@ The generator supports the following AI coding assistants: - Windows: `%APPDATA%\Code - Insiders\User\prompts` - **OpenCode CLI**: Commands installed to `~/.config/opencode/command` - **Amazon Q**: Commands installed to `~/.aws/amazonq/prompts` (Windows & macOS/Linux) +- **Kiro CLI**: Commands installed to `~/.kiro/prompts` + > **Note**: Kiro CLI prompts do not support tool permissions. Run `/tools trust-all` at the start of your session to auto-approve file operations (write, shell, etc.). +- **Kiro IDE**: Agents installed to `~/.kiro/agents` ## Documentation diff --git a/docs/slash-command-generator.md b/docs/slash-command-generator.md index e11eea1..b7e0323 100644 --- a/docs/slash-command-generator.md +++ b/docs/slash-command-generator.md @@ -6,7 +6,7 @@ The Slash Command Generator automates the creation of slash command files for AI The generator reads markdown prompts from the `prompts/` directory and produces command files in the appropriate format for each configured AI assistant. It supports: -- **Multiple agents**: 7 supported AI assistants with different command formats +- **Multiple agents**: 11 supported AI assistants with different command formats - **Auto-detection**: Automatically detects configured agents in your workspace - **Dry run mode**: Preview changes without writing files - **Safe overwrite handling**: Prompts before overwriting existing files with backup support @@ -189,10 +189,13 @@ The following agents are supported: | Agent | Display Name | Format | Extension | Target Directory | Reference | |-------|--------------|--------|-----------|------------------|-----------| +| `amazon-q` | Amazon Q | Markdown | `.md` | `.aws/amazonq/prompts` | [Home](https://aws.amazon.com/q/) · [Docs](https://docs.aws.amazon.com/amazonq/) | | `claude-code` | Claude Code | Markdown | `.md` | `.claude/commands` | [Home](https://docs.claude.com/) · [Docs](https://docs.claude.com/en/docs/claude-code/overview) | | `codex-cli` | Codex CLI | Markdown | `.md` | `.codex/prompts` | [Home](https://developers.openai.com/codex) · [Docs](https://developers.openai.com/codex/cli/) | | `cursor` | Cursor | Markdown | `.md` | `.cursor/commands` | [Home](https://cursor.com/) · [Docs](https://cursor.com/docs) | | `gemini-cli` | Gemini CLI | TOML | `.toml` | `.gemini/commands` | [Home](https://github.com/google-gemini/gemini-cli) · [Docs](https://geminicli.com/docs/) | +| `kiro-cli` | Kiro CLI | Kiro | `.md` | `.kiro/prompts` | [Home](https://kiro.dev/cli/) · [Docs](https://kiro.dev/docs/cli/) | +| `kiro-ide` | Kiro IDE | Kiro IDE | `.md` | `.kiro/agents` | [Home](https://kiro.dev/) · [Docs](https://kiro.dev/docs/) | | `opencode` | OpenCode CLI | Markdown | `.md` | `.config/opencode/command` | [Home](https://opencode.ai) · [Docs](https://opencode.ai/docs/commands) | | `vs-code` | VS Code | Markdown | `.prompt.md` | Platform-specific (see note below) | [Home](https://code.visualstudio.com/) · [Docs](https://code.visualstudio.com/docs) | | `vs-code-insiders` | VS Code Insiders | Markdown | `.prompt.md` | Platform-specific (see note below) | [Home](https://code.visualstudio.com/insiders/) · [Docs](https://code.visualstudio.com/docs) | @@ -214,6 +217,18 @@ The following agents are supported: The generator automatically detects your platform and installs commands to the correct location. VS Code and VS Code Insiders maintain separate prompt directories and do not share configurations. +### Kiro CLI Tool Permissions + +Kiro CLI prompts are plain markdown files and do not support declaring tool permissions. By default, Kiro CLI will prompt you to confirm every `write` and `shell` operation, which interrupts workflows like SDD that need to create files automatically. + +**Workaround**: Run `/tools trust-all` at the start of your Kiro CLI session to auto-approve all tool operations for that session. This only needs to be done once per session. + +``` +> /tools trust-all +``` + +See the [Kiro CLI permissions documentation](https://kiro.dev/docs/cli/chat/permissions/) for more details. + ## Command File Formats ### Markdown Format diff --git a/slash_commands/config.py b/slash_commands/config.py index acfa89d..d6d0b78 100644 --- a/slash_commands/config.py +++ b/slash_commands/config.py @@ -13,6 +13,8 @@ class CommandFormat(str, Enum): MARKDOWN = "markdown" TOML = "toml" + KIRO = "kiro" + KIRO_IDE = "kiro-ide" @dataclass(frozen=True) @@ -140,6 +142,24 @@ def get_command_dir(self) -> str: (".aws/amazonq",), None, ), + ( + "kiro-cli", + "Kiro CLI", + ".kiro/prompts", + CommandFormat.KIRO, + ".md", + (".kiro",), + None, + ), + ( + "kiro-ide", + "Kiro IDE", + ".kiro/agents", + CommandFormat.KIRO_IDE, + ".md", + (".kiro",), + None, + ), ) _SORTED_AGENT_DATA = tuple(sorted(_SUPPORTED_AGENT_DATA, key=lambda item: item[0])) diff --git a/slash_commands/generators.py b/slash_commands/generators.py index 35c4b82..6521c4d 100644 --- a/slash_commands/generators.py +++ b/slash_commands/generators.py @@ -2,6 +2,7 @@ from __future__ import annotations +import re from datetime import UTC, datetime from typing import Any, Protocol @@ -291,6 +292,126 @@ def _dict_to_toml(self, data: dict) -> str: return tomli_w.dumps(data) +def _strip_ordering_prefix(name: str) -> str: + """Strip ordering prefixes like 'SDD-1-' from a prompt name.""" + return re.sub(r"^[A-Z]+-\d+-", "", name) + + +def _rewrite_kiro_command_references(body: str) -> str: + """Rewrite /SDD-N-command-name references to @command-name for Kiro CLI.""" + return re.sub(r"/[A-Z]+-\d+-([a-z0-9-]+)", r"@\1", body) + + +class KiroCommandGenerator: + """Generator for Kiro CLI prompts. + + Kiro CLI expects simple markdown files with no frontmatter. + The prompt content is injected directly when the user invokes @prompt_name. + Tracking metadata is appended as a trailing HTML comment so it does not + interfere with the prompt instructions the model sees first. + """ + + def generate( + self, + prompt: MarkdownPrompt, + agent: AgentConfig, + source_metadata: dict[str, Any] | None = None, + ) -> str: + """Generate a Kiro CLI prompt file. + + Args: + prompt: The source prompt to generate from + agent: The agent configuration + source_metadata: Optional source metadata (local or GitHub) + + Returns: + Simple markdown content for Kiro CLI + """ + _description, arguments, _enabled = _apply_agent_overrides(prompt, agent) + + # Replace placeholders in body + body = _replace_placeholders(prompt.body, arguments, replace_double_braces=True) + + # Rewrite command cross-references for Kiro's @prompt-name syntax + body = _rewrite_kiro_command_references(body) + + # Output the prompt body directly — no extra headers or preamble. + # The body already contains its own structure (headings, sections, etc.) + output = body + "\n" + + # Append tracking metadata as a trailing HTML comment. + # Placed at the end so it doesn't pollute the instructions the model sees first. + meta_lines = [ + f"source: {prompt.name}", + f"version: {__version__}", + f"updated: {datetime.now(UTC).strftime('%Y-%m-%d')}", + ] + if source_metadata: + if "github_repo" in source_metadata: + meta_lines.append(f"repo: {source_metadata['github_repo']}") + + output += "\n\n" + + return _normalize_output(output) + + +class KiroIdeCommandGenerator: + """Generator for Kiro IDE custom subagents. + + Kiro IDE expects markdown files with YAML frontmatter containing + name, description, and tools fields. The prompt body goes below + the frontmatter. Files are stored at ~/.kiro/agents/*.md and + invoked with /agent-name in the IDE. + """ + + def generate( + self, + prompt: MarkdownPrompt, + agent: AgentConfig, + source_metadata: dict[str, Any] | None = None, + ) -> str: + """Generate a Kiro IDE agent file. + + Args: + prompt: The source prompt to generate from + agent: The agent configuration + source_metadata: Optional source metadata (local or GitHub) + + Returns: + Markdown with Kiro IDE frontmatter + """ + description, arguments, _enabled = _apply_agent_overrides(prompt, agent) + + # Build Kiro IDE frontmatter (strip ordering prefix for clean /agent-name invocation) + frontmatter: dict[str, Any] = { + "name": _strip_ordering_prefix(prompt.name), + "description": description, + "tools": ["*"], + } + + # Replace placeholders and rewrite command references + body = _replace_placeholders(prompt.body, arguments, replace_double_braces=True) + body = _rewrite_kiro_command_references(body) + + # Format as YAML frontmatter + body + yaml_content = yaml.safe_dump(frontmatter, allow_unicode=True, sort_keys=False) + output = f"---\n{yaml_content}---\n\n{body}\n" + + # Append tracking metadata as a trailing HTML comment + meta_lines = [ + f"source: {prompt.name}", + f"version: {__version__}", + f"updated: {datetime.now(UTC).strftime('%Y-%m-%d')}", + ] + if source_metadata: + if "github_repo" in source_metadata: + meta_lines.append(f"repo: {source_metadata['github_repo']}") + + output += "\n\n" + + return _normalize_output(output) + + class CommandGenerator: """Base class for command generators.""" @@ -301,5 +422,9 @@ def create(format: CommandFormat) -> CommandGeneratorProtocol: return MarkdownCommandGenerator() elif format == CommandFormat.TOML: return TomlCommandGenerator() + elif format == CommandFormat.KIRO: + return KiroCommandGenerator() + elif format == CommandFormat.KIRO_IDE: + return KiroIdeCommandGenerator() else: raise ValueError(f"Unsupported command format: {format}") diff --git a/slash_commands/writer.py b/slash_commands/writer.py index 07eb9a0..d663211 100644 --- a/slash_commands/writer.py +++ b/slash_commands/writer.py @@ -284,6 +284,23 @@ def _load_prompts(self) -> list[MarkdownPrompt]: return prompts + def _get_output_name(self, prompt_name: str, agent: AgentConfig) -> str: + """Get the output name for a prompt, applying agent-specific transforms. + + Args: + prompt_name: Original prompt name (e.g. "SDD-1-generate-spec") + agent: Agent configuration + + Returns: + Transformed name suitable for this agent (e.g. "generate-spec" for Kiro) + """ + name = prompt_name + if agent.command_format.value in ("kiro", "kiro-ide"): + from slash_commands.generators import _strip_ordering_prefix + + name = _strip_ordering_prefix(name) + return name + def _sanitize_filename(self, name: str, extension: str) -> str: """Sanitize a filename by removing path components and unsafe characters. @@ -317,7 +334,8 @@ def _find_existing_files( continue for agent in agent_configs: # Determine output path (same logic as _generate_file) - filename = self._sanitize_filename(prompt.name, agent.command_file_extension) + name = self._get_output_name(prompt.name, agent) + filename = self._sanitize_filename(name, agent.command_file_extension) output_path = self.base_path / agent.get_command_dir() / filename if output_path.exists(): @@ -379,7 +397,8 @@ def _generate_file(self, prompt: MarkdownPrompt, agent: AgentConfig) -> dict[str # Determine output path (resolve relative to base_path) # Sanitize file stem: drop any path components and restrict to safe chars - filename = self._sanitize_filename(prompt.name, agent.command_file_extension) + name = self._get_output_name(prompt.name, agent) + filename = self._sanitize_filename(name, agent.command_file_extension) output_path = self.base_path / agent.get_command_dir() / filename # Handle existing files @@ -510,6 +529,8 @@ def _is_generated_file(self, file_path: Path, agent: AgentConfig) -> bool: return self._is_generated_markdown(content) elif agent.command_format.value == "toml": return self._is_generated_toml(content) + elif agent.command_format.value in ("kiro", "kiro-ide"): + return self._is_generated_kiro(content) return False def _is_generated_markdown(self, content: str) -> bool: @@ -561,6 +582,18 @@ def _is_generated_toml(self, content: str) -> bool: except tomllib.TOMLDecodeError: return False + def _is_generated_kiro(self, content: str) -> bool: + """Check if Kiro CLI content was generated by this tool. + + Args: + content: File content + + Returns: + True if generated by this tool + """ + # Kiro files have an HTML comment marker (at the end of the file) + return "") + + # Must NOT have frontmatter + assert not generated.startswith("---") + + +# -- Kiro IDE agent generator tests -------------------------------------------- + + +def test_kiro_ide_generator_produces_frontmatter_with_tools(sample_prompt): + """Test that KiroIdeCommandGenerator produces markdown with Kiro IDE frontmatter.""" + agent = get_agent_config("kiro-ide") + generator = KiroIdeCommandGenerator() + + generated = generator.generate(sample_prompt, agent) + frontmatter, body = _extract_frontmatter_and_body(generated) + + assert frontmatter["name"] == "sample-prompt" + assert "description" in frontmatter + assert frontmatter["tools"] == ["*"] + # Should NOT have markdown-generator fields + assert "tags" not in frontmatter + assert "arguments" not in frontmatter + assert "meta" not in frontmatter + assert "enabled" not in frontmatter + + assert "# Sample Prompt" in body + + +def test_kiro_ide_generator_includes_tracking_comment(sample_prompt): + """Test that tracking metadata is appended as a trailing HTML comment.""" + agent = get_agent_config("kiro-ide") + generator = KiroIdeCommandGenerator() + + generated = generator.generate(sample_prompt, agent) + + assert "") diff --git a/tests/test_writer.py b/tests/test_writer.py index 23c3370..d2bae0c 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -885,3 +885,119 @@ def test_writer_writes_vs_code_to_platform_specific_path( assert len(result["files"]) == 1 assert result["files"][0]["path"] == str(expected_path) assert result["files"][0]["agent"] == "vs-code" + + +def test_writer_finds_generated_kiro_prompt_files(tmp_path): + """Test that writer can find generated Kiro prompt files.""" + command_dir = tmp_path / ".kiro" / "prompts" + command_dir.mkdir(parents=True, exist_ok=True) + + generated_file = command_dir / "test-command.md" + generated_file.write_text( + "# Test Command\n\nDo things.\n\n" + "\n" + ) + + # Create a non-generated file (no tracking comment) + non_generated_file = command_dir / "manual-prompt.md" + non_generated_file.write_text("# Manual Prompt\n\nJust a plain file.\n") + + writer = SlashCommandWriter( + prompts_dir=tmp_path / "prompts", + agents=[], + dry_run=False, + base_path=tmp_path, + ) + + found_files = writer.find_generated_files(agents=["kiro-cli"], include_backups=False) + + assert len(found_files) == 1 + assert isinstance(found_files[0]["path"], str) + assert found_files[0]["path"] == str(generated_file) + assert found_files[0]["agent"] == "kiro-cli" + assert found_files[0]["type"] == "command" + + +def test_writer_generates_kiro_prompt_files(mock_prompt_load: Path, tmp_path): + """Test that writer generates Kiro prompt markdown files.""" + prompts_dir = mock_prompt_load + + writer = SlashCommandWriter( + prompts_dir=prompts_dir, + agents=["kiro-cli"], + dry_run=False, + base_path=tmp_path, + ) + + writer.generate() + + # Verify markdown files were created in the prompts directory + prompts_output_dir = tmp_path / ".kiro" / "prompts" + assert prompts_output_dir.exists() + + # Check that at least one markdown file was created + md_files = list(prompts_output_dir.glob("*.md")) + assert len(md_files) > 0 + + # Verify the generated file is plain markdown with tracking comment + for md_file in md_files: + content = md_file.read_text() + assert not content.startswith("---") # No frontmatter + assert "\n" + ) + + # Create a non-generated file (no tracking comment) + non_generated_file = command_dir / "manual-agent.md" + non_generated_file.write_text("---\nname: manual\ntools: ['*']\n---\n\n# Manual\n") + + writer = SlashCommandWriter( + prompts_dir=tmp_path / "prompts", + agents=[], + dry_run=False, + base_path=tmp_path, + ) + + found_files = writer.find_generated_files(agents=["kiro-ide"], include_backups=False) + + assert len(found_files) == 1 + assert found_files[0]["path"] == str(generated_file) + assert found_files[0]["agent"] == "kiro-ide" + assert found_files[0]["type"] == "command" + + +def test_writer_generates_kiro_ide_agent_files(mock_prompt_load: Path, tmp_path): + """Test that writer generates Kiro IDE agent markdown files.""" + prompts_dir = mock_prompt_load + + writer = SlashCommandWriter( + prompts_dir=prompts_dir, + agents=["kiro-ide"], + dry_run=False, + base_path=tmp_path, + ) + + writer.generate() + + agents_output_dir = tmp_path / ".kiro" / "agents" + assert agents_output_dir.exists() + + md_files = list(agents_output_dir.glob("*.md")) + assert len(md_files) > 0 + + for md_file in md_files: + content = md_file.read_text() + assert content.startswith("---") # Has frontmatter + assert "tools:" in content # Has tools field + assert "\n" @@ -404,8 +404,8 @@ def generate( f"updated: {datetime.now(UTC).strftime('%Y-%m-%d')}", ] if source_metadata: - if "github_repo" in source_metadata: - meta_lines.append(f"repo: {source_metadata['github_repo']}") + if "source_repo" in source_metadata: + meta_lines.append(f"repo: {source_metadata['source_repo']}") output += "\n\n" diff --git a/tests/test_generators.py b/tests/test_generators.py index ccfdc76..81e72e9 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -388,7 +388,7 @@ def test_kiro_generator_github_source_metadata(sample_prompt): generator = KiroCommandGenerator() source_metadata = { - "github_repo": "liatrio-labs/spec-driven-workflow", + "source_repo": "liatrio-labs/spec-driven-workflow", } generated = generator.generate(sample_prompt, agent, source_metadata) @@ -503,7 +503,7 @@ def test_kiro_ide_generator_github_source_metadata(sample_prompt): generator = KiroIdeCommandGenerator() source_metadata = { - "github_repo": "liatrio-labs/spec-driven-workflow", + "source_repo": "liatrio-labs/spec-driven-workflow", } generated = generator.generate(sample_prompt, agent, source_metadata) From 42686a10c5e7142d18358a58a28409803d8234da Mon Sep 17 00:00:00 2001 From: Greg Kitchen Date: Mon, 16 Feb 2026 16:05:33 -0600 Subject: [PATCH 04/11] docs: add Kiro quick start and detailed usage guide Replaces the minimal permissions note with a full Kiro section covering CLI vs IDE differences, quick start commands, automatic command reference rewriting, and ordering prefix stripping. --- docs/slash-command-generator.md | 40 +++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/slash-command-generator.md b/docs/slash-command-generator.md index a3f2243..8e4c8ff 100644 --- a/docs/slash-command-generator.md +++ b/docs/slash-command-generator.md @@ -217,9 +217,45 @@ The following agents are supported: The generator automatically detects your platform and installs commands to the correct location. VS Code and VS Code Insiders maintain separate prompt directories and do not share configurations. -### Kiro CLI Tool Permissions +### Kiro (CLI and IDE) -Kiro CLI prompts are plain markdown files and do not support declaring tool permissions. By default, Kiro CLI will prompt you to confirm every `write` and `shell` operation, which interrupts workflows like SDD that need to create files automatically. +Kiro has two separate products that use different command formats. You can install for one or both: + +| Product | What it does | Install path | Invocation | +|---------|-------------|--------------|------------| +| **Kiro CLI** | Terminal-based AI assistant | `~/.kiro/prompts/*.md` | `@prompt-name` | +| **Kiro IDE** | VS Code-based IDE with AI agents | `~/.kiro/agents/*.md` | `/agent-name` | + +#### Quick Start + +```bash +# Install for Kiro CLI only +uv run slash-man --agents kiro-cli --yes + +# Install for Kiro IDE only +uv run slash-man --agents kiro-ide --yes + +# Install for both +uv run slash-man --agents kiro-cli --agents kiro-ide --yes + +# Install SDD workflow prompts from GitHub +uv run slash-man --agents kiro-cli --agents kiro-ide \ + --github-repo liatrio-labs/spec-driven-workflow \ + --github-branch main --github-path prompts --yes +``` + +#### How It Works + +The generator automatically adapts prompts for Kiro's conventions: + +- **Command references are rewritten**: Source prompts that reference `/SDD-2-generate-task-list-from-spec` (Claude Code syntax) are automatically converted to `@generate-task-list-from-spec` for Kiro +- **Ordering prefixes are stripped**: A source prompt named `SDD-1-generate-spec` becomes `generate-spec.md` so you invoke it as `@generate-spec` (CLI) or `/generate-spec` (IDE) +- **Kiro IDE agents get YAML frontmatter** with `name`, `description`, and `tools: ["*"]` (wildcard tool access) +- **Kiro CLI prompts are plain markdown** with no frontmatter — just the prompt body + +#### Kiro CLI Tool Permissions + +Kiro CLI prompts do not support declaring tool permissions. By default, Kiro CLI will prompt you to confirm every `write` and `shell` operation, which interrupts workflows like SDD that need to create files automatically. **Workaround**: Run `/tools trust-all` at the start of your Kiro CLI session to auto-approve all tool operations for that session. This only needs to be done once per session. From 5b7b5a409924a03bbf8eb76e15b122ac2ce5b020 Mon Sep 17 00:00:00 2001 From: Morgan Snow Date: Thu, 26 Feb 2026 10:50:10 -0700 Subject: [PATCH 05/11] feat: using steering folder for kiro ide support (#22) * feat: adding in support for kiro ide * fix: formatting of the test_writer * chore: fix comment to / command --------- Co-authored-by: Morgan Snow --- slash_commands/config.py | 2 +- slash_commands/generators.py | 14 +++++++------- tests/test_config.py | 3 ++- tests/test_generators.py | 4 +++- tests/test_writer.py | 19 +++++++++++-------- 5 files changed, 24 insertions(+), 18 deletions(-) diff --git a/slash_commands/config.py b/slash_commands/config.py index d6d0b78..10eaba8 100644 --- a/slash_commands/config.py +++ b/slash_commands/config.py @@ -154,7 +154,7 @@ def get_command_dir(self) -> str: ( "kiro-ide", "Kiro IDE", - ".kiro/agents", + ".kiro/steering", CommandFormat.KIRO_IDE, ".md", (".kiro",), diff --git a/slash_commands/generators.py b/slash_commands/generators.py index 54eb055..2ce6c49 100644 --- a/slash_commands/generators.py +++ b/slash_commands/generators.py @@ -356,12 +356,11 @@ def generate( class KiroIdeCommandGenerator: - """Generator for Kiro IDE custom subagents. + """Generator for Kiro IDE steering files. Kiro IDE expects markdown files with YAML frontmatter containing - name, description, and tools fields. The prompt body goes below - the frontmatter. Files are stored at ~/.kiro/agents/*.md and - invoked with /agent-name in the IDE. + inclusion mode. Files are stored at ~/.kiro/steering/*.md and + are manually included via / command markers in chat. """ def generate( @@ -370,7 +369,7 @@ def generate( agent: AgentConfig, source_metadata: dict[str, Any] | None = None, ) -> str: - """Generate a Kiro IDE agent file. + """Generate a Kiro IDE steering file. Args: prompt: The source prompt to generate from @@ -378,12 +377,13 @@ def generate( source_metadata: Optional source metadata (local or GitHub) Returns: - Markdown with Kiro IDE frontmatter + Markdown with Kiro IDE steering frontmatter """ description, arguments, _enabled = _apply_agent_overrides(prompt, agent) - # Build Kiro IDE frontmatter (strip ordering prefix for clean /agent-name invocation) + # Build Kiro IDE steering frontmatter with inclusion first, then name, description, tools frontmatter: dict[str, Any] = { + "inclusion": "manual", "name": _strip_ordering_prefix(prompt.name), "description": description, "tools": ["*"], diff --git a/tests/test_config.py b/tests/test_config.py index f4b25af..0f620eb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -67,8 +67,9 @@ def test_supported_agents_have_valid_structure( or agent.command_dir.endswith("/global_workflows") or agent.command_dir.endswith("/command") or agent.command_dir.endswith("/agents") + or agent.command_dir.endswith("/steering") ), ( - f"{agent.key}: command_dir must end with /commands, /prompts, /global_workflows, /command, or /agents" + f"{agent.key}: command_dir must end with /commands, /prompts, /global_workflows, /command, /agents, or /steering" ) # File extension must start with a dot assert agent.command_file_extension.startswith("."), ( diff --git a/tests/test_generators.py b/tests/test_generators.py index 81e72e9..56c6fa8 100644 --- a/tests/test_generators.py +++ b/tests/test_generators.py @@ -424,13 +424,15 @@ def test_kiro_generator_snapshot_regression(sample_prompt): def test_kiro_ide_generator_produces_frontmatter_with_tools(sample_prompt): - """Test that KiroIdeCommandGenerator produces markdown with Kiro IDE frontmatter.""" + """Test that KiroIdeCommandGenerator produces markdown with Kiro IDE steering frontmatter.""" agent = get_agent_config("kiro-ide") generator = KiroIdeCommandGenerator() generated = generator.generate(sample_prompt, agent) frontmatter, body = _extract_frontmatter_and_body(generated) + # Check that inclusion is first, then name, description, tools + assert frontmatter["inclusion"] == "manual" assert frontmatter["name"] == "sample-prompt" assert "description" in frontmatter assert frontmatter["tools"] == ["*"] diff --git a/tests/test_writer.py b/tests/test_writer.py index d2bae0c..2690371 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -947,20 +947,22 @@ def test_writer_generates_kiro_prompt_files(mock_prompt_load: Path, tmp_path): def test_writer_finds_generated_kiro_ide_agent_files(tmp_path): - """Test that writer can find generated Kiro IDE agent files.""" - command_dir = tmp_path / ".kiro" / "agents" + """Test that writer can find generated Kiro IDE steering files.""" + command_dir = tmp_path / ".kiro" / "steering" command_dir.mkdir(parents=True, exist_ok=True) generated_file = command_dir / "test-agent.md" generated_file.write_text( - "---\nname: test-agent\ndescription: Test agent\ntools:\n- '*'\n---\n\n" + "---\ninclusion: manual\nname: test-agent\ndescription: Test agent\ntools:\n- '*'\n---\n\n" "# Test Agent\n\nDo things.\n\n" "\n" ) # Create a non-generated file (no tracking comment) non_generated_file = command_dir / "manual-agent.md" - non_generated_file.write_text("---\nname: manual\ntools: ['*']\n---\n\n# Manual\n") + non_generated_file.write_text( + "---\ninclusion: manual\nname: manual\ntools: ['*']\n---\n\n# Manual\n" + ) writer = SlashCommandWriter( prompts_dir=tmp_path / "prompts", @@ -978,7 +980,7 @@ def test_writer_finds_generated_kiro_ide_agent_files(tmp_path): def test_writer_generates_kiro_ide_agent_files(mock_prompt_load: Path, tmp_path): - """Test that writer generates Kiro IDE agent markdown files.""" + """Test that writer generates Kiro IDE steering markdown files.""" prompts_dir = mock_prompt_load writer = SlashCommandWriter( @@ -990,14 +992,15 @@ def test_writer_generates_kiro_ide_agent_files(mock_prompt_load: Path, tmp_path) writer.generate() - agents_output_dir = tmp_path / ".kiro" / "agents" - assert agents_output_dir.exists() + steering_output_dir = tmp_path / ".kiro" / "steering" + assert steering_output_dir.exists() - md_files = list(agents_output_dir.glob("*.md")) + md_files = list(steering_output_dir.glob("*.md")) assert len(md_files) > 0 for md_file in md_files: content = md_file.read_text() assert content.startswith("---") # Has frontmatter + assert "inclusion: manual" in content # Has inclusion field assert "tools:" in content # Has tools field assert "