Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
565 changes: 565 additions & 0 deletions .claude/skills/workflows-create/SKILL.md

Large diffs are not rendered by default.

157 changes: 157 additions & 0 deletions .claude/skills/workflows-doctor/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
name: workflows-doctor
description: Diagnose Zapier Workflows skill and SDK CLI compatibility. Use when a workflow skill asks for a compatibility check, when SDK commands or flags are missing, when a workflow skill may be stale, or when updating workflow skills after an SDK CLI change.
license: MIT
metadata:
author: zapier
version: "1.2.1"
sdk_cli_min: "0.54.3"
sdk_cli_validated: "0.54.3"
refresh_source: "zapier/agent-skills"
---

# Zapier Workflows Doctor

Diagnose whether the installed Zapier SDK CLI can support the Zapier Workflows skill bundle. Be diagnostic first. Do not refresh skills unless SDK/skill drift is detected or compatibility cannot be confirmed.

## Compatibility Metadata

Workflow skills use these metadata fields:

- `sdk_cli_min`: oldest SDK CLI version the skill is allowed to run against. Set it to the first SDK CLI version that supports the newest command or flag the skill depends on. If that exact first-supported version is uncertain, use the SDK CLI version used when introducing the skill instruction change.
- `sdk_cli_validated`: SDK CLI version used during the latest validation pass. Update it whenever workflow skills are intentionally tested and republished against a newer SDK CLI, even if `sdk_cli_min` does not change.
- `refresh_source`: canonical skill source. For these skills, keep this as `zapier/agent-skills`.

Command-surface checks verify required bundle capabilities only. They do not prove full workflow correctness or that JSON payload semantics are unchanged.

## Step 0: Daily Skill-Freshness Check

Run this before the SDK compatibility steps below. It keeps the workflow skills current with `zapier/agent-skills` even when the SDK CLI has not changed, by occasionally running `npx skills update` for the bundle. It is **soft and non-blocking**: it self-throttles to roughly once per day per project, never stops the calling skill, and prints nothing unless it actually applied an update.

Run it exactly once, then continue to Step 1 regardless of its output. Do **not** parse or branch on the result:

```bash
bash scripts/skill-freshness-check.sh
```

Resolve `scripts/skill-freshness-check.sh` relative to this skill's own directory. The script locates the installed skill bundle from its own path and runs the bundle update from the scope root that contains it (the directory holding `.agents`/`.claude`), so it does not matter which directory you invoke it from.

- If it prints a note that skills were refreshed, pass that note along to the user and keep going; the update takes full effect on the next workspace reload.
- If it prints nothing, say nothing and continue.

This freshness check is independent of the SDK command-surface compatibility check in Steps 1–4 below, which is unchanged and remains a hard gate. For troubleshooting, set `ZAPIER_WORKFLOWS_DEBUG=1` to see the freshness check's decision on stderr.

## Step 1: Check Bundle Compatibility

Check the workflow skill bundle as one unit. Do not maintain separate compatibility checks for `workflows-install`, `workflows-create`, `workflows-list`, `workflows-history`, and `workflows-modify`; users will normally use these skills together, and drift in any core workflow SDK surface should refresh the whole bundle.

Current workflow skills use `sdk_cli_min: "0.54.3"` and `sdk_cli_validated: "0.54.3"` unless the installed skills' metadata says otherwise.

## Step 2: Check SDK CLI Versions

Run:

```bash
which zapier-sdk
zapier-sdk --version
npm view @zapier/zapier-sdk-cli version
```

If `zapier-sdk` is missing or `zapier-sdk --version` is below the bundle's `sdk_cli_min`, update the SDK CLI before continuing:

```bash
npm install -g @zapier/zapier-sdk-cli@latest
zapier-sdk --version
```

If global npm installs fail because of permissions, tell the user to fix their Node/npm setup before retrying. Prefer a user-owned Node install through nvm or Homebrew over `sudo npm install -g`.

