feat(templates): ship awizemann/template-author skill bundle

A new .scarftemplate in the public catalog whose only content is
a Hermes skill that teaches an agent how to scaffold a new
Scarf-compatible project — dashboard, optional configuration
schema, optional cron job, AGENTS.md — from a short conversational
interview. Scaffolded projects are usable locally and cleanly
exportable as .scarftemplate bundles later.

The skill itself (~400 lines of structured markdown at
skills/scarf-template-author/SKILL.md) covers:

- When to invoke vs. when to answer inline
- The on-disk project shape Scarf expects
- A 5-question interview flow
- Full widget catalog (all 7 widget types) with JSON shapes
- Config schema design + hard invariants (no defaults on secrets,
  `contents.config` must match field count, etc.)
- Cron-job design including the {{PROJECT_DIR}} gotcha
- Step-by-step file writing (dashboard, manifest, AGENTS.md, README)
- Testing + catalog validation instructions
- Common pitfalls + source-of-truth references

Delivered as a .scarftemplate so the install flow's normal
safeguards apply: preview sheet shows one project + one skill
+ zero cron jobs + no config step, uninstall drops both the
project dir and the namespaced skill folder via the existing
lock-file mechanism.

Scope per user sign-off: blank-slate / fully conversational for
v1. Pre-baked archetypes (`monitor`, `dev-dashboard`, etc.) are
deferred to v1.1 pending real usage data on what shapes users
actually ask for.

