Skip to main content

kirocrew-app-dev

Build, package, and publish Kiro Crew external apps. Covers app.json manifests, UI components, crons, skills, lifecycle hooks, development mode, registry publishing, and common pitfalls.

소스 정보

저장소
kirodotdev/KiroCrew
최근 소스 활동
2026년 10월 16일 00:30
감지된 SKILL.md 언어
영어
스타
4,452
포크
820

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
kirocrew-app-dev
description
Build, package, and publish Kiro Crew external apps. Covers app.json manifests, UI components, crons, skills, lifecycle hooks, development mode, registry publishing, and common pitfalls.
# Kiro Crew App Development Guide for building external Kiro Crew apps against the current App Kit manifest, lifecycle, UI SDK, and registry contracts. The canonical field and API references live in [App Kit](../../docs/app-kit/README.md). ## When to Use - User says "create a kirocrew app", "build an app", "make an app for kirocrew" - User says "package this as an app", "publish this to the app store" - User is building UI pages, crons, or skills that should be distributable ## App Structure ``` my-app/ ├── app.json # Manifest (required) ├── ui/ │ ├── index.mjs # ESM React component (default export) │ └── icon.svg # Sidebar icon (SVG or PNG) ├── skills/ │ └── my-skill/ │ └── SKILL.md # Skill spec ├── scripts/ │ ├── install.sh # Optional registry-install setup hook │ └── uninstall.sh # Cleanup script └── README.md # Optional docs ``` ## app.json Manifest ```json { "name": "my-app", "displayName": "My App", "version": "1.0.0", "description": "What it does in one sentence.", "author": "login", "tags": ["relevant", "tags"], "skills": [ "skills/my-skill" ], "crons": [ { "name": "my-cron", "message": "Run the scheduled app workflow.", "every": 900, "silent": true, "persistent_session": false }, { "name": "market-open", "message": "Summarise the overnight tape.", "cron_expr": "30 9 * * 1-5", "timezone": "America/New_York", "skip_dates": ["2026-12-25"] } ], "permissions": { "api": ["/api/apps/my-app/config", "/api/chat", "/api/chat/*"], "mcpTools": ["local_knowledge_search", "send_message"], "network": true, "cron": true }, "ui": { "entry": "index.mjs", "pages": [ { "route": "/apps/my-app", "label": "My App", "iconUrl": "icon.svg" } ] }, "setup": { "onInstall": "bash scripts/install.sh", "onUninstall": "bash scripts/uninstall.sh" } } ``` ### Critical Rules | Field | Rule | Why | |-------|------|-----| | `skills` | Prefer canonical string paths `["skills/my-skill"]` | Legacy object entries with `path` or `name` are normalized by `AppManifest.from_dict`, but the public manifest schema is `string[]` | | `permissions` | Must be an **object**; supported grants are `api`, `events`, `mcpTools`, `storage`, `network`, `memory`, `cron`, `sessionApproval`, `spawn`, `jobs`, and `exposeToApps` | A non-object parses to empty permissions, and malformed capability grants fail closed | | `resources` | Omit from `app.json`; declare app resources with `agents`, `skills`, `sops`, and `mcpServers` | `resources` is install/registry ownership metadata (`"gateway"` or `"app"`), not an app-manifest resource array | | `ui.entry` | Use an ESM `.mjs` bundle; `.js` is also served | HTML is not in the app UI static-file allowlist | | `ui.pages[].iconUrl` | Use an app-relative image path for a custom icon | `ui.pages[].icon` also works for supported host Lucide names and falls back to `Package` | | `displayName` | Required | Gateway uses it for UI display | | `version` | Semver string | Used by the App Store registry to compute `updateAvailable` | | `crons[].timezone` | IANA zone name; omit it only when the hour is zone-agnostic | An empty timezone falls back to the gateway config's zone and then to **UTC**, so `"cron_expr": "0 6 * * *"` without it fires at 06:00 UTC — the wrong calendar day for most users. A per-USER zone is not manifest data: pass `timezone=` to `ctx.cron.add_job` instead | | `crons[].skip_dates` | Zero-padded `YYYY-MM-DD`, evaluated in `timezone` | `"2026-1-1"` parses but never matches the padded fire-time rendering, so the skip silently does nothing. Both fields are rejected at manifest validation, not at fire time | | `crons[].timeout` / `crons[].timeout_secs` | Set both on a `script` cron that runs longer than 30s, e.g. `"timeout": 60, "timeout_secs": 65` | Omitted, a script is killed at 30s and a command at 300s. `timeout_secs` must cover `timeout` plus 5s of cleanup; a pair the cron SDK would refuse is rejected at manifest validation | ## Lifecycle and Resource Registration (CRITICAL) Do not make a cron mutate the installed app tree or create skill links. When an app is enabled, the gateway registers its declared agents, skills, SOPs, MCP servers, and crons; gateway startup reconciles enabled app resources again. Lifecycle scripts have explicit scope: - A local-path `kirocrew app install <dir>` copies the app but intentionally does not run `setup.onInstall`; build the local source before installing it. - A registry install runs `setup.onInstall` in the cloned source before copying it into the data home. Keep that hook idempotent. - `setup.onEnable` / `onDisable` run around enablement, and `onUninstall` is only for external state Kiro Crew cannot remove itself. Keep runtime data under the app's `data/` directory. The gateway owns registered resource links and scheduler entries; app code must not recreate them manually. ## UI Development ### Design System Kiro Crew apps should default to the host visual language. Colors come from the theme tokens (`var(--accent)` and friends) — see "Don't Reinvent the Dashboard" below, which is the normative rule. The hex values here are the legacy palette, kept for apps that predate the tokens and as the fallback inside `var(--accent, #7c3aed)`; a hardcoded hex breaks every custom palette, so reach for one only when a deliberate custom style is the point. | Element | Style | |---------|-------| | **Styling method** | Prefer `@kirocrew/app-sdk/ui` components and theme tokens; CSS, host utility classes, or inline styles are all valid. | | **Primary accent** | `var(--accent)` (legacy: `#7c3aed`) | | **Light accent bg** | legacy `#e8d5f5` | | **Success color** | `#047857` (green) | | **Warning color** | `#b45309` (amber) | | **Danger color** | `#b91c1c` (red) | | **Buttons** | `borderRadius: '9999px'` (full pill), font 11px weight 500 | | **Badges** | `borderRadius: '9999px'`, 10px bold, colored bg+text | | **Cards** | `background: 'var(--bg)', border: '1px solid var(--border)', borderRadius: '6px', padding: '14px'` | | **Max width** | `maxWidth: '1200px'` | | **Font sizes** | 10px (version/badges), 11px (body/buttons), 12px (table), 13px (section headers), 18px (title) | | **Theme vars** | `var(--bg)`, `var(--text)`, `var(--muted)`, `var(--border)`, `var(--card)` | **Example badge:** ```javascript _jsx('span', { style: { background: '#e8d5f5', color: '#7c3aed', padding: '2px 7px', borderRadius: '9999px', fontSize: '10px', fontWeight: 600, letterSpacing: '0.02em' }, children: 'LABEL' }) ``` **Example primary button:** ```javascript _jsx('button', { style: { background: '#7c3aed', color: '#fff', border: 'none', padding: '5px 14px', borderRadius: '9999px', fontSize: '11px', fontWeight: 500, cursor: 'pointer' }, children: 'Action' }) ``` **Example secondary/ghost button:** ```javascript _jsx('button', { style: { background: 'transparent', color: '#7c3aed', border: '1px solid #e8d5f5', padding: '5px 14px', borderRadius: '9999px', fontSize: '11px', fontWeight: 500, cursor: 'pointer', whiteSpace: 'nowrap' }, children: '↻ Refresh' }) ``` ### Reference Implementation For a working example, use `docs/app-kit/examples/`, which contains installable external-app patterns. Builtins under `src/kiro_crew/apps/builtins/` are useful for UI idioms, but their backend and lifecycle contracts differ from external apps. Visual mock of the target style: ```html <!-- Legacy geometry reference for a Kiro Crew app header and card. Use host components and theme tokens instead of copying the hardcoded colors. --> <div style="max-width:1200px; margin:0 auto; padding:16px; font-family:system-ui; color:#e2e8f0; background:#1a1b26"> <!-- Header --> <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px"> <div style="display:flex; align-items:center; gap:10px"> <span style="font-size:18px; font-weight:600">Review Tender</span> <span style="background:#e8d5f5; color:#7c3aed; padding:2px 8px; border-radius:9999px; font-size:10px; font-weight:600">Every 15 min</span> </div> <div style="display:flex; align-items:center; gap:10px"> <span style="font-size:11px; color:#6b7280">Last scan: 3m ago</span> <button style="background:transparent; color:#7c3aed; border:1px solid #e8d5f5; padding:5px 14px; border-radius:9999px; font-size:11px; font-weight:500">↻ Refresh</button> <span style="font-size:10px; color:#6b7280">v1.7.0</span> </div> </div> <!-- Card --> <div style="background:#1a1b26; border:1px solid #2d2f3d; border-radius:6px; padding:14px; margin-bottom:12px"> <div style="font-size:13px; font-weight:600; color:#7c3aed; margin-bottom:8px">Open Reviews Being Tended (2)</div> <!-- Table row example --> <div style="display:flex; align-items:center; gap:8px; padding:6px 0; border-bottom:1px solid #2d2f3d; font-size:12px"> <a style="color:#7c3aed; text-decoration:none">#1234</a> <span style="font-size:11px; max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap">Fix manifest dict format</span> <span style="background:#fef3c7; color:#b45309; padding:2px 7px; border-radius:9999px; font-size:10px; font-weight:600">iterating</span> <span style="margin-left:auto"> <button style="background:transparent; color:#7c3aed; border:1px solid #e8d5f5; padding:5px 14px; border-radius:9999px; font-size:11px; font-weight:500">💬 Chat</button> </span> </div> </div> <!-- Update banner --> <div style="background:#e8d5f5; color:#7c3aed; padding:8px 14px; border-radius:9999px; font-size:12px; display:flex; justify-content:space-between; align-items:center"> <span>Update available: v1.6.0 → v1.7.0</span> <button style="background:#7c3aed; color:#fff; border:none; padding:5px 14px; border-radius:9999px; font-size:11px; font-weight:500">Update Now</button> </div> </div> ``` Key visual rules from the mock: - Dark theme uses `var(--bg)` / `var(--border)` / `var(--text)` — don't hardcode dark hex - Light purple badge for metadata (`#e8d5f5` bg, `#7c3aed` text) - Amber status pills (`#fef3c7` bg, `#b45309` text) - Ghost buttons with purple text on light purple border - Version as plain muted text (smallest element, 10px) - Cards have 14px padding, 6px radius, 12px bottom margin ### Entry Module Pattern ```javascript import { useState, useEffect } from 'react' import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from 'react/jsx-runtime' import { useAppApi, useAppInfo, useNavigate } from '@kirocrew/app-sdk' export default function MyApp() { const [state, setState] = useState({}) const [loading, setLoading] = useState(true) const api = useAppApi() const { version: appVersion } = useAppInfo() const navigate = useNavigate() useEffect(() => { async function load() { try { setState(await api.get('/api/apps/MY-APP/config')) } finally { setLoading(false) } } load() const interval = setInterval(load, 30000) // 30s polling return () => clearInterval(interval) }, [api]) // ... render UI } ```
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기