If the installed SDK CLI version is newer than the bundle's `sdk_cli_validated`, continue to command-surface discovery. Do not refresh skills solely because the SDK CLI is newer.

## Step 3: Discover Current Command Surface

Start from the SDK help output:

```bash
zapier-sdk --experimental --help
```

Use the help output to discover the current command names and flags for the required bundle capabilities below. Current command names in this skill are examples from the SDK CLI version the workflow skill bundle was validated against; they are not the compatibility contract. If the current help output exposes an equivalent way to perform a required capability, use the current help output.

For each discovered candidate command, inspect command-specific help:

```bash
zapier-sdk --experimental <candidate-command> --help
```

## Required Bundle Capabilities

Confirm that the SDK CLI exposes a clear way to perform these operations for the workflow skill bundle:

- Create a workflow container.
- Publish a workflow version.
- Run a durable workflow locally or synthetically.
- List workflows.
- List workflow runs.
- Inspect a workflow run.
- Discover or list app triggers.
- Trigger a workflow.
- Control workflow visibility, including private workflow creation or the current equivalent.
- Bind app connections for test runs and published workflow versions.
- Bind app implementation/version metadata when required.
- Provide trigger configuration for published workflow versions.
- Pass workflow input when running or triggering workflows.
- Control enabled state when publishing workflow versions.
- Run synthetic durable tests privately or with the current equivalent behavior.

When the current SDK help output is clear, prefer it over the example commands below. If discovery is ambiguous or a required capability appears absent, treat compatibility as unconfirmed and refresh the workflow skill bundle.

Example commands from the validated SDK CLI surface:

```bash
zapier-sdk --experimental create-workflow --help
zapier-sdk --experimental publish-workflow-version --help
zapier-sdk --experimental run-durable --help
zapier-sdk --experimental list-workflows --help
zapier-sdk --experimental list-workflow-runs --help
zapier-sdk --experimental get-workflow-run --help
zapier-sdk --experimental list-triggers --help
zapier-sdk --experimental trigger-workflow --help
zapier-sdk --experimental get-trigger-run --help
zapier-sdk --experimental get-workflow --help
zapier-sdk --experimental get-workflow-version --help
```

Example flags from the validated SDK CLI surface:

- `create-workflow`: `--private`
- `publish-workflow-version`: `--connections`, `--app_versions`, `--trigger`, `--enabled`
- `run-durable`: `--connections`, `--input`, `--private`
- `trigger-workflow`: `--input`

Equivalent current flags or command shapes are acceptable if the help text clearly supports the same required bundle capability.

## Step 4: Decide Whether To Refresh Skills

If all required bundle capabilities are confirmed, tell the calling skill to continue without refreshing.

If any required capability is missing, or compatibility cannot be confirmed, update the entire workflow skill bundle so the skills stay in sync.

Prefer the standard day-2 update path first:

```bash
npx skills update workflows-install workflows-doctor workflows-create workflows-list workflows-history workflows-modify -y
```

If `skills update` cannot find the installed skills, updates the wrong scope, or otherwise fails, fall back to explicit installs from canonical GitHub:

```bash
npx skills add zapier/agent-skills --skill workflows-install --yes
npx skills add zapier/agent-skills --skill workflows-doctor --yes
npx skills add zapier/agent-skills --skill workflows-create --yes
npx skills add zapier/agent-skills --skill workflows-list --yes
npx skills add zapier/agent-skills --skill workflows-history --yes
npx skills add zapier/agent-skills --skill workflows-modify --yes
```

