Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 20 additions & 13 deletions .claude/skills/recall/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ description: >
record outside a git repo, go to $HOME/.eidetic/memory (never committed). An
explicit EIDETIC_DATA_DIR wins and short-circuits to that single dir. Recall
reads both stores and merges. The wrapper defaults queries to this agent's
PERSONAL, PRIVATE scope (`--scope eidetic-cli --visibility private`, suffix
read from culture.yaml) — matching where /remember writes — so a no-flag
recall returns this agent's own private records plus the shared public pool,
and Claude and the colleague backend recall each other's memories because both
resolve the same suffix via this skill. Use
when the user says "recall", "what do we know about X", "search memory",
PERSONAL, PUBLIC scope (`--scope eidetic-cli --visibility public`, suffix
read from culture.yaml) — the memory scope+visibility convention (v1,
docs/contract.md), matching where /remember writes — so a no-flag recall
returns the full public pool, and Claude and the colleague backend recall
each other's memories because both resolve the same suffix via this skill
(pass --visibility private to also see this agent's own private records).
Use when the user says "recall", "what do we know about X", "search memory",
"have we seen X before", "look it up in memory", "eidetic recall", or before
answering from scratch when prior context may already be stored. Pairs with
the sibling /remember skill.
Expand Down Expand Up @@ -123,13 +124,15 @@ compete on score/signal just like active ones when included.
- `--case-sensitive` — for `--mode exact`.
- `--filter KEY=VALUE` — metadata facet filter (repeatable): e.g. `--filter source=docs`.
- `--scope NAME` / `--visibility public|private` — scope isolation (no private
leak). **The wrapper defaults this to the agent's PERSONAL, PRIVATE scope**
(`--scope eidetic-cli --visibility private`, suffix read from `culture.yaml`),
matching where `/remember` writes — so a no-flag recall returns this agent's
own private records **plus** the shared public pool, while those private records
stay invisible to a `default`/other-scope recall. Pass `--scope`/`--visibility`
to query elsewhere; a wheel install with no `culture.yaml` falls back to the
CLI default `default`/`public`.
leak). **The wrapper defaults this to the agent's PERSONAL, PUBLIC scope**
(`--scope eidetic-cli --visibility public`, suffix read from `culture.yaml`),
matching where `/remember` writes and the memory scope+visibility convention
(v1, `docs/contract.md`) — so a no-flag recall returns the full public pool.
Pass `--visibility private` to instead query this agent's own private records
**plus** the shared public pool (a private query still sees every public
record; only a private record is scope-isolated). Pass `--scope` to query a
different scope entirely; a wheel install with no `culture.yaml` falls back to
the CLI default `default`/`public`.
- `--backend files|mongo|neo4j` — default `files` (the shared home-dir store).
- `--include-shadowed` — include shadowed records in results (excluded by default).
- `--include-archived` — include archived records in results (excluded by default).
Expand Down Expand Up @@ -177,6 +180,10 @@ bash .claude/skills/recall/scripts/recall.sh "power" --include-archived --includ
Claude + colleague). It is not isolated between OS users by anything but file
permissions; keep genuinely private data in a `--visibility private` scope and
treat the host as the trust boundary.
- **Scope + visibility convention:** see [`docs/contract.md`](../../../docs/contract.md)
(memory scope+visibility convention, v1) for the naming/default rules this
wrapper (and every other `eidetic remember`/`recall` consumer, including
the colleague backend's `colleague/memory.py`) pins to.

## Provenance

Expand Down
52 changes: 33 additions & 19 deletions .claude/skills/recall/scripts/recall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ esac

resolve_eidetic || exit 2

# ── default to this agent's PERSONAL, PRIVATE scope (culture.yaml `suffix`) ──
# ── default to this agent's PERSONAL, PUBLIC scope (culture.yaml `suffix`) ──
# Query this agent's OWN personal scope by default, matching where /remember
# writes, instead of the global `default` scope shared by every project on this
# host. We read the `suffix` from the nearest culture.yaml (walking up from this
Expand All @@ -88,15 +88,26 @@ resolve_eidetic || exit 2
# backend (running in a worktree of this same repo) resolves the same suffix,
# keeping the Claude↔colleague shared-memory story intact.
#
# The personal scope is PRIVATE by default to match /remember: in eidetic's model
# a private record is served only to a recall in the SAME scope (`can_serve`), so
# querying with --scope <suffix> --visibility private is what retrieves those
# isolated records (a public/default recall can't see them). Scope and visibility
# are paired — the private default applies only when we inject the resolved scope,
# and only if the caller didn't pass --visibility (so an explicit
# `--visibility public` still wins). An explicit --scope on the command line takes
# over steering entirely; a wheel install with no culture.yaml falls back to the
# plain CLI default (`default`/`public`).
# The personal scope is PUBLIC by default to match /remember — the memory
# scope+visibility convention (v1, docs/contract.md): a public record is
# visible to ANY query scope regardless of name (`can_serve`,
# eidetic/memory/scope.py), so a no-flag recall here returns the full public
# pool, matching both the plain `eidetic recall` CLI's own --visibility
# default and the colleague backend's runtime (colleague/memory.py hardcodes
# --visibility public). Scope and visibility are paired — the public default
# applies only when we inject the resolved scope, and only if the caller
# didn't pass --visibility (so an explicit `--visibility private` still
# wins). Passing --visibility private restores exactly what used to be the
# implicit default: this agent's own private notes (scope=<suffix>, matched
# exactly — `can_serve` only serves a private record to a query in the SAME
# scope) PLUS the full public pool (a private query still sees every public
# record too — that "private + public" merge is `can_serve`'s behavior
# whenever the QUERY itself is private, unrelated to this default). What
# changed is only the no-flag case: a plain recall now returns public-only,
# since a public-visibility query structurally excludes every private
# record regardless of scope name. An explicit --scope on the command line
# takes over steering entirely; a wheel install with no culture.yaml falls
# back to the plain CLI default (`default`/`public`).
resolve_scope() {
local dir suffix=""
dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
Expand Down Expand Up @@ -136,21 +147,24 @@ if ! has_flag --scope "$@"; then
EIDETIC_SCOPE=$(resolve_scope)
if [ -n "$EIDETIC_SCOPE" ]; then
SCOPE_ARGS+=(--scope "$EIDETIC_SCOPE")
has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility private)
elif ! has_flag --visibility "$@"; then
# No suffix AND no explicit --visibility: the query runs against
# eidetic's own default (scope=default, visibility=public), not this
# agent's private personal scope — so an empty result isn't silently
# misread. Warn on stderr (stdout stays clean for --json). Warn ONLY
# here: an explicit --scope (outer guard) or --visibility (this guard) is
# a deliberate choice, honored verbatim, so either flag silences this.
printf 'warning: no culture.yaml suffix resolved; querying the public default scope rather than a private personal scope. Pass --scope or --visibility to target deliberately.\n' >&2
has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility public)
fi
# No suffix resolved (e.g. a wheel install with no culture.yaml): leave
# --scope/--visibility unset entirely, so the plain `eidetic recall`
# defaults apply (scope=default, visibility=public) — identical
# visibility to the suffix-resolved case above, just grouped under the
# `default` scope name instead of this agent's personal one. Nothing to
# warn about: unlike the old private-by-default behavior, there is no
# unexpected-leak or unexpected-empty-result surprise here either way.
fi

