Skip to content

Commit 13fd15a

Browse files
acn-ericlawclaude
andcommitted
chore: AI-enable with agent-memory v4.38.0 — shared memory layer + contributor/consumer fork
Mode A fresh enable: committed memory/ layer (instructions, continuity with seeded invariants from README Scope, DRAFT vision, decay policy, smoke test, first session log), vendor bootstraps, built-in skills, hook dispatchers with secret guard + ritual capture, advisory CI floor. Root AGENTS.md forks contributors (memory protocol) from package consumers (README.md), per the two-audience model. Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent 99ae249 commit 13fd15a

53 files changed

Lines changed: 7700 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agent/schema.md

Lines changed: 387 additions & 0 deletions
Large diffs are not rendered by default.

.agent/secret-scan-ignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# agent-memory — secret-scan exemptions (v4.34.0). Committed and human-audited.
2+
#
3+
# The pre-commit secret guard and the CI floor's changed-config scan check staged/changed
4+
# .json / .yml / .yaml / .properties / .toml / .ini / .env* files for [secret-material].
5+
# Formats that carry comments (YAML, TOML, INI, markdown) waive a deliberately-quoted example
6+
# by tagging the LINE with `lint:allow-secret-material`. JSON has no comments and a
7+
# .properties same-line comment corrupts the value — exempt such files HERE instead.
8+
#
9+
# One shell-glob pattern per line; `#` starts a comment. Applies to CONFIG files only —
10+
# memory/ is never exempt. Every entry is a human decision: prefer fixing the file (move the
11+
# value to an env var / placeholder) over exempting it, and never exempt a live credential.
12+
#
13+
# Examples:
14+
# test/fixtures/postman-collection.json
15+
# **/test/resources/dummy-creds.properties