After updating skills, stop the current skill invocation. Tell the user to reload the agent workspace and rerun their original request. Do not promise that the current invocation has changed its already-loaded instructions.
174 changes: 174 additions & 0 deletions .claude/skills/workflows-doctor/scripts/skill-freshness-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/bin/bash
# ABOUTME: Skill-freshness check for the Zapier Workflows skill bundle.
# ABOUTME: Throttled (~daily) best-effort `npx skills update`; ALWAYS exits 0, never blocks the caller.
#
# Invoked by workflows-doctor "Step 0". Soft and non-blocking by design.
# DELIBERATELY no `set -e` (the repo's usual convention): a failure here must
# never abort the worker skill that ran the doctor. Every path ends with exit 0.
#
# Env hooks:
# ZAPIER_WORKFLOWS_DEBUG=1 verbose decision log -> stderr (bundle-wide flag)
# ZAPIER_WORKFLOWS_DOCTOR_NOW=<epoch> override the clock (tests)
# ZAPIER_WORKFLOWS_DOCTOR_UPDATE_CMD=<cmd> override the update command (tests)
# ZAPIER_WORKFLOWS_DOCTOR_BUNDLE_ROOT=<dir> override the fingerprint root (tests)
# XDG_CACHE_HOME=<dir> override cache root (tests / XDG)

DAILY=86400
BURST_COOLDOWN=900
MAX_FAILURES=3
UPDATE_NOTE="Refreshed the Zapier Workflows skills; the updates take full effect the next time you reload this workspace."
DEFAULT_UPDATE_CMD="npx --yes skills update workflows-install workflows-doctor workflows-create workflows-list workflows-history workflows-modify -y"

debug() {
if [ "${ZAPIER_WORKFLOWS_DEBUG:-}" = "1" ]; then
printf '[workflows-doctor freshness] %s\n' "$*" >&2
fi
return 0
}

now_epoch() {
if [ -n "${ZAPIER_WORKFLOWS_DOCTOR_NOW:-}" ]; then
printf '%s' "$ZAPIER_WORKFLOWS_DOCTOR_NOW"
else
date +%s
fi
}

as_int() {
case "$1" in
''|*[!0-9]*) printf '0' ;;
*) printf '%s' "$1" ;;
esac
}

# The installed bundle root. Resolved with `pwd -P` so it works through the
# symlinks the skills CLI creates (~/.claude/skills/<skill> -> ~/.agents/skills/<skill>);
# the skill is installed at <root>/<skill>/scripts/skill-freshness-check.sh.
bundle_root() {
if [ -n "${ZAPIER_WORKFLOWS_DOCTOR_BUNDLE_ROOT:-}" ]; then
printf '%s' "$ZAPIER_WORKFLOWS_DOCTOR_BUNDLE_ROOT"
return
fi
( cd "$(dirname "$0")/../.." 2>/dev/null && pwd -P )
}

# Working directory for the update command. The `skills` CLI resolves *project*
# skills relative to its CWD -- it only finds skills under a `.agents`/`.claude`
# directory that is a direct child of the working directory. This script is invoked
# from inside a skill subdirectory, so running `npx skills update` there discovers
# no project skills and silently refreshes nothing (exit 0, no on-disk change).
# The install root is `<scope>/.agents/skills` (or the `.claude` equivalent), so the
# scope root the CLI needs is two levels up. Fall back to the current directory when
# that can't be resolved or doesn't look like a scope root (e.g. test fixtures),
# which preserves prior behavior.
scope_root() {
local install_root="$1" candidate
candidate="$( cd "$install_root/../.." 2>/dev/null && pwd -P )" || candidate=""
if [ -n "$candidate" ] && { [ -d "$candidate/.agents" ] || [ -d "$candidate/.claude" ]; }; then
printf '%s' "$candidate"
else
printf '%s' "$PWD"
fi
}

# Aggregate checksum of every installed workflow skill's SKILL.md. Used to detect
# "did an update actually change anything on disk" -- a signal that cannot misfire
# on CLI wording, unlike output parsing. cksum/find/sort only (bash 3.2-safe; no
# jq/stat/shasum). No matches -> a stable constant, so before==after when nothing changed.
bundle_fingerprint() {
local root="$1"
[ -d "$root" ] || { printf '0'; return; }
find "$root" -maxdepth 3 -path '*workflows*/SKILL.md' -type f -exec cksum {} + 2>/dev/null \
| sort | cksum | awk '{print $1}'
}

