From 00805b396cf7dac50ccfddc3aebf23e61417301f Mon Sep 17 00:00:00 2001 From: lxcario Date: Tue, 23 Jun 2026 21:27:51 +0300 Subject: [PATCH] feat(agent): add kiro as an install target --- DOCUMENTATION.md | 5 +++-- README.md | 4 ++-- src/lib/agent-targets.test.ts | 41 +++++++++++++++++++++++++++++++---- src/lib/agent-targets.ts | 8 ++++++- 4 files changed, 49 insertions(+), 9 deletions(-) diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 3746f46..0217376 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -112,11 +112,12 @@ testsprite agent install claude # install the skill for Claude Code testsprite agent install codex # install into AGENTS.md for Codex (managed-section) testsprite agent install cursor # .cursor/rules/testsprite-verify.mdc testsprite agent install cline # .clinerules/testsprite-verify.md +testsprite agent install kiro # .kiro/skills/testsprite-verify/SKILL.md testsprite agent install antigravity # .agents/skills/testsprite-verify/SKILL.md -testsprite agent list # list all 5 targets with status + mode + path +testsprite agent list # list all 6 targets with status + mode + path ``` -Supported targets: `claude` (GA), `codex` (experimental), `cursor` (experimental), `cline` (experimental), `antigravity` (experimental). +Supported targets: `claude` (GA), `codex` (experimental), `cursor` (experimental), `cline` (experimental), `kiro` (experimental), `antigravity` (experimental). 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. diff --git a/README.md b/README.md index 2d90012..02afe03 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ npm install -g @testsprite/testsprite-cli testsprite setup ``` -`testsprite setup` prompts for your [API key](https://www.testsprite.com), verifies it, and installs the verification-loop skill for your coding agent (`claude`, `cursor`, `cline`, `antigravity`, `codex`, etc.) — one command, so your agent is wired to verify its own work. Non-interactive (CI / onboarding scripts): +`testsprite setup` prompts for your [API key](https://www.testsprite.com), verifies it, and installs the verification-loop skill for your coding agent (`claude`, `cursor`, `cline`, `kiro`, `antigravity`, `codex`, etc.) — one command, so your agent is wired to verify its own work. Non-interactive (CI / onboarding scripts): ```bash TESTSPRITE_API_KEY=sk-... testsprite setup --from-env --yes --agent claude @@ -110,7 +110,7 @@ Prefer to configure each step by hand (or learn the surface offline with `--dry- | | `test rerun` | Cheap replay of one/many tests (FE verbatim; BE with deps); `--all --project ` reruns all tests | | | `test wait` | Block on a `runId` until terminal | | | `test artifact get` | Download the failure bundle for a specific `runId` | -| **Agent** | `agent install` / `agent list` | Add or list coding-agent targets (pure-local): `claude`, `codex`, `cursor`, `cline`, `antigravity` | +| **Agent** | `agent install` / `agent list` | Add or list coding-agent targets (pure-local): `claude`, `codex`, `cursor`, `cline`, `kiro`, `antigravity` | > 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). diff --git a/src/lib/agent-targets.test.ts b/src/lib/agent-targets.test.ts index 2a2262c..fcacacc 100644 --- a/src/lib/agent-targets.test.ts +++ b/src/lib/agent-targets.test.ts @@ -60,20 +60,21 @@ testsprite test artifact get --out ./out/ // --------------------------------------------------------------------------- describe('TARGETS', () => { - it('has all five required keys', () => { + it('has all six required keys', () => { const keys = Object.keys(TARGETS).sort(); - expect(keys).toEqual(['antigravity', 'claude', 'cline', 'codex', 'cursor']); + expect(keys).toEqual(['antigravity', 'claude', 'cline', 'codex', 'cursor', 'kiro']); }); it('claude is GA', () => { expect(TARGETS.claude.status).toBe('ga'); }); - it('cursor, cline, antigravity, and codex are experimental', () => { + it('cursor, cline, antigravity, codex, and kiro are experimental', () => { expect(TARGETS.cursor.status).toBe('experimental'); expect(TARGETS.cline.status).toBe('experimental'); expect(TARGETS.antigravity.status).toBe('experimental'); expect(TARGETS.codex.status).toBe('experimental'); + expect(TARGETS.kiro.status).toBe('experimental'); }); it('each target has a non-empty POSIX path', () => { @@ -88,6 +89,7 @@ describe('TARGETS', () => { expect(TARGETS.antigravity.mode).toBe('own-file'); expect(TARGETS.cursor.mode).toBe('own-file'); expect(TARGETS.cline.mode).toBe('own-file'); + expect(TARGETS.kiro.mode).toBe('own-file'); }); it('codex target has mode managed-section', () => { @@ -255,16 +257,47 @@ describe('renderForTarget("cline")', () => { }); }); +describe('renderForTarget("kiro")', () => { + const result = renderForTarget('kiro', STUB_BODY); + + it('returns the correct path', () => { + expect(result.path).toBe('.kiro/skills/testsprite-verify/SKILL.md'); + }); + + it('frontmatter contains name: testsprite-verify', () => { + expect(result.content).toContain('name: testsprite-verify'); + }); + + it('frontmatter contains description:', () => { + expect(result.content).toContain(`description: ${SKILL_DESCRIPTION}`); + }); + + it('content ends with a trailing newline', () => { + expect(result.content.endsWith('\n')).toBe(true); + }); + + it('produces the same content as claude (same wrapSkill)', () => { + const claude = renderForTarget('claude', STUB_BODY); + expect(result.content).toBe(claude.content); + }); + + it('differs only in landing path from claude', () => { + const claude = renderForTarget('claude', STUB_BODY); + expect(result.path).not.toBe(claude.path); + }); +}); + // --------------------------------------------------------------------------- // Content integrity — load-bearing command strings must survive any body trim // --------------------------------------------------------------------------- describe('content integrity — own-file targets', () => { - const ownFileTargets: Array<'claude' | 'cursor' | 'cline' | 'antigravity'> = [ + const ownFileTargets: Array<'claude' | 'cursor' | 'cline' | 'antigravity' | 'kiro'> = [ 'claude', 'cursor', 'cline', 'antigravity', + 'kiro', ]; // Use the real body for these checks, since we're guarding against trimming. diff --git a/src/lib/agent-targets.ts b/src/lib/agent-targets.ts index fb83ca2..ac8c508 100644 --- a/src/lib/agent-targets.ts +++ b/src/lib/agent-targets.ts @@ -1,6 +1,6 @@ import { readFileSync } from 'node:fs'; -export type AgentTarget = 'claude' | 'cursor' | 'cline' | 'antigravity' | 'codex'; +export type AgentTarget = 'claude' | 'cursor' | 'cline' | 'antigravity' | 'codex' | 'kiro'; export interface TargetSpec { status: 'ga' | 'experimental'; @@ -58,6 +58,12 @@ export const TARGETS: Record = { mode: 'own-file', wrap: body => body, }, + kiro: { + status: 'experimental', + path: '.kiro/skills/testsprite-verify/SKILL.md', + mode: 'own-file', + wrap: wrapSkill, + }, /** * codex target — managed-section mode. *