TL;DR
On a clean OpenClaw 2026.5.18 after bash install.sh:
- 0 skills discovered — wrong symlink target; the bundle is registered via the wrong mechanism.
- 0/18 cron jobs registered —
cron add needs scopes a fresh CLI device doesn't have; the script silently bumps its success counter anyway and prints Cron jobs registered: 18.
openclaw cron rm --name was removed — re-running creates duplicates.
Tested commit: superpowers a95a59d, OpenClaw 2026.5.18 (50a2481), Ubuntu 24.04, systemd gateway.
Bug 1 — Skills aren't discovered
install.sh#L18:
ln -s "$REPO_DIR/skills" "$INSTALL_TARGET" # → ~/.openclaw/extensions/superpowers
extensions/<name>/ is the plugin-runtime load path — OpenClaw looks for openclaw.plugin.json + index.js there. Skill-only bundles don't go there; openclaw plugins install confirms this with HOOK.md missing.
The right channel for skill bundles is the skills.load.extraDirs config key (source: openclaw-extra). Reading the bundled discovery code (~/.npm-global/lib/node_modules/openclaw/dist/):
const extraDirs = (opts?.config?.skills?.load?.extraDirs ?? []).map(...).filter(Boolean);
const extraSkills = mergedExtraDirs.flatMap((dir) =>
loadSkills({ dir: resolveUserPath(dir), source: "openclaw-extra" }));
Note: extraDirs does not recurse over the skills/{openclaw-native,community,core}/ tree — each category needs its own entry.
Fix that worked
openclaw config set skills.load.extraDirs '[
"<repo>/skills/openclaw-native",
"<repo>/skills/community",
"<repo>/skills/core"
]'
openclaw gateway restart
openclaw skills list | grep -c openclaw-extra
# → 57 ✓ ready
Suggested change in install.sh
Replace the symlink block with a config-set call that merges with existing extraDirs (overwriting would clobber user-managed paths).
Bug 2 — Cron registration silently fails (and lies about it)
install.sh#L41-L42:
timeout 10 openclaw cron rm --name "$skill_name" >/dev/null 2>&1 || true
if timeout 15 openclaw cron add --name "$skill_name" --cron "$cron_expr" ...
A fresh CLI device starts with roles:[operator], scopes:[operator.read]. cron add needs operator.admin / operator.write / operator.pairing (gateway scope-gates the RPC). First run:
gateway connect failed: scope upgrade pending approval (requestId: 2d02ea6d-...)
gateway closed (1008): pairing required: device is asking for more scopes than currently approved
Each call spawns a pending scope-upgrade request (PENDING_TTL_MS = 300_000). Looping over 18 skills generates 18 pendings in seconds, each invalidated by the next. User sees:
WARN: 'openclaw cron add' failed for <skill> (is gateway running?)
…which is misleading — the gateway is fine; the device isn't authorised.
The counter lies too. install.sh#L67-L68:
register_cron_skill "$skill_name" "$fm_cron"
CRON_COUNT=$((CRON_COUNT + 1))
CRON_COUNT++ runs unconditionally — register_cron_skill swallows the error and the final line reports Cron jobs registered: 18 even when 0 were created.
Recovery flow (for users hitting this now)
- Trigger any single
openclaw cron add to spawn a pending scope-upgrade. Note the requestId in the error.
- Within 5 min, approve it: Web UI → Settings → Devices → Pending → Approve, or
openclaw devices approve <requestId> from an already-paired admin device.
- Re-run installer.
Suggested changes in install.sh
- Probe scopes before the loop with a read-only call (
openclaw cron list --json). If it fails with pairing required or scope error, print an actionable message naming the pairing approval step, and exit non-zero rather than continue and inflate the counter.
- Move
CRON_COUNT++ inside the success branch of register_cron_skill, or have the function return non-zero on failure and gate the increment on $?.
- The existing
OPENCLAW_SKIP_CRON=1 escape hatch is good — keep it, and surface it in the error message as the bypass.
Bug 3 — openclaw cron rm --name doesn't exist; installer not idempotent
install.sh#L41 calls cron rm --name but in 2026.5.18:
$ openclaw cron rm --name foo
OpenClaw does not recognize option "--name".
cron rm only accepts a positional <id>. The pre-add delete silently no-ops, and since cron jobs aren't unique by name, every re-run adds duplicates.
Fix
Resolve the ID first:
ID=$(openclaw cron list --json | jq -r --arg n "$skill_name" '.[] | select(.name==$n) | .id' | head -1)
[ -n "$ID" ] && openclaw cron rm "$ID"
Nits
openclaw.plugin.json has "activation": { "onStartup": false }. That field is read by the plugin-runtime loader (extensions/<name>/index.js), not by skill discovery. For a skill-only bundle it has no effect — drop it or document its no-op status.
- README doesn't mention the fresh-CLI scope-upgrade pairing flow. Caught me cold; worth a paragraph in installation.
Verified end state after manual fixes
- Skills: 82/117 ready (was 26/61), 57 entries
openclaw-extra source — all superpowers, all ✓ ready.
- Cron: 18 jobs registered, all
idle/ok with correct schedules.
- State dirs: 38 under
~/.openclaw/skill-state/ (this part of the script works fine).
- Gateway: pid running, probe ok, no errors in journalctl beyond expected
ws closed before connect 1008 from the restart.
TL;DR
On a clean OpenClaw
2026.5.18afterbash install.sh:cron addneeds scopes a fresh CLI device doesn't have; the script silently bumps its success counter anyway and printsCron jobs registered: 18.openclaw cron rm --namewas removed — re-running creates duplicates.Tested commit: superpowers
a95a59d, OpenClaw2026.5.18(50a2481), Ubuntu 24.04, systemd gateway.Bug 1 — Skills aren't discovered
install.sh#L18:extensions/<name>/is the plugin-runtime load path — OpenClaw looks foropenclaw.plugin.json+index.jsthere. Skill-only bundles don't go there;openclaw plugins installconfirms this withHOOK.md missing.The right channel for skill bundles is the
skills.load.extraDirsconfig key (source:openclaw-extra). Reading the bundled discovery code (~/.npm-global/lib/node_modules/openclaw/dist/):Note:
extraDirsdoes not recurse over theskills/{openclaw-native,community,core}/tree — each category needs its own entry.Fix that worked
Suggested change in
install.shReplace the symlink block with a config-set call that merges with existing
extraDirs(overwriting would clobber user-managed paths).Bug 2 — Cron registration silently fails (and lies about it)
install.sh#L41-L42:A fresh CLI device starts with
roles:[operator],scopes:[operator.read].cron addneedsoperator.admin/operator.write/operator.pairing(gateway scope-gates the RPC). First run:Each call spawns a pending scope-upgrade request (
PENDING_TTL_MS = 300_000). Looping over 18 skills generates 18 pendings in seconds, each invalidated by the next. User sees:…which is misleading — the gateway is fine; the device isn't authorised.
The counter lies too.
install.sh#L67-L68:CRON_COUNT++runs unconditionally —register_cron_skillswallows the error and the final line reportsCron jobs registered: 18even when 0 were created.Recovery flow (for users hitting this now)
openclaw cron addto spawn a pending scope-upgrade. Note therequestIdin the error.openclaw devices approve <requestId>from an already-paired admin device.Suggested changes in
install.shopenclaw cron list --json). If it fails withpairing requiredor scope error, print an actionable message naming the pairing approval step, and exit non-zero rather than continue and inflate the counter.CRON_COUNT++inside the success branch ofregister_cron_skill, or have the function return non-zero on failure and gate the increment on$?.OPENCLAW_SKIP_CRON=1escape hatch is good — keep it, and surface it in the error message as the bypass.Bug 3 —
openclaw cron rm --namedoesn't exist; installer not idempotentinstall.sh#L41callscron rm --namebut in2026.5.18:cron rmonly accepts a positional<id>. The pre-add delete silently no-ops, and since cron jobs aren't unique by name, every re-run adds duplicates.Fix
Resolve the ID first:
Nits
openclaw.plugin.jsonhas"activation": { "onStartup": false }. That field is read by the plugin-runtime loader (extensions/<name>/index.js), not by skill discovery. For a skill-only bundle it has no effect — drop it or document its no-op status.Verified end state after manual fixes
openclaw-extrasource — all superpowers, all✓ ready.idle/okwith correct schedules.~/.openclaw/skill-state/(this part of the script works fine).ws closed before connect 1008from the restart.