desplega-ai/agent-swarm/apps

Claude Code and agent skill by desplega-ai/agent-swarm

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Build and iterate on schema-backed Swarm Apps with live models, named queries, custom actions, and json-render interfaces using the app-upsert, app-get, app-list, and app-patch tools.

Install

npx vskill@latest install desplega-ai/agent-swarm/apps

SKILL.md

Swarm Apps

Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at /apps/<id>. Use an app when people need to view and change live records; use create_page for a static snapshot.

Capability gate: app tools are available when the server's CAPABILITIES includes pages (for example CAPABILITIES=core,task-pool,pages). There is no separate apps capability. If these tools are absent from your MCP list, this is why.

Tools and iteration loop

ToolUse it for
app-listDiscover apps by id, name, description, and timestamps. Definitions are omitted.
app-getRead one complete stored definition before changing it.
app-upsertCreate an app, or replace an existing app's entire definition when you intentionally have the full desired state.
app-patchMake a focused change while preserving everything not mentioned in the patch. Prefer this for iteration.
app-queryRun one declared named query and return its rows. Use this when an agent or saved script needs to consume app data rather than render the app UI.
app-rows-listList rows with typed equality filters, sorting, and a limit. Returns { rows, total }.
app-row-getRead a row by rowId. Returns { row }.
app-row-createCreate a row from values. Returns { row }.
app-rows-createCreate 1 to 500 rows from a rows: values[] array, within 10 MiB. Returns { rows }.
app-row-patchPatch a row by rowId with values. Returns { row }.
app-row-deleteDelete a row by rowId. Returns { ok: true }.
app-rows-deleteDelete matching rows with a non-empty equals filter. Returns { deleted }.
app-action-runRun a declared script, task, or sync action with { appId, action, input? }. Agents can invoke it through MCP only.
app-historyList definition snapshots and their compact model/column digests before choosing a restore point.
app-diffShow a unified definition diff; defaults to newest snapshot → CURRENT.
app-rollbackRestore a snapshot through the normal schema-migration engine.

For every edit: app-get -> modify the smallest coherent subtree -> app-patch -> if rejected, fix every returned issues[] entry (path + message) and retry. Validation happens before storage, so a rejected update leaves the saved app unchanged. Do not guess at the current definition or use app-upsert with a partial definition.

app-patch can also change name; omit description to preserve it or pass description: null to clear it.

Reading an app's callable surface

One app-get returns everything needed to drive an app you did not write: definition.queries are runnable with app-query (supply every $param the query references), and definition.actions are invocable with app-action-run or POST /api/apps/<id>/actions/<name> (a script action runs its script, a task action spawns a task, a sync action refreshes sources). Read the query parameters and action arguments from the definition before invocation. Do not guess parameter names.

Verified action caller

Script actions receive caller as a reserved, server-stamped argument. The server overwrites caller from definition defaults and request input.

typescript
type AppActionCaller = {
kind: "user" | "agent" | "operator" | "guest";
id: string | null;
name?: string;
pageId?: string;
};

The caller identifies the authenticated user or agent. Operator and guest callers have id: null. A signed page session adds pageId. With the swarm key, a registered X-Agent-ID produces an agent caller and agent:<id> row provenance. Missing or unknown agent IDs remain operator callers. Task actions include the same caller as server-verified JSON in the task prompt. Script execution still uses the saved script owner's bindings.

Verified action caller

Script actions receive caller as a reserved, server-stamped argument. The server overwrites caller from definition defaults and request input.

typescript
type AppActionCaller = {
kind: "user" | "agent" | "operator" | "guest";
id: string | null;
name?: string;
pageId?: string;
};

The caller identifies the authenticated user or agent. Operator and guest callers have id: null. A signed page session adds pageId. With the swarm key, a registered X-Agent-ID produces an agent caller and agent:<id> row provenance. Missing or unknown agent IDs remain operator callers. Task actions include the same caller as server-verified JSON in the task prompt. Script execution still uses the saved script owner's bindings.

Safe schema evolution and rollback

Prefer hiding a column over deleting it. A hidden column's values stay on every existing row, so an unhide or rollback can make it visible again. Hiding is metadata-only: hidden columns cannot be used in queries, page bindings, app.mutate values, or new row writes, and required is ignored while hidden. Row GETs still expose the historical value for backward compatibility.

Do not reuse a hidden column name. The name remains held until you either unhide it or explicitly purge its stored values. To permanently remove a column, delete its declaration with null and include { "columnName": { "purge": true } } in the same migration object. That is deliberately one-shot and irreversible for row data.

Schema-changing writes can carry this sibling migration object:

json
{
"migration": {
"priority": { "from": "flag", "map": { "urgent": "high", "watch": "low" }, "else": "none" },
"status": { "from": "flag", "map": { "watch": "watching", "done": "done" }, "else": "open" },
"oldColumn": { "purge": true },
"count": { "coerce": true, "else": 0 },
"owner": { "set": "unassigned" }
}
}

