Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b387ea6
feat: add Claude Code Agent Skill template
claude Jun 9, 2026
0bac2e2
ci: smoke-test the Claude Code Agent Skill wiring
claude Jun 9, 2026
a6a775e
feat: install Memory Hive as a native Claude Code Agent Skill
claude Jun 9, 2026
4da2e01
feat: platform-neutral operating guide via `memory-hive guide`
claude Jun 9, 2026
c434c4c
feat: harness hooks — mechanical hydrate + task-end ritual for Claude…
claude Jun 9, 2026
579e75d
release: cut v1.3.0 — Memory everywhere, enforced
claude Jun 9, 2026
67aa2e6
ci: add manual-dispatch path to the release workflow
claude Jun 10, 2026
5f5aba2
ci: one-shot workflow to cut the v1.3.0 release from a branch push
claude Jun 11, 2026
11531d5
ci: remove one-shot v1.3.0 release workflow (release is live)
claude Jun 11, 2026
dddb4e6
feat: log + learn verbs — the task-end ritual as commands on every pl…
claude Jun 11, 2026
09c666a
release: cut v1.4.0 — Ritual as commands
claude Jun 11, 2026
38aee23
Merge remote-tracking branch 'origin/main' into claude/busy-cannon-80…
claude Jun 11, 2026
6ba4231
Merge remote-tracking branch 'origin/main' into claude/busy-cannon-80…
claude Jun 11, 2026
3de8408
ci: one-shot workflow to cut the v1.4.0 release from a branch push
claude Jun 11, 2026
94fc383
ci: remove one-shot v1.4.0 release workflow (release is live)
claude Jun 11, 2026
1970d57
feat: MCP server + ambient capture — Pieces-style memory, hive idiom
claude Jun 12, 2026
f60ceb8
release: cut v1.5.0 — The hive speaks MCP
claude Jun 12, 2026
9011068
Merge remote-tracking branch 'origin/main' into claude/busy-cannon-80…
claude Jun 12, 2026
39822b2
ci: one-shot workflow to cut the v1.5.0 release from a branch push
claude Jun 12, 2026
52a88ba
ci: remove one-shot v1.5.0 release workflow (release is live)
claude Jun 12, 2026
f7d795b
feat: Cursor harness hooks — enforcement parity via hooks.json
claude Jun 12, 2026
93fd014
release: cut v1.6.0 — Cursor falls in line
claude Jun 12, 2026
37467c2
Merge remote-tracking branch 'origin/main' into claude/busy-cannon-80…
claude Jun 12, 2026
e992674
ci: one-shot workflow to cut the v1.6.0 release from a branch push
claude Jun 12, 2026
d3d842c
ci: remove one-shot v1.6.0 release workflow (release is live)
claude Jun 12, 2026
808f358
fix: self-update corrupted the running installer and CLI (v1.6.1)
claude Jun 12, 2026
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
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,51 @@ jobs:
[ "$fail" -eq 0 ] || exit 1
echo "Platform integration smoke OK"

- name: Self-update smoke (offset-shift regression)
shell: bash
run: |
set -eu
FAKE_HOME="$(mktemp -d)"
INSTALL_DIR="$(mktemp -d)"
export HOME="$FAKE_HOME"
export MEMORY_HIVE_DIR="$INSTALL_DIR"
export MEMORY_HIVE_REPO="$(pwd)"
mkdir -p "$FAKE_HOME/.claude"
sh install.sh < /dev/null > /dev/null 2>&1

# Build a mutated upstream whose install.sh / memory-hive /
# update.sh are thousands of bytes longer — every later byte
# offset shifts. Updating by executing the INSTALLED installer
# while it copies the longer copy over itself used to corrupt the
# running interpreter ("syntax error near unexpected token `do'").
MUT="$(mktemp -d)"
cp -R "$(pwd)/." "$MUT/"
for f in install.sh memory-hive update.sh; do
python3 - "$MUT/$f" <<'PYPAD'
import sys
path = sys.argv[1]
lines = open(path).readlines()
pad = ["# pad-for-offset-shift %04d\n" % i for i in range(200)]
lines[1:1] = pad
open(path, "w").writelines(lines)
PYPAD
done

# The dangerous invocation: the installed installer replacing
# itself with the longer upstream copy while executing.
MEMORY_HIVE_SYNC=1 MEMORY_HIVE_REPO="$MUT" sh "$INSTALL_DIR/install.sh" < /dev/null > /tmp/mh-selfupdate.log 2>&1 \
|| { echo "FAIL: self-update crashed"; tail -20 /tmp/mh-selfupdate.log; exit 1; }
grep -q "pad-for-offset-shift" "$INSTALL_DIR/install.sh" || { echo "FAIL: install.sh not refreshed"; exit 1; }
grep -q "pad-for-offset-shift" "$INSTALL_DIR/memory-hive" || { echo "FAIL: memory-hive not refreshed"; exit 1; }
sh "$INSTALL_DIR/memory-hive" status > /dev/null || { echo "FAIL: updated CLI broken"; exit 1; }

# And the update.sh wrapper (temp-copy execution) survives the
# reverse shift back to the shorter upstream copy.
sh "$INSTALL_DIR/update.sh" < /dev/null > /tmp/mh-selfupdate2.log 2>&1 \
|| { echo "FAIL: update.sh wrapper crashed"; tail -20 /tmp/mh-selfupdate2.log; exit 1; }
sh "$INSTALL_DIR/memory-hive" status > /dev/null || { echo "FAIL: CLI broken after wrapper update"; exit 1; }
echo "Self-update smoke OK"

