Skip to content

install.sh: skills not discovered + cron registration silently fails on fresh install #56

@s-salamatov

Description

@s-salamatov

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 registeredcron 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)

  1. Trigger any single openclaw cron add to spawn a pending scope-upgrade. Note the requestId in the error.
  2. Within 5 min, approve it: Web UI → Settings → Devices → Pending → Approve, or openclaw devices approve <requestId> from an already-paired admin device.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions