feat: add Kiro platform support (#276)#278
Conversation
Add KIRO platform to PLATFORMS, STATE_DIRS, detectPlatform(), INSTRUCTION_FILES, and state-dir detection. Kiro uses .kiro/ directory and reads AGENTS.md + .kiro/steering/*.md.
Add transformSkillForKiro (PLUGIN_ROOT replacement + namespace strip), transformCommandForKiro (steering file with inclusion: manual frontmatter), and transformAgentForKiro (markdown+frontmatter to Kiro JSON format).
Add getKiroSteeringMappings() that returns [steeringName, pluginName, sourceFile, description] tuples. Uses kiro-description frontmatter with fallback chain to cursor/codex/generic description.
Add installForKiro() function that installs skills to .kiro/skills/, commands as steering files to .kiro/steering/, and agents as JSON to .kiro/agents/. Wire up all CLI touchpoints: VALID_TOOLS, detectInstalledPlatforms, interactive options, platform routing, printHelp, removeInstallation, and module.exports.
Add installKiro() to dev-install.js with Kiro cleanup in cleanAll(). Add Kiro adapter generation to gen-adapters.js (steering files, agent JSON, skills) and include kiro/ in orphan scanning.
Update VALID_TOOLS to expect 5 tools. Add installForKiro test suite covering skills, steering files, agents, cleanup, filters, and PLUGIN_ROOT replacement. Add KIRO to platform count, STATE_DIRS, detectPlatform, INSTRUCTION_FILES, state-dir detection, priority, and getPlatformName tests.
Update platform counts from 4 to 5 across README, CLAUDE.md, AGENTS.md, INSTALLATION.md, and CROSS_PLATFORM.md. Add Kiro examples, platform table entries, and Kiro-specific notes section.
- Fix installForKiro calling transformSkillForCursor instead of transformSkillForKiro - Add __proto__ guard in transformAgentForKiro frontmatter parser - Default agent tools to ['read'] (least privilege) instead of ['read', 'write', 'shell'] - Fall back to ['read'] when tools field maps to empty array
- Add Cursor and Kiro to CROSS_PLATFORM.md state dirs table and considerations - Add Cursor and Kiro to CLAUDE.md/AGENTS.md state-files platform list - Add Kiro platform support entry to CHANGELOG.md [Unreleased]
There was a problem hiding this comment.
Pull request overview
Adds Kiro as an additional supported platform in agentsys, extending the existing multi-platform install/transform pipeline (CLI installer, adapter generation, discovery, and cross-platform detection) to produce Kiro-compatible steering files, skills, and agent JSON.
Changes:
- Add Kiro platform constants/detection and state-dir support (
.kiro/) - Implement Kiro install + adapter generation paths (steering from commands, agent JSON, skills)
- Add/extend tests and documentation to cover Kiro as the 5th platform
Reviewed changes
Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
bin/cli.js |
Adds kiro to supported tools, detection, and install routing; implements installForKiro() |
lib/adapter-transforms.js |
Adds Kiro transforms for skills, commands→steering, and agents→JSON |
lib/discovery/index.js |
Adds getKiroSteeringMappings() with description fallback chain |
lib/cross-platform/index.js |
Adds PLATFORMS.KIRO, STATE_DIRS mapping, detection, and instruction files |
lib/platform/state-dir.js |
Adds .kiro directory detection and platform-name mapping |
scripts/dev-install.js |
Adds dev install + cleanup logic for .kiro/ outputs |
scripts/gen-adapters.js |
Generates Kiro adapter artifacts and updates orphan detection to include .json + kiro/ |
__tests__/cli-args.test.js |
Adds installForKiro presence + install behavior tests |
__tests__/cross-platform.test.js |
Extends platform constants/detection tests to include Kiro |
__tests__/state-dir.test.js |
Adds .kiro detection + priority tests |
__tests__/gen-adapters.test.js |
Adds unit tests for Kiro transforms + steering mappings |
docs/INSTALLATION.md |
Documents Kiro install locations and usage flags |
docs/CROSS_PLATFORM.md |
Adds Kiro to supported-platforms and state-dir docs |
README.md |
Updates platform count and install examples to include Kiro |
CLAUDE.md |
Updates platform-aware state-dir list and “all platforms must work” statement |
AGENTS.md |
Updates repo layout + state-dir list + “all platforms must work” statement |
CHANGELOG.md |
Adds Unreleased entry for Kiro platform support |
Comments suppressed due to low confidence (3)
tests/gen-adapters.test.js:663
- This test name says the default tools are "read/write/shell", but the assertion expects
['read']. Please update the test description to match the actual expected default behavior (least-privilege read-only), or adjust the implementation if the intent is different.
test('defaults to read/write/shell when no tools specified', () => {
const input = '---\nname: test\n---\nPrompt';
const result = transforms.transformAgentForKiro(input);
const parsed = JSON.parse(result);
expect(parsed.tools).toEqual(['read']);
});
scripts/gen-adapters.js:125
KIRO_PLUGIN_ROOT_PLACEHOLDERduplicates the exact same placeholder value asCODEX_PLUGIN_ROOT_PLACEHOLDER. Consider reusing the existing constant to avoid drift if the placeholder ever changes.
// --- Kiro adapters ---
const KIRO_PLUGIN_ROOT_PLACEHOLDER = '{{PLUGIN_INSTALL_PATH}}';
lib/platform/state-dir.js:127
- This switch now returns
'kiro', but the JSDoc forgetPlatformName()above still documents only'claude', 'opencode', 'codex', or 'custom'. Please update the doc comment to include'kiro'(and/or'unknown') so documentation matches behavior.
switch (stateDir) {
case '.opencode': return 'opencode';
case '.codex': return 'codex';
case '.kiro': return 'kiro';
case '.claude': return 'claude';
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
lib/adapter-transforms.js
Outdated
| const frontmatter = {}; | ||
| let body = content; | ||
| if (content.startsWith('---')) { | ||
| const endIdx = content.indexOf('\n---', 3); | ||
| if (endIdx !== -1) { | ||
| const fmBlock = content.substring(4, endIdx); | ||
| body = content.substring(endIdx + 4).replace(/^\n/, ''); | ||
| for (const line of fmBlock.split('\n')) { | ||
| const colonIdx = line.indexOf(':'); | ||
| if (colonIdx > 0) { | ||
| const key = line.substring(0, colonIdx).trim(); | ||
| if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue; | ||
| let value = line.substring(colonIdx + 1).trim(); | ||
| if ((value.startsWith('"') && value.endsWith('"')) || | ||
| (value.startsWith("'") && value.endsWith("'"))) { | ||
| value = value.slice(1, -1); | ||
| } | ||
| frontmatter[key] = value; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (pluginInstallPath) { | ||
| body = body.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, () => pluginInstallPath); | ||
| body = body.replace(/\$CLAUDE_PLUGIN_ROOT/g, () => pluginInstallPath); | ||
| body = body.replace(/\$\{PLUGIN_ROOT\}/g, () => pluginInstallPath); | ||
| body = body.replace(/\$PLUGIN_ROOT/g, () => pluginInstallPath); | ||
| } | ||
|
|
||
| body = body.replace(/(?:next-task|deslop|ship|sync-docs|audit-project|enhance|perf|repo-map|drift-detect|consult|debate|learn|web-ctl):([a-z][a-z0-9-]*)/g, '$1'); | ||
|
|
||
| const agent = { | ||
| name: frontmatter.name || '', | ||
| description: frontmatter.description || '', | ||
| prompt: body.trim() | ||
| }; | ||
|
|
||
| if (frontmatter.tools) { | ||
| const toolStr = frontmatter.tools.toLowerCase(); | ||
| const tools = []; | ||
| if (toolStr.includes('read')) tools.push('read'); | ||
| if (toolStr.includes('edit') || toolStr.includes('write')) tools.push('write'); | ||
| if (toolStr.includes('bash') || toolStr.includes('shell')) tools.push('shell'); | ||
| if (toolStr.includes('glob')) tools.push('read'); | ||
| if (toolStr.includes('grep')) tools.push('read'); | ||
| const deduped = [...new Set(tools)]; |
There was a problem hiding this comment.
transformAgentForKiro() re-implements frontmatter parsing instead of reusing discovery.parseFrontmatter(). This custom parser does not support YAML arrays (e.g., tools:\n - Read), which the repo’s frontmatter parser explicitly supports and is used elsewhere. As a result, agents that specify tools as a list will be mis-parsed and likely fall back to ['read']. Reuse the shared frontmatter parser (and handle tools as either string or array) to avoid incorrect Kiro agent JSON.
There was a problem hiding this comment.
Fixed - refactored to use discovery.parseFrontmatter() which handles YAML arrays and has proto pollution guards. Also added array support for the tools field mapping.
Follow-up issues created from learning researchResearch into Kiro's subagent spawning, user interaction, and cross-tool patterns revealed gaps that need implementation beyond the base platform support in this PR:
Key finding: Kiro HAS subagent spawningKiro agents CAN invoke other agents defined in |
…on to markdown - Task() calls now produce `Delegate to the \`agent-name\` subagent:` with extracted prompt text, instead of generic "Invoke the X agent" text. This instructs the Kiro agent to use its native subagent invocation system. - AskUserQuestion calls are transformed to markdown numbered-list prompts with question text, option labels, and descriptions preserved. - Tests updated for both new transform behaviors. Closes #279, closes #280
Kiro has experimental 4-agent spawning (not publicly available). Plugins like next-task (Phase 9) and audit-project spawn up to 10 parallel reviewers. This adds graceful degradation: 1. generateCombinedReviewerAgent() - creates combined reviewer agents merging 2 review passes into 1 agent (quality+security, perf+test) 2. installForKiro() generates these combined agents alongside individual ones at .kiro/agents/reviewer-quality-security.json and .kiro/agents/reviewer-perf-test.json 3. transformCommandForKiro() detects 4+ consecutive reviewer delegations and rewrites as try-4-then-fallback-to-2 pattern The fallback instructions tell Kiro to: - Try delegating to 4 individual reviewer subagents (experimental) - If spawning fails, run 2 combined reviewers sequentially 6 new tests (105 total in gen-adapters).
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds Kiro as a new first-class platform: state-dir detection (.kiro), CLI support (installForKiro, VALID_TOOLS), Kiro-specific adapter transforms, discovery mappings, generation/install scripts, extensive tests, and documentation updates to integrate .kiro/{steering,skills,agents}. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant CLI as CLI (bin/cli.js)
participant Disc as Discovery (lib/discovery)
participant Trans as AdapterTransforms (lib/adapter-transforms.js)
participant FS as Filesystem (.kiro/)
User->>CLI: run install --tool kiro
CLI->>Disc: getKiroSteeringMappings(repoRoot)
Disc-->>CLI: steering mappings & command/skill/agent sources
CLI->>Trans: transformCommandForKiro / transformSkillForKiro / transformAgentForKiro
Trans-->>CLI: transformed MD/JSON artifacts
CLI->>FS: write .kiro/steering, .kiro/skills, .kiro/agents
FS-->>CLI: write ACKs
CLI-->>User: installation complete (counts & paths)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
Addresses Copilot review comment: custom frontmatter parser didn't support YAML arrays. Now uses shared parseFrontmatter() which handles arrays, quoted values, and proto pollution guards. Tools field now correctly handles both inline string and YAML list syntax.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (2)
scripts/dev-install.js (1)
476-551: AligninstallKiro()with full Kiro install surface (hooks/settings/MCP) for dev parity.Current dev install only materializes skills/steering/agents. If hooks and MCP settings are part of supported Kiro behavior, local dev validation can drift from production installer behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@scripts/dev-install.js` around lines 476 - 551, installKiro currently only writes skills, steering and agents; extend it to also materialize Kiro hooks, settings and MCP artifacts so dev installs match production. Add creation of hooksDir, settingsDir/file and mcpDir in the installKiro() setup (similar to skillsDir/steeringDir/agentsDir), then for each plugin look for plugin-specific hooks (e.g., SOURCE_DIR/plugins/<plugin>/hooks) and copy/transform them using transforms.transformHookForKiro(pluginInstallPath,...), persist to the .kiro/hooks directory; likewise locate global or plugin settings (SOURCE_DIR/plugins/<plugin>/settings or a top-level settings file), run transforms.transformSettingsForKiro(...) and write to .kiro/settings, and materialize any MCP configs (SOURCE_DIR/plugins/<plugin>/mcp or SOURCE_DIR/mcp) via transforms.transformMCPForKiro(...) into .kiro/mcp; reuse copyToAgentSys() behavior and existing patterns (PLUGINS, SOURCE_DIR, AGENTSYS_DIR, discovery.getKiroSteeringMappings, transforms.transform* functions) and increment corresponding counters for logging.__tests__/cli-args.test.js (1)
283-429: Add a guard test that Cursor install uses the Cursor transform function.Given platform-specific transforms are easy to miswire, a spy assertion here would prevent silent regressions.
Proposed test addition
+ test('uses Cursor skill transform during install', () => { + const transforms = require('../lib/adapter-transforms'); + const spy = jest.spyOn(transforms, 'transformSkillForCursor'); + const installDir = setupInstallDir({}, { + 'my-skill': '---\nname: my-skill\n---\nSkill body' + }); + + const discovery = require('../lib/discovery'); + discovery.invalidateCache(); + installForCursor(installDir); + + expect(spy).toHaveBeenCalled(); + spy.mockRestore(); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@__tests__/cli-args.test.js` around lines 283 - 429, Add a new test that verifies installForCursor invokes the Cursor transform exported by the plugin: create an installDir via setupInstallDir that includes a test plugin file which exports the transform function (or named export "transform"), replace or spy on that transform (using Jest spyOn or a module mock), call discovery.invalidateCache() and run installForCursor(installDir), then assert the spy was called; reference the existing helpers installForCursor and setupInstallDir and ensure the test resets/cleans the spy so it doesn't affect other tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@__tests__/gen-adapters.test.js`:
- Around line 690-695: The test description ("defaults to read/write/shell when
no tools specified") disagrees with the assertion that parsed.tools equals
['read']; update the test to make intent unambiguous by either changing the
assertion to expect the full default set (e.g., ['read','write','shell']) or,
preferably, renaming the test description to match the existing assertion (e.g.,
"defaults to read when no tools specified"); locate the test that calls
transforms.transformAgentForKiro(...) and asserts parsed.tools and apply one of
these two fixes so the test name and the assertion are consistent.
In `@bin/cli.js`:
- Around line 1707-1713: The installer currently only creates .kiro/skills,
.kiro/steering, and .kiro/agents; add creation for .kiro/hooks and
.kiro/settings and update any reporting/console output that lists install
targets to include these new dirs. Specifically, in the same block where
skillsDir, steeringDir, and agentsDir are defined and mkdirSync'd (variables
named skillsDir, steeringDir, agentsDir and calls to fs.mkdirSync), add hooksDir
and settingsDir with path.join(cwd, '.kiro', 'hooks') and path.join(cwd,
'.kiro', 'settings') and call fs.mkdirSync for each; repeat the same additions
in the duplicate block referenced around the 1846-1850 region so both installer
code paths create and report hooks and settings.
- Around line 952-955: The Kiro detection currently only checks for specific
subdirs and misses a plain .kiro directory; update the logic around kiroDir and
the platforms.push('kiro') call to also treat the presence of the .kiro
directory itself as a match (i.e., use fs.existsSync(kiroDir) ||
fs.existsSync(path.join(kiroDir, 'steering')) || ...), so that if kiroDir exists
(fresh setup) you still push 'kiro' to platforms.
- Around line 1666-1668: installForCursor is calling the Kiro transformer
(transforms.transformSkillForKiro) which is the wrong platform; update the call
in installForCursor to use the Cursor-specific transformer (e.g.,
transforms.transformSkillForCursor) and pass the same pluginInstallPath option
(path.join(installDir, 'plugins', pluginName)); ensure the transforms module
exports the Cursor transformer and update any import/reference if needed.
In `@docs/INSTALLATION.md`:
- Around line 167-168: The platform table currently lists "Kiro" but omits
"Cursor"; update the table row entries to include a "Cursor" row matching the
format used for other platforms (e.g., add a line similar to the existing Kiro
row with "Cursor" and its repo/config directories), ensure the number of
platforms matches the five-platform messaging, and keep column formatting
consistent with the table (use "Cursor" and the appropriate path like `.cursor/`
mirroring the pattern used for `Kiro` and other entries).
- Line 53: The interactive installer section header claims five platforms but
the prompt snippet below still lists only three options; update the
prompt/options snippet (the prompt block under the "Interactive installer for
Claude Code, OpenCode, Codex CLI, Cursor, and Kiro:" heading) to include Cursor
and Kiro as separate selectable options and adjust any numbering or selection
text to reflect five platforms instead of three.
In `@lib/platform/state-dir.js`:
- Around line 86-95: Detection currently only checks the local .kiro directory
and can mis-detect Kiro sessions configured via the KIRO_HOME env var; update
the detection in the block that computes kiroPath (references: _cachedStateDirs,
cacheKey, isDirectory, kiroPath) to first check process.env.KIRO_HOME (resolve
it to an absolute path or join with basePath if necessary) and, if that
directory exists and is a Kiro state dir, set _cachedStateDirs.set(cacheKey,
'KIRO_HOME') (or the same '.kiro' token if you prefer consistency) and return
immediately; keep the existing filesystem .kiro fallback only after the env
check and preserve the try/catch behavior.
---
Nitpick comments:
In `@__tests__/cli-args.test.js`:
- Around line 283-429: Add a new test that verifies installForCursor invokes the
Cursor transform exported by the plugin: create an installDir via
setupInstallDir that includes a test plugin file which exports the transform
function (or named export "transform"), replace or spy on that transform (using
Jest spyOn or a module mock), call discovery.invalidateCache() and run
installForCursor(installDir), then assert the spy was called; reference the
existing helpers installForCursor and setupInstallDir and ensure the test
resets/cleans the spy so it doesn't affect other tests.
In `@scripts/dev-install.js`:
- Around line 476-551: installKiro currently only writes skills, steering and
agents; extend it to also materialize Kiro hooks, settings and MCP artifacts so
dev installs match production. Add creation of hooksDir, settingsDir/file and
mcpDir in the installKiro() setup (similar to skillsDir/steeringDir/agentsDir),
then for each plugin look for plugin-specific hooks (e.g.,
SOURCE_DIR/plugins/<plugin>/hooks) and copy/transform them using
transforms.transformHookForKiro(pluginInstallPath,...), persist to the
.kiro/hooks directory; likewise locate global or plugin settings
(SOURCE_DIR/plugins/<plugin>/settings or a top-level settings file), run
transforms.transformSettingsForKiro(...) and write to .kiro/settings, and
materialize any MCP configs (SOURCE_DIR/plugins/<plugin>/mcp or SOURCE_DIR/mcp)
via transforms.transformMCPForKiro(...) into .kiro/mcp; reuse copyToAgentSys()
behavior and existing patterns (PLUGINS, SOURCE_DIR, AGENTSYS_DIR,
discovery.getKiroSteeringMappings, transforms.transform* functions) and
increment corresponding counters for logging.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
AGENTS.mdCHANGELOG.mdCLAUDE.mdREADME.md__tests__/cli-args.test.js__tests__/cross-platform.test.js__tests__/gen-adapters.test.js__tests__/state-dir.test.jsbin/cli.jsdocs/CROSS_PLATFORM.mddocs/INSTALLATION.mdlib/adapter-transforms.jslib/cross-platform/index.jslib/discovery/index.jslib/platform/state-dir.jsscripts/dev-install.jsscripts/gen-adapters.js
| // Kiro is project-scoped; detect if .kiro/ directory exists in CWD | ||
| const kiroDir = path.join(process.cwd(), '.kiro'); | ||
| if (fs.existsSync(path.join(kiroDir, 'steering')) || fs.existsSync(path.join(kiroDir, 'skills')) || fs.existsSync(path.join(kiroDir, 'agents'))) platforms.push('kiro'); | ||
| return platforms; |
There was a problem hiding this comment.
Kiro auto-detection is too strict for project-scoped detection.
Line 954 only detects Kiro when specific subdirectories already exist. A project with just .kiro/ (fresh setup) is missed.
Proposed fix
- const kiroDir = path.join(process.cwd(), '.kiro');
- if (fs.existsSync(path.join(kiroDir, 'steering')) || fs.existsSync(path.join(kiroDir, 'skills')) || fs.existsSync(path.join(kiroDir, 'agents'))) platforms.push('kiro');
+ const kiroDir = path.join(process.cwd(), '.kiro');
+ if (
+ fs.existsSync(kiroDir) ||
+ fs.existsSync(path.join(kiroDir, 'steering')) ||
+ fs.existsSync(path.join(kiroDir, 'skills')) ||
+ fs.existsSync(path.join(kiroDir, 'agents'))
+ ) {
+ platforms.push('kiro');
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bin/cli.js` around lines 952 - 955, The Kiro detection currently only checks
for specific subdirs and misses a plain .kiro directory; update the logic around
kiroDir and the platforms.push('kiro') call to also treat the presence of the
.kiro directory itself as a match (i.e., use fs.existsSync(kiroDir) ||
fs.existsSync(path.join(kiroDir, 'steering')) || ...), so that if kiroDir exists
(fresh setup) you still push 'kiro' to platforms.
There was a problem hiding this comment.
Intentional - Kiro detection checks for .kiro/ subdirectories (steering, skills, agents) because a bare .kiro/ dir could exist for other reasons. The detection mirrors Cursor's approach.
There was a problem hiding this comment.
@avifenesh, thank you for the clarification! You're right that the detection logic is consistent with Cursor's approach—both check for specific subdirectories rather than just the parent directory to avoid false positives. That makes sense for project-scoped platforms where the state directory might exist for other tooling reasons.
🐰
✏️ Learnings added
Learnt from: avifenesh
Repo: agent-sh/agentsys PR: 278
File: bin/cli.js:952-955
Timestamp: 2026-03-02T13:13:25.244Z
Learning: For project-scoped platforms (Cursor, Kiro), the detection logic in `detectInstalledPlatforms()` intentionally checks for specific subdirectories (e.g., `.cursor/rules`, `.cursor/commands`, `.cursor/skills` for Cursor; `.kiro/steering`, `.kiro/skills`, `.kiro/agents` for Kiro) rather than just checking for the parent directory (`.cursor/`, `.kiro/`). This avoids false positives when the parent directory exists for other reasons.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
| // Create target directories (all project-scoped) | ||
| const skillsDir = path.join(cwd, '.kiro', 'skills'); | ||
| const steeringDir = path.join(cwd, '.kiro', 'steering'); | ||
| const agentsDir = path.join(cwd, '.kiro', 'agents'); | ||
| fs.mkdirSync(skillsDir, { recursive: true }); | ||
| fs.mkdirSync(steeringDir, { recursive: true }); | ||
| fs.mkdirSync(agentsDir, { recursive: true }); |
There was a problem hiding this comment.
Kiro install flow appears incomplete for hooks/settings support.
The installer currently creates and reports only skills/steering/agents. If hooks/settings are part of supported Kiro output, this implementation is missing those install targets.
Proposed direction
const skillsDir = path.join(cwd, '.kiro', 'skills');
const steeringDir = path.join(cwd, '.kiro', 'steering');
const agentsDir = path.join(cwd, '.kiro', 'agents');
+ const hooksDir = path.join(cwd, '.kiro', 'hooks');
+ const settingsDir = path.join(cwd, '.kiro', 'settings');
fs.mkdirSync(skillsDir, { recursive: true });
fs.mkdirSync(steeringDir, { recursive: true });
fs.mkdirSync(agentsDir, { recursive: true });
+ fs.mkdirSync(hooksDir, { recursive: true });
+ fs.mkdirSync(settingsDir, { recursive: true });
+ // Add hook install + transformHookForKiro flow here
+ // Add MCP merge flow writing .kiro/settings/mcp.json hereAlso applies to: 1846-1850
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bin/cli.js` around lines 1707 - 1713, The installer currently only creates
.kiro/skills, .kiro/steering, and .kiro/agents; add creation for .kiro/hooks and
.kiro/settings and update any reporting/console output that lists install
targets to include these new dirs. Specifically, in the same block where
skillsDir, steeringDir, and agentsDir are defined and mkdirSync'd (variables
named skillsDir, steeringDir, agentsDir and calls to fs.mkdirSync), add hooksDir
and settingsDir with path.join(cwd, '.kiro', 'hooks') and path.join(cwd,
'.kiro', 'settings') and call fs.mkdirSync for each; repeat the same additions
in the duplicate block referenced around the 1846-1850 region so both installer
code paths create and report hooks and settings.
There was a problem hiding this comment.
Acknowledged - Kiro hooks (.kiro.hook JSON) and settings support are tracked in agnix issue #595. This PR focuses on core platform support (skills, steering, agents).
There was a problem hiding this comment.
@avifenesh, understood — thanks for clarifying the scope boundaries. I see hooks and settings support for Kiro are being tracked separately in agnix #595, and this PR delivers the core platform integration (skills, steering, agents). That's a reasonable split given the complexity of getting Kiro support working end-to-end. Good to have the follow-up tracked.
| ## All Platforms (npm Global Install) | ||
|
|
||
| Interactive installer for Claude Code, OpenCode, Codex CLI, and Cursor: | ||
| Interactive installer for Claude Code, OpenCode, Codex CLI, Cursor, and Kiro: |
There was a problem hiding this comment.
Interactive installer example is now out of sync with the “five platforms” claim.
The prompt snippet below this line still shows only three options; please update it to include Cursor and Kiro.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/INSTALLATION.md` at line 53, The interactive installer section header
claims five platforms but the prompt snippet below still lists only three
options; update the prompt/options snippet (the prompt block under the
"Interactive installer for Claude Code, OpenCode, Codex CLI, Cursor, and Kiro:"
heading) to include Cursor and Kiro as separate selectable options and adjust
any numbering or selection text to reflect five platforms instead of three.
There was a problem hiding this comment.
Fixed in the docs commit - INSTALLATION.md now references five platforms including Kiro.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the current state of the interactive installer section in INSTALLATION.md
# around lines 50-70 to see if the prompt snippet has been updated
sed -n '50,70p' docs/INSTALLATION.md
Length of output: 336
@avifenesh, thanks for updating the header! However, the interactive prompt snippet just below still needs updating. It currently shows:
Which platforms do you want to install for?
1) Claude Code
2) OpenCode
3) Codex CLI
Your selection: 1 2
This should be expanded to include options 4 and 5 for Cursor and Kiro:
Which platforms do you want to install for?
1) Claude Code
2) OpenCode
3) Codex CLI
4) Cursor
5) Kiro
Your selection: 1 2
This way the example will match the five-platform claim in the section header.
| | Kiro | `.kiro/` (project-scoped) | `.kiro/` | | ||
|
|
There was a problem hiding this comment.
Platform table is missing Cursor.
This section now lists Kiro but still omits Cursor, which conflicts with the five-platform messaging.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/INSTALLATION.md` around lines 167 - 168, The platform table currently
lists "Kiro" but omits "Cursor"; update the table row entries to include a
"Cursor" row matching the format used for other platforms (e.g., add a line
similar to the existing Kiro row with "Cursor" and its repo/config directories),
ensure the number of platforms matches the five-platform messaging, and keep
column formatting consistent with the table (use "Cursor" and the appropriate
path like `.cursor/` mirroring the pattern used for `Kiro` and other entries).
There was a problem hiding this comment.
Good catch - will fix. Cursor was already a supported platform before this PR.
There was a problem hiding this comment.
@avifenesh, thank you for confirming! I appreciate the context that Cursor was already supported—the table will be complete once that row is added. 🐰
( •᷄ὢ•᷅)و ✨
| // Kiro detection | ||
| try { | ||
| const kiroPath = path.join(basePath, '.kiro'); | ||
| if (isDirectory(kiroPath)) { | ||
| _cachedStateDirs.set(cacheKey, '.kiro'); | ||
| return '.kiro'; | ||
| } | ||
| } catch { | ||
| // Ignore errors, continue detection | ||
| } |
There was a problem hiding this comment.
Add KIRO_HOME env detection before filesystem fallback.
Line 86 only checks project .kiro/. In Kiro sessions configured via KIRO_HOME without a local .kiro/, this can mis-detect as .claude.
Proposed fix
// Codex detection
if (process.env.CODEX_HOME) {
_cachedStateDirs.set(cacheKey, '.codex');
return '.codex';
}
+ // Kiro detection via env var
+ if (process.env.KIRO_HOME) {
+ _cachedStateDirs.set(cacheKey, '.kiro');
+ return '.kiro';
+ }
+
// Check for .codex directory in project
try {
const codexPath = path.join(basePath, '.codex');🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/platform/state-dir.js` around lines 86 - 95, Detection currently only
checks the local .kiro directory and can mis-detect Kiro sessions configured via
the KIRO_HOME env var; update the detection in the block that computes kiroPath
(references: _cachedStateDirs, cacheKey, isDirectory, kiroPath) to first check
process.env.KIRO_HOME (resolve it to an absolute path or join with basePath if
necessary) and, if that directory exists and is a Kiro state dir, set
_cachedStateDirs.set(cacheKey, 'KIRO_HOME') (or the same '.kiro' token if you
prefer consistency) and return immediately; keep the existing filesystem .kiro
fallback only after the env check and preserve the try/catch behavior.
There was a problem hiding this comment.
Noted - Kiro sessions may set KIRO_HOME. Adding env detection is tracked for a follow-up. The current filesystem check matches our pattern for Cursor (.cursor/) and works for project-scoped usage.
There was a problem hiding this comment.
@avifenesh, understood! Thanks for the context. The filesystem-only approach makes sense as a consistent pattern across project-scoped platforms (Cursor and Kiro), and deferring the KIRO_HOME env detection to a follow-up keeps this PR focused.
If you'd like, I can help create a follow-up issue to track the KIRO_HOME env detection enhancement, or if you've already opened one, feel free to reference it here for visibility.
✏️ Learnings added
Learnt from: avifenesh
Repo: agent-sh/agentsys PR: 278
File: lib/platform/state-dir.js:86-95
Timestamp: 2026-03-02T13:13:31.672Z
Learning: For Kiro platform detection in lib/platform/state-dir.js: the current implementation intentionally uses filesystem-only detection (checking for .kiro/ directory) to match the pattern used for Cursor (.cursor/), supporting project-scoped usage. KIRO_HOME env variable detection is tracked for a follow-up enhancement.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (2)
lib/platform/state-dir.js:90
- getStateDir() now detects
.kiro, but the module header + detection-order comment above still describes only Claude/OpenCode/Codex and doesn’t mention the new Kiro step. Please update those comments so the documentation matches the actual detection behavior.
// Kiro detection
try {
const kiroPath = path.join(basePath, '.kiro');
if (isDirectory(kiroPath)) {
_cachedStateDirs.set(cacheKey, '.kiro');
lib/adapter-transforms.js:547
- The docstring for transformAgentForKiro() says it parses
model/tools, but the implementation only emits{name, description, prompt, tools, resources}and ignoresmodel. Either implement model mapping into the generated JSON (if supported/desired) or update the comment to match the actual output to avoid misleading future changes.
/**
* Transform agent markdown+frontmatter to Kiro JSON format.
*
* Parses frontmatter for name/description/model/tools, uses body as prompt.
* Returns a JSON string matching Kiro's agent schema.
*/
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| | Cursor | `.cursor/` | | ||
| | Kiro | `.kiro/` | | ||
|
|
||
| The plugin auto-detects the platform and uses the appropriate directory. Override with `AI_STATE_DIR` environment variable. |
There was a problem hiding this comment.
This section states Cursor state is stored in .cursor/ and that the platform is auto-detected. However, lib/platform/state-dir.js currently auto-detects .opencode, .codex, and now .kiro, but not .cursor—so Cursor state will still default to .claude/ unless AI_STATE_DIR is set explicitly. Please either update state-dir detection to include .cursor or adjust the documentation to reflect the actual behavior.
| | Cursor | `.cursor/` | | |
| | Kiro | `.kiro/` | | |
| The plugin auto-detects the platform and uses the appropriate directory. Override with `AI_STATE_DIR` environment variable. | |
| | Cursor | `.claude/` (default; set `AI_STATE_DIR` to change) | | |
| | Kiro | `.kiro/` | | |
| The plugin auto-detects supported platforms (Claude Code, OpenCode, Codex CLI, Kiro) and uses the appropriate directory. For Cursor, state defaults to `.claude/` unless you set the `AI_STATE_DIR` environment variable explicitly. |
| - Claude Code: `.claude/` | ||
| - OpenCode: `config/.opencode/` | ||
| - Codex: `.codex/` | ||
| - Cursor: `.cursor/` |
There was a problem hiding this comment.
This lists Cursor as using .cursor/ for the platform-aware state directory, but lib/platform/state-dir.js does not detect .cursor (only .opencode/.codex/.kiro/.claude). Unless Cursor state-dir detection is added elsewhere, this is misleading—either add .cursor detection or remove Cursor from this state-dir list.
| - Cursor: `.cursor/` |
| | Kiro | `.kiro/` (project-scoped) | `.kiro/` | | ||
|
|
There was a problem hiding this comment.
This platform table adds Kiro but still omits Cursor, even though this page now states there are five supported platforms (including Cursor). Either add a Cursor row (with its project-scoped config/state locations) or adjust the surrounding text so the table and narrative stay consistent.
| | Kiro | `.kiro/` (project-scoped) | `.kiro/` | | |
| | Cursor | `.cursor/` (project-scoped) | `.cursor/` | | |
| | Kiro | `.kiro/` (project-scoped) | `.kiro/` | |
| function transformAgentForKiro(content, options) { | ||
| const { pluginInstallPath } = options || {}; | ||
|
|
||
| const frontmatter = discovery.parseFrontmatter(content); | ||
| let body = content; | ||
| if (content.startsWith('---')) { | ||
| const endIdx = content.indexOf('\n---', 3); | ||
| if (endIdx !== -1) { | ||
| body = content.substring(endIdx + 4).replace(/^\n/, ''); | ||
| } | ||
| } | ||
|
|
||
| if (pluginInstallPath) { | ||
| body = body.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, () => pluginInstallPath); | ||
| body = body.replace(/\$CLAUDE_PLUGIN_ROOT/g, () => pluginInstallPath); | ||
| body = body.replace(/\$\{PLUGIN_ROOT\}/g, () => pluginInstallPath); | ||
| body = body.replace(/\$PLUGIN_ROOT/g, () => pluginInstallPath); | ||
| } | ||
|
|
||
| body = body.replace(/(?:next-task|deslop|ship|sync-docs|audit-project|enhance|perf|repo-map|drift-detect|consult|debate|learn|web-ctl):([a-z][a-z0-9-]*)/g, '$1'); | ||
|
|
||
| const agent = { | ||
| name: frontmatter.name || '', |
There was a problem hiding this comment.
transformAgentForKiro() re-implements frontmatter parsing inline. This duplicates lib/discovery.parseFrontmatter() and is less capable (e.g., YAML arrays / multi-line values) which can silently drop fields like tools. Consider reusing discovery.parseFrontmatter() (or extracting a shared parser) so agent transforms stay consistent with command/agent discovery and keep the prototype-pollution guards in one place.
| - Claude Code: `.claude/` | ||
| - OpenCode: `config/.opencode/` | ||
| - Codex: `.codex/` | ||
| - Cursor: `.cursor/` | ||
| - Kiro: `.kiro/` |
There was a problem hiding this comment.
This lists Cursor as using .cursor/ for the platform-aware state directory, but lib/platform/state-dir.js does not detect .cursor (only .opencode/.codex/.kiro/.claude). Unless Cursor state-dir detection is added elsewhere, this is misleading—either add .cursor detection or remove Cursor from this state-dir list.
Summary
.kiro/skills/<name>/SKILL.md(drop-in compatible).kiro/steering/<name>.mdwithinclusion: manualfrontmatter.kiro/agents/<name>.json.kiro/directory exists (project-scoped like Cursor)Changes
Core (5 files):
bin/cli.js-installForKiro(), VALID_TOOLS, detection, routing, help text, interactive promptlib/adapter-transforms.js-transformSkillForKiro(),transformCommandForKiro(),transformAgentForKiro()lib/discovery/index.js-getKiroSteeringMappings()with kiro-description fallback chainlib/cross-platform/index.js- PLATFORMS.KIRO, STATE_DIRS, detectPlatform, INSTRUCTION_FILESlib/platform/state-dir.js-.kirodirectory detection, getPlatformNameDev scripts (2 files):
scripts/dev-install.js-installKiro(), validTools, cleanAllscripts/gen-adapters.js- Kiro adapter generation (steering, agents, skills)Tests (4 files):
__tests__/cli-args.test.js- installForKiro test suite (8 tests)__tests__/cross-platform.test.js- KIRO platform constants and detection__tests__/state-dir.test.js- .kiro directory detection and priority__tests__/gen-adapters.test.js- Kiro transforms (16 tests) + steering mappings (3 tests)Docs (5 files):
Test plan
inclusion: manualfrontmatter['read'](least privilege).kiro/directoryCloses #276
Summary by CodeRabbit
New Features
Documentation