New Swift test exercises the bundle through the installer's
plan builder — asserts manifest shape, that the skill lands at
~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md,
and that no-config templates correctly skip the manifest cache.
58/58 Swift tests pass; 24/24 Python tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alan Wizemann
2026-04-23 19:41:50 +02:00
parent 3e0d2db4c7
commit ea4032766b
8 changed files with 638 additions and 0 deletions
@@ -1063,6 +1063,68 @@ final class TestRegistryLock: @unchecked Sendable {
#expect(cronPrompt.contains("{{PROJECT_DIR}}"))
}
/// Exercises the second shipped template `awizemann/template-author`
/// which is a skill-only bundle (no config, no cron, no memory). The
/// shape is deliberately different from site-status-checker so a
/// regression in the installer's "no config, no cron" path can't hide
/// behind the richer example template. Also asserts the skill lands
/// under the expected namespaced path so Hermes's recursive skill
/// discovery finds it.
@Test func templateAuthorParsesAndPlans() throws {
let bundle = try Self.locateExample(author: "awizemann", name: "template-author")
let service = ProjectTemplateService(context: .local)
let inspection = try service.inspect(zipPath: bundle)
defer { service.cleanupTempDir(inspection.unpackedDir) }
// Manifest shape: schemaVersion 2 (contains `skills` claim, which
// wasn't part of v1), no config, no cron, one skill.
#expect(inspection.manifest.id == "awizemann/template-author")
#expect(inspection.manifest.name == "Scarf Template Author")
#expect(inspection.manifest.version == "1.0.0")
#expect(inspection.manifest.schemaVersion == 2)
#expect(inspection.manifest.contents.dashboard)
#expect(inspection.manifest.contents.agentsMd)
#expect(inspection.manifest.contents.cron == nil)
#expect(inspection.manifest.contents.config == nil)
#expect(inspection.manifest.contents.memory == nil)
#expect(inspection.manifest.contents.skills == ["scarf-template-author"])
#expect(inspection.manifest.config == nil)
#expect(inspection.cronJobs.isEmpty)
// Plan: empty config, empty cron, but one skill queued for install
// under the template's namespaced dir. The namespace path has to
// match what the uninstaller wipes `skills/templates/<slug>`
// or uninstall leaves orphan skill files.
let scratch = try ProjectTemplateServiceTests.makeTempDir()
defer { try? FileManager.default.removeItem(atPath: scratch) }
let plan = try service.buildPlan(inspection: inspection, parentDir: scratch)
#expect(plan.projectDir.hasSuffix("awizemann-template-author"))
#expect(plan.cronJobs.isEmpty)
#expect(plan.configSchema == nil)
#expect(plan.configValues.isEmpty)
#expect(plan.memoryAppendix == nil)
// The skill should land at
// `~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md`
// namespace dir + skill folder + SKILL.md. Anything else
// breaks Hermes's recursive discovery or the uninstaller's
// `rm -rf` on the namespace dir.
let namespaceDir = try #require(plan.skillsNamespaceDir)
#expect(namespaceDir.hasSuffix("/skills/templates/awizemann-template-author"))
#expect(plan.skillsFiles.count == 1)
let skillDest = try #require(plan.skillsFiles.first?.destinationPath)
#expect(skillDest.hasSuffix("/scarf-template-author/SKILL.md"))
#expect(skillDest.hasPrefix(namespaceDir))
// No-config templates deliberately skip the manifest cache
// the dashboard's Configuration button only shows up when
// `.scarf/manifest.json` exists, so a skill-only template
// like this one correctly doesn't surface that button.
// (See ProjectTemplateService.buildPlan lines 198227.)
#expect(plan.manifestCachePath == nil)
}
/// Resolve the example bundle path robustly. Unit-test working dirs
/// differ between `xcodebuild test` (project root) and an Xcode IDE
/// run (build-output dir), so we walk up from this source file until
@@ -0,0 +1,55 @@
# Template Author — Agent Instructions
This project is a help surface for the `scarf-template-author` Hermes skill. The same instructions apply whether you're Claude Code, Cursor, Codex, Aider, or any other agent that reads `AGENTS.md`.
## What this project is
Two things:
1. A minimal dashboard (`.scarf/dashboard.json`) the user lands on after install. It's a Quick Start text widget + an empty list widget. The list is an optional scratchpad where you can log projects you've scaffolded for the user, giving them a running audit trail. That's nice-to-have, not mandatory.
2. A skill at `~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md`. The skill is the real value — it teaches you how to interview the user and scaffold a new Scarf-compatible project.
## What this project is NOT
- Not a running service. No cron jobs, no background tasks, no secrets.
- Not a dashboard you need to keep updated. The dashboard is documentation; the only mutation worth doing is appending to the Scaffolded Projects list after you scaffold something.
## When the user asks to create a Scarf project
The primary trigger. Phrases that should activate the full scaffolding flow:
- "Create a new Scarf project that …"
- "Scaffold a dashboard for …"
- "Set up a project to watch / track / report on …"
- "Help me author a Scarf template."
- "Build me a project that runs daily and …"
When you hear those:
1. Load the skill at `~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md` and follow its interview flow. Do not improvise — the skill encodes the specific invariants Scarf enforces (widget types, field-type constraints, the `{{PROJECT_DIR}}` token, the paused-on-install cron rule, the secret-fields-have-no-defaults rule).
2. Scaffold into a directory the user picks. Use absolute paths.
3. After writing files, tell the user to register the project: click **+** in Scarf's Projects sidebar and pick the directory. Do not try to edit `~/.hermes/scarf/projects.json` yourself — Scarf reloads the registry on its own and the UI path is safer.
4. Optionally append to the Scaffolded Projects list in this project's `dashboard.json` so the user has a local record of what you've built for them. Preserve every other field in the dashboard as-is.
## When the user asks reference questions
If the user asks something like "what widget types does Scarf support?" or "how do I add a secret field?", you don't need to scaffold anything — answer inline. The skill's reference sections cover:
- The seven widget types (`stat`, `progress`, `text`, `table`, `chart`, `list`, `webview`) and their required fields.
- The seven config field types (`string`, `text`, `number`, `bool`, `enum`, `list`, `secret`) and their constraint keys.
- The `AGENTS.md` contract that every scaffolded project should honour.
Point them at the skill file if they want to read it directly. It's ~400 lines of structured markdown.
## What not to do
- Don't scaffold without asking the user where the project should live. The interview always asks for a parent directory.
- Don't register secrets in `<project>/.scarf/config.json`. Secret field values go through the macOS Keychain at install time; `config.json` stores `keychain://…` URIs, never plaintext. A scaffolded project that hasn't been installed yet has no secrets on disk at all.
- Don't claim dashboard widget titles the cron job doesn't actually update. The scaffolded `AGENTS.md` is a contract — if it says "the cron updates Sites Up / Sites Down", the cron prompt must match.
- Don't skip `{{PROJECT_DIR}}` token substitution in cron prompts. Hermes doesn't set a CWD for cron runs, so relative paths resolve against the agent's own dir — the installer swaps `{{PROJECT_DIR}}` for the absolute project path at install time.
## Reference
- `SKILL.md` at `~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md` — the full scaffolding playbook.
- [Project Templates wiki page](https://github.com/awizemann/scarf/wiki/Project-Templates) — user-facing docs.
- [`awizemann/site-status-checker`](https://awizemann.github.io/scarf/templates/awizemann-site-status-checker/) — a complete working example covering dashboard stats, a configurable list, a cron job, a Site-tab webview, and a full AGENTS.md contract. Read it when you're unsure how a piece should look.
@@ -0,0 +1,46 @@
# Scarf Template Author
A Hermes skill that teaches your agent how to scaffold a new Scarf project — and, because Scarf's `.scarftemplate` format is symmetric with a live project on disk, how to shape it so you can publish it to the catalog later if you want.
## What you get
Installing this template drops a skill at `~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md` and a minimal "how to use" project in a folder of your choice. Every agent that reads the standard `~/.hermes/skills/` directory — Claude Code, Cursor, Codex, Aider, and the rest of the [agents.md](https://agents.md/) family — picks the skill up automatically.
## How to use it
After install, open your agent in any directory and say something like:
- *"Create a new Scarf project that watches the number of open PRs in my GitHub repo."*
- *"Scaffold a Scarf dashboard that tracks daily focus time from my Toggl logs."*
- *"Set up a project that runs a cron job to summarise my inbox each morning."*
- *"Help me author a Scarf template I can share."*
The agent will ask four or five questions (purpose, data source, cadence, what to display, any secrets) and then write:
- `<your-dir>/.scarf/dashboard.json`
- `<your-dir>/.scarf/manifest.json` — only if you're going to use a configuration form or want to export later
- `<your-dir>/AGENTS.md`
- `<your-dir>/README.md`
- Optionally a cron job registered via `hermes cron create` (always created paused — you enable it from Scarf's Cron sidebar when ready).
When it's done, click **+** in Scarf's Projects sidebar and pick the directory. Your dashboard appears. Iterate on it by asking your agent to tweak widgets or add fields.
## Turning a local project into a shareable template
Once you're happy with the result, Scarf → Projects → Templates → *Export "&lt;name&gt;" as Template…* produces a `.scarftemplate` anyone can install. The exporter carries the configuration *schema* but never your filled-in values — so your secrets and personal settings stay local.
## About this template's own dashboard
The installed project itself is tiny — a single Quick Start text widget and an empty list widget meant to serve as a scratchpad for tracking which scaffolded projects you've created. Its only purpose is to give you a place to land after install and a reminder of the trigger phrases above. The real value is the skill.
## Reference
- [Project Templates wiki page](https://github.com/awizemann/scarf/wiki/Project-Templates) — full spec + troubleshooting.
- [`awizemann/site-status-checker`](https://awizemann.github.io/scarf/templates/awizemann-site-status-checker/) — a complete, non-trivial example the skill studies and references.
- Dashboard / configuration schemas are Swift-authoritative at `scarf/scarf/Core/Models/ProjectDashboard.swift` and `scarf/scarf/Core/Models/TemplateConfig.swift` in the Scarf repo.
## What this template intentionally is not
- Not an archetype picker. v1 is blank-slate conversational; pre-baked starters (`monitor`, `dev-dashboard`, `personal-log`, etc.) may land in v1.1 once we see what shapes people ask for most often.
- Not a graphical wizard. The conversational agent path is strictly richer than a fixed form, and dogfoods Scarf's agent-first philosophy.
- Not a remote-scaffolding tool. It writes files into a directory on the machine where the agent runs; pair with Scarf's remote-server mode if you want to scaffold onto another box.
@@ -0,0 +1,33 @@
{
"version": 1,
"title": "Template Author",
"description": "A Hermes skill that helps your agent scaffold new Scarf projects — ask in chat, answer a short interview, and land a working dashboard with the right shape to export as a .scarftemplate later. The Scaffolded Projects list below grows as you use the skill.",
"theme": { "accent": "blue" },
"sections": [
{
"title": "Quick Start",
"columns": 1,
"widgets": [
{
"type": "text",
"title": "Ask your agent",
"format": "markdown",
"content": "**This project gives you a skill, not a service.** There are no cron jobs running, no dashboards to maintain. The real value lives at `~/.hermes/skills/templates/awizemann-template-author/scarf-template-author/SKILL.md`.\n\n**Trigger phrases** your agent listens for:\n\n- *\"Create a new Scarf project that watches …\"*\n- *\"Scaffold a dashboard to track …\"*\n- *\"Set up a project that runs a daily check on …\"*\n- *\"Help me author a Scarf template.\"*\n\nThe agent will interview you (purpose → data source → cadence → widgets → config → secrets), write `<your-dir>/.scarf/dashboard.json`, `<your-dir>/.scarf/manifest.json`, `<your-dir>/AGENTS.md`, and `<your-dir>/README.md`, then tell you to click **+** in Scarf's Projects sidebar to register the directory.\n\nWhen you're happy with the result, **Projects → Templates → Export** turns it into a `.scarftemplate` you can share.\n\nSee the [Project Templates wiki page](https://github.com/awizemann/scarf/wiki/Project-Templates) for the full spec."
}
]
},
{
"title": "Scaffolded Projects",
"columns": 1,
"widgets": [
{
"type": "list",
"title": "Projects this skill has built for you",
"items": [
{ "text": "Nothing yet — ask your agent to scaffold a project and it'll optionally log entries here.", "status": "pending" }
]
}
]
}
]
}
@@ -0,0 +1,392 @@
---
name: scarf-template-author
description: Scaffold a new Scarf project — dashboard, optional configuration schema, optional cron job, and AGENTS.md — from a short conversational interview with the user. Output is immediately usable locally and cleanly exportable as a .scarftemplate bundle.
version: 1.0.0
author: Alan Wizemann
license: MIT
platforms: [macos]
metadata:
hermes:
tags: [Scarf, templates, scaffolding, dashboard, authoring]
homepage: https://github.com/awizemann/scarf/wiki/Project-Templates
prerequisites:
commands: [hermes]
---
# Scarf Template Author
Scaffold a new Scarf-compatible project from a conversational interview. The output is both (a) a working project on disk the user can register with Scarf and use immediately, and (b) correctly shaped to be exported as a `.scarftemplate` bundle via Scarf's Export flow later.
## When to invoke this skill
Activate when the user says things like:
- *"Create a new Scarf project that watches / tracks / reports on …"*
- *"Scaffold a dashboard for …"*
- *"Set up a project that runs a daily check on …"*
- *"Help me author a Scarf template."*
- *"Build me a Scarf project to monitor …"*
Do **not** activate for pure reference questions like *"what widget types does Scarf support?"* or *"how does Scarf handle secrets?"* — answer those inline from the reference sections below.
Also do not activate when the user explicitly wants to edit an existing project's dashboard — that's a plain file edit, not a scaffold.
## How a Scarf project is shaped on disk
A Scarf project is just a directory registered in `~/.hermes/scarf/projects.json`. For Scarf to render a useful dashboard and for the project to be exportable as a `.scarftemplate`, it needs these files at minimum:
```
<project>/
├── .scarf/
│ ├── dashboard.json # REQUIRED for dashboard rendering
│ └── manifest.json # OPTIONAL — required only if the project declares a config schema or you want to export cleanly
├── AGENTS.md # Cross-agent instructions (agents.md standard) — ship this for every project
└── README.md # User-facing explanation
```
If the project will have a scheduled job, ALSO register a cron entry via `hermes cron create`. For an exportable bundle, also author `cron/jobs.json` in the staging directory — that's where Scarf's exporter will pick jobs up from.
Secrets never land in `dashboard.json` or `config.json`. At install time, Scarf routes secret-type config values to the macOS Keychain; `config.json` stores `keychain://service/account` URIs. When scaffolding from scratch (no install), the user either manages secrets via the post-install Configuration editor after export, or stashes them in their `~/.hermes/config.yaml` if they're Hermes-level secrets rather than project-level.
## The interview
Ask these questions in order. Don't batch. Each answer shapes the next question.
### 1. Purpose and data source
- *"In one sentence — what does this project do?"*
- *"Where does its data come from? Files, a URL, a shell command's output, an API call, a database, a spreadsheet?"*
Goal: figure out whether the project is **passive** (user maintains some files, dashboard reflects them), **pull-based** (we fetch from an HTTP endpoint or CLI tool on a schedule), or **push-based** (something external writes to a file we watch).
### 2. Refresh cadence
- *"How often should it refresh? Every hour? Daily? Weekly? Only when I ask?"*
If "only when I ask" → no cron job; user invokes the agent manually. If any scheduled cadence → cron job.
Map to cron expressions:
- Every hour: `0 * * * *`
- Daily at 9 AM: `0 9 * * *`
- Weekly Monday 9 AM: `0 9 * * 1`
- Every 15 minutes: `*/15 * * * *`
### 3. What the dashboard shows
Explain the seven widget types (see Widget Catalog below) in plain English, then ask which ones feel right. Offer concrete suggestions based on the purpose:
- Counting things (open PRs, failing tests, up/down sites) → `stat` widgets.
- A list of items with status → `list` with `text` + `status` per item.
- Time-series data → `chart` with `line` or `bar` type.
- Rows × columns of heterogeneous data → `table`.
- A live URL (useful for monitoring a site) → `webview`. **Including a webview widget exposes a Site tab** next to the Dashboard tab — worth noting to the user.
- A progress bar for something with a clear 0-to-N scale → `progress`.
- Static help / markdown → `text` with `format: "markdown"`.
### 4. Configuration needs
- *"Does this project need anything configurable by the user — URLs to watch, API tokens, thresholds, a list of accounts?"*
If yes → design a config schema. Fields map to seven types (see Config Schema Design below). Remember: **secret fields never have defaults**; that's a hard validator rule.
If no → skip `.scarf/manifest.json`; the project works but won't have a Configuration form.
### 5. Target agents
- *"Which agents will operate this project? Just Claude Code? Also Cursor / Codex / Aider / other?"*
For v1 just write `AGENTS.md` — every modern agent reads it, and if you need a specific shim (CLAUDE.md, GEMINI.md, .cursorrules), add it as a symlink to AGENTS.md so content stays in sync.
## Widget Catalog (JSON shapes)
All widgets require `type` and `title`. Type-specific fields:
### `stat` — single metric
```json
{ "type": "stat", "title": "Sites Up", "value": 0,
"icon": "checkmark.circle.fill", "color": "green", "subtitle": "responded 2xx/3xx" }
```
`value` accepts number OR string (`WidgetValue` enum). `icon` is an SF Symbol name. `color` is one of: `green`, `red`, `blue`, `orange`, `yellow`, `purple`, `gray`.
### `progress` — 0.0 to 1.0 progress bar
```json
{ "type": "progress", "title": "Test Coverage", "value": 0.72, "label": "72% of statements" }
```
### `text` — markdown or plain text block
```json
{ "type": "text", "title": "Quick Start", "format": "markdown",
"content": "**1.** Click + in the Projects sidebar.\n\n**2.** ..." }
```
`format` is `"markdown"` or `"plain"`.
### `table` — columns × rows of strings
```json
{ "type": "table", "title": "Failing Tests",
"columns": ["Test", "Duration", "Last Passed"],
"rows": [["testFoo", "4.2s", "Apr 20"], ["testBar", "0.9s", "Apr 18"]] }
```
Every row MUST have the same length as `columns`.
### `chart` — line / bar / area / pie with series
```json
{ "type": "chart", "title": "Requests / day", "chartType": "line",
"xLabel": "Date", "yLabel": "Count",
"series": [{
"name": "staging",
"color": "blue",
"data": [{"x": "Apr 20", "y": 142}, {"x": "Apr 21", "y": 189}]
}]
}
```
`chartType` is `"line"`, `"bar"`, `"area"`, or `"pie"`.
### `list` — items with optional status badge
```json
{ "type": "list", "title": "Watched Sites",
"items": [
{ "text": "https://example.com", "status": "up" },
{ "text": "https://example.org", "status": "down" }
]
}
```
`status` values: `"up"`, `"down"`, `"pending"`, `"ok"`, `"warn"`, `"error"` — render as coloured badges.
### `webview` — embedded live URL
```json
{ "type": "webview", "title": "First Watched Site",
"url": "https://awizemann.github.io/scarf/", "height": 420 }
```
**Important:** including any `webview` widget in a dashboard exposes a **Site** tab next to the Dashboard tab in the project view. Useful for templates that watch something renderable. The agent can update `url` on cron runs to keep the Site tab in sync with config (e.g., set it to `values.sites[0]`).
## Config Schema Design
If the project needs user-configurable values, design a schema. Put it in `<project>/.scarf/manifest.json` with this shape:
```json
{
"schemaVersion": 2,
"id": "author/project",
"name": "My Project",
"version": "1.0.0",
"description": "Short one-liner.",
"contents": { "dashboard": true, "agentsMd": true, "config": 2 },
"config": {
"schema": [
{ "key": "sites", "type": "list", "itemType": "string", "label": "Sites",
"required": true, "minItems": 1, "maxItems": 25,
"default": ["https://example.com"] },
{ "key": "api_token", "type": "secret", "label": "API Token", "required": true }
],
"modelRecommendation": {
"preferred": "claude-haiku-4",
"rationale": "Short-running, tool-light workload — haiku is plenty."
}
}
}
```
Note: `contents.config` is the **count of schema fields**, not a boolean. In the example above it's `2` because there are two fields.
### Field types and constraints
| Type | Rendered as | Constraint keys |
|---|---|---|
| `string` | Text field | `pattern` (regex), `minLength`, `maxLength` |
| `text` | Multi-line editor | `minLength`, `maxLength` |
| `number` | Number field | `min`, `max` |
| `bool` | Toggle | — |
| `enum` | Segmented (≤4) / Dropdown (>4) | `options: [{value, label}]` (REQUIRED) |
| `list` | Repeatable rows | `itemType: "string"` (required), `minItems`, `maxItems` |
| `secret` | Password field, routes to Keychain | — |
Every field takes `key` (required), `label` (required), `description` (optional — markdown), `required` (bool), `default` (optional; type matches the field type).
### Hard rules
- **Secret fields MUST NOT have a `default`.** The validator rejects the manifest if they do — a default makes no sense because the Keychain entry doesn't exist yet at install time.
- **Enum fields MUST have non-empty `options`.**
- **List fields MUST have `itemType: "string"`** in v1 (only itemType supported).
- **Field keys MUST be unique** within a schema.
- **`schemaVersion` MUST be 2** when a `config` block is present; it stays 1 if there's no config.
- **`contents.config`** must equal the actual count of schema fields — a claim mismatch is rejected.
## Cron Job Design
If the project has a scheduled task, register a cron job via `hermes cron create` AND — if you expect the user to export this as a `.scarftemplate` — author a `cron/jobs.json` in the staging layout so the exporter picks it up.
### Staging shape (for exportable templates)
```
<project>/
├── .scarf/
├── AGENTS.md
├── README.md
└── cron/
└── jobs.json
```
Where `cron/jobs.json` is:
```json
[
{
"name": "Check site status",
"schedule": "0 9 * * *",
"prompt": "Read {{PROJECT_DIR}}/.scarf/config.json — get values.sites and values.timeout_seconds — then HTTP GET each URL with that timeout, write the results to {{PROJECT_DIR}}/status-log.md, and update {{PROJECT_DIR}}/.scarf/dashboard.json's stat widgets by title (Sites Up, Sites Down, Last Checked). Reply with a one-line summary."
}
]
```
### Gotchas
- **Hermes does not set a CWD when firing cron jobs.** Relative paths in the prompt resolve against wherever the Hermes process happens to be running, not the project. Always use `{{PROJECT_DIR}}` in the prompt — the installer substitutes the absolute path at install time. This is THE most common template-author mistake.
- **Cron jobs created by the installer start paused.** Their name is auto-prefixed with `[tmpl:<template-id>]`. The user enables them from Scarf's Cron sidebar when ready.
- **Registering a cron job for a user's local (non-exported) project:** run `hermes cron create --name "<descriptive name>" "<schedule>" "<prompt>"` directly, substituting the absolute `<project>` path for `{{PROJECT_DIR}}` yourself. Then `hermes cron pause <id>` so it doesn't run until the user opts in.
### Schedule quick reference
| Cadence | Expression |
|---|---|
| Every 15 minutes | `*/15 * * * *` |
| Hourly at :00 | `0 * * * *` |
| Daily at 9 AM | `0 9 * * *` |
| Weekly Monday 9 AM | `0 9 * * 1` |
| First of the month, 9 AM | `0 9 1 * *` |
## Writing the files
After the interview, write files in this order.
### Step 1 — confirm parent directory
Ask: *"Where should I create the project? Give me an absolute path — I'll make a `<project-name>` directory inside it."*
Make sure the parent exists and is writable. Make sure `<parent>/<project-name>` does NOT already exist. If it does, ask whether to pick a different name or bail.
### Step 2 — create the skeleton
```bash
mkdir -p <parent>/<project-name>/.scarf
```
### Step 3 — write `dashboard.json`
Use the Widget Catalog above. Always include:
- `version: 1`
- `title` (the project's display name)
- `description` (a one-liner shown under the title)
- `sections` (array; each has `title`, optional `columns` (14, default 3), `widgets`)
Keep section titles short. Group related widgets. First section is usually "Current Status" or similar with the key stats.
### Step 4 — write `manifest.json` (only if the project has a config schema)
Put the full manifest shape from Config Schema Design above. Use `schemaVersion: 2`, match `contents.config` to the actual field count, and ensure every secret field has no `default`.
If there's no config schema, skip this file — the project still works, it just won't have a Configuration button. You can add it later.
### Step 5 — write `AGENTS.md`
Every scaffolded project needs an `AGENTS.md` that covers:
- **Purpose** — what the project does.
- **Layout** — which files exist and what they're for.
- **Configuration** — if there's a config schema, document every field: what it's for, what valid values look like, what happens when it's missing.
- **Dashboard** — list every widget the cron job (if any) updates, by title. If the cron updates a webview widget's URL, document that explicitly.
- **Cron behaviour** — what the cron job does, what it reads, what it writes, what its exit criteria are.
- **Chat prompts** — common user questions and how to answer them (e.g., *"What's the status of my sites?"* → "read the top section of `status-log.md` and summarise").
- **What NOT to do** — e.g., *don't modify `.scarf/config.json` yourself; tell the user to open the Configuration button.*
Use `{{PROJECT_DIR}}` placeholders in AGENTS.md only if the template will be installed through the installer (which substitutes the token). For a hand-scaffolded local-only project, substitute the absolute path yourself — `{{PROJECT_DIR}}` only resolves at install time.
### Step 6 — write `README.md`
User-facing. Keep it short:
- One-paragraph purpose.
- How to install / first run (for an unexported project: "click + in Scarf's Projects sidebar").
- How to trigger the cron job manually (Cron sidebar → Run Now).
- A pointer at `AGENTS.md` for agents.
### Step 7 — register the cron job (if any)
For a local non-exported project:
```bash
hermes cron create --name "<descriptive name>" "<schedule>" "<prompt with absolute project dir substituted>"
# Then pause it so it doesn't fire until the user's ready:
hermes cron pause <newly-created-job-id>
```
Read the id back from `hermes cron list --json` or parse the create output.
For an exportable template (one you're staging in `templates/<author>/<name>/staging/`): just author `cron/jobs.json` — the installer registers + pauses at install time, and prefixes the name with `[tmpl:<id>]`.
### Step 8 — register the project with Scarf
Tell the user: *"I've written the files. Click the **+** button in Scarf's Projects sidebar and pick `<absolute-project-dir>`. The dashboard will appear."*
Do NOT edit `~/.hermes/scarf/projects.json` directly — Scarf owns that file and reloads it on its own. The UI path is safer.
### Step 9 (optional) — log to the Template Author project's list
If the user has the `awizemann/template-author` project installed (the one that shipped this skill), append an entry to its `dashboard.json`'s `Scaffolded Projects` list widget:
```json
{ "text": "<absolute-project-dir> — <one-line purpose>", "status": "ok" }
```
This gives the user a running audit trail of everything you've scaffolded for them. Preserve every other field in the dashboard as-is.
## Testing your scaffold
### Minimum smoke test
1. Tell the user to click **+** in Scarf's Projects sidebar and pick the directory.
2. Dashboard appears — sanity check every widget renders correctly.
3. If there's a cron job: click the job in Scarf's Cron sidebar → **Run Now**. The agent executes the prompt; dashboard updates when it finishes.
### Configuration-form test (only if schema was declared)
To verify the Configuration form renders, you need to *install* the project as a template — scaffolded projects don't go through the installer, so the form never runs. Export the project first:
1. Projects → Templates → **Export "&lt;name&gt;" as Template…** → save the `.scarftemplate` somewhere.
2. Projects → Templates → **Install from File…** → pick the bundle → the Configure step should render the form you designed.
3. Cancel the install (the preview sheet has a Cancel button) — you just wanted to verify the form shape.
### Catalog validation (only if publishing)
If the user plans to submit this to the public catalog at `awizemann.github.io/scarf/templates/`:
```bash
# From the repo root
./scripts/catalog.sh check
```
Validates every template in `templates/<author>/<name>/` against the Python validator — the same one the PR CI uses. Catches schema issues, claim mismatches, size violations, common secret patterns.
## Common pitfalls
Things to check before declaring the scaffold done:
- [ ] Every cron prompt uses `{{PROJECT_DIR}}` (for exported) OR an absolute path (for local-only). Relative paths will fail.
- [ ] `contents.config` in the manifest equals the actual field count. Claim mismatch = rejected.
- [ ] No `default` on any `secret` field.
- [ ] Every enum field has non-empty `options`.
- [ ] Every list field has `itemType: "string"`.
- [ ] Every table widget has rows of length equal to `columns`.
- [ ] Every webview widget has an https URL that renders something meaningful even pre-first-run (Scarf homepage is a decent placeholder).
- [ ] `dashboard.json` has `version: 1` at the top.
- [ ] `AGENTS.md` documents every config field, every updated widget, and the cron behaviour — the user relies on it as the source of truth when things drift.
## Reference — source of truth files
- **Dashboard widget schema** — `scarf/scarf/Core/Models/ProjectDashboard.swift` in the Scarf repo. If you need exact field types or defaults, read it.
- **Config schema + validation** — `scarf/scarf/Core/Models/TemplateConfig.swift` and `scarf/scarf/Core/Services/ProjectConfigService.swift`.
- **Exporter behaviour** — `scarf/scarf/Core/Services/ProjectTemplateExporter.swift`. Verifies what files the exporter will pick up from a live project and what it'll carry into a bundle.
- **Installer contract** — `scarf/scarf/Core/Services/ProjectTemplateInstaller.swift`. Verifies what `{{PROJECT_DIR}}` substitution covers and where installed files land.
- **Catalog validator** — `tools/build-catalog.py` in the Scarf repo. Run with `./scripts/catalog.sh check` for the same rules CI uses.
- **Worked example** — `templates/awizemann/site-status-checker/staging/` in the Scarf repo. Complete end-to-end: dashboard with stats + list + webview, a config schema with a list + a number, a cron job, an AGENTS.md that documents every moving part. Read it first whenever you're unsure how a piece should look.
- **User-facing docs** — [Project Templates wiki page](https://github.com/awizemann/scarf/wiki/Project-Templates).
@@ -0,0 +1,19 @@
{
"schemaVersion": 2,
"id": "awizemann/template-author",
"name": "Scarf Template Author",
"version": "1.0.0",
"description": "Install this to give your agent a skill that scaffolds new Scarf projects — dashboards, optional configuration schemas, cron jobs, and AGENTS.md — from a short conversational interview. Scaffolded projects are usable locally and cleanly exportable as .scarftemplate bundles.",
"minScarfVersion": "2.2.0",
"author": {
"name": "Alan Wizemann",
"url": "https://github.com/awizemann"
},
"category": "developer-tools",
"tags": ["meta", "authoring", "skill", "scaffolding"],
"contents": {
"dashboard": true,
"agentsMd": true,
"skills": ["scarf-template-author"]
}
}
+31
View File
@@ -63,6 +63,37 @@
"configurable"
],
"version": "1.1.0"
},
{
"author": {
"name": "Alan Wizemann",
"url": "https://github.com/awizemann"
},
"bundleSha256": "670b2e07ad9bb327c11fa64db1beacf86614a3d388de6fe6e2a19ac957e1346b",
"bundleSize": 13889,
"category": "developer-tools",
"config": null,
"contents": {
"agentsMd": true,
"dashboard": true,
"skills": [
"scarf-template-author"
]
},
"description": "Install this to give your agent a skill that scaffolds new Scarf projects \u2014 dashboards, optional configuration schemas, cron jobs, and AGENTS.md \u2014 from a short conversational interview. Scaffolded projects are usable locally and cleanly exportable as .scarftemplate bundles.",
"detailSlug": "awizemann-template-author",
"id": "awizemann/template-author",
"installUrl": "https://raw.githubusercontent.com/awizemann/scarf/main/templates/awizemann/template-author/template-author.scarftemplate",
"minHermesVersion": null,
"minScarfVersion": "2.2.0",
"name": "Scarf Template Author",
"tags": [
"meta",
"authoring",
"skill",
"scaffolding"
],
"version": "1.0.0"
}
]
}