From 7c9e76f04cf5c6d057e437b550ca706b1eea568d Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Sat, 3 Oct 2026 18:55:33 +0200 Subject: [PATCH 1/2] chore(beads): stop the repo hooks failing on clones without lefthook or git-lfs Replace the seven chained hook files with the five bd 1.1.2 wrappers and the standard commit-msg hook. Add the note that the tracker syncs only to giteaer/Pumperly-beads, and ignore events.jsonl next to the other exports. --- .beads/.gitignore | 1 + .beads/hooks/commit-msg | 21 +++++++--- .beads/hooks/post-checkout | 10 ++--- .beads/hooks/post-commit | 4 -- .beads/hooks/post-merge | 10 ++--- .beads/hooks/pre-commit | 66 ++--------------------------- .beads/hooks/pre-push | 10 ++--- .beads/hooks/prepare-commit-msg | 74 ++------------------------------- .gitignore | 1 + AGENTS.md | 4 ++ CLAUDE.md | 4 ++ 11 files changed, 40 insertions(+), 165 deletions(-) delete mode 100755 .beads/hooks/post-commit diff --git a/.beads/.gitignore b/.beads/.gitignore index 71ef677..71480b0 100644 --- a/.beads/.gitignore +++ b/.beads/.gitignore @@ -65,6 +65,7 @@ backup/ # Tracker data (lives in the Dolt remote named by sync.remote, never in git) issues.jsonl interactions.jsonl +events.jsonl # Legacy files (from pre-Dolt versions) *.db diff --git a/.beads/hooks/commit-msg b/.beads/hooks/commit-msg index 20abc1e..2556e58 100755 --- a/.beads/hooks/commit-msg +++ b/.beads/hooks/commit-msg @@ -1,6 +1,15 @@ -#!/bin/sh -# Strip Co-Authored-By lines from commit messages -sed -i.bak '/Co-Authored-By/Id' "$1" -# Remove trailing blank lines left behind -sed -i.bak -e :a -e '/^\n*$/{$d;N;ba' -e '}' "$1" -rm -f "$1.bak" +#!/usr/bin/env sh +# Every commit in this repository has one author. Clones that point +# core.hooksPath at .beads/hooks skip the machine-wide git hooks, so this +# hook does the one job those had here: it removes Co-Authored-By trailer +# lines from the commit message. A mention inside a sentence is kept. +msg="$1" +[ -f "$msg" ] || exit 0 +tmp="$msg.strip.$$" +grep -v -i '^[[:space:]]*co-authored-by:' "$msg" > "$tmp" || true +awk '{ line[NR] = $0 } + END { n = NR + while (n > 0 && line[n] ~ /^[[:space:]]*$/) n-- + for (i = 1; i <= n; i++) print line[i] }' "$tmp" > "$msg" +rm -f "$tmp" +exit 0 diff --git a/.beads/hooks/post-checkout b/.beads/hooks/post-checkout index 525ed3a..42cc454 100755 --- a/.beads/hooks/post-checkout +++ b/.beads/hooks/post-checkout @@ -1,9 +1,5 @@ -#!/bin/sh -case ":$PATH:" in *":/opt/homebrew/bin:"*) ;; *) PATH="/opt/homebrew/bin:$PATH"; export PATH ;; esac -command -v git-lfs >/dev/null 2>&1 || { printf >&2 "\n%s\n\n" "This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-checkout' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks')."; exit 2; } -git lfs post-checkout "$@" - -# --- BEGIN BEADS INTEGRATION v1.0.5 --- +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.1.2 --- # This section is managed by beads. Do not remove these markers. if command -v bd >/dev/null 2>&1; then export BD_GIT_HOOK=1 @@ -34,4 +30,4 @@ if command -v bd >/dev/null 2>&1; then fi if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi fi -# --- END BEADS INTEGRATION v1.0.5 --- +# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/post-commit b/.beads/hooks/post-commit deleted file mode 100755 index 459b7f9..0000000 --- a/.beads/hooks/post-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -case ":$PATH:" in *":/opt/homebrew/bin:"*) ;; *) PATH="/opt/homebrew/bin:$PATH"; export PATH ;; esac -command -v git-lfs >/dev/null 2>&1 || { printf >&2 "\n%s\n\n" "This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-commit' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks')."; exit 2; } -git lfs post-commit "$@" diff --git a/.beads/hooks/post-merge b/.beads/hooks/post-merge index 4e78c74..37bdb30 100755 --- a/.beads/hooks/post-merge +++ b/.beads/hooks/post-merge @@ -1,9 +1,5 @@ -#!/bin/sh -case ":$PATH:" in *":/opt/homebrew/bin:"*) ;; *) PATH="/opt/homebrew/bin:$PATH"; export PATH ;; esac -command -v git-lfs >/dev/null 2>&1 || { printf >&2 "\n%s\n\n" "This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'post-merge' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks')."; exit 2; } -git lfs post-merge "$@" - -# --- BEGIN BEADS INTEGRATION v1.0.5 --- +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.1.2 --- # This section is managed by beads. Do not remove these markers. if command -v bd >/dev/null 2>&1; then export BD_GIT_HOOK=1 @@ -34,4 +30,4 @@ if command -v bd >/dev/null 2>&1; then fi if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi fi -# --- END BEADS INTEGRATION v1.0.5 --- +# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/pre-commit b/.beads/hooks/pre-commit index f5ae3c4..952c254 100755 --- a/.beads/hooks/pre-commit +++ b/.beads/hooks/pre-commit @@ -1,65 +1,5 @@ -#!/bin/sh - -if [ "$LEFTHOOK_VERBOSE" = "1" -o "$LEFTHOOK_VERBOSE" = "true" ]; then - set -x -fi - -if [ "$LEFTHOOK" = "0" ]; then - exit 0 -fi - -call_lefthook() -{ - if test -n "$LEFTHOOK_BIN" - then - "$LEFTHOOK_BIN" "$@" - elif lefthook -h >/dev/null 2>&1 - then - lefthook "$@" - else - dir="$(git rev-parse --show-toplevel)" - osArch=$(uname | tr '[:upper:]' '[:lower:]') - cpuArch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/') - if test -f "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" - then - "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" "$@" - elif test -f "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" - then - "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" "$@" - elif test -f "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" - then - "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" "$@" - elif test -f "$dir/node_modules/lefthook/bin/index.js" - then - "$dir/node_modules/lefthook/bin/index.js" "$@" - - elif bundle exec lefthook -h >/dev/null 2>&1 - then - bundle exec lefthook "$@" - elif yarn lefthook -h >/dev/null 2>&1 - then - yarn lefthook "$@" - elif pnpm lefthook -h >/dev/null 2>&1 - then - pnpm lefthook "$@" - elif swift package plugin lefthook >/dev/null 2>&1 - then - swift package --disable-sandbox plugin lefthook "$@" - elif command -v mint >/dev/null 2>&1 - then - mint run csjones/lefthook-plugin "$@" - elif command -v npx >/dev/null 2>&1 - then - npx lefthook "$@" - else - echo "Can't find lefthook in PATH" - fi - fi -} - -call_lefthook run "pre-commit" "$@" - -# --- BEGIN BEADS INTEGRATION v1.0.5 --- +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.1.2 --- # This section is managed by beads. Do not remove these markers. if command -v bd >/dev/null 2>&1; then export BD_GIT_HOOK=1 @@ -90,4 +30,4 @@ if command -v bd >/dev/null 2>&1; then fi if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi fi -# --- END BEADS INTEGRATION v1.0.5 --- +# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/pre-push b/.beads/hooks/pre-push index c5338a8..6a61e9d 100755 --- a/.beads/hooks/pre-push +++ b/.beads/hooks/pre-push @@ -1,9 +1,5 @@ -#!/bin/sh -case ":$PATH:" in *":/opt/homebrew/bin:"*) ;; *) PATH="/opt/homebrew/bin:$PATH"; export PATH ;; esac -command -v git-lfs >/dev/null 2>&1 || { printf >&2 "\n%s\n\n" "This repository is configured for Git LFS but 'git-lfs' was not found on your path. If you no longer wish to use Git LFS, remove this hook by deleting the 'pre-push' file in the hooks directory (set by 'core.hookspath'; usually '.git/hooks')."; exit 2; } -git lfs pre-push "$@" - -# --- BEGIN BEADS INTEGRATION v1.0.5 --- +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.1.2 --- # This section is managed by beads. Do not remove these markers. if command -v bd >/dev/null 2>&1; then export BD_GIT_HOOK=1 @@ -34,4 +30,4 @@ if command -v bd >/dev/null 2>&1; then fi if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi fi -# --- END BEADS INTEGRATION v1.0.5 --- +# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.beads/hooks/prepare-commit-msg b/.beads/hooks/prepare-commit-msg index 1aa423b..d5b13ff 100755 --- a/.beads/hooks/prepare-commit-msg +++ b/.beads/hooks/prepare-commit-msg @@ -1,73 +1,5 @@ -#!/bin/sh - -if [ "$LEFTHOOK_VERBOSE" = "1" -o "$LEFTHOOK_VERBOSE" = "true" ]; then - set -x -fi - -if [ "$LEFTHOOK" = "0" ]; then - exit 0 -fi - -call_lefthook() -{ - if test -n "$LEFTHOOK_BIN" - then - "$LEFTHOOK_BIN" "$@" - elif lefthook -h >/dev/null 2>&1 - then - lefthook "$@" - else - dir="$(git rev-parse --show-toplevel)" - osArch=$(uname | tr '[:upper:]' '[:lower:]') - cpuArch=$(uname -m | sed 's/aarch64/arm64/;s/x86_64/x64/') - if test -f "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" - then - "$dir/node_modules/lefthook-${osArch}-${cpuArch}/bin/lefthook" "$@" - elif test -f "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" - then - "$dir/node_modules/@evilmartians/lefthook/bin/lefthook-${osArch}-${cpuArch}/lefthook" "$@" - elif test -f "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" - then - "$dir/node_modules/@evilmartians/lefthook-installer/bin/lefthook" "$@" - elif test -f "$dir/node_modules/lefthook/bin/index.js" - then - "$dir/node_modules/lefthook/bin/index.js" "$@" - elif go tool lefthook -h >/dev/null 2>&1 - then - go tool lefthook "$@" - elif bundle exec lefthook -h >/dev/null 2>&1 - then - bundle exec lefthook "$@" - elif yarn lefthook -h >/dev/null 2>&1 - then - yarn lefthook "$@" - elif pnpm lefthook -h >/dev/null 2>&1 - then - pnpm lefthook "$@" - elif swift package lefthook >/dev/null 2>&1 - then - swift package --build-path .build/lefthook --disable-sandbox lefthook "$@" - elif command -v mint >/dev/null 2>&1 - then - mint run csjones/lefthook-plugin "$@" - elif uv run lefthook -h >/dev/null 2>&1 - then - uv run lefthook "$@" - elif mise exec -- lefthook -h >/dev/null 2>&1 - then - mise exec -- lefthook "$@" - elif devbox run lefthook -h >/dev/null 2>&1 - then - devbox run lefthook "$@" - else - echo "Can't find lefthook in PATH" - fi - fi -} - -call_lefthook run "prepare-commit-msg" "$@" - -# --- BEGIN BEADS INTEGRATION v1.0.5 --- +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.1.2 --- # This section is managed by beads. Do not remove these markers. if command -v bd >/dev/null 2>&1; then export BD_GIT_HOOK=1 @@ -98,4 +30,4 @@ if command -v bd >/dev/null 2>&1; then fi if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi fi -# --- END BEADS INTEGRATION v1.0.5 --- +# --- END BEADS INTEGRATION v1.1.2 --- diff --git a/.gitignore b/.gitignore index 7ebdd1a..8bc94f7 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ bun.lockb # remote (see .beads/config.yaml sync.remote); only the scaffolding under .beads/ is committed. .beads/issues.jsonl .beads/interactions.jsonl +.beads/events.jsonl .beads/dolt/ .beads/embeddeddolt/ .beads/backup/ diff --git a/AGENTS.md b/AGENTS.md index c254216..429237a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,3 +102,7 @@ This protocol applies when ending a Beads implementation workflow. It is subordi - Do not commit or push without clear authority from the active profile or the current user request. - If a required sync or push is blocked, stop and report the exact command and error. + +## Where the tracker syncs + +This repo is public, so its tracker syncs only to the private Dolt remote named by `sync.remote` in `.beads/config.yaml` (`giteaer/Pumperly-beads` on Gitea). The block above says sync uses "your git remote". Here that never means this GitHub repo. Don't add it as a Dolt remote and don't push `refs/dolt/*` to it. diff --git a/CLAUDE.md b/CLAUDE.md index cd016fc..dbb4629 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -525,3 +525,7 @@ This protocol applies when ending a Beads implementation workflow. It is subordi - Do not commit or push without clear authority from the active profile or the current user request. - If a required sync or push is blocked, stop and report the exact command and error. + +## Where the tracker syncs + +This repo is public, so its tracker syncs only to the private Dolt remote named by `sync.remote` in `.beads/config.yaml` (`giteaer/Pumperly-beads` on Gitea). The block above says sync uses "your git remote". Here that never means this GitHub repo. Don't add it as a Dolt remote and don't push `refs/dolt/*` to it. From 82808b742a5c4c8d124cebbae5f91d489b7fb4aa Mon Sep 17 00:00:00 2001 From: GeiserX <9169332+GeiserX@users.noreply.github.com> Date: Sat, 3 Oct 2026 21:22:54 +0200 Subject: [PATCH 2/2] fix(git): touch only messages that carry the trailer, and replace them atomically The earlier commit-msg hook could empty the message when it failed to write its temporary file and still exit 0. This version never touches a message without a Co-Authored-By line, writes the cleaned text to a fresh mktemp file, checks it against a second run, and renames it over the message only when every step worked; on any failure the message stays and the commit stops. --- .beads/hooks/commit-msg | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/.beads/hooks/commit-msg b/.beads/hooks/commit-msg index 2556e58..2a27374 100755 --- a/.beads/hooks/commit-msg +++ b/.beads/hooks/commit-msg @@ -3,13 +3,30 @@ # core.hooksPath at .beads/hooks skip the machine-wide git hooks, so this # hook does the one job those had here: it removes Co-Authored-By trailer # lines from the commit message. A mention inside a sentence is kept. +# +# A message without such a line is never touched. Otherwise the cleaned +# text goes to a fresh file from mktemp, is compared with a second run, +# and is renamed over the message only when every step worked; on any +# failure the message stays as it was and the commit stops. msg="$1" [ -f "$msg" ] || exit 0 -tmp="$msg.strip.$$" -grep -v -i '^[[:space:]]*co-authored-by:' "$msg" > "$tmp" || true -awk '{ line[NR] = $0 } - END { n = NR - while (n > 0 && line[n] ~ /^[[:space:]]*$/) n-- - for (i = 1; i <= n; i++) print line[i] }' "$tmp" > "$msg" -rm -f "$tmp" +case $msg in -*) msg="./$msg" ;; esac +found=0 +LC_ALL=C grep -q -i '^[[:space:]]*co-authored-by:' "$msg" || found=$? +case $found in + 0) ;; + 1) exit 0 ;; + *) echo "commit-msg: cannot read $msg" >&2; exit 1 ;; +esac +clean() { + LC_ALL=C awk 'tolower($0) !~ /^[ \t\r\f\v]*co-authored-by:/ { line[++n] = $0 } + END { while (n > 0 && line[n] ~ /^[ \t\r\f\v]*$/) n-- + for (i = 1; i <= n; i++) print line[i] }' < "$msg" +} +tmp=$(mktemp "$msg.XXXXXX") || { echo "commit-msg: cannot create a temporary file beside $msg" >&2; exit 1; } +trap 'rm -f "$tmp"' EXIT +trap 'exit 1' HUP INT TERM +clean > "$tmp" && clean | cmp -s - "$tmp" || { echo "commit-msg: cannot rewrite $msg" >&2; exit 1; } +trap '' HUP INT TERM +mv -f "$tmp" "$msg" || { echo "commit-msg: cannot replace $msg" >&2; exit 1; } exit 0