# Default the embedding endpoint to the local model-gear embed gear. eidetic
# falls back to a deterministic offline embedding if it's unreachable, so this
# is safe even when the gear is down. Override by exporting these yourself.
# As of eidetic-cli#28 (colleague#293) these values now match eidetic's own
# code default in eidetic/memory/embed.py, so this export is a redundant-but-
# harmless belt-and-suspenders default kept for explicitness and for older
# eidetic installs that predate the code-default alignment.
: "${EIDETIC_EMBED_URL:=http://localhost:8002/v1}"
: "${EIDETIC_EMBED_MODEL:=Qwen/Qwen3-Embedding-0.6B}"
export EIDETIC_EMBED_URL EIDETIC_EMBED_MODEL
Expand Down
39 changes: 23 additions & 16 deletions .claude/skills/remember/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ description: >
(committed, team-shared); PRIVATE records, or any record outside a git repo,
go to $HOME/.eidetic/memory (never committed). An explicit EIDETIC_DATA_DIR
wins and short-circuits to that single dir. The wrapper defaults records to
this agent's PERSONAL, PRIVATE scope (`--scope eidetic-cli --visibility
private`, suffix read from culture.yaml) so they don't leak to a
default/other-scope recall — Claude and the colleague backend still share them
because both resolve the same suffix via this skill. Pass `--visibility public`
to contribute to the shared public pool instead. Use when the user says
this agent's PERSONAL, PUBLIC scope (`--scope eidetic-cli --visibility
public`, suffix read from culture.yaml) — the memory scope+visibility
convention (v1, docs/contract.md) — matching both the plain `eidetic
remember` CLI's own default and the colleague backend's runtime, so a
no-flag remember here and a no-flag `eidetic remember` elsewhere land
mutually-visible records. Pass `--visibility private` to keep a record out
of the shared/committed store instead. Use when the user says
"remember this", "store this", "save to memory", "index these", "eidetic
remember", or when something learned this session should outlive it. Pairs with
the sibling /recall skill.
Expand Down Expand Up @@ -62,11 +64,12 @@ The wrapper resolves the CLI portably (installed `eidetic` on `PATH`, else
| `links` | optional | list of related-memory ids; persisted for future corroboration scoring |

`score` and `signal` are recall-only and are ignored on ingest. **Mind the
scope:** the default personal scope is **private** (`--scope eidetic-cli
--visibility private`), so personal/role-gated notes stay isolated to this
agent's recall and are safe to store. Only when you deliberately write to a
**public** scope (`--visibility public`) does the record enter the shared pool
visible to every scope — keep public-scope records to public data only.
scope:** the default personal scope is **public** (`--scope eidetic-cli
--visibility public`), so a plain remember is team-shared and travels with
the repo (`<repo-root>/.eidetic/memory`, committed) — keep default-scope
records to shareable data. Only when you deliberately write to a **private**
scope (`--visibility private`) is the record isolated to `$HOME/.eidetic/memory`
and invisible to any other scope's recall (including a public one).

## Idempotency

Expand All @@ -93,12 +96,12 @@ eidetic sweep # apply transitions

- `--json` — structured result (`{"upserted": N, "ids": [...]}`) to stdout.
- `--scope NAME` / `--visibility public|private` — record scope. **The wrapper
defaults this to the agent's PERSONAL, PRIVATE scope** — `--scope <suffix>
--visibility private`, where `<suffix>` is read from the nearest `culture.yaml`
(here, `eidetic-cli`). Private records are served only to a recall in the same
scope, so they don't leak to a `default`/other-scope query. Pass `--scope` to
steer to a different scope (which then uses the plain CLI default visibility),
or `--visibility public` to keep the personal scope but make it shared. A wheel
defaults this to the agent's PERSONAL, PUBLIC scope** — `--scope <suffix>
--visibility public`, where `<suffix>` is read from the nearest `culture.yaml`
(here, `eidetic-cli`). Public records are visible to any scope's recall (no
isolation). Pass `--scope` to steer to a different scope (which then uses
the plain CLI default visibility, also `public`), or `--visibility private`
to keep the personal scope but isolate it to `$HOME/.eidetic/memory`. A wheel
install with no `culture.yaml` falls back to the CLI default `default`/`public`.
- `--backend files|mongo|neo4j` — default `files` (the shared home-dir store);
use `mongo`/`neo4j` (with `EIDETIC_MONGO_URI` / `NEO4J_URI`) for a server store.
Expand All @@ -113,6 +116,10 @@ eidetic sweep # apply transitions
`PATH` (in a dev checkout it isn't); the wrapper resolves it (`PATH` first, else
`uv run eidetic`). For the docs, run `eidetic explain remember` if installed,
otherwise `uv run --project <eidetic-cli checkout> eidetic explain remember`.
- **Scope + visibility convention:** see [`docs/contract.md`](../../../docs/contract.md)
(memory scope+visibility convention, v1) for the naming/default rules this
wrapper (and every other `eidetic remember`/`recall` consumer, including
the colleague backend's `colleague/memory.py`) pins to.

## Provenance

Expand Down
53 changes: 31 additions & 22 deletions .claude/skills/remember/scripts/remember.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ Usage:

A record needs `id`, `text`, and `type`; `hash` and `metadata` are recommended
(hash is derived from text when omitted). Upsert is idempotent by id.
Records default to this agent's PRIVATE personal scope (--scope from the
culture.yaml suffix); pass --visibility public to contribute to the shared
public pool. Every flag is forwarded verbatim to `eidetic remember`.
See `eidetic explain remember`.
Records default to this agent's PERSONAL, PUBLIC scope (--scope from the
culture.yaml suffix, --visibility public); pass --visibility private to keep
a record out of the shared/committed store. Every flag is forwarded verbatim
to `eidetic remember`. See `eidetic explain remember`.
EOF
}

Expand All @@ -85,7 +85,7 @@ fi

resolve_eidetic || exit 2

# ── default to this agent's PERSONAL, PRIVATE scope (culture.yaml `suffix`) ──
# ── default to this agent's PERSONAL, PUBLIC scope (culture.yaml `suffix`) ──
# A record this agent remembers should land in its OWN personal scope, not the
# global `default` scope shared by every project on this host. We read the
# `suffix` from the nearest culture.yaml (walking up from this script), so the
Expand All @@ -94,14 +94,20 @@ resolve_eidetic || exit 2
# (running in a worktree of this same repo) resolves the same suffix, keeping
# the Claude↔colleague shared-memory story intact.
#
# The personal scope is PRIVATE by default: in eidetic's model only a private
# record is isolated to its scope (`can_serve`), so private is what actually
# keeps these records from leaking to a default/other-scope recall. Scope and
# visibility are paired — the private default applies only when we inject the
# resolved scope, and only if the caller didn't pass --visibility (so an
# explicit `--visibility public` still wins). An explicit --scope on the command
# line takes over steering entirely; a wheel install with no culture.yaml falls
# back to the plain CLI default (`default`/`public`).
# The personal scope is PUBLIC by default — the memory scope+visibility
# convention (v1, docs/contract.md): an in-repo record is team-shared by
# default (routing already sends a public write inside a git repo to
# <repo-root>/.eidetic/memory, committed alongside the code), matching both
# the plain `eidetic remember` CLI's own --visibility default and the
# colleague backend's runtime (colleague/memory.py hardcodes --visibility
# public) — so a no-flag remember here and a no-flag `eidetic remember`
# elsewhere land mutually-visible records instead of silently disagreeing.
# Scope and visibility are paired — the public default applies only when we
# inject the resolved scope, and only if the caller didn't pass --visibility
# (so an explicit `--visibility private` still wins, keeping genuinely
# personal/non-shareable data out of the committed store). An explicit
# --scope on the command line takes over steering entirely; a wheel install
# with no culture.yaml falls back to the plain CLI default (`default`/`public`).
resolve_scope() {
local dir suffix=""
dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
Expand Down Expand Up @@ -141,18 +147,21 @@ if ! has_flag --scope "$@"; then
EIDETIC_SCOPE=$(resolve_scope)
if [ -n "$EIDETIC_SCOPE" ]; then
SCOPE_ARGS+=(--scope "$EIDETIC_SCOPE")
has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility private)
elif ! has_flag --visibility "$@"; then
# No suffix AND no explicit --visibility: the record falls back to
# eidetic's own default (scope=default, visibility=public). Don't let an
# expected-private record go public silently — warn on stderr (stdout
# stays clean for --json). Warn ONLY here: an explicit --scope (outer
# guard) or --visibility (this guard) means the caller chose deliberately
# and is honored verbatim, so either flag silences this.
printf 'warning: no culture.yaml suffix resolved; this record falls back to the public default scope. Pass --scope or --visibility to place it deliberately.\n' >&2
has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility public)
fi
# No suffix resolved (e.g. a wheel install with no culture.yaml): leave
# --scope/--visibility unset entirely, so the plain `eidetic remember`
# defaults apply (scope=default, visibility=public) — identical
# visibility to the suffix-resolved case above, just grouped under the
# `default` scope name instead of this agent's personal one. Nothing to
# warn about: unlike the old private-by-default behavior, there is no
# privacy downgrade here either way.
fi

# As of eidetic-cli#28 (colleague#293) these values now match eidetic's own
# code default in eidetic/memory/embed.py, so this export is a redundant-but-
# harmless belt-and-suspenders default kept for explicitness and for older
# eidetic installs that predate the code-default alignment.
: "${EIDETIC_EMBED_URL:=http://localhost:8002/v1}"
: "${EIDETIC_EMBED_MODEL:=Qwen/Qwen3-Embedding-0.6B}"
export EIDETIC_EMBED_URL EIDETIC_EMBED_MODEL
Expand Down
Loading
Loading