Use set to backfill a changed column, from with optional map/else to derive it from one existing (including hidden) column, coerce for a kind change, and purge only for explicit destruction. Non-purge directives target a column changed in that same write. The server fails loudly before writing when a lossy change needs a directive: inspect every returned issues[] item, including its per-value row counts, then choose an explicit policy and retry. Never guess a migration just to get a 200.

Source lifecycle rides the same engine with the same all-or-nothing guarantees. Free: adding a source, adding a NEW column with a source binding, binding an existing column that holds zero values, changing a binding's field/transform, and changing a source's args/config/connection/scriptId (window changes churn staleness on the next pass — expect stale flips when narrowing). Rejected with a path-bearing issue: changing joinKey (immutable — remove the source and add it again), changing connector while the source owns rows (row count in the message), and binding an existing column that already holds values (purge it — or add a fresh column — and bind that instead; a sync pass would otherwise overwrite the data). Removing a source detaches its rows: values survive as ordinary columns, the source/syncedAt/stale envelope is stripped, and the report counts them in detachedRows. A patch removing a source must drop its column bindings in the same call — a dangling source.of is rejected before anything is written.

Sync never adopts rows it does not own: a pulled record whose join key matches an unowned row creates a second, source-owned row. Adding a source over a join-key column that already holds values therefore duplicates those records on the first pass — and remove-source → re-add duplicates for the same reason, because detached rows keep their values. To convert a hand-maintained model into a synced one, purge the old rows first or use a fresh join-key column.

For example, split a legacy flag into priority and status without losing data in one patch: add the two columns, map both from flag, update every affected query/page binding, then hide flag.

json
{
"appId": "<app-id>",
"definition": {
"models": {
"ticket": {
"columns": {
"priority": { "kind": "enum", "enum": ["none", "low", "high"] },
"status": { "kind": "enum", "enum": ["open", "watching", "done"] },
"flag": { "kind": "string", "hidden": true }
}
}
}
},
"migration": {
"priority": { "from": "flag", "map": { "urgent": "high", "watch": "low" }, "else": "none" },
"status": { "from": "flag", "map": { "watch": "watching", "done": "done" }, "else": "open" }
}
}

Every schema migration returns { scanned, backfilled, coerced, mapped, elsed, purgedValues, idxRebuilt, detachedRows, orphanFields }. Read orphanFields: it reports extra row fields that predate or no longer appear in the definition; they are preserved until an explicit purge. schemaVersion is server-managed: never set it in an input patch or try to use it as application data.

Every successful definition write snapshots the previous definition. Use app-history to select a version, app-diff({ appId, from: <version> }) to inspect it against CURRENT, then app-rollback({ appId, version: <version>, migration? }) to restore it. Rollback is a forward migration over live rows, not row-level time travel: it creates a new pre-rollback snapshot and may reject a lossy restore with the same migration directives and counts. A rejected rollback changes nothing.

Per-user configuration

userConfig declares up to 20 typed settings in the versioned definition; its values are deliberately stored outside the definition, separately for each user (and the operator dashboard). Each field is { "kind": "string" | "number" | "boolean" | "date" | "enum", "default"?: ..., "enum"?: [...], "label"?: "..." }. Defaults follow column rules, including enum membership; required is not allowed. A missing or obsolete stored value reads as its default, or null when no default is declared.

Use { "$state": "/user/<field>" } on a page for the read-only current-user binding; the field must be declared and the path cannot continue below it. Pure and bound reusable elements cannot bind /user directly — pass the value through an element prop from the consuming page. An agent acting on an owned user-requested task reads and edits that requester's preferences. userConfig schema changes are always migration-compatible and appear in migration reports; no migration directives are needed. Rollback restores the historical schema but never alters saved per-user values, so values can resurface when a field returns.

Theming

An optional top-level "theme" sets the preset the dashboard applies to the app's rendered canvas — a lowercase slug validated by shape only. The preset catalog ships with the dashboard (apps/ui/src/lib/themes.ts + theme-classics.ts — update this list and the app-upsert tool description when it changes); current ids: hive (the stock look), the accent/field presets meadow, iris, rose, cobalt, ember, carbon, and the classic editor/platform presets github, vscode, material, solarized, tokyo, monokai, gruvbox, catppuccin. Action and destructive tones are never themed; status tones are themed ONLY by the classic presets (their identity includes a status palette — hue semantics stay fixed, success reads green and error reads red everywhere). An id the viewer's dashboard build does not know degrades to the dashboard's own theme, so unknown slugs are safe but pointless. Omit theme to inherit whatever theme the viewer runs; "hive" is an explicit reset to the stock look even inside a themed dashboard.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

7-day trend score
0

Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/desplega-ai/agent-swarm/apps/badge)](https://verified-skill.com/skills/desplega-ai/agent-swarm/apps)

Something wrong with this skill or its page? Tell us.