From 3974c0272162af3ead017294d4af7028f5fb8fa6 Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 10:27:15 +0000 Subject: [PATCH 1/7] feat(openclaw): pre-install loki-skills library on bootstrap Skills now ship with the openclaw pack out of the box. Clones inceptionstack/loki-skills into ~/.openclaw/workspace/skills during pack install (Phase 2), so OpenClaw's skill auto-discovery picks them up immediately without needing the manual BOOTSTRAP-SKILLS.md flow. - packs/common.sh: add shared LOKI_SKILLS_REPO_URL constant. This is the only shared piece -- each pack owns its own install step so pack-specific layout / wiring can diverge (Hermes external_dirs, Pi extensions, IronClaw MCP, etc). - packs/openclaw/install.sh: clone (or fast-forward) skills repo right after workspace setup. Writes the same .bootstrapped-skills marker BOOTSTRAP-SKILLS.md uses so the manual first-boot flow becomes a no-op. Best-effort: clone failure does not fail install. Other packs (hermes, nemoclaw, pi, ironclaw, kiro-cli, codex-cli, roundhouse, claude-code) unchanged for now; add the equivalent step to their own install.sh when ready. --- packs/common.sh | 7 +++++++ packs/openclaw/install.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packs/common.sh b/packs/common.sh index 504db1b..4efcddd 100755 --- a/packs/common.sh +++ b/packs/common.sh @@ -3,6 +3,13 @@ # Source this file; do not execute directly. # Usage: source "$(dirname "$0")/../common.sh" +# ── Shared constants ───────────────────────────────────────────────────────── +# Loki skills library — the single source of truth used by every pack that +# wants to pre-install agent skills. Each pack owns its own install step (so +# pack-specific layout / post-clone wiring can differ); only this URL is +# shared. Override at install time with: LOKI_SKILLS_REPO_URL=... +export LOKI_SKILLS_REPO_URL="${LOKI_SKILLS_REPO_URL:-https://github.com/inceptionstack/loki-skills.git}" + # Colors _CLR_GREEN='\033[0;32m' _CLR_CYAN='\033[0;36m' diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index fec9c81..5fb93cb 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -143,6 +143,37 @@ chmod 700 "${HOME}/.openclaw" chmod 700 "${HOME}/.openclaw/workspace" ok "Workspace ready: ${HOME}/.openclaw/workspace" +# ── Pre-install loki-skills library ───────────────────────────────── +# OpenClaw auto-discovers skills under ~/.openclaw/workspace/skills. +# We clone (or fast-forward) the shared loki-skills repo into that path and +# write the same .bootstrapped-skills marker BOOTSTRAP-SKILLS.md uses, so the +# manual first-boot flow becomes a no-op. +# +# Repo URL is shared via LOKI_SKILLS_REPO_URL (see packs/common.sh). Each +# pack owns its own install step here so pack-specific wiring can diverge. +# Best-effort: a transient clone failure must not fail the pack install. +step "Installing loki-skills library" +SKILLS_DIR="${HOME}/.openclaw/workspace/skills" +if ! command -v git &>/dev/null; then + warn "git not found -- skipping loki-skills (agent can run BOOTSTRAP-SKILLS.md manually)" +elif [[ -d "${SKILLS_DIR}/.git" ]]; then + if git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then + ok "loki-skills updated ($(ls -1 "${SKILLS_DIR}" | grep -c -v '^\.' || echo 0) entries)" + else + warn "loki-skills update failed -- keeping existing copy" + fi +else + if git clone --depth 1 --quiet "${LOKI_SKILLS_REPO_URL}" "${SKILLS_DIR}" 2>/dev/null; then + ok "loki-skills cloned from ${LOKI_SKILLS_REPO_URL}" + mkdir -p "${HOME}/.openclaw/workspace/memory" + printf 'Skills bootstrapped %s (auto via openclaw pack)\n' \ + "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + > "${HOME}/.openclaw/workspace/memory/.bootstrapped-skills" + else + warn "loki-skills clone failed -- agent can run BOOTSTRAP-SKILLS.md manually" + fi +fi + # ── Generate token if not provided ──────────────────────────────────────────── if [[ -z "${GW_TOKEN}" ]]; then GW_TOKEN="$(openssl rand -hex 24)" From e74ae5f8556b8cddfe940322c0d36ef387c355a3 Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 10:29:59 +0000 Subject: [PATCH 2/7] fix(openclaw/skills): self-heal partial clones, validate origin, always write marker Address codex review on PR #64: - High: previous code permanently wedged after a partial clone where ${SKILLS_DIR} existed but had no .git inside. Now: detect that case, rmdir if empty (leave alone if non-empty + warn), then re-test and fresh-clone. On clone failure, rm -rf the partial dir so the next pack run starts clean. - Medium: marker was only written on a fresh clone. A pre-seeded / manually-cloned skills tree never flipped the manual first-boot flow to a no-op. Now: write_skills_marker() runs on every successful path (fresh clone, fast-forward, untouched-because-mismatch). - Medium: previous code blindly 'git pull' on whatever repo lived at ${SKILLS_DIR}. Now: read remote.origin.url and verify it matches ${LOKI_SKILLS_REPO_URL} before pulling; if mismatch, leave the tree untouched and warn (still write the marker, recording the source). The Low (env-overridable repo URL) is intentional -- it's the documented override knob for forks/mirrors and is only consumable on the install host where the operator already has root. --- packs/openclaw/install.sh | 48 ++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index 5fb93cb..dcce3c4 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -154,22 +154,54 @@ ok "Workspace ready: ${HOME}/.openclaw/workspace" # Best-effort: a transient clone failure must not fail the pack install. step "Installing loki-skills library" SKILLS_DIR="${HOME}/.openclaw/workspace/skills" +SKILLS_MARKER="${HOME}/.openclaw/workspace/memory/.bootstrapped-skills" + +# write_skills_marker -- idempotent; called on every successful path so a +# pre-seeded skills directory also flips the manual first-boot flow to a no-op. +write_skills_marker() { + mkdir -p "$(dirname "${SKILLS_MARKER}")" + printf 'Skills bootstrapped %s (auto via openclaw pack, source=%s)\n' \ + "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ + "${1:-unknown}" \ + > "${SKILLS_MARKER}" +} + if ! command -v git &>/dev/null; then warn "git not found -- skipping loki-skills (agent can run BOOTSTRAP-SKILLS.md manually)" elif [[ -d "${SKILLS_DIR}/.git" ]]; then - if git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then - ok "loki-skills updated ($(ls -1 "${SKILLS_DIR}" | grep -c -v '^\.' || echo 0) entries)" + # Existing repo -- verify it points at the expected origin before touching it. + # Defends against a local repo repoint from corrupting an unrelated tree. + EXISTING_ORIGIN="$(git -C "${SKILLS_DIR}" config --get remote.origin.url 2>/dev/null || echo '')" + if [[ "${EXISTING_ORIGIN}" != "${LOKI_SKILLS_REPO_URL}" ]]; then + warn "loki-skills origin mismatch (expected ${LOKI_SKILLS_REPO_URL}, found ${EXISTING_ORIGIN:-none}) -- leaving existing tree untouched" + write_skills_marker "existing:${EXISTING_ORIGIN:-unknown}" + elif git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then + ok "loki-skills updated ($(find "${SKILLS_DIR}" -maxdepth 1 -mindepth 1 ! -name '.*' | wc -l) entries)" + write_skills_marker "${LOKI_SKILLS_REPO_URL}" else - warn "loki-skills update failed -- keeping existing copy" + warn "loki-skills fast-forward failed -- keeping existing copy" + write_skills_marker "${LOKI_SKILLS_REPO_URL}" fi -else +elif [[ -e "${SKILLS_DIR}" ]]; then + # Path exists but is not a git checkout (partial clone, manual files, etc). + # Clear it iff it's empty; otherwise leave alone and warn. + if [[ -z "$(ls -A "${SKILLS_DIR}" 2>/dev/null)" ]]; then + rmdir "${SKILLS_DIR}" 2>/dev/null || true + else + warn "${SKILLS_DIR} exists but is not a git repo -- leaving alone, skipping skills install" + fi +fi + +# Re-test: if the path is now absent, do a fresh clone. Self-heals from a +# previous partial-clone failure where the dir exists with no .git inside. +if command -v git &>/dev/null && [[ ! -e "${SKILLS_DIR}" ]]; then if git clone --depth 1 --quiet "${LOKI_SKILLS_REPO_URL}" "${SKILLS_DIR}" 2>/dev/null; then ok "loki-skills cloned from ${LOKI_SKILLS_REPO_URL}" - mkdir -p "${HOME}/.openclaw/workspace/memory" - printf 'Skills bootstrapped %s (auto via openclaw pack)\n' \ - "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - > "${HOME}/.openclaw/workspace/memory/.bootstrapped-skills" + write_skills_marker "${LOKI_SKILLS_REPO_URL}" else + # Clean up any partial directory git may have left behind so the next + # pack run is not permanently wedged into the "exists but not a repo" path. + rm -rf "${SKILLS_DIR}" 2>/dev/null || true warn "loki-skills clone failed -- agent can run BOOTSTRAP-SKILLS.md manually" fi fi From def8e2955655acf54719d9d7fa061dc93b88c914 Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 10:47:54 +0000 Subject: [PATCH 3/7] refactor(openclaw/skills): extract named functions, single git guard Apply clean-code review (no behavior change): - Extract Method: replace the 50-line procedural if/elif/elif block at the top level with named functions, each doing one thing (skills_write_marker, skills_origin_url, skills_origin_matches_expected, skills_count_entries, skills_dir_is_empty, skills_update_existing, skills_prepare_for_fresh_clone, skills_fresh_clone, skills_install). - Newspaper Rule: install.sh top-level reads as 'step / skills_install', with implementation details below. The state machine is now explicit in skills_install -- no implicit cross-block coupling, no 'Re-test' comment-as-deodorant. - Single guard: 'command -v git' is checked once in skills_install instead of twice across separate if blocks. - Self-documenting predicates: 'skills_origin_matches_expected', 'skills_dir_is_empty' replace inline expression conditions. Verified: bash -n clean, shellcheck clean for the new block, pack tests 60/0/0 pass, plus a 6-scenario harness covering fresh clone, fast-forward, origin-mismatch refusal, partial-dir self-heal, non-empty non-repo preservation, and missing-git graceful skip. --- packs/openclaw/install.sh | 109 ++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index dcce3c4..434785e 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -145,66 +145,97 @@ ok "Workspace ready: ${HOME}/.openclaw/workspace" # ── Pre-install loki-skills library ───────────────────────────────── # OpenClaw auto-discovers skills under ~/.openclaw/workspace/skills. -# We clone (or fast-forward) the shared loki-skills repo into that path and -# write the same .bootstrapped-skills marker BOOTSTRAP-SKILLS.md uses, so the -# manual first-boot flow becomes a no-op. +# We clone the shared loki-skills repo into that path and write the same +# .bootstrapped-skills marker BOOTSTRAP-SKILLS.md uses, so the manual +# first-boot flow becomes a no-op. # # Repo URL is shared via LOKI_SKILLS_REPO_URL (see packs/common.sh). Each # pack owns its own install step here so pack-specific wiring can diverge. # Best-effort: a transient clone failure must not fail the pack install. -step "Installing loki-skills library" SKILLS_DIR="${HOME}/.openclaw/workspace/skills" SKILLS_MARKER="${HOME}/.openclaw/workspace/memory/.bootstrapped-skills" -# write_skills_marker -- idempotent; called on every successful path so a -# pre-seeded skills directory also flips the manual first-boot flow to a no-op. -write_skills_marker() { +skills_write_marker() { + local source="${1:-unknown}" mkdir -p "$(dirname "${SKILLS_MARKER}")" printf 'Skills bootstrapped %s (auto via openclaw pack, source=%s)\n' \ - "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \ - "${1:-unknown}" \ + "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "${source}" \ > "${SKILLS_MARKER}" } -if ! command -v git &>/dev/null; then - warn "git not found -- skipping loki-skills (agent can run BOOTSTRAP-SKILLS.md manually)" -elif [[ -d "${SKILLS_DIR}/.git" ]]; then - # Existing repo -- verify it points at the expected origin before touching it. - # Defends against a local repo repoint from corrupting an unrelated tree. - EXISTING_ORIGIN="$(git -C "${SKILLS_DIR}" config --get remote.origin.url 2>/dev/null || echo '')" - if [[ "${EXISTING_ORIGIN}" != "${LOKI_SKILLS_REPO_URL}" ]]; then - warn "loki-skills origin mismatch (expected ${LOKI_SKILLS_REPO_URL}, found ${EXISTING_ORIGIN:-none}) -- leaving existing tree untouched" - write_skills_marker "existing:${EXISTING_ORIGIN:-unknown}" - elif git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then - ok "loki-skills updated ($(find "${SKILLS_DIR}" -maxdepth 1 -mindepth 1 ! -name '.*' | wc -l) entries)" - write_skills_marker "${LOKI_SKILLS_REPO_URL}" +skills_origin_url() { + git -C "${SKILLS_DIR}" config --get remote.origin.url 2>/dev/null || true +} + +skills_origin_matches_expected() { + [[ "$(skills_origin_url)" == "${LOKI_SKILLS_REPO_URL}" ]] +} + +skills_count_entries() { + find "${SKILLS_DIR}" -maxdepth 1 -mindepth 1 ! -name '.*' 2>/dev/null | wc -l +} + +skills_dir_is_empty() { + [[ -z "$(ls -A "${SKILLS_DIR}" 2>/dev/null)" ]] +} + +# Update an existing checkout in place, or refuse if its origin has been +# repointed (defends against the installer touching an unrelated repo). +skills_update_existing() { + if ! skills_origin_matches_expected; then + local origin; origin="$(skills_origin_url)" + warn "loki-skills origin mismatch (expected ${LOKI_SKILLS_REPO_URL}, found ${origin:-none}) -- leaving existing tree untouched" + skills_write_marker "existing:${origin:-unknown}" + return 0 + fi + if git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then + ok "loki-skills updated ($(skills_count_entries) entries)" else warn "loki-skills fast-forward failed -- keeping existing copy" - write_skills_marker "${LOKI_SKILLS_REPO_URL}" fi -elif [[ -e "${SKILLS_DIR}" ]]; then - # Path exists but is not a git checkout (partial clone, manual files, etc). - # Clear it iff it's empty; otherwise leave alone and warn. - if [[ -z "$(ls -A "${SKILLS_DIR}" 2>/dev/null)" ]]; then + skills_write_marker "${LOKI_SKILLS_REPO_URL}" +} + +# Returns 0 if the path is now ready for a fresh clone (absent or just cleared); +# returns 1 if it exists with content we will not touch, signalling "skip clone". +skills_prepare_for_fresh_clone() { + [[ ! -e "${SKILLS_DIR}" ]] && return 0 + if skills_dir_is_empty; then rmdir "${SKILLS_DIR}" 2>/dev/null || true - else - warn "${SKILLS_DIR} exists but is not a git repo -- leaving alone, skipping skills install" + return 0 fi -fi + warn "${SKILLS_DIR} exists but is not a git repo -- leaving alone, skipping skills install" + return 1 +} -# Re-test: if the path is now absent, do a fresh clone. Self-heals from a -# previous partial-clone failure where the dir exists with no .git inside. -if command -v git &>/dev/null && [[ ! -e "${SKILLS_DIR}" ]]; then +skills_fresh_clone() { if git clone --depth 1 --quiet "${LOKI_SKILLS_REPO_URL}" "${SKILLS_DIR}" 2>/dev/null; then ok "loki-skills cloned from ${LOKI_SKILLS_REPO_URL}" - write_skills_marker "${LOKI_SKILLS_REPO_URL}" - else - # Clean up any partial directory git may have left behind so the next - # pack run is not permanently wedged into the "exists but not a repo" path. - rm -rf "${SKILLS_DIR}" 2>/dev/null || true - warn "loki-skills clone failed -- agent can run BOOTSTRAP-SKILLS.md manually" + skills_write_marker "${LOKI_SKILLS_REPO_URL}" + return 0 fi -fi + # Self-heal: a partial dir from a failed clone would wedge the next run. + rm -rf "${SKILLS_DIR}" 2>/dev/null || true + warn "loki-skills clone failed -- agent can run BOOTSTRAP-SKILLS.md manually" +} + +skills_install() { + if ! command -v git &>/dev/null; then + warn "git not found -- skipping loki-skills (agent can run BOOTSTRAP-SKILLS.md manually)" + return 0 + fi + if [[ -d "${SKILLS_DIR}/.git" ]]; then + skills_update_existing + return 0 + fi + if ! skills_prepare_for_fresh_clone; then + return 0 + fi + skills_fresh_clone +} + +step "Installing loki-skills library" +skills_install # ── Generate token if not provided ──────────────────────────────────────────── if [[ -z "${GW_TOKEN}" ]]; then From 47e1951123ecb628544a52e627ee74af1a1484bd Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 12:15:07 +0000 Subject: [PATCH 4/7] fix(openclaw/skills): don't write bootstrap marker on origin mismatch Final review (codex) caught: writing .bootstrapped-skills on the origin-mismatch path tells the bootstrap flow 'skills already done', which suppresses BOOTSTRAP-SKILLS.md as a recovery path even though the canonical loki-skills tree is NOT installed. A repointed origin means the user opted out of canonical. Leave the marker absent so the manual recovery path remains available; warn explicitly that the marker was NOT written. Verified via the harness: T3 (mismatch) now ends with marker absent; T1 (fresh clone) and T2 (fast-forward) still write it. --- packs/openclaw/install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index 434785e..87e8ec6 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -184,8 +184,10 @@ skills_dir_is_empty() { skills_update_existing() { if ! skills_origin_matches_expected; then local origin; origin="$(skills_origin_url)" - warn "loki-skills origin mismatch (expected ${LOKI_SKILLS_REPO_URL}, found ${origin:-none}) -- leaving existing tree untouched" - skills_write_marker "existing:${origin:-unknown}" + # Do NOT write the bootstrap marker here. A repointed origin means the + # canonical loki-skills tree is not installed; leaving the marker absent + # preserves the manual BOOTSTRAP-SKILLS.md recovery path for the agent. + warn "loki-skills origin mismatch (expected ${LOKI_SKILLS_REPO_URL}, found ${origin:-none}) -- leaving existing tree untouched, skills marker NOT written" return 0 fi if git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then From 16496b97f3996e9559674131be9bde5bf4a861d1 Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 12:23:08 +0000 Subject: [PATCH 5/7] fix(openclaw/skills): subshell + ||-guard so skills install can never kill pack install Belt-and-suspenders for the best-effort contract. Even with the per-branch error handling, set -euo pipefail could still fire on something unexpected inside skills_install or its helpers (e.g., pipefail on find|wc, an unguarded command-sub in a future helper, or a bug in this code). Wrap the orchestrator call in a subshell + ||-warn so ANY failure -- errexit, pipefail, explicit exit, etc -- is contained and the pack install proceeds. Verified: forcing 'exit 99' inside skills_install now produces only a warn, pack install continues, parent script exits 0. --- packs/openclaw/install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index 87e8ec6..d0c1613 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -237,7 +237,11 @@ skills_install() { } step "Installing loki-skills library" -skills_install +# Wrap in a subshell + ||-guard so ANY unexpected failure in skills_install +# (errexit, pipefail, exit, etc.) cannot kill the pack install. This is the +# best-effort contract: skills are nice-to-have, the agent can recover via +# BOOTSTRAP-SKILLS.md if anything here goes sideways. +( skills_install ) || warn "loki-skills install hit an unexpected error -- continuing pack install (best-effort)" # ── Generate token if not provided ──────────────────────────────────────────── if [[ -z "${GW_TOKEN}" ]]; then From 71ff2c3eb50c74d90dd9152186621578123bf352 Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 13:19:05 +0000 Subject: [PATCH 6/7] fix(openclaw/skills): capture and surface real rc on subshell guard Address codex review on commit 16496b9: - Comment was overclaiming: subshell + ||-guard contains RUNTIME failures (errexit, pipefail, exit, signals, 127s) but NOT parse-time syntax errors in install.sh itself -- those must be caught by bash -n in CI. Comment now reflects this accurately. - Warn message dropped the failing exit status, making ops triage harder. Capture rc via '( cmd ) || _rc=$?' (the if-! form would have flipped $? to 0) and surface it in the warn, plus point at BOOTSTRAP-SKILLS.md as the documented recovery path. - Note: the bash gotcha that errexit is disabled inside subshells whose result is tested by ||/&&/if is documented but doesn't matter here -- every external command in skills_install and its helpers is already wrapped in explicit if/else or '|| true', so failures propagate via tested return values. inherit_errexit does NOT override this and would not help. Verified: rc=99 from explicit 'exit 99' is now reported correctly (was rc=0 with the if-! form). Pack tests still 60/0/0. --- packs/openclaw/install.sh | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index d0c1613..ebdce92 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -237,11 +237,19 @@ skills_install() { } step "Installing loki-skills library" -# Wrap in a subshell + ||-guard so ANY unexpected failure in skills_install -# (errexit, pipefail, exit, etc.) cannot kill the pack install. This is the -# best-effort contract: skills are nice-to-have, the agent can recover via -# BOOTSTRAP-SKILLS.md if anything here goes sideways. -( skills_install ) || warn "loki-skills install hit an unexpected error -- continuing pack install (best-effort)" +# Wrap in a subshell + ||-guard so ANY runtime failure in skills_install +# (errexit, pipefail, explicit exit, missing command, signal death, unbound +# var) cannot kill the pack install. Parse-time syntax errors in this file +# itself would still fail before we reach this line -- those must be caught +# by `bash -n` in CI. This is the best-effort contract: skills are +# nice-to-have, the agent can recover via BOOTSTRAP-SKILLS.md if anything +# here goes sideways. +_skills_rc=0 +( skills_install ) || _skills_rc=$? +if (( _skills_rc != 0 )); then + warn "loki-skills install failed (rc=${_skills_rc}) -- continuing pack install (best-effort); agent can recover via BOOTSTRAP-SKILLS.md" +fi +unset _skills_rc # ── Generate token if not provided ──────────────────────────────────────────── if [[ -z "${GW_TOKEN}" ]]; then From 1c4eef391557a56acbeecf833eb7061d11d95569 Mon Sep 17 00:00:00 2001 From: Roy Osherove <575051+royosherove@users.noreply.github.com> Date: Mon, 18 May 2026 13:26:35 +0000 Subject: [PATCH 7/7] fix(openclaw/skills): gate marker on non-empty tree when ff-update fails Address codex inline P2 on commit 71ff2c3 (PR #64): skills_update_existing wrote the .bootstrapped-skills marker even when 'git pull --ff-only' failed. If the repo was corrupt/empty (interrupted clone, bare 'git init' with matching origin), the marker would suppress BOOTSTRAP-SKILLS.md recovery while leaving the agent with no actual skills. Now: only write the marker when fast-forward fails IF the tree has non-empty content (entries > 0). An empty tree -> no marker -> manual recovery path remains available. Verified via the harness: corrupt empty repo with matching origin now leaves the marker absent. Pack tests still 60/0/0. --- packs/openclaw/install.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packs/openclaw/install.sh b/packs/openclaw/install.sh index ebdce92..501ee5d 100755 --- a/packs/openclaw/install.sh +++ b/packs/openclaw/install.sh @@ -192,10 +192,21 @@ skills_update_existing() { fi if git -C "${SKILLS_DIR}" pull --ff-only --quiet 2>/dev/null; then ok "loki-skills updated ($(skills_count_entries) entries)" + skills_write_marker "${LOKI_SKILLS_REPO_URL}" + return 0 + fi + # Fast-forward failed. Only mark bootstrapped if the existing tree is + # non-empty (still usable for the agent, e.g., transient network blip). + # An empty/corrupt repo (interrupted clone, bare 'git init') would leave + # no skills behind -- in that case keep the marker absent so the manual + # BOOTSTRAP-SKILLS.md recovery path stays available. + local entries; entries="$(skills_count_entries)" + if (( entries > 0 )); then + warn "loki-skills fast-forward failed -- keeping existing tree (${entries} entries)" + skills_write_marker "${LOKI_SKILLS_REPO_URL}" else - warn "loki-skills fast-forward failed -- keeping existing copy" + warn "loki-skills fast-forward failed and tree is empty -- skills marker NOT written, agent can recover via BOOTSTRAP-SKILLS.md" fi - skills_write_marker "${LOKI_SKILLS_REPO_URL}" } # Returns 0 if the path is now ready for a fresh clone (absent or just cleared);