Once your app works locally, publish it to the App Store registry so other Crew users can install it with one click. This page covers the workflow end to end.
Create an app directory with an app.json at the root. Refer to:
app.json fieldVerify the manifest validates:
kirocrew doctor # or: curl http://localhost:5476/api/apps/my-app/manifest | python3 -m json.tool
# Build UI if you have one cd my-app/ui && npm install && npm run build && cd .. # Install via REST API curl -X POST http://localhost:5476/api/apps/install \ -H 'Content-Type: application/json' \ -d '{"source": "./my-app"}' # Enable it curl -X POST http://localhost:5476/api/apps/my-app/enable
Or use the App Store UI in the dashboard to install from a local path.
kirocrew token → open the URL)# Check the app is registered curl http://localhost:5476/api/apps | python3 -m json.tool # Check the manifest is valid curl http://localhost:5476/api/apps/my-app/manifest | python3 -m json.tool # Tail gateway logs kirocrew logs -f
# Edit code vim ui/src/App.tsx # Rebuild UI cd ui && npm run build && cd .. # Update the installed app curl -X POST http://localhost:5476/api/apps/my-app/update
For faster iteration, use kirocrew app dev my-app — the gateway hot-reloads the app on file changes.
Before submitting to the registry:
app.json passes validationname is kebab-case, globally unique, descriptiveversion follows semver (1.0.0)displayName and description are clear and conciseauthor is settags help with discoverypermissions are minimal — only declare what you actually useui/dist/index.mjs)SKILL.md files have proper frontmatterREADME.md explains what the app does and how to use itsetup.onInstall, test it on a clean machineWhen Crew installs or updates your app, it copies the source tree into ~/.kiro/crew/apps/{name}/ with two safeguards:
node_modules, .git, __pycache__, .venv (at any depth) are dropped from the installed copy.Serve your UI from the committed ui/dist/ bundle. Nothing your app needs at runtime may live under those excluded names.
Your app should live in its own git repo (or a subdirectory of an existing repo):
MyAppRepo/ ├── app.json ├── agents/ ├── skills/ ├── ui/ │ ├── src/ │ └── dist/index.mjs ← committed build artifact ├── scripts/ │ └── install.sh ← optional; invoked by setup.onInstall in app.json └── README.md
App icon:
assets/icon/logo.png)Screenshots:
Hero images (16:9 recommended, 1200×675):
heroImage — light themeheroImageDark — dark themeheroImageDetail / heroImageDetailDark — detail-page banner (25:6, 1200×288)The App Store serves icons and images via a git blob proxy — no CDN or external hosting needed.
The App Registry is src/kiro_crew/apps/app-registry.json in the Crew repo. Adding your app means opening a pull request.
[ { "name": "my-app", "gitUrl": "https://github.com/yourname/my-app", "branch": "main" } ]
If your app is in a subdirectory of a larger repo:
{ "name": "my-app", "gitUrl": "https://github.com/yourname/monorepo", "branch": "main", "subdirectory": "apps/my-app" }
| Field | Required | Description |
|---|---|---|
name | Yes | Must match the name in your app.json |
gitUrl | Yes | Any git-cloneable URL (https://…, git@…) |
branch | Yes | Branch to clone from (usually main) |
subdirectory | No | Path within the repo if app.json isn't at root |
resources | No | "gateway" (default) or "app" — see below |
lifecycle | No | "gateway" (default), "app", or "locked" |
detectInstalled | No | Shell command that exits 0 if the app is already installed (for self-managed apps) |
The repo field is the legacy shorthand for gitUrl and is still accepted.
The registry entry is intentionally minimal — your app.json is the single source of truth. All display info (description, screenshots, highlights, tags, version) is fetched from your repo. You don't sync metadata between your repo and the registry.
cd /path/to/Crew git checkout -b add-my-app # Edit src/kiro_crew/apps/app-registry.json git add src/kiro_crew/apps/app-registry.json git commit -m "feat(apps): add my-app to registry" git push origin add-my-app # Open a PR titled "Add my-app to App Store registry"
app.json is valid and completeTeams can host their own app registries without requiring Crew team review. Users opt in by adding external registries to their config:
{ "registries": [ {"name": "team-a", "repo": "TeamAKirocrewAppRegistry", "branch": "main"} ] }
The registry entry file at the root of the registry repo mirrors the format above (an array of app entries).
Trust model: user explicitly opts in by adding the registry to their config. The repo must be accessible via git.
Management API (/api/apps/registries):
| Method | Purpose |
|---|---|
GET /api/apps/registries | Returns current registries list from config |
PUT /api/apps/registries | Validates and replaces the registries array |
Input validation: repo and branch names are validated against strict regex patterns to reject path traversal. The Crew repo itself is blocked.
After your PR merges:
setup.onInstall if defined~/.kiro/crew/apps/my-app/Push an update:
version in app.jsonUsers can update via the App Store UI (refresh button) or REST API:
curl -X POST http://localhost:5476/api/apps/my-app/update
This re-clones, re-runs onUpdate, and re-registers resources.
Some apps manage their own installation and resource registration. They register with Crew for App Store visibility but handle their own lifecycle.
{ "name": "my-desktop-app", "repo": "MyDesktopApp", "branch": "mainline", "resources": "app", "lifecycle": "app", "detectInstalled": "test -d ~/Applications/MyDesktopApp.app" }
Self-managed apps:
POST /api/apps/register at runtimeApps declare minCrewVersion in app.json. Install and update check this — if the current version is too old, the operation is rejected with a clear error message telling the user to update Crew first.
Apps can use npm (for TypeScript/React) or pip (for Python) as their build system. The App Store clones the repo and runs the appropriate build command.
~/.kiro/crew/app-sources/{name}/npm install && npm run build (JS/TS) or pip install . (Python)setup.onInstall runs after build (for post-build steps like electron-builder)Because the standard build step handles dependency resolution and compilation, your onInstall script should only do post-build packaging:
#!/usr/bin/env bash set -euo pipefail [ -d "node_modules" ] || exit 1 # sanity check — build should have run npx electron-builder --mac --dir cp -R release/mac-arm64/MyApp.app ~/Applications/MyApp.app
major.minor.patchpatch for bug fixesminor for new featuresmajor for breaking changes (agent config schema, MCP tool interface)The registry entry has no version field — just bump app.json and push. The App Store fetches the latest app.json from your repo (cached 24h, or immediately on user refresh).
| Stage | Command / action |
|---|---|
| Create | Create app directory with app.json |
| Build UI | cd ui && npm run build |
| Install locally | POST /api/apps/install or App Store UI |
| Enable | POST /api/apps/{name}/enable |
| Test | Open dashboard, verify UI + agents + crons |
| Submit | Add to app-registry.json, open a PR |
| User install | App Store → Browse → Install |
| Update | Bump version, push, users re-install |
app-store
Publishing & guidelines