- name: Platform integration smoke — opt-out
shell: bash
run: |
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,31 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

## [1.6.1] — 2026-06-12 — `Self-update no longer eats itself`

### Fixed

- `memory-hive update` could crash mid-run with phantom syntax errors
("syntax error near unexpected token") and leave a half-applied
update. Cause: the installer copied `install.sh` and `memory-hive`
over themselves in place while those very files were executing (the
PATH shim is a symlink to the same inode), so the running interpreter
read the new, longer file at old byte offsets. The recent releases
grew both files enough to make this near-deterministic. Fixes:
- all helper installs and hook renders now stage to a temp file and
`mv` into place — rename keeps the old inode readable for running
processes;
- `update.sh` executes the installed installer from a temp copy,
never from the path being refreshed;
- new CI step installs, then self-updates from a deliberately
offset-shifted upstream copy and asserts the run completes and the
refreshed CLI works (both the direct and the `update.sh` wrapper
paths).
Affected machines recover with one
`curl -fsSL .../install.sh | sh` (or a second `memory-hive update`,
which is byte-identical and therefore safe); agent silos were never
touched — the crash happened after the preserve/restore step.

## [1.6.0] — 2026-06-12 — `Cursor falls in line`

### Added
Expand Down
23 changes: 19 additions & 4 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,20 @@ fi
# Install the helper scripts into the install dir so users can run them
# locally without a curl round-trip. These are tools (not user content) and
# we always keep them current with upstream.
#
# Stage + rename, never cp in place: on the update path THIS process IS
# $INSTALL_DIR/install.sh, and the CLI that launched it is (or is symlinked
# to) $INSTALL_DIR/memory-hive. An in-place cp rewrites the bytes under the
# running interpreter, which then reads the new file at old offsets and
# dies with phantom syntax errors. rename() just swaps the directory
# entry — the old inode stays readable until the process exits.
for helper in create-agent.sh update.sh install.sh check-compliance.sh memory-hive memory_hive_recall.py memory_hive_mcp.py; do
_src="$TMP_DIR/memory-hive/$helper"
[ -f "$_src" ] || continue
cp "$_src" "$INSTALL_DIR/$helper"
chmod +x "$INSTALL_DIR/$helper" 2>/dev/null || true
_hstage="$INSTALL_DIR/$helper.memhive.$$"
cp "$_src" "$_hstage" || { rm -f "$_hstage"; die "Failed to stage $helper"; }
chmod +x "$_hstage" 2>/dev/null || true
mv "$_hstage" "$INSTALL_DIR/$helper" || { rm -f "$_hstage"; die "Failed to install $helper"; }
done

# Install a bare `memory-hive` command into the user's existing PATH when
Expand Down Expand Up @@ -700,10 +709,16 @@ if [ -d "$_hooks_src_dir" ]; then
for _hk in "$_hooks_src_dir"/*.sh; do
[ -f "$_hk" ] || continue
_hk_dst="$INSTALL_DIR/hooks/$(basename "$_hk")"
# Stage + rename for the same reason as the helper loop: a hook
# could be mid-execution while an update refreshes it.
_hk_stage="$_hk_dst.memhive.$$"
if sed -e "s|\${HIVE_DIR}|$_hive_dir_escaped|g" \
-e "s|\${INSTALL_DIR}|$_install_dir_escaped|g" \
"$_hk" > "$_hk_dst" 2>/dev/null; then
chmod +x "$_hk_dst" 2>/dev/null || true
"$_hk" > "$_hk_stage" 2>/dev/null; then
chmod +x "$_hk_stage" 2>/dev/null || true
mv "$_hk_stage" "$_hk_dst" 2>/dev/null || rm -f "$_hk_stage"
else
rm -f "$_hk_stage" 2>/dev/null || true
fi
done
fi
Expand Down
16 changes: 15 additions & 1 deletion update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,22 @@ REMOTE_INSTALLER="https://raw.githubusercontent.com/TJCurnutte/memory-hive/main/
# Prefer the locally-installed install.sh so offline/private forks still work;
# fall back to curl from GitHub. In both cases, set MEMORY_HIVE_SYNC=1 so
# install.sh knows this is an update and should refresh shared content.
#
# CRITICAL: run the installer from a TEMP COPY, never from
# $INSTALL_DIR/install.sh directly — the installer refreshes that very file
# mid-run, and executing a file while it is replaced corrupts the running
# interpreter (phantom "syntax error near unexpected token").
if [ -f "$INSTALL_DIR/install.sh" ]; then
MEMORY_HIVE_SYNC=1 MEMORY_HIVE_DIR="$INSTALL_DIR" sh "$INSTALL_DIR/install.sh"
_tmp_installer="$(mktemp "${TMPDIR:-/tmp}/memory-hive-install.XXXXXX")" \
|| { printf 'ERROR: mktemp failed\n' >&2; exit 1; }
cp "$INSTALL_DIR/install.sh" "$_tmp_installer"
if MEMORY_HIVE_SYNC=1 MEMORY_HIVE_DIR="$INSTALL_DIR" sh "$_tmp_installer"; then
_rc=0
else
_rc=$?
fi
rm -f "$_tmp_installer"
exit "$_rc"
elif command -v curl >/dev/null 2>&1; then
MEMORY_HIVE_SYNC=1 MEMORY_HIVE_DIR="$INSTALL_DIR" \
sh -c "$(curl -fsSL "$REMOTE_INSTALLER")"
Expand Down
Loading