.agent/version.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# agent-memory install manifest
2+
3+
> Records which version of the agent-memory tool this repo is on, so `ENABLE.md`
4+
> Mode B can detect drift and upgrade in place (see the tool's `UPGRADE.md`).
5+
> `version` gates the upgrade ladder — don't hand-edit it unless you mean to.
6+
7+
- **version:** 4.38.0
8+
- **enabled_with:** 4.38.0
9+
- **last_upgraded:** 2026-08-22
10+
- **mode:** A

.cursorrules

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
This project uses the agent-memory shared memory system.
2+
3+
Follow memory/PROTOCOL.md first. AGENTS.md is its one-line universal discovery shim.
4+
5+
Identify yourself as Cursor in all session logs.

.gitattributes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# agent-memory — keep executable scripts + git hooks LF so bash runs them on Windows (Git Bash / WSL).
2+
# Git for Windows defaults to core.autocrlf=true; without this, *.sh and .githooks/* would be rewritten
3+
# to CRLF on checkout and bash would fail with "bad interpreter: /usr/bin/env bash^M".
4+
*.sh text eol=lf
5+
.githooks/* text eol=lf
6+
.githooks/*.d/* text eol=lf

.githooks/README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# `.githooks/` — composable, vendor-neutral ritual triggers
2+
3+
These are **committed, vendor-neutral git hook dispatchers and fragments** that reinforce the
4+
after-session ritual for *any* agent (Claude, Copilot, Kiro, …) — because everyone commits,
5+
regardless of which AI did the work. They are **advisory by default** — the pre-commit secret guard
6+
alone **enforces** (findings block the
7+
commit; a deliberate, scoped exception, because secrets carry irreversible after-the-fact cost) —
8+
and the **tool runs nothing itself**: git invokes them in your env at your
9+
opt-in (`no-build-step-agent-run`). See `docs/optional-ritual-hook.md` and `DECAY.md` for the rationale.
10+
11+
## First-run init (one command)
12+
13+
A fresh clone has the gitignored skill **adapters absent** and the dispatchers **unactivated** (git can't
14+
auto-run committed hooks on clone — security). Set both up with one idempotent command:
15+
16+
```sh
17+
bash .githooks/init.sh
18+
```
19+
20+
It regenerates the vendor skill adapters **and** runs `git config core.hooksPath .githooks`. **The agent
21+
runs this itself on a first session** (see `memory/PROTOCOL.md`), so an untrained user does nothing. To
22+
activate the dispatchers alone: `git config core.hooksPath .githooks` (undo:
23+
`git config --unset core.hooksPath`).
24+
**CI is the zero-config floor**`.github/workflows/agent-memory.yml` on GitHub; `.gitlab-ci.yml` +
25+
`.gitlab/agent-memory-ci.yml` on GitLab (v4.31.0); `.azuredevops/agent-memory-ci.yml` on Azure
26+
DevOps (v4.32.0) — it runs server-side on every push and, on GitHub/GitLab, every pull/merge
27+
request (Azure DevOps PR-time runs need the optional Build Validation policy), so the ritual is
28+
enforced even on a clone where the local hook was never activated. (Honest limits: a self-managed GitLab instance needs an admin-registered runner;
29+
an Azure DevOps pipeline is inert until its one-time `az pipelines create` binding.)
30+
31+
## Dispatcher contract
32+
33+
The `pre-commit` and `post-commit` entrypoints are deliberately small dispatchers. Each runs the
34+
executable regular files in its matching directory — `.githooks/pre-commit.d/` or
35+
`.githooks/post-commit.d/` — in deterministic C-locale filename order. Hidden and non-executable
36+
files are ignored. Hook arguments are forwarded unchanged.
37+
38+
Every fragment runs even when an earlier fragment fails, so independent hook layers all get a
39+
chance to report. The dispatcher returns the **first non-zero status** after the run. That blocks a
40+
pre-commit when any enforcing fragment fails; Git ignores post-commit status, while direct invocation
41+
and tests can still observe failures.
42+
43+
Agent-memory's managed fragments use the `50-` slot:
44+
45+
- `pre-commit.d/50-agent-memory-secret-guard`
46+
- `post-commit.d/50-agent-memory-ritual-capture`
47+
48+
Add another executable fragment instead of replacing either hook entrypoint: use `00-``49-` to run
49+
before agent-memory or `51-``99-` to run after it. Upgrades refresh unchanged copies of the two
50+
dispatchers and managed `50-` fragments, preserve every differently named fragment, and human-gate
51+
any locally modified managed file instead of silently overwriting it. This gives other hook layers
52+
a stable composition seam and makes each behavior independently runnable in CI.
53+
54+
The dispatcher contract itself is covered by `tests/test_githook_dispatchers.sh` in the tool repo.
55+
56+
## Managed fragments
57+
58+
- **`pre-commit.d/50-agent-memory-secret-guard`** (v4.34.0) — the **`[secret-material]` guard**:
59+
before the commit exists, scans the
60+
**staged content** (the index, not the worktree — exactly what this commit would publish; a
61+
pre-existing finding elsewhere never gates an unrelated commit) of **two surfaces**:
62+
`memory/**.md` (the full profile — credentials + PII) and **config files**`.json` / `.yml` /
63+
`.yaml` / `.properties` / `.toml` / `.ini` / `.env*` anywhere in the repo, credential-class checks
64+
only (token shapes, key assignments, Authorization headers, private keys; config files
65+
legitimately carry contact emails and paths). The config surface exists because of a real
66+
incident: live credentials entered a repo inside a Postman JSON and an OpenShift YAML, then
67+
contaminated a session log downstream. Findings print with the linter's non-echoing report plus
68+
redaction/waiver/rotation guidance. **Enforcing by default** — findings **block the commit**
69+
(secrets are the one category with irreversible after-the-fact cost); opt down to warn-only with
70+
`AGENT_MEMORY_SECRET_GUARD=advisory` (env, or `git config agent-memory.secretguard advisory`);
71+
one-off bypass: `git commit --no-verify`.
72+
Waivers: tag the line `lint:allow-secret-material` where the format has comments (markdown,
73+
YAML, TOML, INI); JSON has no comments and a `.properties` same-line comment corrupts the value —
74+
list those files in the committed, human-audited **`.agent/secret-scan-ignore`** (shell-glob per
75+
line; exempts config files only, never `memory/`). Runs on python3 or node, whichever exists —
76+
with neither, it skips with a note. Why it exists: the ritual rule covers agents at write time
77+
and the CI floor covers pushes, but by push time the remote already has the secret and redaction
78+
is not un-leaking (rotation is) — this is the **one placement that prevents instead of detects**
79+
(see the `memory/PROTOCOL.md` redaction rule).
80+
81+
- **`post-commit.d/50-agent-memory-ritual-capture`** — after a commit: re-syncs skill adapters if a
82+
skill changed; and if the commit did
83+
real work but carried no session log, ensures the session is captured — **once per working session, not
84+
per commit.** If there is **no** session log within the active-session window (default **30 min**; override
85+
`AGENT_MEMORY_SESSION_WINDOW_MINUTES`) it **auto-stubs** `memory/sessions/<ts>.md`; if a recent log already
86+
covers this session — committed *or* a waiting stub, detected by the newest session **filename** (immutable
87+
and clone-safe, unlike mtime) — it instead **nudges you to enrich that existing log**. The stub guarantees
88+
the ledger never has a silent gap; the *thoughtful* summary stays the agent's job (capture vs. judgment —
89+
same split as `memory-lint`).
90+
91+
> **Splitting code and memory into two commits?** The advisory may fire on the code-only commit (it
92+
> carries no session log) and point you at the session's existing log — **expected and benign**, not a
93+
> failure, and it will **not** pile up a second stub (one log per session). To skip the nudge entirely,
94+
> prefer a **single atomic commit** that includes the work *and* its session log. The hook is advisory
95+
> and never blocks.
96+
97+
To deactivate: `git config --unset core.hooksPath`.

.githooks/init.sh

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
# agent-memory — first-run init. Run ONCE after a fresh clone (or any time setup looks off).
3+
# Idempotent + advisory. Closes the fresh-clone gap: a clone has the gitignored skill adapters
4+
# ABSENT and the git hook dispatchers UNACTIVATED (git can't auto-run committed hooks on clone — security).
5+
# This single command sets both up. The agent runs it itself on a first session (see memory/PROTOCOL.md);
6+
# this is the human one-command fallback. On GitHub / GitLab.com, CI runs server-side regardless,
7+
# with or without this (a self-managed GitLab needs an admin-registered runner; an Azure DevOps
8+
# pipeline runs once its one-time activation is done).
9+
set -e
10+
cd "$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
11+
12+
echo "agent-memory init: (1/2) regenerating skill adapters…"
13+
if [ -f agent-skills/sync-adapters/scripts/sync-adapters.sh ]; then
14+
bash agent-skills/sync-adapters/scripts/sync-adapters.sh \
15+
|| node agent-skills/sync-adapters/scripts/sync-adapters.mjs \
16+
|| python3 agent-skills/sync-adapters/scripts/sync-adapters.py \
17+
|| echo " (no bash/node/python runtime for sync-adapters; skipped — adapters can be synced later)"
18+
else
19+
echo " (no sync-adapters skill present; skipped)"
20+
fi
21+
22+
echo "agent-memory init: (2/2) activating the git hook dispatchers (pre-commit secret guard + post-commit ritual capture fragments)…"
23+
if [ -d .githooks ]; then
24+
git config core.hooksPath .githooks
25+
echo " core.hooksPath = $(git config core.hooksPath)"
26+
fi
27+
28+
echo "agent-memory init: done — adapters synced + git hook dispatchers active. (On GitHub / GitLab.com, CI runs server-side regardless.)"

.githooks/post-commit

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env bash
2+
# post-commit fragment dispatcher. Executable files in post-commit.d run in C-locale
3+
# filename order. Every fragment runs; the first non-zero status is returned (Git ignores
4+
# post-commit status, but direct invocation and tests can still observe it).
5+
# Bash 3.2-compatible.
6+
7+
LC_COLLATE=C
8+
9+
hook_dir="$(CDPATH= cd "$(dirname "$0")" 2>/dev/null && pwd)" || exit 1
10+
fragment_dir="$hook_dir/post-commit.d"
11+
[ -d "$fragment_dir" ] || exit 0
12+
13+
status=0
14+
for fragment in "$fragment_dir"/*; do
15+
[ -f "$fragment" ] || continue
16+
[ -x "$fragment" ] || continue
17+
18+
"$fragment" "$@"
19+
fragment_status=$?
20+
if [ "$fragment_status" -ne 0 ]; then
21+
[ "$status" -ne 0 ] || status="$fragment_status"
22+
printf 'post-commit: fragment %s failed with status %s\n' \
23+
"${fragment##*/}" "$fragment_status" >&2
24+
fi
25+
done
26+
27+
exit "$status"
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# agent-memory post-commit fragment — vendor-neutral, advisory ritual reinforcement.
3+
# (Introduced v4.19.0; auto-stub made per-session in v4.22.1, window retuned to 30 min in v4.22.3.)
4+
#
5+
# Activated by `git config core.hooksPath .githooks` (the agent runs this at enable — no manual
6+
# user step). Fires after every commit; NEVER blocks (post-commit exit codes are ignored by git).
7+
# The tool runs nothing itself — git invokes this in the user's env at their opt-in
8+
# (`no-build-step-agent-run`). Bash 3.2-compatible.
9+
#
10+
# What it does, only in an agent-memory repo:
11+
# 1) keep skill adapters current when a skill changed (idempotent, gitignored-only);
12+
# 2) if the commit did real work but no session log exists within the active-session window
13+
# (default 30 min; override AGENT_MEMORY_SESSION_WINDOW_MINUTES), auto-stub one; if a recent
14+
# log already covers this session, nudge to enrich it instead — at most ONE stub per session,
15+
# not per commit. Capture only; the thoughtful summary stays the agent's job (no silent gap).
16+
17+
[ -f memory/continuity.md ] || exit 0 # only act in an agent-memory repo
18+
git rev-parse -q --verify HEAD^2 >/dev/null 2>&1 && exit 0 # skip merge commits
19+
20+
changed="$(git diff-tree --no-commit-id --name-only -r HEAD 2>/dev/null)"
21+
22+
# (1) re-sync adapters if a skill changed in this commit
23+
if printf '%s\n' "$changed" | grep -qE '^agent-skills/'; then
24+
bash agent-skills/sync-adapters/scripts/sync-adapters.sh >/dev/null 2>&1 \
25+
|| node agent-skills/sync-adapters/scripts/sync-adapters.mjs >/dev/null 2>&1 \
26+
|| python3 agent-skills/sync-adapters/scripts/sync-adapters.py >/dev/null 2>&1 || true
27+
fi
28+
29+
# (2) auto-stub the session log if real work landed without one
30+
work="$(printf '%s\n' "$changed" | grep -vE '^(memory/|\.agent/)' | grep -v '^$' || true)"
31+
logged="$(printf '%s\n' "$changed" | grep -E '^memory/sessions/.*\.md$' || true)"
32+
33+
if [ -n "$work" ] && [ -z "$logged" ]; then
34+
# A "session" is a *burst of work within a window*, not a single commit. A session log added
35+
# earlier in this same session — whether already committed OR still an untracked stub — must
36+
# suppress a NEW stub, or a multi-commit session piles up one near-identical stub per commit
37+
# (and inflates the decay session-count). Detect it by the newest session-log FILENAME
38+
# timestamp: it is immutable and clone-safe (unlike mtime, which `git clone`/checkout resets
39+
# to now — that would wrongly suppress the first stub for hours after every clone). Compare,
40+
# lexicographically, against a "window-ago" stamp in the same YYYY-MM-DD-HHMMSS form (which
41+
# sorts chronologically). Window default 30 min — long enough to span a working session's commit
42+
# cadence (incl. a short test/think gap), short enough that a genuinely NEW session (a longer pause)
43+
# still gets its own stub. Override with AGENT_MEMORY_SESSION_WINDOW_MINUTES (integer minutes).
44+
# Primary guard: if a session log is already uncommitted (staged, modified, or untracked),
45+
# the agent has an in-flight log for this session — skip the stub regardless of its age.
46+
# This handles the recommended two-commit pattern (feature commit → chore(memory) commit) where
47+
# the log is written but not yet committed, and long sessions where the write→commit gap exceeds
48+
# the time window.
49+
if git status --porcelain -- memory/sessions/ 2>/dev/null | grep -qE '\.md$'; then
50+
echo "agent-memory: this commit changed tracked files but added no session log; an uncommitted" >&2
51+
echo " session log already covers this session — enrich/commit it (one log per" >&2
52+
echo " session, not per commit)." >&2
53+
exit 0
54+
fi
55+
56+
win="${AGENT_MEMORY_SESSION_WINDOW_MINUTES:-30}"
57+
newest="$(ls memory/sessions/ 2>/dev/null | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{6}\.md$' | sort | tail -n1)"
58+
newest="${newest%.md}"
59+
threshold="$(date -u -v-"${win}"M +%Y-%m-%d-%H%M%S 2>/dev/null \
60+
|| date -u -d "${win} minutes ago" +%Y-%m-%d-%H%M%S 2>/dev/null || true)"
61+
62+
if [ -n "$newest" ] && [ -n "$threshold" ] && [[ "$newest" > "$threshold" ]]; then
63+
# a session log already exists for the current working session — don't pile on another stub
64+
echo "agent-memory: this commit changed tracked files but added no session log; a recent one" >&2
65+
echo " (memory/sessions/$newest.md, within ${win} min) covers this session — enrich/update" >&2
66+
echo " it if this commit added something noteworthy (one log per session, not per commit)." >&2
67+
else
68+
# genuinely new session (no session log within the window) — stub one so the ledger never gaps
69+
ts="$(date -u +%Y-%m-%d-%H%M%S)"
70+
stub="memory/sessions/$ts.md"
71+
{
72+
printf '# Session (%s)\n\n' "$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"
73+
printf '**Agent:** (auto-stub — replace with your agent name and enrich)\n'
74+
printf '**Lightweight:** auto-captured by the post-commit hook; summary pending.\n\n'
75+
printf 'Commit `%s` — %s\n\n' "$(git rev-parse --short HEAD)" "$(git log -1 --pretty=%s)"
76+
printf '```\n%s\n```\n\n' "$(git diff-tree --no-commit-id --stat -r HEAD 2>/dev/null)"
77+
printf '## Memory References\n'
78+
printf '(none — auto-stub; enrich per memory/PROTOCOL.md "Close every session", or leave as a lite log)\n'
79+
} > "$stub"
80+
echo "agent-memory: auto-stubbed a session log → $stub" >&2
81+
echo " enrich it (or keep it as a lite log) and commit it, per memory/PROTOCOL.md." >&2
82+
fi
83+
fi
84+
exit 0

.githooks/pre-commit

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
# pre-commit fragment dispatcher. Executable files in pre-commit.d run in C-locale
3+
# filename order. Every fragment runs; the first non-zero status is returned.
4+
# Bash 3.2-compatible.
5+
6+
LC_COLLATE=C
7+
8+
hook_dir="$(CDPATH= cd "$(dirname "$0")" 2>/dev/null && pwd)" || exit 1
9+
fragment_dir="$hook_dir/pre-commit.d"
10+
[ -d "$fragment_dir" ] || exit 0
11+
12+
status=0
13+
for fragment in "$fragment_dir"/*; do
14+
[ -f "$fragment" ] || continue
15+
[ -x "$fragment" ] || continue
16+
17+
"$fragment" "$@"
18+
fragment_status=$?
19+
if [ "$fragment_status" -ne 0 ]; then
20+
[ "$status" -ne 0 ] || status="$fragment_status"
21+
printf 'pre-commit: fragment %s failed with status %s\n' \
22+
"${fragment##*/}" "$fragment_status" >&2
23+
fi
24+
done
25+
26+
exit "$status"

0 commit comments

Comments
 (0)