From 81e8da91d6b5be6d343661cac1645c4d53e50ba0 Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Thu, 23 Apr 2026 02:21:14 +0200 Subject: [PATCH] feat(templates): upgrade site-status-checker to v1.1.0 with config schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First real exercise of the v2.3 configuration feature. The template no longer asks the agent to bootstrap sites.txt on first run — instead, users enter their list of URLs through the Configure form during install, and change them later via the dashboard's Configuration button. This makes the template a complete round-trip test of the new feature end-to-end. Schema (manifest.config.schema): - `sites` — list, required, 1–25 items, default two example URLs. This is the list the cron job hits. - `timeout_seconds` — number, 1–60, default 10. Per-URL HTTP timeout. - `modelRecommendation.preferred = claude-haiku-4` — rationale: simple tool-use task, Haiku is cost-effective for daily cron. Manifest bumped: schemaVersion 1 → 2, version 1.0.0 → 1.1.0, minScarfVersion 2.2.0 → 2.3.0, contents.config = 2. AGENTS.md rewritten for the config-driven flow: - Reads values from `.scarf/config.json` at run time (values.sites + values.timeout_seconds). No more sites.txt bootstrap. - "Add a site" / "Remove a site" no longer mean the agent edits a file — they mean "open the Configuration button on the dashboard." The agent points the user there rather than trying to mutate config.json itself. A future Scarf release may expose a tool for agents to write config programmatically; until then, config is strictly a user action. - First-run bootstrap now only creates status-log.md (if absent). README.md rewritten to walk users through the new form-based flow, explain the Configuration button, and document the model recommendation. Uninstall instructions point at the right-click Uninstall Template action rather than manual steps. Cron prompt updated to reference config.json (values.sites, values.timeout_seconds) instead of sites.txt. ProjectTemplateExampleTemplateTests.siteStatusCheckerParsesAndPlans extended with v2-specific assertions: manifest.schemaVersion == 2, contents.config == 2, schema.fields.count == 2, per-field constraints (sites type/itemType/minItems/maxItems, timeout min/max), modelRecommendation.preferred, plan.configSchema + plan.manifestCachePath are populated, plan.projectFiles includes both config.json + manifest.json destinations. Cron-prompt assertion swapped from sites.txt to config.json/values.sites. Three suites that touch ~/.hermes/scarf/projects.json now carry .serialized — the new Phase B install-with-config tests stressed the parallel-execution race in the snapshot/restore helpers. Serializing within each suite deflakes without any architectural change. Swift 50/50, Python 24/24, catalog validator accepts the upgraded bundle. Site detail page now has manifest.json for renderConfigSchema to pick up. Co-Authored-By: Claude Opus 4.7 (1M context) --- scarf/scarfTests/ProjectTemplateTests.swift | 39 ++++++++++++--- .../site-status-checker.scarftemplate | Bin 5410 -> 6797 bytes .../site-status-checker/staging/AGENTS.md | 46 ++++++++++------- .../site-status-checker/staging/README.md | 34 +++++++------ .../staging/cron/jobs.json | 2 +- .../site-status-checker/staging/template.json | 42 +++++++++++++--- templates/catalog.json | 47 +++++++++++++++--- 7 files changed, 157 insertions(+), 53 deletions(-) diff --git a/scarf/scarfTests/ProjectTemplateTests.swift b/scarf/scarfTests/ProjectTemplateTests.swift index c8a0e65..61a3ded 100644 --- a/scarf/scarfTests/ProjectTemplateTests.swift +++ b/scarf/scarfTests/ProjectTemplateTests.swift @@ -253,7 +253,7 @@ import Foundation /// are exhaustively tested; global-state side effects (skills namespace, /// cron CLI, memory append) are covered by manual verification per the /// plan's step 7. -@Suite struct ProjectTemplateInstallerTests { +@Suite(.serialized) struct ProjectTemplateInstallerTests { @Test func installsMinimalBundleAndWritesLockFile() throws { let scratch = try ProjectTemplateServiceTests.makeTempDir() @@ -370,7 +370,7 @@ import Foundation /// it, verify every tracked file is gone, the registry is restored to its /// pre-install state, and user-added files (if any) are preserved. Scoped /// to bundles with no skills/cron/memory so no global state is touched. -@Suite struct ProjectTemplateUninstallerTests { +@Suite(.serialized) struct ProjectTemplateUninstallerTests { @Test func roundTripsInstallThenUninstall() throws { let scratch = try ProjectTemplateServiceTests.makeTempDir() @@ -496,7 +496,7 @@ import Foundation /// against a synthesized schemaful bundle. Uses an isolated Keychain /// service suffix so no leftover login-Keychain items remain after the /// test — every secret we write is deleted on teardown. -@Suite struct ProjectTemplateConfigInstallTests { +@Suite(.serialized) struct ProjectTemplateConfigInstallTests { /// Minimal schemaful manifest with one non-secret field + one /// secret field. Written into the synthesized `.scarftemplate` @@ -781,13 +781,31 @@ import Foundation defer { service.cleanupTempDir(inspection.unpackedDir) } #expect(inspection.manifest.id == "awizemann/site-status-checker") + #expect(inspection.manifest.schemaVersion == 2) // config-enabled #expect(inspection.manifest.contents.dashboard) #expect(inspection.manifest.contents.agentsMd) #expect(inspection.manifest.contents.cron == 1) + #expect(inspection.manifest.contents.config == 2) #expect(inspection.cronJobs.count == 1) #expect(inspection.cronJobs.first?.name == "Check site status") #expect(inspection.cronJobs.first?.schedule == "0 9 * * *") + // Schema assertions — the two fields we declared should survive + // unzip + parse + validate with their constraints intact. + let schema = try #require(inspection.manifest.config) + #expect(schema.fields.count == 2) + let sitesField = try #require(schema.field(for: "sites")) + #expect(sitesField.type == .list) + #expect(sitesField.itemType == "string") + #expect(sitesField.required == true) + #expect(sitesField.minItems == 1) + #expect(sitesField.maxItems == 25) + let timeoutField = try #require(schema.field(for: "timeout_seconds")) + #expect(timeoutField.type == .number) + #expect(timeoutField.minNumber == 1) + #expect(timeoutField.maxNumber == 60) + #expect(schema.modelRecommendation?.preferred == "claude-haiku-4") + let scratch = try ProjectTemplateServiceTests.makeTempDir() defer { try? FileManager.default.removeItem(atPath: scratch) } let plan = try service.buildPlan(inspection: inspection, parentDir: scratch) @@ -795,6 +813,12 @@ import Foundation #expect(plan.skillsFiles.isEmpty) #expect(plan.memoryAppendix == nil) #expect(plan.cronJobs.count == 1) + #expect(plan.configSchema?.fields.count == 2) + #expect(plan.manifestCachePath?.hasSuffix("/.scarf/manifest.json") == true) + // Plan queues both config.json + manifest.json in projectFiles. + let destinations = plan.projectFiles.map(\.destinationPath) + #expect(destinations.contains { $0.hasSuffix("/.scarf/config.json") }) + #expect(destinations.contains { $0.hasSuffix("/.scarf/manifest.json") }) // Cron job name gets prefixed with the template tag so users can // find + remove it later. #expect(plan.cronJobs.first?.name == "[tmpl:awizemann/site-status-checker] Check site status") @@ -820,10 +844,13 @@ import Foundation #expect(statTitles.contains("Sites Down")) #expect(statTitles.contains("Last Checked")) - // The cron prompt mentions sites.txt and dashboard.json — if it - // ever stops doing that, the agent won't know what files to touch. + // Cron prompt references .scarf/config.json (where values.sites + // + values.timeout_seconds live) and the dashboard/log it writes. + // If either stops being referenced, the cron wouldn't know which + // data to read or where to write results. let cronPrompt = inspection.cronJobs.first?.prompt ?? "" - #expect(cronPrompt.contains("sites.txt")) + #expect(cronPrompt.contains("config.json")) + #expect(cronPrompt.contains("values.sites")) #expect(cronPrompt.contains("dashboard.json")) #expect(cronPrompt.contains("status-log.md")) } diff --git a/templates/awizemann/site-status-checker/site-status-checker.scarftemplate b/templates/awizemann/site-status-checker/site-status-checker.scarftemplate index 35bca1d6c1cf7c1e45a1cdbcd2dd23ec03eba80b..81d75d108bbdd1e388076be4b0fdd25a95332826 100644 GIT binary patch delta 5688 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 KnDKvo=Dz^FJEg|} delta 4289 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 hG3bu@bFlw5*}rSmlO{i - + | URL | Status | Code | Latency | |-----|--------|------|---------| | … | up | 200 | 142 ms | @@ -53,14 +61,14 @@ The cron job runs this project's "Check site status" prompt. When invoked: ## What not to do - Don't modify the structure of `dashboard.json` (section titles, widget types, widget titles, `columns`). Only the values listed above are writable. +- Don't edit `.scarf/config.json` — that's the user's responsibility via the Configuration UI. - Don't truncate `status-log.md` — it's the historical record. If it grows past 1 MB, add a one-line note at the top of the file asking the user to archive it. -- Don't invent URLs. If `sites.txt` is empty or missing, leave the dashboard untouched and write a single `status-log.md` entry noting "no sites configured." +- Don't invent URLs or pull them from anywhere other than `values.sites`. - Don't run browsers or headless Chrome. Plain HTTP GET is sufficient. ## When the user asks you things - "What's the status of my sites?" — read the top section of `status-log.md` and summarize. -- "Add a site" — append the URL to `sites.txt` on its own line. Don't sort or reorder existing entries. Confirm back to the user which URL you added. -- "Remove a site" — delete the matching line from `sites.txt`. If multiple match, ask before choosing. +- "Add a site" / "Remove a site" — tell them: *"Click the Configuration button on the dashboard header (the slider icon, next to the folder). Add or remove the URL there and save. The next cron run will pick it up."* Don't try to edit config.json yourself. - "Run the check now" — do everything in the cron flow above, then summarize the results in chat. -- "Why is [site] down?" — read the last 3-5 entries for that URL in `status-log.md` and report any pattern you see (consistent timeouts, intermittent 5xx, DNS failures, etc.). Don't speculate beyond what the log shows. +- "Why is [site] down?" — read the last 3–5 entries for that URL in `status-log.md` and report any pattern you see (consistent timeouts, intermittent 5xx, DNS failures, etc.). Don't speculate beyond what the log shows. diff --git a/templates/awizemann/site-status-checker/staging/README.md b/templates/awizemann/site-status-checker/staging/README.md index 51bd3d4..e726386 100644 --- a/templates/awizemann/site-status-checker/staging/README.md +++ b/templates/awizemann/site-status-checker/staging/README.md @@ -2,32 +2,38 @@ A minimal uptime watchdog that pings a list of URLs once a day, records pass/fail results, and keeps a simple Scarf dashboard up to date. +**Requires Scarf 2.3+** — this template uses the configuration feature (a form during install, and a Configuration button on the dashboard for editing later). + ## What you get -- **`sites.txt`** — one URL per line. This is the source of truth for what the cron job checks. Edit it to add or remove sites. -- **`status-log.md`** — the agent's append-only log of check results. New runs append a section at the top. +- **Configurable site list** — you tell Scarf which URLs to watch during install, via a form. No file editing required. Edit the list later via the **Configuration** button on the project dashboard (slider icon next to the folder). +- **Configurable timeout** — how long to wait per URL before giving up, also set via the form. +- **`.scarf/config.json`** — where your configured values land. The agent reads this at run time; you never need to open it by hand. +- **`status-log.md`** — the agent's append-only log of check results. New runs append a section at the top. Created automatically on first run. - **`.scarf/dashboard.json`** — Scarf dashboard with live stat widgets (sites up, sites down, last checked), the full list of watched sites with their last-known status, and a usage guide. -- **Cron job `Check site status`** — registered (paused) by the installer; tag `[tmpl:awizemann/site-status-checker]`. Runs daily at 9:00 AM when enabled. The prompt tells the agent to read `sites.txt`, check each URL, write results to `status-log.md`, and update the stat widgets in `dashboard.json`. +- **Cron job `Check site status`** — registered (paused) by the installer; tag `[tmpl:awizemann/site-status-checker]`. Runs daily at 9:00 AM when enabled. Reads your configured sites + timeout, hits each URL, writes results to `status-log.md`, and updates the dashboard. ## First steps -1. Open the **Cron** sidebar and enable the `[tmpl:awizemann/site-status-checker] Check site status` job. It's paused on install so nothing runs without your explicit say-so. -2. Edit `sites.txt` in your project root — replace the two placeholder URLs with the sites you actually want to watch. -3. From the project's dashboard, ask your agent to run the job now: "Run the site status check and update the dashboard." +1. During install, fill in the Configuration form: add the URLs you want to watch and (optionally) adjust the timeout. Hit Continue, then Install. +2. After install, open the **Cron** sidebar and enable the `[tmpl:awizemann/site-status-checker] Check site status` job. It's paused on install so nothing runs without your explicit say-so. +3. From the project's dashboard, ask your agent to run the job now: *"Run the site status check and update the dashboard."* 4. Future runs happen automatically at 9 AM daily. +## Changing sites or timeout later + +Click the **Configuration** button (slider icon, dashboard toolbar) to re-open the form pre-filled with your current values. Add, remove, or edit URLs. Save. The next cron run picks up the changes. + ## Customizing - **Change the schedule.** Edit the cron job in the Cron sidebar — the schedule field accepts `30m`, `every 2h`, or standard cron expressions like `0 9 * * *`. -- **Change what "down" means.** By default the agent treats any non-2xx HTTP response as down. If you want to check for specific strings in the body (e.g. "Maintenance"), tell the agent in `AGENTS.md` and it will adapt. +- **Change what "down" means.** By default the agent treats any non-2xx/3xx HTTP response as down. If you want to check for specific strings in the body (e.g. "Maintenance"), tell the agent in `AGENTS.md` and it will adapt. - **Add alerting.** Set a `deliver` target on the cron job (Discord, Slack, Telegram) — the agent will post the run summary there instead of just writing to `status-log.md`. +## Recommended model + +`claude-haiku-4` works well — this is a simple tool-use task (HTTP GETs + a short summary). Haiku keeps costs low when the cron runs daily. The recommendation appears in the Configuration form; Scarf doesn't auto-switch your active model, so adjust via Settings if you'd like. + ## Uninstalling -Templates don't auto-uninstall in Scarf 2.2. To remove this one by hand: - -1. Delete this project directory (removes the dashboard, AGENTS.md, sites.txt, status-log.md). -2. Remove the project entry from the Scarf sidebar (click the `−` next to the project name). -3. Delete the `[tmpl:awizemann/site-status-checker] Check site status` cron job from the Cron sidebar. - -No memory appendix or skills were installed, so nothing else needs cleanup. +Right-click the project in the sidebar → **Uninstall Template…** (or click the shippingbox icon on the dashboard header). Scarf walks you through exactly what's about to be removed: template-installed files in the project dir, the `[tmpl:…]` cron job, and the Configuration values you entered (`config.json` + Keychain items for any secrets — though this template has none). User-created files (like `status-log.md`) are preserved. diff --git a/templates/awizemann/site-status-checker/staging/cron/jobs.json b/templates/awizemann/site-status-checker/staging/cron/jobs.json index 84dd951..cad1290 100644 --- a/templates/awizemann/site-status-checker/staging/cron/jobs.json +++ b/templates/awizemann/site-status-checker/staging/cron/jobs.json @@ -2,6 +2,6 @@ { "name": "Check site status", "schedule": "0 9 * * *", - "prompt": "Run the site status check for this project. Follow the instructions in AGENTS.md: read sites.txt, HTTP GET each URL, prepend a results section to status-log.md, and update the three stat widgets plus the Watched Sites list items in .scarf/dashboard.json. When done, reply with a one-line summary like '3 up, 1 down — example.com timed out'." + "prompt": "Run the site status check for this project. Follow the instructions in AGENTS.md: read .scarf/config.json to get values.sites (the URL list) and values.timeout_seconds, HTTP GET each URL with the configured timeout, prepend a results section to status-log.md (creating it with the stub header if it doesn't exist yet), and update the three stat widgets plus the Watched Sites list items in .scarf/dashboard.json. When done, reply with a one-line summary like '3 up, 1 down — example.com timed out'." } ] diff --git a/templates/awizemann/site-status-checker/staging/template.json b/templates/awizemann/site-status-checker/staging/template.json index e4f1a44..5ce1279 100644 --- a/templates/awizemann/site-status-checker/staging/template.json +++ b/templates/awizemann/site-status-checker/staging/template.json @@ -1,20 +1,50 @@ { - "schemaVersion": 1, + "schemaVersion": 2, "id": "awizemann/site-status-checker", "name": "Site Status Checker", - "version": "1.0.0", - "minScarfVersion": "2.2.0", + "version": "1.1.0", + "minScarfVersion": "2.3.0", "minHermesVersion": "0.9.0", "author": { "name": "Alan Wizemann", "url": "https://github.com/awizemann/scarf" }, - "description": "A daily uptime check for a short list of URLs. Writes status to status-log.md and updates the dashboard with current counts.", + "description": "A daily uptime check for a list of URLs you configure on install. Writes status to status-log.md and updates the dashboard with current counts.", "category": "monitoring", - "tags": ["monitoring", "uptime", "cron", "starter"], + "tags": ["monitoring", "uptime", "cron", "starter", "configurable"], "contents": { "dashboard": true, "agentsMd": true, - "cron": 1 + "cron": 1, + "config": 2 + }, + "config": { + "schema": [ + { + "key": "sites", + "type": "list", + "itemType": "string", + "label": "Sites to Watch", + "description": "One URL per item. HTTP or HTTPS. You can add and remove entries after install via the Configuration button on the dashboard.", + "required": true, + "minItems": 1, + "maxItems": 25, + "default": ["https://example.com", "https://example.org"] + }, + { + "key": "timeout_seconds", + "type": "number", + "label": "Request Timeout (seconds)", + "description": "How long to wait for each URL before giving up.", + "required": false, + "min": 1, + "max": 60, + "default": 10 + } + ], + "modelRecommendation": { + "preferred": "claude-haiku-4", + "rationale": "Simple tool-use task — HTTP GETs + a short summary. Haiku is plenty and keeps cost low when the cron runs daily." + } } } diff --git a/templates/catalog.json b/templates/catalog.json index ded59fc..d717bbe 100644 --- a/templates/catalog.json +++ b/templates/catalog.json @@ -7,29 +7,62 @@ "name": "Alan Wizemann", "url": "https://github.com/awizemann/scarf" }, - "bundleSha256": "32b8c12706de8596be63dcdda32d46fc5bf478d5b9f7c1fc4c6d96ced251186a", - "bundleSize": 5410, + "bundleSha256": "ce68cc20cc67fe688a7ddf0638d35dce3247ba7ed234e6f9d99a1ad3964a81e0", + "bundleSize": 6797, "category": "monitoring", - "config": null, + "config": { + "modelRecommendation": { + "preferred": "claude-haiku-4", + "rationale": "Simple tool-use task \u2014 HTTP GETs + a short summary. Haiku is plenty and keeps cost low when the cron runs daily." + }, + "schema": [ + { + "default": [ + "https://example.com", + "https://example.org" + ], + "description": "One URL per item. HTTP or HTTPS. You can add and remove entries after install via the Configuration button on the dashboard.", + "itemType": "string", + "key": "sites", + "label": "Sites to Watch", + "maxItems": 25, + "minItems": 1, + "required": true, + "type": "list" + }, + { + "default": 10, + "description": "How long to wait for each URL before giving up.", + "key": "timeout_seconds", + "label": "Request Timeout (seconds)", + "max": 60, + "min": 1, + "required": false, + "type": "number" + } + ] + }, "contents": { "agentsMd": true, + "config": 2, "cron": 1, "dashboard": true }, - "description": "A daily uptime check for a short list of URLs. Writes status to status-log.md and updates the dashboard with current counts.", + "description": "A daily uptime check for a list of URLs you configure on install. Writes status to status-log.md and updates the dashboard with current counts.", "detailSlug": "awizemann-site-status-checker", "id": "awizemann/site-status-checker", "installUrl": "https://raw.githubusercontent.com/awizemann/scarf/main/templates/awizemann/site-status-checker/site-status-checker.scarftemplate", "minHermesVersion": "0.9.0", - "minScarfVersion": "2.2.0", + "minScarfVersion": "2.3.0", "name": "Site Status Checker", "tags": [ "monitoring", "uptime", "cron", - "starter" + "starter", + "configurable" ], - "version": "1.0.0" + "version": "1.1.0" } ] }