Skip to content

Commit e1d00f1

Browse files
authored
feat(agent): add kiro as an install target (#10)
* feat(agent): add kiro as an install target Adds kiro as an own-file agent target on the current multi-skill model: AgentTarget union, a pathFor('kiro') case landing at .kiro/skills/<skill>/SKILL.md, and a TARGETS entry (experimental, own-file, wrapSkill frontmatter like claude/antigravity). Kiro installs both default skills (testsprite-verify + testsprite-onboard). Updates the --target help string, README/DOCUMENTATION target lists and counts, unit + command tests (six keys, list 12 rows, five own-file targets, 10 dry-run would-write lines, renderForTarget + content-integrity coverage), and regenerates the agent/setup help snapshots. Rebuilt on current main. * test(e2e): include kiro in target matrix guards and multi-target install The Local E2E Tests CI job failed because two matrix-coverage guards hardcoded the target set (claude, antigravity, cursor, cline, codex) and did not include the new kiro target. Add kiro (own-file, between cline and codex to match TARGETS order) to both guards, and add kiro to the multi-target install e2e so the target is actually exercised end-to-end.
1 parent 38e0f19 commit e1d00f1

9 files changed

Lines changed: 77 additions & 29 deletions

File tree

DOCUMENTATION.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,15 @@ testsprite agent install codex # install into AGENTS.md for Codex (managed-
113113
testsprite agent install cursor # .cursor/rules/testsprite-verify.mdc
114114
testsprite agent install cline # .clinerules/testsprite-verify.md
115115
testsprite agent install antigravity # .agents/skills/testsprite-verify/SKILL.md
116-
testsprite agent list # list all 5 targets with status + mode + path
116+
testsprite agent install kiro # .kiro/skills/testsprite-verify/SKILL.md
117+
testsprite agent list # list all 6 targets with status + mode + path
117118
```
118119

119-
Supported targets: `claude` (GA), `codex` (experimental), `cursor` (experimental), `cline` (experimental), `antigravity` (experimental).
120+
Supported targets: `claude` (GA), `codex` (experimental), `cursor` (experimental), `cline` (experimental), `antigravity` (experimental), `kiro` (experimental).
120121

121122
The `codex` target uses **managed-section mode** — it writes only a sentinel-delimited section inside your existing `AGENTS.md`, so your project instructions are never clobbered. Re-running without `--force` replaces the section in-place; user content outside the sentinels is always preserved.
122123

123-
Re-running with `--force` on **own-file targets** (claude, cursor, cline, antigravity) backs up the existing file to `<path>.bak` first.
124+
Re-running with `--force` on **own-file targets** (claude, cursor, cline, antigravity, kiro) backs up the existing file to `<path>.bak` first.
124125

125126
## Command reference
126127

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ Prefer to configure each step by hand (or learn the surface offline with `--dry-
110110
| | `test rerun` | Cheap replay of one/many tests (FE verbatim; BE with deps); `--all --project <id>` reruns all tests |
111111
| | `test wait` | Block on a `runId` until terminal |
112112
| | `test artifact get` | Download the failure bundle for a specific `runId` |
113-
| **Agent** | `agent install` / `agent list` | Add or list coding-agent targets (pure-local): `claude`, `codex`, `cursor`, `cline`, `antigravity` |
113+
| **Agent** | `agent install` / `agent list` | Add or list coding-agent targets (pure-local): `claude`, `codex`, `cursor`, `cline`, `antigravity`, `kiro` |
114114

115115
> The earlier command names — `init`, `auth configure`, `auth whoami`, `auth logout` — still work as hidden, deprecated aliases (each prints a one-line notice pointing at the new name), so existing scripts keep running. `auth configure` now runs the full `setup` (it also installs the skill).
116116

src/commands/agent.test.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,7 @@ describe('runList', () => {
741741
expect(out).toContain('cursor');
742742
expect(out).toContain('cline');
743743
expect(out).toContain('antigravity');
744+
expect(out).toContain('kiro');
744745
expect(out).toContain('codex');
745746
expect(out).toContain('ga');
746747
expect(out).toContain('experimental');
@@ -749,6 +750,7 @@ describe('runList', () => {
749750
expect(out).toContain(TARGETS.cursor.path);
750751
expect(out).toContain(TARGETS.cline.path);
751752
expect(out).toContain(TARGETS.antigravity.path);
753+
expect(out).toContain(TARGETS.kiro.path);
752754
expect(out).toContain(TARGETS.codex.path);
753755
});
754756

@@ -759,13 +761,14 @@ describe('runList', () => {
759761

760762
const json = JSON.parse(capture.stdout.join('\n')) as ListResult[];
761763
expect(Array.isArray(json)).toBe(true);
762-
// 5 targets × 2 default skills = 10 rows
763-
expect(json).toHaveLength(10);
764+
// 6 targets × 2 default skills = 12 rows
765+
expect(json).toHaveLength(12);
764766
const targets = json.map(r => r.target);
765767
expect(targets).toContain('claude');
766768
expect(targets).toContain('cursor');
767769
expect(targets).toContain('cline');
768770
expect(targets).toContain('antigravity');
771+
expect(targets).toContain('kiro');
769772
expect(targets).toContain('codex');
770773
// skill field present on each row
771774
const skills = json.map(r => r.skill);
@@ -905,11 +908,11 @@ describe('createAgentCommand wiring', () => {
905908
});
906909

907910
// ---------------------------------------------------------------------------
908-
// All four own-file targets installed at once
911+
// All five own-file targets installed at once
909912
// ---------------------------------------------------------------------------
910913

911-
describe('runInstall — all four own-file targets', () => {
912-
it('installs all four own-file targets in one invocation', async () => {
914+
describe('runInstall — all five own-file targets', () => {
915+
it('installs all five own-file targets in one invocation', async () => {
913916
const { store, fs: agentFs } = makeMemFs();
914917
const { capture, deps } = makeCapture();
915918

@@ -919,7 +922,7 @@ describe('runInstall — all four own-file targets', () => {
919922
output: 'text',
920923
debug: false,
921924
dryRun: false,
922-
target: ['claude', 'cursor', 'cline', 'antigravity'],
925+
target: ['claude', 'cursor', 'cline', 'antigravity', 'kiro'],
923926
skills: ['testsprite-verify'],
924927
force: false,
925928
},
@@ -937,11 +940,11 @@ describe('runInstall — all four own-file targets', () => {
937940
});
938941

939942
// ---------------------------------------------------------------------------
940-
// Dry-run for all four own-file targets
943+
// Dry-run for all five own-file targets
941944
// ---------------------------------------------------------------------------
942945

943946
describe('runInstall — dry-run all own-file targets', () => {
944-
it('writes nothing for any of the four own-file targets (default 2 skills = 8 would-write lines)', async () => {
947+
it('writes nothing for any of the five own-file targets (default 2 skills = 10 would-write lines)', async () => {
945948
const { store, fs: agentFs } = makeMemFs();
946949
const { capture, deps } = makeCapture();
947950

@@ -951,7 +954,7 @@ describe('runInstall — dry-run all own-file targets', () => {
951954
output: 'text',
952955
debug: false,
953956
dryRun: true,
954-
target: ['claude', 'cursor', 'cline', 'antigravity'],
957+
target: ['claude', 'cursor', 'cline', 'antigravity', 'kiro'],
955958
force: false,
956959
},
957960
{ cwd: CWD, fs: agentFs, ...deps },
@@ -961,9 +964,9 @@ describe('runInstall — dry-run all own-file targets', () => {
961964
const stderrOut = capture.stderr.join('\n');
962965
// Banner appears once
963966
expect(stderrOut).toContain('[dry-run] no files written');
964-
// 4 targets × 2 default skills = 8 would-write lines
967+
// 5 targets × 2 default skills = 10 would-write lines
965968
const wouldWriteLines = stderrOut.split('\n').filter(l => l.includes('would write'));
966-
expect(wouldWriteLines.length).toBe(8);
969+
expect(wouldWriteLines.length).toBe(10);
967970
});
968971
});
969972

src/commands/agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ export function createAgentCommand(deps: AgentDeps = {}): Command {
809809
)
810810
.option(
811811
'--target <t>',
812-
'Agent target(s): claude, cursor, cline, antigravity, codex (comma-separated or repeated)',
812+
'Agent target(s): claude, cursor, cline, antigravity, kiro, codex (comma-separated or repeated)',
813813
collect,
814814
[],
815815
)

src/lib/agent-targets.test.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,20 @@ testsprite test artifact get <run-id> --out ./out/
7474
// ---------------------------------------------------------------------------
7575

7676
describe('TARGETS', () => {
77-
it('has all five required keys', () => {
77+
it('has all six required keys', () => {
7878
const keys = Object.keys(TARGETS).sort();
79-
expect(keys).toEqual(['antigravity', 'claude', 'cline', 'codex', 'cursor']);
79+
expect(keys).toEqual(['antigravity', 'claude', 'cline', 'codex', 'cursor', 'kiro']);
8080
});
8181

8282
it('claude is GA', () => {
8383
expect(TARGETS.claude.status).toBe('ga');
8484
});
8585

86-
it('cursor, cline, antigravity, and codex are experimental', () => {
86+
it('cursor, cline, antigravity, kiro, and codex are experimental', () => {
8787
expect(TARGETS.cursor.status).toBe('experimental');
8888
expect(TARGETS.cline.status).toBe('experimental');
8989
expect(TARGETS.antigravity.status).toBe('experimental');
90+
expect(TARGETS.kiro.status).toBe('experimental');
9091
expect(TARGETS.codex.status).toBe('experimental');
9192
});
9293

@@ -102,6 +103,7 @@ describe('TARGETS', () => {
102103
expect(TARGETS.antigravity.mode).toBe('own-file');
103104
expect(TARGETS.cursor.mode).toBe('own-file');
104105
expect(TARGETS.cline.mode).toBe('own-file');
106+
expect(TARGETS.kiro.mode).toBe('own-file');
105107
});
106108

107109
it('codex target has mode managed-section', () => {
@@ -200,6 +202,22 @@ describe('renderForTarget("antigravity")', () => {
200202
});
201203
});
202204

205+
describe('renderForTarget("kiro")', () => {
206+
const result = renderForTarget('kiro', 'testsprite-verify', STUB_BODY);
207+
208+
it('returns the correct path', () => {
209+
expect(result.path).toBe('.kiro/skills/testsprite-verify/SKILL.md');
210+
});
211+
212+
it('frontmatter contains name: testsprite-verify', () => {
213+
expect(result.content).toContain('name: testsprite-verify');
214+
});
215+
216+
it('frontmatter contains description:', () => {
217+
expect(result.content).toContain(`description: ${SKILL_DESCRIPTION}`);
218+
});
219+
});
220+
203221
describe('renderForTarget("claude") vs renderForTarget("antigravity")', () => {
204222
it('produce the same frontmatter lines (name + description)', () => {
205223
const claude = renderForTarget('claude', 'testsprite-verify', STUB_BODY);
@@ -274,11 +292,12 @@ describe('renderForTarget("cline")', () => {
274292
// ---------------------------------------------------------------------------
275293

276294
describe('content integrity — own-file targets', () => {
277-
const ownFileTargets: Array<'claude' | 'cursor' | 'cline' | 'antigravity'> = [
295+
const ownFileTargets: Array<'claude' | 'cursor' | 'cline' | 'antigravity' | 'kiro'> = [
278296
'claude',
279297
'cursor',
280298
'cline',
281299
'antigravity',
300+
'kiro',
282301
];
283302

284303
// Use the real body for these checks, since we're guarding against trimming.

src/lib/agent-targets.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { readFileSync } from 'node:fs';
22

3-
export type AgentTarget = 'claude' | 'cursor' | 'cline' | 'antigravity' | 'codex';
3+
export type AgentTarget = 'claude' | 'cursor' | 'cline' | 'antigravity' | 'codex' | 'kiro';
44

55
export interface TargetSpec {
66
status: 'ga' | 'experimental';
@@ -140,6 +140,8 @@ export function pathFor(target: AgentTarget, skill: string): string {
140140
return `.cursor/rules/${skill}.mdc`;
141141
case 'cline':
142142
return `.clinerules/${skill}.md`;
143+
case 'kiro':
144+
return `.kiro/skills/${skill}/SKILL.md`;
143145
case 'codex':
144146
return 'AGENTS.md';
145147
}
@@ -170,6 +172,14 @@ export const TARGETS: Record<AgentTarget, TargetSpec> = {
170172
mode: 'own-file',
171173
wrap: (_name, _description, body) => body,
172174
},
175+
kiro: {
176+
status: 'experimental',
177+
path: pathFor('kiro', SKILL_NAME),
178+
mode: 'own-file',
179+
// kiro reads SKILL.md files with name/description frontmatter, same as
180+
// claude/antigravity, so it shares the wrapSkill wrapper.
181+
wrap: wrapSkill,
182+
},
173183
/**
174184
* codex target — managed-section mode.
175185
*

test/__snapshots__/help.snapshot.test.ts.snap

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ Write the TestSprite agent skills (verification loop + first-run onboarding)
2525
into a project for a coding agent
2626
2727
Options:
28-
--target <t> Agent target(s): claude, cursor, cline, antigravity, codex
29-
(comma-separated or repeated) (default: [])
28+
--target <t> Agent target(s): claude, cursor, cline, antigravity, kiro,
29+
codex (comma-separated or repeated) (default: [])
3030
--skill <name> Skill(s) to install: testsprite-verify, testsprite-onboard
3131
(comma-separated or repeated; default: all) (default: [])
3232
--dir <path> Project root to write into (default: cwd)
@@ -112,7 +112,8 @@ Options:
112112
--from-env Read TESTSPRITE_API_KEY from the environment instead of
113113
prompting (default: false)
114114
--agent <target> Coding-agent target to install: claude, antigravity,
115-
cursor, cline, codex (default: claude) (default: "claude")
115+
cursor, cline, kiro, codex (default: claude) (default:
116+
"claude")
116117
--no-agent Skip the agent skill install (configure credentials only)
117118
--force Overwrite an existing skill file (a .bak backup is kept)
118119
--dir <path> Project root for the skill install (default: current

test/e2e/agent-install.e2e.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -430,13 +430,13 @@ describe('dry-run', () => {
430430
// ---------------------------------------------------------------------------
431431

432432
describe('multi-target install', () => {
433-
it('--target=claude,cursor,cline,antigravity,codex writes all targets + skills, exit 0', () => {
433+
it('--target=claude,cursor,cline,antigravity,kiro,codex writes all targets + skills, exit 0', () => {
434434
const tmpDir = freshTmpDir();
435435

436436
const result = runCli([
437437
'agent',
438438
'install',
439-
'--target=claude,cursor,cline,antigravity,codex',
439+
'--target=claude,cursor,cline,antigravity,kiro,codex',
440440
'--dir',
441441
tmpDir,
442442
'--output',
@@ -449,7 +449,7 @@ describe('multi-target install', () => {
449449
action: string;
450450
path: string;
451451
}>;
452-
const allTargets: AgentTarget[] = ['claude', 'cursor', 'cline', 'antigravity', 'codex'];
452+
const allTargets: AgentTarget[] = ['claude', 'cursor', 'cline', 'antigravity', 'kiro', 'codex'];
453453

454454
for (const target of allTargets) {
455455
if (TARGETS[target].mode === 'managed-section') {
@@ -819,7 +819,14 @@ describe('agent list', () => {
819819
// ---------------------------------------------------------------------------
820820
describe('matrix coverage guard', () => {
821821
it('TARGETS matches the documented, e2e-covered set (update this list when adding a target)', () => {
822-
expect(Object.keys(TARGETS)).toEqual(['claude', 'antigravity', 'cursor', 'cline', 'codex']);
822+
expect(Object.keys(TARGETS)).toEqual([
823+
'claude',
824+
'antigravity',
825+
'cursor',
826+
'cline',
827+
'kiro',
828+
'codex',
829+
]);
823830
});
824831

825832
it('SKILLS matches the documented, e2e-covered set (update this list when adding a skill)', () => {

test/e2e/setup.e2e.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,14 @@ describe('deprecated `init` alias', () => {
222222

223223
describe('matrix coverage guard', () => {
224224
it('TARGETS matches the documented set (update this list when adding a target)', () => {
225-
expect(Object.keys(TARGETS)).toEqual(['claude', 'antigravity', 'cursor', 'cline', 'codex']);
225+
expect(Object.keys(TARGETS)).toEqual([
226+
'claude',
227+
'antigravity',
228+
'cursor',
229+
'cline',
230+
'kiro',
231+
'codex',
232+
]);
226233
});
227234
});
228235

0 commit comments

Comments
 (0)