homeclaw/skills/skill-creator/SKILL.md
Create and edit homeclaw skills. Use when someone asks to add a new skill, teach homeclaw something new, create an automation, or set up a recurring workflow. Also use when editing or improving an existing skill.
npx skillsauth add jayphen/homeclaw skill-creatorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A skill is a self-contained capability that teaches homeclaw how to handle a specific domain — budgets, meal planning, workout tracking, API integrations, etc.
Create a skill when:
Do NOT create a skill when:
Every skill is a directory containing a SKILL.md file:
skill-name/
├── SKILL.md # Required: YAML frontmatter + instructions
├── .env # Optional: secrets (HA_URL, API keys) — loaded automatically
├── data/ # Optional: persistent data files
├── scripts/ # Optional: executable scripts
├── references/ # Optional: reference docs
└── assets/ # Optional: templates, resources
IMPORTANT: The .env file MUST be in the skill root, not in data/.
Use skill_edit_file(name='x', file='.env', content='KEY=value') to create it.
Do NOT use data_write for .env — that puts it in the wrong place.
Use YAML frontmatter followed by markdown instructions:
---
name: skill-name
description: What this skill does and when to use it. Be specific — this is what determines when the skill gets activated.
allowed-domains:
- api.example.com
metadata:
key: value
---
Instructions for how to use this skill go here.
Include step-by-step guidance, edge cases, and examples.
| Field | Required | Notes | |-------|----------|-------| | name | Yes | Lowercase letters, numbers, hyphens. Must match directory name. | | description | Yes | What + when. Be specific — this triggers activation. | | allowed-domains | No | Domains for http_call (homeclaw extension). | | license | No | License name. | | compatibility | No | Environment requirements (bins, network, etc.). | | metadata | No | Arbitrary key-value pairs (can be nested). |
The description determines when the skill gets activated. Include:
Good: "Track household spending, categorize expenses, and show budget summaries. Use when someone mentions money, spending, bills, budget, or expenses."
Bad: "Budget helper."
Instructions are loaded into context when the skill is activated. Keep them:
references/)Include:
Every skill automatically gets these tools, namespaced with the skill name:
{name}__data_list — list files in data/{name}__data_read — read a data file{name}__data_write — write a data file{name}__data_delete — delete a data fileIf allowed-domains is set, the skill also gets:
{name}__http_call — make HTTP requests to the allowed domainsYou do NOT define these tools manually — they are registered automatically.
To make API calls, just set allowed-domains to the API hosts and use
{name}__http_call with the full URL.
If the skill has a scripts/ directory, use run_skill_script to execute
bundled scripts (30s timeout, path-traversal protected).
After creating or installing a skill, call read_skill to see the exact
tool names and available resources.
Always prefer installing over recreating. If a skill already exists online:
skill_install with the URL to download itskill_edit_file with find/replace to adapt specific partsskill_create — the instructions
will be truncated. Install first, then make targeted edits.skill_install accepts:
Example workflow to fork a skill:
skill_install(url="https://github.com/user/some-skill")
skill_edit_file(name="some-skill", file="SKILL.md", find="old text", replace="new text")
Call skill_create with:
[{filename, content}] (optional)Note: if skill approval is enabled and you're not an admin, the skill goes
to a pending queue. An admin must approve it with skill_approve before
it becomes active. Check pending skills with skill_pending_list.
| Data type | Format | Tools |
|-----------|--------|-------|
| Structured growing data (transactions, logs, health records) | SQLite | db_execute (write), db_query (read) |
| Small config / metadata | JSON | data_write, data_read |
| Freeform notes, diary | Markdown | data_write, data_read |
Use SQLite for any data that grows over time. A budget skill used for a
year accumulates hundreds of transactions — SQLite handles queries like
"sum spending by category last month" with a single SELECT, without reading
the entire history into context. The database lives at data/{skill_name}.db.
Schema setup: On first use, call db_execute with
CREATE TABLE IF NOT EXISTS ... to initialize the schema.
Web UI access: Arrow.js apps can query SQLite via
POST /api/skills/{owner}/{name}/db/query with {sql, params} — SELECT only.
Writes go through the agent (LLM calls db_execute).
data/{skill_name}.db)data_write only for small JSON config files, never for growing listsdata_list before data_write to check for existing files| Tool | Purpose |
|------|---------|
| read_skill | Load a skill's instructions and see its tools + resources |
| skill_list | List all available skills |
| skill_create | Create a new skill from scratch |
| skill_install | Install a skill from a URL (GitHub, gist, or direct) |
| skill_edit_file | Read, write, or find/replace a file in a skill |
| skill_update | Update a skill's description or instructions |
| skill_remove | Archive a skill (soft delete) |
| skill_migrate | Move a skill between scopes (household ↔ private) |
| skill_pending_list | List skills awaiting admin approval |
| skill_approve | Approve a pending skill (admin only) |
| skill_reject | Reject and delete a pending skill (admin only) |
| run_skill_script | Execute a script in a skill's scripts/ directory |
Skills that need API keys or URLs use a .env file in the skill root:
# workspaces/household/skills/homeassistant-skill/.env
HA_URL=http://home.local:8123
HA_TOKEN=eyJhbGciOiJIUzI1NiIs...
Create it with:
skill_edit_file(name="homeassistant-skill", file=".env", content="HA_URL=http://...\nHA_TOKEN=eyJ...")
The env vars are automatically substituted in http_call:
${HA_URL} in URLs → replaced with the value from .env${HA_TOKEN} in headers → replaced with the value from .envExample http_call:
http_call(url="${HA_URL}/api/states", headers={"Authorization": "Bearer ${HA_TOKEN}"})
NEVER use data_write for .env — it goes in data/ which is wrong.
ALWAYS use skill_edit_file with file=".env".
Budget tracker, habit log, reading list — just needs data files.
Set allowed-domains to empty, focus instructions on data file conventions.
Weather, transit, Home Assistant — calls external APIs.
Set allowed-domains to the API hosts, include API usage in instructions.
If the service is on LAN, the admin needs to enable "Allow local network"
in the Skills settings.
Morning briefing, weekly review — orchestrates multiple tools. Instructions describe the workflow steps and what to include.
Skills can embed a small interactive web app in the homeclaw UI. It runs
inline, in a sandboxed WASM VM (@arrow-js/sandbox) — no iframe. The
mini-app's code is untrusted and isolated: it cannot read the session token,
cannot fetch, and cannot touch the host page. It reaches skill data
only through a host bridge. The app shows up in the Apps section and on
the skill's detail page.
Declare it with ui-app: in the frontmatter, pointing at an Arrow source entry:
---
name: my-skill
description: Track items and show them in an interactive list.
ui-app:
entry: app/main.ts # Arrow source — a .ts/.js entry selects the sandbox model
title: My App # optional display title
---
If the skill already exists, do NOT call skill_create again. Prefer
skill_enable_ui_app — it deterministically adds the ui-app: block and writes
app/main.ts (+ app/main.css). Omit main_ts for a working default scaffold,
or pass your own. Do NOT hand the user raw source to save/run — the goal is a
live panel in homeclaw.
A mini-app is one entry file, app/main.ts (or main.js), plus optional
app/main.css. The sandbox compiles and runs main.ts; it must
export default an Arrow template.
import { reactive, html } from '@arrow-js/core'.import { query, schema } from 'homeclaw'.
query(sql, params?) — read-only SELECT against this skill's SQLite db; returns the rows.schema() — returns tables/columns; call it (or the skill_db_schema tool) before writing a SELECT.fetch() and never a token. The VM has no network and no credentials; the host runs the authenticated call for you.reactive({...}); mutate fields directly (state.loading = false).${() => state.x}. A bare ${state.x} renders once and never updates.@-prefixed attribute: @click="${() => ...}" (NEVER onclick).export default an html...template (or component result). Do NOT callhtml...(el)`` — the sandbox mounts the default export.output(payload) when needed (output is a global inside the sandbox; payload must be JSON-serializable).app/main.css.Never import @arrow-js/framework / /ssr / /hydrate, and never call
render() / boundary() / hydrate() — those are not part of the sandbox.
Minimal app:
// app/main.ts
import { reactive, html } from '@arrow-js/core'
const state = reactive({ count: 0 })
export default html`
<p>Count: ${() => state.count}</p>
<button @click="${() => state.count++}">+1</button>
`
Data-driven app (via the host bridge):
// app/main.ts
import { reactive, html } from '@arrow-js/core'
import { query } from 'homeclaw'
const state = reactive({ rows: [], loading: true, error: null })
// Surface failures into state.error — never swallow them into an empty list, or
// a broken query looks identical to "no data yet".
query('SELECT id, title FROM jobs ORDER BY id')
.then((rows) => { state.rows = rows; state.loading = false })
.catch((err) => { state.error = String(err?.message ?? err); state.loading = false })
export default html`
${() => state.loading ? html`<p>Loading…</p>` : null}
${() => state.error ? html`<p class="error">${() => state.error}</p>` : null}
${() => !state.loading && !state.error
? html`<ul>${() => state.rows.map((r) => html`<li>${() => r.title}</li>`.key(r.id))}</ul>`
: null}
`
Canonical starting point — copy this, don't write from scratch. A complete,
source-based reference ships at assets/reference-mini-app.ts (+ .css) in this
skill: keyed list, loading/error/empty states, a host-bridge query, and error
surfacing. Read it, then adapt it:
skill_edit_file(name="skill-creator", file="assets/reference-mini-app.ts")
Copy it into your skill's app/main.ts and change only the SQL SELECT + the
columns the template reads + the markup. Keep the event/reactivity wiring as-is.
The bundled references expand on Arrow itself — read them for more patterns:
references/api.md — reactive state, html templates, events (@click), components, watchreferences/examples.md — counter, list rendering, keyed listsreferences/getting-started.md — the mental modelreferences/advanced-ssr.md — framework/SSR/hydrate; not for mini-apps, ignore it hereHow to create / convert:
skill_create(...), then skill_enable_ui_app(name=..., person=...) to write the scaffold.read_skill(...), then skill_enable_ui_app(...) — it adds the ui-app: block and writes app/main.ts + app/main.css. Pass main_ts= / main_css= to supply your own source.skill_enable_ui_app(main_ts=...); for later edits use skill_edit_file find/replace — never re-send the whole file.Notes:
homeclaw bridge (query / schema). Do not write fetch('/api/...') or read localStorage — they don't exist in the VM.skill_db_schema tool (or schema() in the app). Querying a column that doesn't exist is a common reason a mini-app shows nothing.db_execute / data_write).skill_render_status). You still must surface query failures into state.error and render them.tools
Read and search homeclaw application logs. Admin only.
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------