feat(agents): author agent templates from the dashboard - #2148
Conversation
An agent template is the kiro spec at `~/.kiro/agents/<name>.json` — the system prompt, tool surface, auto-approve list, MCP servers and skill mapping an agent boots from. The Agent Templates tab could list, retarget the model, edit skills and delete one, but there was no route that CREATED one: `/api/agents/detail` served only GET/PATCH/DELETE, and PATCH accepted just `model` and `skills`. A template could therefore only arrive by installing a package, by an app shipping one, or by hand-writing JSON. Crews have full CRUD, but a crew only BINDS to a template — its picker offers whatever is already installed — so "make a new agent" meant adopting someone else's definition under a new label. Two capabilities close that: * `POST /api/agents/detail` writes `<name>.json`, either from a conservative blank baseline or as a copy of an existing template (`from`). * `PATCH` accepts `prompt` and `description`, so a created template can be corrected without editing JSON by hand. The privilege surface is deliberately NOT writable through either verb. `tools`, `allowedTools` and `toolsSettings` are the auto-approve list and the bash deny patterns, so a request body cannot mint a spec that auto-approves everything: a copy inherits them from a spec already on disk, and a blank template gets a tool surface whose auto-approved subset is read-only — editing a file or running a command has to be approved the first time. Kiro Crew's own privileged MCP servers are absent from the blank baseline; getting that surface means duplicating `kirocrew`, which is an explicit act. Guards, each pinned by a revert-verified test: * The name must be safe as a bare filename stem, so traversal and separators are rejected before any path is built. * Managed spec names and the built-in `default` are reserved, read from `agent_files.OWNED_KIRO_AGENT_FILES` rather than duplicated. * A name any EXISTING spec already answers to conflicts, not just a filename collision — kiro-cli resolves by the `name` field, so a second spec declaring a package agent's name makes which one wins a coin flip. * The file is created with `O_EXCL`. The collision scan and the write are two steps and only the kernel can make "create only if absent" atomic against a concurrent POST; the test reproduces that window rather than trusting the scan. * `prompt`/`description` edits are refused on Kiro Crew's own specs, which are rewritten on every install — accepting them would show a save that silently reverts. The `managed` flag is served by the backend so the editor disables exactly what PATCH refuses instead of keeping a second copy of the owned-file list in TypeScript. * Validation runs before any mutation, so a rejected combined PATCH cannot leave a half-applied model change behind. Every rejection carries a machine-readable `code`; the prose is advisory, since the dashboard renders it into a localized UI. UI: a New template button on the Installed Agents card and a Duplicate action on the selected template, both opening one dialog (name, start-from, description, system prompt). The detail panel's read-only prompt block becomes an editor with explicit Save/Revert — a prompt is prose typed over many keystrokes, so save-per-keystroke would rewrite a spec kiro-cli reads for every half-finished sentence. A managed template shows the same content with the reason it is locked. 33 new strings across all 10 locales plus the pseudolocale.
Design Review (Fable 5) — ✅ PASSAdvisory design-level review of Design-Verdict: PASS Real gap, additive API, and the privilege surface (tools/allowedTools/toolsSettings) is correctly fenced out as a separate capability — sound, proportionate design. Suggestions
[DESIGN-REVIEWED] 6b57074 |
GPT 5.6 Review — 🔴 changes requested (blocking)GPT 5.6 found at least one blocking issue that must be resolved before merging This comment is updated in place on each push. BLOCKING -- website/src/components/AgentTemplateEditor.tsx:51 -- Preserve edits made during an active save BLOCKING -- website/src/components/AgentTemplateCreateDialog.tsx:73 -- Prevent stale create completions from discarding a new draft FINDING -- src/kiro_crew/dashboard/handlers/agents.py:1292 -- Duplicating without a replacement description executes FINDING -- src/kiro_crew/dashboard/handlers/agents.py:1066 -- Calling [BLOCK-MERGE] 6b57074 |
UX Review (Fable 5) — 🟡 CONCERNSAdvisory UX-level review of I have what I need. Two real UX risks surfaced: silent loss of unsaved prompt text, and the fetch-failure fallback masquerading as the "managed template" lock. Here is the review. UX-Verdict: CONCERNS Long-form prompt edits can be silently destroyed, and a failed detail fetch shows a misleading "managed by Kiro Crew" lock explanation. Watch
Suggestions
[UX-REVIEWED] 6b5707454d95ec0e64f76c61a87f0b3c651fbfff |
Opus 5 Review — ✅ no blocking findingsReviewed Review detailsNo blocking findings. FINDING — src/kiro_crew/dashboard/handlers/agents.py:1066 — [OPUS-REVIEWED] 6b57074 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
|
Overlap notice — three PRs implement Agent Template authoring from the dashboard:
Recommendation: consolidate on #2383 as the base — widest scope (create/edit/clone + all #2255 items), the only branch without merge conflicts, and it matches what issues #1829 and #2255 asked for. However, #2383 is not yet merge-ready: CI is failing (frontend i18n gates, backend test shard, coverage gate), it ships no tests, and only the
@kyleseaman @RohanK6 — inviting you both to review #2383 so the best parts of all three land. If maintainers prefer the original sequencing (#2023 first, #2383 rebased back into the fast-follow it started as), that also works; the main thing is that the tests and i18n coverage from the other two PRs are not lost. |
|
Closing as superseded by #2383, which covers strictly more of this surface and now carries this PR's verification. Why this one loses #2383 does everything here — What moved across rather than being thrown away Rather than let the broader-but-untested implementation ship as-is, this PR's test suite went onto #2383 as commit
Also carried over: 87 tests now cover the two endpoints and the MCP env credential predicates on #2383, with every schema guard revert-verified. The one deliberate design difference: this PR refused Nothing here is worth reviving. Branch |
The gap
An agent template is the kiro spec at
~/.kiro/agents/<name>.json— the system prompt, tool surface, auto-approve list, MCP servers and skill mapping an agent boots from. The Agent Templates tab could list one, retarget its model, edit its skills, and delete it. It could not create one.server.pyregistered only three verbs for the spec, andapi_agent_detail's PATCH accepted exactly two fields:So a template could only arrive four ways:
kirocrew setupwritingkirocrew.json, a package install, an app shipping<app>--<agent>.json, or someone hand-writing the file. The prompt, tools and description rendered into a read-only<pre>.Crews (
POST/PUT/DELETE /api/agents) do have full CRUD, which is what makes this easy to miss — but a crew only binds to a template, and its template picker offers whatever is already installed. "Make a new agent" therefore meant adopting someone else's definition under a new label.What this adds
POST /api/agents/detailwrites<name>.json, either from a conservative blank baseline or as a copy of an existing template (from).PATCHacceptspromptanddescription, so a created template can be corrected without hand-editing JSON. Without this, phase 1 alone would give you a template you could create once and never fix from the UI.What is deliberately not writable
tools,allowedToolsandtoolsSettingsare not accepted from either verb's request body. Those three are the privilege surface —allowedToolsis the auto-approve list anddeniedCommandsis the guard that actually blocks commands — so a create call cannot mint a spec that auto-approves everything with the deny patterns stripped.fs_read,code,grep,glob. Editing a file or running a command has to be approved the first time.kirocrew, which is an explicit act rather than a default.Editing those three fields is a separate capability and a real design conversation; this PR does not grant it.
Guards
Each is pinned by a test that was revert-verified — the fix was patched out and the test confirmed to fail:
defaultare reservedagent_files.OWNED_KIRO_AGENT_FILES, not duplicatednamefield, so a second spec declaring a package agent's name makes which one wins a coin flip — a free filename is not enoughO_EXCLon createprompt/descriptionrefused on Kiro Crew's own specsThe
managedflag is served by the backend rather than derived client-side, so the editor disables exactly what PATCH refuses instead of keeping a second copy of the owned-file list in TypeScript that would drift.sourcewould not do: it is filename-derived and only marks the two user-facing specs, while the knowledge/research/heartbeat specs present as ordinary built-ins.Every rejection carries a machine-readable
code; the prose is advisory, since the dashboard renders it into a localized UI.UI
A New template button on the Installed Agents card and a Duplicate action on the selected template, both opening one dialog (name, start-from, description, system prompt). One component for both entry points because they differ only in that one field — two would have to keep the same name validation, conflict messages and tool-surface explainer in sync.
The detail panel's read-only prompt block becomes an editor with explicit Save/Revert. Unlike the model picker and skills editor on the same page, these do not save on change: a prompt is prose typed over many keystrokes, and save-per-keystroke would rewrite a spec kiro-cli reads for every half-finished sentence. A managed template shows the same content with the reason it is locked.
Verification
tsc -b, eslint (0 errors), vitest (10,486 pass), jscpd (0 clones), brand gate, and all 13 i18n checks green againstorigin/main.i18n-translate.mjsand each verified by its own verifier, plus the regenerated pseudolocale. One key that the editor orphaned (pages.agentsPage.system_prompt) was deleted rather than having the dead-key baseline raised.Pre-existing failures, measured not assumed
21 backend tests and one vitest test fail on this host and are not caused by this diff. Both claims were measured rather than reasoned:
test_app_manager,test_search_sessions_cost— main'shandlers/__init__.pyimports a module absent from this checkout's base) were run in a separate clone at main, where both fail identically.src/i18n/format.test.tsassertsIntl.DurationFormatis absent. This host runs Node 24, which has it; CI runs Node 20, which does not. The same test fails on main under Node 24.Follow-up
Editors for
tools/allowedTools/deniedCommands— the privilege surface this PR deliberately leaves read-only.