|
| 1 | +# Design Document |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The Kiro Config Indexer extends SpecMem's existing Kiro adapter to comprehensively index all Kiro CLI configuration artifacts. This includes project-level steering files, MCP server configurations, hooks, and agent settings. The feature enables SpecMem to provide context-aware responses about project configuration and automatically include relevant guidelines when generating context bundles. |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +``` |
| 10 | +┌─────────────────────────────────────────────────────────────────┐ |
| 11 | +│ .kiro/ Directory │ |
| 12 | +├─────────────────────────────────────────────────────────────────┤ |
| 13 | +│ steering/ settings/ hooks/ │ |
| 14 | +│ ├── python.md ├── mcp.json ├── validate.json │ |
| 15 | +│ ├── testing.md └── agent.json └── coverage.json │ |
| 16 | +│ └── security.md │ |
| 17 | +└─────────────────────────────────────────────────────────────────┘ |
| 18 | + │ |
| 19 | + ▼ |
| 20 | +┌─────────────────────────────────────────────────────────────────┐ |
| 21 | +│ KiroConfigIndexer │ |
| 22 | +├─────────────────────────────────────────────────────────────────┤ |
| 23 | +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ |
| 24 | +│ │ Steering │ │ MCP │ │ Hooks │ │ |
| 25 | +│ │ Parser │ │ Parser │ │ Parser │ │ |
| 26 | +│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ |
| 27 | +│ │ │ │ │ |
| 28 | +│ └────────────────┼────────────────┘ │ |
| 29 | +│ ▼ │ |
| 30 | +│ ┌───────────────────────┐ │ |
| 31 | +│ │ SpecBlock Factory │ │ |
| 32 | +│ └───────────┬───────────┘ │ |
| 33 | +└──────────────────────────┼──────────────────────────────────────┘ |
| 34 | + │ |
| 35 | + ▼ |
| 36 | +┌─────────────────────────────────────────────────────────────────┐ |
| 37 | +│ Memory Bank │ |
| 38 | +│ ┌─────────────────────────────────────────────────────────┐ │ |
| 39 | +│ │ SpecBlocks with tags: steering, mcp, hook, config │ │ |
| 40 | +│ └─────────────────────────────────────────────────────────┘ │ |
| 41 | +└─────────────────────────────────────────────────────────────────┘ |
| 42 | +``` |
| 43 | + |
| 44 | +## Components and Interfaces |
| 45 | + |
| 46 | +### KiroConfigIndexer |
| 47 | + |
| 48 | +Main class that orchestrates indexing of all Kiro configuration artifacts. |
| 49 | + |
| 50 | +```python |
| 51 | +@dataclass |
| 52 | +class KiroConfigIndexer: |
| 53 | + """Indexes Kiro CLI configuration artifacts.""" |
| 54 | + |
| 55 | + workspace_path: Path |
| 56 | + |
| 57 | + def index_all(self) -> list[SpecBlock]: |
| 58 | + """Index all Kiro configuration files.""" |
| 59 | + ... |
| 60 | + |
| 61 | + def index_steering(self) -> list[SpecBlock]: |
| 62 | + """Index steering files from .kiro/steering/.""" |
| 63 | + ... |
| 64 | + |
| 65 | + def index_mcp_config(self) -> list[SpecBlock]: |
| 66 | + """Index MCP configuration from .kiro/settings/mcp.json.""" |
| 67 | + ... |
| 68 | + |
| 69 | + def index_hooks(self) -> list[SpecBlock]: |
| 70 | + """Index hooks from .kiro/hooks/.""" |
| 71 | + ... |
| 72 | + |
| 73 | + def get_steering_for_file(self, file_path: str) -> list[SpecBlock]: |
| 74 | + """Get steering files applicable to a specific file.""" |
| 75 | + ... |
| 76 | + |
| 77 | + def get_available_tools(self) -> list[MCPToolInfo]: |
| 78 | + """Get list of available (enabled) MCP tools.""" |
| 79 | + ... |
| 80 | + |
| 81 | + def get_hooks_for_trigger( |
| 82 | + self, |
| 83 | + trigger: str, |
| 84 | + file_path: str | None = None |
| 85 | + ) -> list[HookInfo]: |
| 86 | + """Get hooks matching a trigger type and optional file pattern.""" |
| 87 | + ... |
| 88 | +``` |
| 89 | + |
| 90 | +### SteeringParser |
| 91 | + |
| 92 | +Parses steering files with YAML frontmatter support. |
| 93 | + |
| 94 | +```python |
| 95 | +@dataclass |
| 96 | +class SteeringFile: |
| 97 | + """Parsed steering file.""" |
| 98 | + |
| 99 | + path: Path |
| 100 | + content: str |
| 101 | + inclusion: Literal["always", "fileMatch", "manual"] |
| 102 | + file_match_pattern: str | None |
| 103 | + title: str |
| 104 | + |
| 105 | + def matches_file(self, file_path: str) -> bool: |
| 106 | + """Check if this steering applies to a file.""" |
| 107 | + ... |
| 108 | + |
| 109 | + |
| 110 | +class SteeringParser: |
| 111 | + """Parses steering files with frontmatter.""" |
| 112 | + |
| 113 | + def parse(self, file_path: Path) -> SteeringFile: |
| 114 | + """Parse a steering file.""" |
| 115 | + ... |
| 116 | + |
| 117 | + def parse_frontmatter(self, content: str) -> dict[str, Any]: |
| 118 | + """Extract YAML frontmatter from content.""" |
| 119 | + ... |
| 120 | +``` |
| 121 | + |
| 122 | +### MCPConfigParser |
| 123 | + |
| 124 | +Parses MCP server configuration. |
| 125 | + |
| 126 | +```python |
| 127 | +@dataclass |
| 128 | +class MCPServerInfo: |
| 129 | + """Parsed MCP server configuration.""" |
| 130 | + |
| 131 | + name: str |
| 132 | + command: str |
| 133 | + args: list[str] |
| 134 | + env: dict[str, str] |
| 135 | + disabled: bool |
| 136 | + auto_approve: list[str] |
| 137 | + |
| 138 | + |
| 139 | +@dataclass |
| 140 | +class MCPToolInfo: |
| 141 | + """Information about an MCP tool.""" |
| 142 | + |
| 143 | + server_name: str |
| 144 | + tool_name: str |
| 145 | + description: str |
| 146 | + auto_approved: bool |
| 147 | + |
| 148 | + |
| 149 | +class MCPConfigParser: |
| 150 | + """Parses MCP configuration.""" |
| 151 | + |
| 152 | + def parse(self, config_path: Path) -> list[MCPServerInfo]: |
| 153 | + """Parse mcp.json configuration.""" |
| 154 | + ... |
| 155 | + |
| 156 | + def get_tools(self, servers: list[MCPServerInfo]) -> list[MCPToolInfo]: |
| 157 | + """Extract tool information from servers.""" |
| 158 | + ... |
| 159 | +``` |
| 160 | + |
| 161 | +### HookParser |
| 162 | + |
| 163 | +Parses hook configuration files. |
| 164 | + |
| 165 | +```python |
| 166 | +@dataclass |
| 167 | +class HookInfo: |
| 168 | + """Parsed hook configuration.""" |
| 169 | + |
| 170 | + name: str |
| 171 | + description: str |
| 172 | + trigger: Literal["file_save", "manual", "session_start"] |
| 173 | + file_pattern: str | None |
| 174 | + action: str |
| 175 | + enabled: bool |
| 176 | + |
| 177 | + def matches_file(self, file_path: str) -> bool: |
| 178 | + """Check if hook's file pattern matches a file.""" |
| 179 | + ... |
| 180 | + |
| 181 | + |
| 182 | +class HookParser: |
| 183 | + """Parses hook configuration files.""" |
| 184 | + |
| 185 | + def parse(self, hook_path: Path) -> HookInfo: |
| 186 | + """Parse a hook JSON file.""" |
| 187 | + ... |
| 188 | +``` |
| 189 | + |
| 190 | +## Data Models |
| 191 | + |
| 192 | +### SteeringFile |
| 193 | + |
| 194 | +```python |
| 195 | +@dataclass |
| 196 | +class SteeringFile: |
| 197 | + path: Path |
| 198 | + content: str |
| 199 | + inclusion: Literal["always", "fileMatch", "manual"] |
| 200 | + file_match_pattern: str | None |
| 201 | + title: str |
| 202 | +``` |
| 203 | + |
| 204 | +### MCPServerInfo |
| 205 | + |
| 206 | +```python |
| 207 | +@dataclass |
| 208 | +class MCPServerInfo: |
| 209 | + name: str |
| 210 | + command: str |
| 211 | + args: list[str] |
| 212 | + env: dict[str, str] |
| 213 | + disabled: bool |
| 214 | + auto_approve: list[str] |
| 215 | +``` |
| 216 | + |
| 217 | +### HookInfo |
| 218 | + |
| 219 | +```python |
| 220 | +@dataclass |
| 221 | +class HookInfo: |
| 222 | + name: str |
| 223 | + description: str |
| 224 | + trigger: Literal["file_save", "manual", "session_start"] |
| 225 | + file_pattern: str | None |
| 226 | + action: str |
| 227 | + enabled: bool |
| 228 | +``` |
| 229 | + |
| 230 | +### KiroConfigSummary |
| 231 | + |
| 232 | +```python |
| 233 | +@dataclass |
| 234 | +class KiroConfigSummary: |
| 235 | + steering_files: list[SteeringFile] |
| 236 | + mcp_servers: list[MCPServerInfo] |
| 237 | + hooks: list[HookInfo] |
| 238 | + total_tools: int |
| 239 | + enabled_servers: int |
| 240 | + active_hooks: int |
| 241 | +``` |
| 242 | + |
| 243 | +## Correctness Properties |
| 244 | + |
| 245 | +*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* |
| 246 | + |
| 247 | +### Property 1: Steering File Parsing Completeness |
| 248 | + |
| 249 | +*For any* directory containing valid steering markdown files, parsing SHALL produce one SpecBlock per file, and each SpecBlock SHALL contain the file's content. |
| 250 | + |
| 251 | +**Validates: Requirements 1.1** |
| 252 | + |
| 253 | +### Property 2: Frontmatter Round-Trip |
| 254 | + |
| 255 | +*For any* valid YAML frontmatter with `inclusion` and `fileMatchPattern` fields, parsing and re-serializing SHALL preserve the original values. |
| 256 | + |
| 257 | +**Validates: Requirements 1.2** |
| 258 | + |
| 259 | +### Property 3: File Pattern Matching Consistency |
| 260 | + |
| 261 | +*For any* steering file with a `fileMatchPattern` and any file path, the `matches_file()` method SHALL return true if and only if the file path matches the glob pattern according to standard glob semantics. |
| 262 | + |
| 263 | +**Validates: Requirements 1.3, 5.1, 5.2** |
| 264 | + |
| 265 | +### Property 4: Inclusion Mode Priority Mapping |
| 266 | + |
| 267 | +*For any* steering file, the resulting SpecBlock's priority SHALL be determined by its inclusion mode: "always" → high priority (pinned), "fileMatch" → normal priority, "manual" → low priority (not auto-included). |
| 268 | + |
| 269 | +**Validates: Requirements 1.4, 1.5** |
| 270 | + |
| 271 | +### Property 5: MCP Server Parsing Completeness |
| 272 | + |
| 273 | +*For any* valid mcp.json configuration, parsing SHALL produce one SpecBlock per defined server, and each SpecBlock SHALL contain the server name and command. |
| 274 | + |
| 275 | +**Validates: Requirements 2.1** |
| 276 | + |
| 277 | +### Property 6: MCP Tool Extraction |
| 278 | + |
| 279 | +*For any* MCP server with tool definitions, the parser SHALL extract all tools, and each tool's SpecBlock SHALL contain its name and description. |
| 280 | + |
| 281 | +**Validates: Requirements 2.2** |
| 282 | + |
| 283 | +### Property 7: Disabled Server Filtering |
| 284 | + |
| 285 | +*For any* query for available tools, the result SHALL exclude tools from servers where `disabled: true`, and SHALL include tools from all enabled servers. |
| 286 | + |
| 287 | +**Validates: Requirements 2.3, 2.4** |
| 288 | + |
| 289 | +### Property 8: Hook Parsing Completeness |
| 290 | + |
| 291 | +*For any* directory containing valid hook JSON files, parsing SHALL produce one SpecBlock per hook file. |
| 292 | + |
| 293 | +**Validates: Requirements 3.1** |
| 294 | + |
| 295 | +### Property 9: Hook Trigger Filtering |
| 296 | + |
| 297 | +*For any* query for hooks by trigger type and file path, the result SHALL include only hooks where the trigger matches AND (file_pattern is null OR file_pattern matches the path). |
| 298 | + |
| 299 | +**Validates: Requirements 3.2, 3.3** |
| 300 | + |
| 301 | +### Property 10: Always-Included Steering |
| 302 | + |
| 303 | +*For any* steering file with `inclusion: always`, querying steering for any file path SHALL include that steering file in the results. |
| 304 | + |
| 305 | +**Validates: Requirements 5.3** |
| 306 | + |
| 307 | +### Property 11: Context Bundle Steering Inclusion |
| 308 | + |
| 309 | +*For any* context bundle generated for changed files, the bundle SHALL include content from all steering files where `inclusion: always` OR `fileMatchPattern` matches any changed file. |
| 310 | + |
| 311 | +**Validates: Requirements 6.1, 6.2** |
| 312 | + |
| 313 | +### Property 12: Context Bundle Hook Mention |
| 314 | + |
| 315 | +*For any* context bundle generated for changed files, if a hook with `trigger: file_save` has a `filePattern` matching any changed file, the bundle SHALL mention this hook. |
| 316 | + |
| 317 | +**Validates: Requirements 6.4** |
| 318 | + |
| 319 | +## Error Handling |
| 320 | + |
| 321 | +| Condition | Handling | |
| 322 | +|-----------|----------| |
| 323 | +| Missing `.kiro/steering/` directory | Return empty list, no error | |
| 324 | +| Missing `.kiro/settings/mcp.json` | Return empty list, no error | |
| 325 | +| Missing `.kiro/hooks/` directory | Return empty list, no error | |
| 326 | +| Malformed YAML frontmatter | Log warning, parse content without frontmatter | |
| 327 | +| Invalid JSON in mcp.json | Log error, return empty list | |
| 328 | +| Invalid JSON in hook file | Log warning, skip that hook | |
| 329 | +| Invalid glob pattern | Log warning, treat as non-matching | |
| 330 | + |
| 331 | +## Testing Strategy |
| 332 | + |
| 333 | +### Property-Based Testing |
| 334 | + |
| 335 | +The implementation will use **Hypothesis** for property-based testing with a minimum of 100 iterations per property. |
| 336 | + |
| 337 | +Each property test will be annotated with: |
| 338 | +```python |
| 339 | +# **Feature: kiro-config-indexer, Property N: Property Name** |
| 340 | +# **Validates: Requirements X.Y** |
| 341 | +``` |
| 342 | + |
| 343 | +### Test Strategies |
| 344 | + |
| 345 | +1. **Steering files**: Generate random markdown content with valid/invalid frontmatter |
| 346 | +2. **MCP config**: Generate random JSON with varying numbers of servers and tools |
| 347 | +3. **Hooks**: Generate random hook configurations with different triggers and patterns |
| 348 | +4. **File matching**: Generate random glob patterns and file paths to test matching |
| 349 | + |
| 350 | +### Unit Tests |
| 351 | + |
| 352 | +- Parse steering file with no frontmatter |
| 353 | +- Parse steering file with partial frontmatter |
| 354 | +- Parse MCP config with disabled servers |
| 355 | +- Parse hook with missing optional fields |
| 356 | +- CLI command output formatting |
0 commit comments