Note
This tool is and unoffical helper cli for managing and sharing kiro{cli|ide|web|crew} configurations such as agents, skills, steerings etc.
The unoffical kirokit Go CLI plus repository layout that lets multiple teams own their own Kiro CLI v3 config sources as templates. Each team's agents, hooks, prompts, skills, and steering docs are rendered deterministically into Kiro's real config layout, MCP server versions are pinned centrally, secrets are injected via SOPS at MCP-launch time (never baked into any committed or rendered file), and the rendering pipeline is locked down with golden-file tests that run in CI.
Homebrew installs only the kirokit binary. The team configs (teams/, global/, env/) live in this repository, so kirokit setup downloads it first, onto its newest release. kirokit reads the configs from the folder KIROKIT_REPO names (else the current directory). The example platform-team also launches SOPS-secured MCP servers, so install sops (brew install sops) and have a key that can decrypt its secrets file before starting the agent.
# install the binary via homebrew
brew install --cask mavogel/tap/kirokit
kirokit version
# download the team configs (newest v* tag) and point kirokit at them;
# use your team's repository address instead of the example one
kirokit setup --source https://github.com/mavogel/kirokit.git --target ~/.kirokit-config
export KIROKIT_REPO=~/.kirokit-config
# let's backup first, if you have used it
export KIRO_HOME_BACKUP=$KIRO_HOME
# create a temporary KIRO_HOME directory
# which kiro respects. No worries it will not
# break your existing setup
export KIRO_HOME=$(mktemp -d)
echo $KIRO_HOME
# install it in $KIRO_HOME
kirokit install platform-team
# now you have the devops agent from the platform-team
kiro-cli chat --agent devops --v3and see the example skill jira-triage in the devops agent
cleanup and verify KIRO_HOME is still set to the temporary directory
echo $KIRO_HOME
rm -rf $KIRO_HOME
# reset to the old value
export KIRO_HOME=$KIRO_HOME_BACKUP
echo $KIRO_HOMEFor less technial folks, who likes UIs. It does the same job and uses the cli under the hood:
The macOS app bundles kirokit, so there is nothing else to install and no terminal needed.
brew install --cask mavogel/tap/kirokit-desktop- Open kirokit. On first launch, enter your team's repository address (for the example configs,
https://github.com/mavogel/kirokit.git) and press Clone. You can also choose the folder it is cloned into. - On Teams, press Install on your team. The app first shows what it needs (SOPS, the decryption key, Kiro CLI), then runs
kirokit install <team>. - Start the agent as above:
kiro-cli chat --agent devops --v3.
Install and sync keeps the installed team up to date, and Settings holds the Kiro home folder and the repository. Every button command runs in the terminal dock, so you see exactly what kirokit does. Desktop app has the details.
This repository has the configs, the cli and desktop app. It can be used as a baseline for the configuration as follows:
teams/<team>/manifest.yaml # team identity + global steering opt-ins + externalSkills
teams/<team>/agents/<agent>/agent.json.tmpl
teams/<team>/agents/<agent>/env/secrets.enc.yaml # optional, SOPS-encrypted
teams/<team>/hooks/*.json.tmpl # shared across the team's agents
teams/<team>/prompts/*.md.tmpl
teams/<team>/skills/<skill>/SKILL.md.tmpl
teams/<team>/steerings/*.md.tmpl
teams/<team>/external/skills/<name>/ # vendored external skills (generated, never hand-edit)
teams/<team>/external/skills.lock.yaml # generated
global/steerings/*.md # shared steerings teams opt into
env/mcp-versions.yaml # central MCP version pins
testdata/golden/<team>/rendered/... # committed golden output tree
testdata/golden/<team>/context.json # committed golden template-context snapshot
- Template sources use a double extension
<ext>.tmpl; the renderer globs**/*.tmpland strips.tmpl. - Agent source
agents/<agent>/agent.json.tmplrenders to flatagents/<agent>.json(Kiro CLI v3 layout). - Paths
teams/,global/steerings/,env/mcp-versions.yamlandtestdata/golden/resolve against the cwd, or againstKIROKIT_REPOwhen that environment variable is set (a relative value is made absolute once at start; empty means unset). WithKIROKIT_REPO=<checkout>every command (render,test,update,install,sync,onboard,skills fetch) runs from any folder. There is no repo-root discovery. - Example teams:
platform-team(devops,platformdev; SOPS MCP servers; opted intosecurity-baseline) anddata-team(javadevwith SOPS Snowflake MCP,godevwith plain MCP; no global steerings). Copy one or usekirokit onboard; duplication between teams is expected. - Prompts are slash commands: Kiro v3 lists every file in
$KIRO_HOME/prompts/(@nameon other engines) even if no agent uses it asprompt;$ARGUMENTS/${1}are filled from user input. Example:teams/platform-team/prompts/create-steering.md.tmplbecomes/create-steering [focus].
team: my-team
globalSteerings:
- security-baseline # must match global/steerings/<slug>.md; [] opts out (see teams/data-team)
externalSkills: # optional, see "External Skills"
- source: https://github.com/anthropics/skills.git # git URL, no credentials
ref: <commit-sha> # tag, branch or commit
paths: # skill directories inside the repo; each is named after its last element
- skills/pdf
- skills/xlsx
- source: https://github.com/anthropics/skills.git
ref: <commit-sha>
paths:
- skills/frontend-design
name: design # optional; only with a single path
- source: https://github.com/anthropics/skills.git
ref: <commit-sha>
paths:
- skills # directory of skills
all: true # vendor each subdirectory containing SKILL.md
exclude: # optional; skip these skill directories (single path only)
- docx- Each opted-in global steering renders to
$KIRO_HOME/kirokit-steerings/<team>/<slug>.mdand is appended (created if missing) to theresourcesof every agent of the team asfile://$KIRO_HOME/kirokit-steerings/<team>/<slug>.md. It is not written to Kiro's auto-loadedsteering/dir, so only opted-in teams load it. Start the file with steering front matter (e.g.inclusion: always). {{ globalSteering "<slug>" }}inlines a steering, front matter included; prefer the automatic resource.- Every agent of every team also gets
file://.kiro/steering/**/*.mdandskill://.kiro/skills/**/SKILL.mdinresources, after global steerings. Relative paths load the steering/skills of the workspace Kiro starts in.skill://loads metadata at startup, the rest on demand. Existing identical entries are not duplicated.
Vendor skills from other git repos (e.g. anthropics/skills), pinned by ref. An entry vendors the skill directories listed in paths from one repo; use all: true when each path is a directory of skills.
kirokit skills fetch <team> # no team = every team declaring external skills
kirokit update <team> # refresh goldens, review and commit the diffskills fetchresolvesrefto a commit with the localgit, copies the directory byte for byte toteams/<team>/external/skills/<name>/, and records source, ref, commit and content hash inteams/<team>/external/skills.lock.yaml. Commit both. It is the only networked step;render,test,install,syncare offline and need no credentials. If any entry fails, nothing changes.- Render/install places files at
$KIRO_HOME/skills/<name>/and addsskill://$KIRO_HOME/skills/<name>/SKILL.mdto every agent'sresources. Executable bits are kept. Vendored files bypass the template engine (*.tmpland{{ }}arrive intact). paths: required; one entry per repo andrefcovers every listed path, and each skill is named after its path's last element. Two paths ending in the same name are a duplicate-name error.nameneeds a single path.all: true: each path inpathsis a directory of skills; each immediate subdirectory with aSKILL.mdis vendored, others ignored;excludeskips directories (single path only);namecannot be combined withall. Each skill has its own lock entry (fromnames the collection) andskill://resource. New upstream skills appear only on re-fetch at a newerref; a longerexcluderemoves skills on re-fetch. Anexcludenaming a nonexistent directory is an error.- Update: change
ref(or re-runfetchto advance a branch ref), runskills fetch, review the vendored diff. Dropped files are removed. - No tags upstream:
anthropics/skillshas none; pin a commit SHA or follow a branch. The lock records the exact commit regardless. - Drift is an error: if
manifest.yaml, the lock and the vendored files disagree (hand edit, changedrefwithout re-fetch, directory with no entry),render,test,install,syncfail naming the skill. Never editteams/<team>/external/**. - Name collisions: an external skill named like a team
skills/<name>or another external skill is an error (also at fetch); setnameon a single-path entry orexcludeit from the collection. - Private sources: SSH URL with an agent, or HTTPS with a git credential helper (
gh auth setup-git). No credentials are stored; asourcecontaining credentials is rejected. Allowedsource:https,http,ssh,git,fileURL, scp-style address, or local path. Rejected: leading-(option),ext::(transport helper), and arefstarting with-or containing whitespace. Only the person runningskills fetchneeds access; on auth failure the error names the source and nothing is written. - Symlinks inside a skill are rejected;
SKILL.mdmust be at the skill root. - Licences: source licence files (e.g.
LICENSE.txt) are vendored with the skill. Check redistribution terms, especially withall: true; someanthropics/skillsentries carry restrictive terms, soexcludeor review them.
Flat map of MCP server name to pinned version:
jira-mcp: "1.4.2"
confluence-mcp: "2.1.0"- Templates read it with
{{ mcpVersion "jira-mcp" }}. An undefined key fails the render naming the key and file. context.jsonsnapshots the entire resolved map, so bumping any entry changes every team's golden context; runkirokit testto see the affected output.
Secrets never appear in plaintext in templates, rendered files or goldens.
-
Each agent needing a secret has
teams/<team>/agents/<agent>/env/secrets.enc.yaml(flat SOPS-encrypted YAML, safe to commit). -
agent.json.tmplwraps the MCP server insops exec-envviasopsCommand/sopsArgsJSON:"command": {{ sopsCommand }}, "args": {{ sopsArgsJSON (printf "%s/kirokit-secrets/<team>/<agent>/secrets.enc.yaml" .KiroHome) "<real-mcp-command>" "<real-arg>" ... }}The secrets path must be absolute and
.KiroHome-based, never repo-relative: it resolves under the installed$KIRO_HOME. -
sops exec-env <file> "<command>"decrypts and launches the command with the keys as env vars at MCP launch time, not render time, and not via Kiro's${VAR}substitution (unreliable). -
kirokit installcopies each secrets file to$KIRO_HOME/kirokit-secrets/<team>/<agent>/secrets.enc.yaml.
Remote (HTTP) MCP servers (e.g. Atlassian https://mcp.atlassian.com/v2/mcp) run through the pinned mcp-remote proxy under the same wrapper. The secret holds the full header value (Bearer <key> for a service-account key, Basic <base64(email:token)> for a personal token); the template passes --header 'Authorization:${ATLASSIAN_AUTH}'. The single quotes keep the placeholder literal in the rendered file; mcp-remote expands it from the env sops sets. See teams/platform-team/agents/devops/agent.json.tmpl.
Atlassian prerequisites:
- An org admin must enable Allow API token authentication in Atlassian Administration (Rovo → Rovo MCP server → Authentication); otherwise every call fails with "You don't have permission to connect via API token".
- Create the token on the MCP token page; it needs the "… via Agent Interface" scopes (e.g.
search:jira:agent-interface), which the generic token page does not offer. Details: Configure authentication via API token. - Jira Cloud rejects unbounded JQL; restrict searches (e.g.
project = KEY).
Keys: decrypting needs a SOPS key (age, PGP, KMS) listed as a recipient in .sops.yaml. Each team has its own creation_rules entry with its age public key(s); a team key cannot decrypt another team's secrets. Never commit private keys (e.g. keep them in ~/.config/sops/age/<team>.txt).
SOPS_AGE_KEY_FILE=~/.config/sops/age/platform-team.txt sops teams/platform-team/agents/devops/env/secrets.enc.yaml # edit
age-keygen -o ~/.config/sops/age/my-team.txt # new team keyAdd an engineer: append their age public key (comma-separated) to the team's rule, then sops updatekeys <file> per secrets file. Committed secret values are placeholders; replace via sops <file>.
kirokit render [team] --out <dir> # render only, never touches testdata/golden/
kirokit test [team] # render + diff against testdata/golden/<team>/ (CI entry point)
kirokit update [team] # render + overwrite testdata/golden/<team>/No [team] = every directory under teams/. test exits non-zero and lists each mismatched file. update regenerates testdata/golden/<team>/rendered/ and context.json; review the diff before committing.
kirokit onboard <team>Scaffolds teams/<team>/ from an embedded starter: manifest.yaml with the team name, one example agent (agents/example-agent/), empty hooks/, prompts/, skills/, steerings/. Errors if teams/<team>/ exists. Copying another team's directory works too.
kirokit install <team>
kirokit install <team> --kb-agent <agent> --kb-source <folder> --kb-mount <path>
kirokit uninstall <team>installrenders with the real$KIRO_HOMEbaked in (KIRO_HOMEenv var, else Kiro's default~/.kiro) and copies the rendered tree plus each agent's encrypted secrets file there.- A Kiro home holds one team.
install <team>first uninstalls every other installed team and printsuninstalled <other> from <home>for each, theninstalled <team> into <home>. Reinstalling the installed team removes nothing. If an uninstall fails, nothing is installed. uninstallremoves exactly what the matchinginstallwrote, tracked in$KIRO_HOME/.kirokit-installed/<team>.json; other teams' files and Kiro-managed files are untouched.--kb-*flags (all or none) attach a localknowledgeBaseresource to one installed agent:--kb-sourcelocal folder,--kb-mountsymlink location (never copied),--kb-agentagent whoseresourcesgets the entry. Entry: name<agent>-kb,indexType: "best",include: ["**/*.md"],autoUpdate: true.includeworks inkiro-clibut is not yet in Kiro's agent-config docs, and Kiro issues report agent-config knowledge bases are not always indexed automatically; check/knowledge show.uninstallremoves the symlink, never the source folder.
Before render --out <dir>, install or sync changes something on disk, the folder is kept as YYYYMMDD-HHmmss-<folder>.backup (local time) beside it:
render --out <dir>: an existing<dir>whose content would change is renamed toYYYYMMDD-HHmmss-<dir name>.backupand a fresh<dir>is rendered. A matching or empty<dir>is left alone.install/sync: each top-level folder in$KIRO_HOMEholding a file that would change (agents,prompts,skills,steering,hooks,kirokit-steerings,kirokit-secrets) is first copied to$KIRO_HOME/YYYYMMDD-HHmmss-<folder>.backup. Unchanged folders and Kiro's own data (sessions etc.) are not copied. The backup name differs from the live folder, so Kiro does not load it.- A no-change re-install or
syncmakes no backup. - Same-second collisions add a counter:
20261015-101530-2-agents.backup. - Backups are not in the install manifest;
uninstallneither removes nor restores them. - Deletion is not backed up:
syncremoves files the team stopped shipping anduninstallremoves everything the install wrote. Only manifest-listed files are ever removed.
brew install --cask mavogel/tap/kirokit
kirokit setup --source https://github.com/mavogel/kirokit.git --target ~/.kirokit-config
export KIROKIT_REPO=~/.kirokit-configsetupis the command-line twin of the desktop app's first launch:git clone -- <source> <target>, then a checkout of the newestv*tag (detached HEAD).--sourceand--targetare required.--branch <name>checks out that branch or tag instead (git clone --branch), skipping the newest-tag rule:kirokit setup --source <address> --target <folder> --branch main. It cannot be empty, start with-or contain whitespace, and an unknown name fails the clone. It is the same thingmanifest.yamlcallsreffor external skills, minus commit hashes.--sourceis anhttps,sshorfileaddress,git@host:path, or an absolute path; an address starting with-is refused.--targetmust not exist or must be an empty folder, so an existing clone is never overwritten (usesyncto update it).- Without
--branch, a repository with nov*tag is kept on its default branch andsetupprints that it has no release yet (exit 0). - It prints the
KIROKIT_REPOline to use next; then runinstallorsync.
kirokit sync <team> --repo ~/.kirokit-config
kirokit sync <team> --repo ~/.kirokit-config --latest-tag
kirokit sync --repo ~/.kirokit-config --latest-tag # no team: update the checkout onlysyncrunsgit fetch --tags, then a fast-forwardgit pullof the checked-out branch (with--latest-tag: checkout of the newestv*tag, so only releases reach the machine), then the same install askirokit install.- Unlike
install, it removes files the previous install wrote that the new version no longer ships, plus directories left empty. - Like
install,sync <team>uninstalls every other installed team (oneuninstalled <other> from <home>line each), but only after the checkout updated and<team>rendered; a failed pull or an unknown team leaves the installed team in place. - Without a team,
synconly updates the checkout and installs nothing: it printsupdated <before> -> <after>when HEAD moved, thenchecked out <tag or branch> at <commit>. It needs no Kiro home, and the--kb-*flags are an error (kirokit sync: --kb-agent needs a team). --repodefaults to$KIROKIT_REPO, else the cwd; an explicit--repowins overKIROKIT_REPO.--kb-*flags behave as forinstall; without them a previously attached knowledge base is dropped, so pass them on every run.
Migration: one machine now holds one team. A second scheduled sync of a different team (a second run of scripts/kirokit-sync-setup.sh --team <other>) replaces the first team on every run instead of installing beside it, so keep one scheduled sync per machine.
scripts/kirokit-sync-setup.sh clones the config repo, runs a first sync and registers a launchd agent (macOS) or systemd user timer (Linux):
scripts/kirokit-sync-setup.sh --team platform-team --repo-url git@github.com:org/kiro-config.git --latest-tag
scripts/kirokit-sync-setup.sh --team platform-team --dry-run ... # print the scheduler definition only
scripts/kirokit-sync-setup.sh --team platform-team --remove # unregister (checkout and installed config stay)Options: --dir (checkout, default ~/.kirokit-config), --interval (hours, default 4). Needs git and kirokit on PATH; logs to ~/.kirokit-sync.log; each user needs their own SOPS age key. Keep the checkout dedicated: a fast-forward pull fails on local commits and --latest-tag leaves a detached HEAD.
desktop/ holds a macOS app (Wails) for people who would rather press buttons than type commands. It bundles kirokit and runs the same commands, so what it writes is byte for byte what the command line writes. Install it with brew install --cask mavogel/tap/kirokit-desktop (GoReleaser attaches kirokit-desktop_<version>_darwin_arm64.dmg and ..._darwin_amd64.dmg to each release and publishes the cask next to the CLI's, in one macOS job; macOS 13 or later, Apple silicon and Intel; both DMGs hold the same universal app). The app is not signed or notarized, so the cask clears the quarantine attribute after install; if you open the DMG by hand, run xattr -dr com.apple.quarantine kirokit.app once, or macOS reports the app as damaged. Build it yourself with task desktop:build, or run it from source with task desktop:dev (task desktop:test runs its checks).
- First launch asks for your team's repository address (https, ssh,
file://,git@host:pathor an absolute path; an address starting with-is refused), runsgit clone -- <address> <repository folder>, thenkirokit sync --latest-tag, so the clone sits on the newestv*tag. A repository with nov*tag is kept and the app says it has no release yet. Saving a different address in Settings clones it the same way (progress in the kirokit tab, catalog refreshed on success) and renames the old clone torepo.old-<unix time>; a failed clone puts it back, keeps the old address stored and shows the failure beside the address field, so Save can retry the same address. The new address is stored only after its clone succeeds. The repository folder defaults to~/Library/Application Support/kirokit-desktop/repo; the first-launch page and Settings both let you choose another absolute folder (new or empty, or an existing git checkout; the home folder itself is refused). Saving a different folder in Settings shows the setup page so you can clone there; the old folder is left as it is. - Teams has an Install button per team. It first shows the checks it needs (SOPS, the decryption key, Kiro CLI, read from your login shell), then runs
kirokit install <team>. A Kiro home holds one team, so installing another team asks before it replaces the installed one. - Install and sync has Sync (
kirokit sync <team> --latest-tag) and Uninstall (kirokit uninstall <team>) per installed team; Settings has Update (kirokit sync --latest-tagfor the installed team, or without a team when none is installed). - Every button command runs in the dock's kirokit tab, where a git sign-in prompt can be answered; the zsh tab is your own login shell with the bundled
kirokitfirst onPATH. The app setsKIROKIT_REPOto its clone andKIRO_HOMEto the Kiro home folder saved in Settings. KIROKIT_REPOalso works on the command line: with it set,kirokit sync --latest-tag(no team) updates a checkout from any folder.
Migration: one machine now holds one team, in the app and on the command line alike. A second scheduled sync from scripts/kirokit-sync-setup.sh for another team replaces the first team on every run.