# Primary signal is the fingerprint diff; prose is only a fallback for the rare
# case fingerprinting can't see the change.
decide_outcome() {
local before="$1" after="$2" rc="$3" out="$4" lc
if [ "$before" != "$after" ]; then printf 'updated'; return; fi
if [ "$rc" -ne 0 ]; then printf 'failed'; return; fi
lc="$(printf '%s' "$out" | tr '[:upper:]' '[:lower:]')"
# `skills update` exits 0 even on its own errors (e.g. "No installed skills
# found"), so scan output too. The substrings can overlap benign text like
# "0 errors"; because the fingerprint check ran first, that only flips a silent
# noop to a (silent) failed -> retry sooner, never suppresses a real update.
case "$lc" in
*error*|*"not found"*|*enotfound*|*etimedout*|*network*|*failed*) printf 'failed'; return ;;
esac
case "$lc" in
*updated*|*upgraded*|*added*|*"->"*|*"→"*) printf 'updated'; return ;;
esac
printf 'noop'
}

write_marker() {
printf '%s\n%s\n%s\n' "$2" "$3" "$4" > "$1" 2>/dev/null || true
}

main() {
local now scope_root key cache_dir marker
now="$(now_epoch)"
scope_root="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
key="$(printf '%s' "$scope_root" | cksum | awk '{print $1}')"
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/zapier-workflows-doctor"
marker="$cache_dir/$key"
debug "scope_root=$scope_root key=$key marker=$marker now=$now"

local last_success last_attempt failures l1 l2 l3
last_success=0; last_attempt=0; failures=0
if [ -f "$marker" ]; then
l1=""; l2=""; l3=""
{ IFS= read -r l1; IFS= read -r l2; IFS= read -r l3; } < "$marker"
last_success="$(as_int "$l1")"
last_attempt="$(as_int "$l2")"
failures="$(as_int "$l3")"
else
debug "marker missing -> treat as due"
fi

local since_success since_attempt due
since_success=$(( now - last_success ))
since_attempt=$(( now - last_attempt ))
due=0
if [ "$since_success" -lt "$DAILY" ]; then
due=0
elif [ "$failures" -ge "$MAX_FAILURES" ]; then
if [ "$since_attempt" -ge "$DAILY" ]; then due=1; fi
else
if [ "$since_attempt" -ge "$BURST_COOLDOWN" ]; then due=1; fi
fi

if [ "$due" -ne 1 ]; then
debug "state=skipped since_success=$since_success since_attempt=$since_attempt failures=$failures"
exit 0
fi

local root run_dir before_fp after_fp update_cmd out rc outcome
root="$(bundle_root)"
run_dir="$(scope_root "$root")"
before_fp="$(bundle_fingerprint "$root")"
update_cmd="${ZAPIER_WORKFLOWS_DOCTOR_UPDATE_CMD:-$DEFAULT_UPDATE_CMD}"
debug "due -> root=$root run_dir=$run_dir before_fp=$before_fp running update: $update_cmd"
out="$( cd "$run_dir" 2>/dev/null && eval "$update_cmd" 2>&1 )"
rc=$?
after_fp="$(bundle_fingerprint "$root")"
outcome="$(decide_outcome "$before_fp" "$after_fp" "$rc" "$out")"
debug "state=$outcome rc=$rc after_fp=$after_fp"

mkdir -p "$cache_dir" 2>/dev/null
if [ "$outcome" = "failed" ]; then
failures=$(( failures + 1 ))
write_marker "$marker" "$last_success" "$now" "$failures"
else
write_marker "$marker" "$now" "$now" "0"
fi

if [ "$outcome" = "updated" ]; then
printf '%s\n' "$UPDATE_NOTE"
fi
exit 0
}

main "$@"
exit 0
Loading
Loading