Build, install, and run your first KiroCrew app in 5 minutes.
- KiroCrew installed and running (
kirocrew gateway) - Node.js 22+ (24 LTS recommended) (for apps with UI)
Create a new directory with an app.json manifest:
my-dashboard/
├── app.json ← App manifest (required)
├── agents/
│ └── sample-agent.json ← Agent definition
├── skills/
│ └── sample-skill/
│ └── SKILL.md ← Skill knowledge file
├── ui/ ← Frontend (if app has UI)
│ ├── package.json
│ ├── vite.config.ts
│ ├── src/App.tsx
│ └── .gitignore
└── README.md
Every app needs an app.json. See Manifest Reference for all fields.
{
"name": "my-dashboard",
"version": "0.1.0",
"displayName": "My Dashboard",
"description": "A KiroCrew app: My Dashboard",
"author": "yourname",
"agents": ["agents/sample-agent.json"],
"skills": ["skills/sample-skill"],
"ui": {
"entry": "dist/index.mjs",
"pages": [
{
"route": "/apps/my-dashboard",
"label": "My Dashboard",
"icon": "Package"
}
]
}
}Edit ui/src/App.tsx. Your app is a standard React component that uses
@kirocrew/app-sdk hooks and @kirocrew/app-sdk/ui shared components.
You do not
npm install@kirocrew/app-sdk. The dashboard host provides it (and React, ReactDOM, lucide-react) at runtime through its import map: the bare@kirocrew/app-sdkspecifier resolves to the host's vendored copy viawindow.__kirocrew_modules. This guarantees your app shares the host's exact React instance (so hooks work) and stays a small bundle. Mark these as externals in your build (don't bundle them).
import { useAppApi, useAppEvents } from '@kirocrew/app-sdk'
import { Card, CardTitle, PageHeader, StatCard } from '@kirocrew/app-sdk/ui'
import { useState, useEffect } from 'react'
export default function MyDashboard() {
const api = useAppApi()
const [data, setData] = useState(null)
useEffect(() => {
api.get('/api/status').then(setData)
}, [])
// Listen to real-time events
useAppEvents('notification', (event) => {
console.log('New notification:', event)
})
return (
<>
<PageHeader title="My Dashboard" subtitle="Custom app page" />
<div className="px-6 pb-8 overflow-y-auto flex-1 min-h-0">
<div className="grid gap-3.5 grid-cols-[repeat(auto-fit,minmax(150px,1fr))] mb-6">
<StatCard label="Status" value={data ? 'Online' : '...'} accent />
</div>
<Card>
<CardTitle>Content</CardTitle>
<p className="text-sm text-muted">Your app content here.</p>
</Card>
</div>
</>
)
}Edit agents/sample-agent.json to customize your agent:
{
"name": "my-agent",
"model": "auto",
"description": "Analyzes data and generates reports",
"prompt": "You are a data analyst assistant.",
"tools": ["@kirocrew-core"]
}Edit skills/sample-skill/SKILL.md to teach your agent domain knowledge.
cd my-dashboard/ui
npm install
npm run buildThis produces dist/index.mjs — the ESM bundle loaded by the dashboard.
Install via the KiroCrew dashboard REST API or the App Store UI:
# Via curl (REST API)
curl -X POST http://localhost:5476/api/apps/install \
-H 'Content-Type: application/json' \
-d '{"source": "/path/to/my-dashboard"}'
curl -X POST http://localhost:5476/api/apps/my-dashboard/enableOr open the KiroCrew dashboard → App Store → install from local path.
Your app now appears in the KiroCrew dashboard sidebar.
During development:
- Edit
ui/src/App.tsx - Run
cd ui && npm run build - Update the installed app:
curl -X POST http://localhost:5476/api/apps/my-dashboard/update
- Refresh the dashboard — changes are live
Agent and skill changes take effect on the next agent invocation (no rebuild needed).
Available in @kirocrew/app-sdk:
| Hook | Purpose |
|---|---|
useAppApi() |
Permission-scoped HTTP client (GET/POST/PUT/DELETE) |
useAppEvents(event, cb) |
Subscribe to real-time WebSocket events |
useTheme() |
Reactive theme (mode, accent, colorTheme) |
useAppInfo() |
App metadata (name, version, permissions) |
useNavigate() |
Navigate to KiroCrew routes |
useNotify() |
Show toast notifications |
useNavBadge() |
Update sidebar badge count |
useChatLauncher() |
Navigate to chat with optional agent and message |
Also in @kirocrew/app-sdk, for an app that renders agent messages itself. An agent puts follow-up
choices and steer acknowledgements inline in its prose ([OPTIONS: a | b],
[STEERING steer-<id>: …]); these parse them out so your UI can show buttons instead of raw syntax.
| Export | Purpose |
|---|---|
parseOptions(content) |
Split the prose from the choices offered with it |
deriveFollowUpOptions(messages, isStreaming) |
The choices that still apply to the conversation |
extractSteeringAcks(content) |
Pull the steer acknowledgement out of the text |
stripPartialOptionMarker(text) |
Hide a marker that is still arriving mid-stream |
Types: ParsedOptions, FollowUpDerivation, ChatMessage. React-free, so it also works in a worker
or a test. Worked examples: api-reference.md.
To render a transcript rather than parse one, ChatMessageList draws it and its row
registry lets you add a row type or replace one without forking the list — including the
roles the dashboard leaves undrawn. See
api-reference.md.
Available in @kirocrew/app-sdk/ui:
Card, CardTitle, Btn, SendBtn, Input, SearchInput, Badge,
AimBadge, StatCard, Skeleton, ContentSkeleton, EmptyState,
PageHeader, Toggle, InfoTip, SegmentedControl, MarkdownRenderer
Declare what your app can access in app.json:
{
"permissions": {
"api": ["/api/crons", "/api/status"],
"events": ["notification", "slots"],
"mcpTools": ["cron_add", "cron_list"],
"storage": true,
"cron": true,
"network": false
}
}The App SDK checks declared permissions before each request — accessing undeclared paths throws an error.
- Backend communication: Your dashboard UI can call your app's backend through the gateway reverse proxy at
/apps/{name}/api/*— no CORS issues. Verify requests withverifyProxyRequest()from the SDK. - See App Manifest Reference for all
app.jsonfields - See API Reference for TypeScript and Python client APIs
- See Publishing Guide for publishing to the App Store registry
For Python apps, CLI tools, or services that need to talk to KiroCrew Gateway:
pip install kirocrew-clientimport asyncio
from kirocrew_client import KiroCrewClient
async def main():
async with KiroCrewClient(app_name="my-tool") as mc:
# Check connectivity
ok = await mc.ping()
print(f"Gateway reachable: {ok}")
# Dispatch an agent
task_id = await mc.dispatch_agent_async("my-agent", "Analyze ticket T-123")
result = await mc.get_task_result(task_id)
print(f"Result: {result}")
# Manage crons
await mc.add_cron("refresh", message="Check for updates", every=3600)
crons = await mc.list_crons()
# Inject silent context (for background info)
await mc.inject_context("slot-id", "PR #456 was approved", source="watch")
asyncio.run(main())The kirocrew-client package is async (uses aiohttp) and standalone — no
dependency on the KiroCrew main package. It covers the full Gateway API surface.
See API Reference for the full method list.
Once your app works locally, publish it to the App Store registry so other KiroCrew users can install it with one click.
See Publishing Guide for the full workflow.