Skip to content
Open
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
48 changes: 35 additions & 13 deletions .claude/skills/recall/scripts/recall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
# forwards every flag verbatim — so `recall.sh "<query>" --mode hybrid --json`
# is exactly `eidetic recall "<query>" --mode hybrid --json`.
#
# The store is the files backend at ~/.eidetic/memory by default — a home-dir
# path OUTSIDE any git worktree, so Claude and the colleague backend (which runs
# in throwaway worktrees) read the SAME memories. Set EIDETIC_DATA_DIR to opt out
# of sharing; set EIDETIC_MONGO_URI / NEO4J_URI + --backend for a server store.
# The store is the files backend. Default location resolves per-operation:
# PUBLIC records inside a git repo → <repo-root>/.eidetic/memory (committed,
# team-shared); PRIVATE records, or any record outside a git repo →
# $HOME/.eidetic/memory (never committed). Recall reads both stores and merges.
# An explicit EIDETIC_DATA_DIR wins and short-circuits to that single dir.
Comment on lines +9 to +13

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. recall.sh modified in vendored skill 📘 Rule violation ⚙ Maintainability

This PR directly edits .claude/skills/recall/scripts/recall.sh and
.claude/skills/remember/scripts/remember.sh, which are vendored skill files that must not be
modified in-repo. These changes risk being overwritten by the next guildmaster re-sync and create
upstream drift/divergence unless made upstream and re-synced.
Agent Prompt
## Issue description
This PR includes direct edits to vendored skill files under `.claude/skills/` (`.claude/skills/recall/scripts/recall.sh` and `.claude/skills/remember/scripts/remember.sh`), which violates the policy against in-repo modifications of these synced assets.

## Issue Context
Vendored skills should be updated upstream and then re-synced from guildmaster (or by updating the sync configuration / upstream source). Otherwise, local edits will drift/diverge from upstream and may be overwritten on the next guildmaster re-sync.

## Fix Focus Areas
- .claude/skills/recall/scripts/recall.sh[9-13]
- .claude/skills/remember/scripts/remember.sh[15-19]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


set -euo pipefail

Expand All @@ -34,12 +35,10 @@ resolve_eidetic() {
fi
dir=$(dirname "$dir")
done
cat >&2 <<'EOF'
error: eidetic CLI not found.
hint: install it with `uv tool install eidetic-cli` (or `pipx install eidetic-cli`),
or run from inside the eidetic-cli checkout with `uv` available.
The console script is `eidetic` (dist name: eidetic-cli).
EOF
# In a vendored copy there is no eidetic-cli checkout to fall back to, so the
# only honest remedy is to install the CLI. One `error:` + one `hint:` line.
printf 'error: eidetic CLI not found.\n' >&2
printf 'hint: install it with: uv tool install eidetic-cli (or pipx install eidetic-cli); the console script is eidetic.\n' >&2
return 1
}

Expand All @@ -65,10 +64,17 @@ EOF
}

case "${1:-}" in
-h | --help | help | "")
-h | --help)
usage
exit 0
;;
"")
# A missing query is a usage error, not success. The bareword `help` is
# a legitimate search term, so it is intentionally NOT a usage alias.
printf 'error: no query given.\n' >&2
printf 'hint: recall.sh "<query>" [--mode ...] [--json]; run recall.sh --help for usage.\n' >&2
exit 1
;;
esac

resolve_eidetic || exit 2
Expand Down Expand Up @@ -100,9 +106,12 @@ resolve_scope() {
# inline `# comment` or trailing space can't bleed into the scope),
# then strip surrounding quotes only — matching the canonical parser
# in .claude/skills/cicd/scripts/_resolve-nick.sh.
# `|| true`: under `set -o pipefail`, `head -n1` closing the pipe
# early can SIGPIPE `sed`, making the substitution non-zero and
# aborting the script. An empty parse must yield "" here, not exit.
suffix=$(sed -n \
's/^[[:space:]]*-\{0,1\}[[:space:]]*suffix:[[:space:]]*\([^[:space:]]*\).*/\1/p' \
"$dir/culture.yaml" | head -n1 | tr -d "\"'")
"$dir/culture.yaml" | head -n1 | tr -d "\"'" || true)
break
fi
dir=$(dirname "$dir")
Expand All @@ -127,7 +136,20 @@ 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)
# rollout-cli eidetic-memory recipe POLICY OVERRIDE (not eidetic's
# upstream private default): default to PUBLIC, so a plain recall queries
# the in-repo public pool (<repo>/.eidetic/memory) this repo writes to.
# Pass --visibility private to also surface this agent's private ($HOME)
# notes. The two-store read model reads both dirs regardless.
has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility public)
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
fi
fi

Expand Down
52 changes: 39 additions & 13 deletions .claude/skills/remember/scripts/remember.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
# Upsert is idempotent by id (and dedups by content hash): re-remembering the
# same record updates it in place, never duplicates.
#
# The store is the files backend at ~/.eidetic/memory by default — a home-dir
# path OUTSIDE any git worktree, so a record Claude remembers is recallable by
# the colleague backend (which runs in throwaway worktrees), and vice versa.
# Set EIDETIC_DATA_DIR to opt out of sharing; use --backend mongo|neo4j (with
# The store is the files backend. Default location resolves per-operation:
# PUBLIC records inside a git repo → <repo-root>/.eidetic/memory (committed,
# team-shared); PRIVATE records, or any record outside a git repo →
# $HOME/.eidetic/memory (never committed). An explicit EIDETIC_DATA_DIR still
# wins and short-circuits to that single dir. Use --backend mongo|neo4j (with
# EIDETIC_MONGO_URI / NEO4J_URI) for a server-backed shared store.

set -euo pipefail
Expand All @@ -40,12 +41,10 @@ resolve_eidetic() {
fi
dir=$(dirname "$dir")
done
cat >&2 <<'EOF'
error: eidetic CLI not found.
hint: install it with `uv tool install eidetic-cli` (or `pipx install eidetic-cli`),
or run from inside the eidetic-cli checkout with `uv` available.
The console script is `eidetic` (dist name: eidetic-cli).
EOF
# In a vendored copy there is no eidetic-cli checkout to fall back to, so the
# only honest remedy is to install the CLI. One `error:` + one `hint:` line.
printf 'error: eidetic CLI not found.\n' >&2
printf 'hint: install it with: uv tool install eidetic-cli (or pipx install eidetic-cli); the console script is eidetic.\n' >&2
return 1
}

Expand All @@ -60,7 +59,9 @@ 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.
Public data only. Every flag is forwarded verbatim to `eidetic remember`.
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`.
EOF
}
Expand All @@ -72,6 +73,16 @@ case "${1:-}" in
;;
esac

# No record argument AND stdin is an interactive terminal → `eidetic remember`
# would block forever waiting for NDJSON. Show usage instead of hanging. A piped
# or redirected stdin (`cat records.ndjson | remember.sh`) is not a TTY and
# proceeds to the batch path normally.
if [ "$#" -eq 0 ] && [ -t 0 ]; then
usage >&2
printf 'hint: pass a JSON record as an argument, or pipe NDJSON on stdin.\n' >&2
exit 1
fi
Comment on lines +76 to +84

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Flags-only remember can hang 🐞 Bug ☼ Reliability

The new interactive-stdin guard only checks for zero arguments, so remember.sh --json (or any
flags-only call) still reaches eidetic remember with no record argument and an interactive stdin,
causing an indefinite block waiting for NDJSON. This can stall CI/agent runs and defeats the
intended hardening.
Agent Prompt
## Issue description
`remember.sh` adds an interactive-stdin guard, but it only triggers when `$# == 0`. If the user passes only flags (e.g. `remember.sh --json`) and stdin is a TTY, `eidetic remember` will still block forever waiting for NDJSON.

## Issue Context
The wrapper’s contract is: either a single JSON record as the first argument, or NDJSON via stdin. When stdin is interactive and there is no JSON record argument, we should fail fast with usage.

## Fix Focus Areas
- .claude/skills/remember/scripts/remember.sh[76-85]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


resolve_eidetic || exit 2

# ── default to this agent's PERSONAL, PRIVATE scope (culture.yaml `suffix`) ──
Expand Down Expand Up @@ -100,9 +111,12 @@ resolve_scope() {
# inline `# comment` or trailing space can't bleed into the scope),
# then strip surrounding quotes only — matching the canonical parser
# in .claude/skills/cicd/scripts/_resolve-nick.sh.
# `|| true`: under `set -o pipefail`, `head -n1` closing the pipe
# early can SIGPIPE `sed`, making the substitution non-zero and
# aborting the script. An empty parse must yield "" here, not exit.
suffix=$(sed -n \
's/^[[:space:]]*-\{0,1\}[[:space:]]*suffix:[[:space:]]*\([^[:space:]]*\).*/\1/p' \
"$dir/culture.yaml" | head -n1 | tr -d "\"'")
"$dir/culture.yaml" | head -n1 | tr -d "\"'" || true)
break
fi
dir=$(dirname "$dir")
Expand All @@ -127,7 +141,19 @@ 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)
# rollout-cli eidetic-memory recipe POLICY OVERRIDE (not eidetic's
# upstream private default): default to PUBLIC so a plain remember lands
# in <repo>/.eidetic/memory — committed, team- and mesh-shared. Pass
# --visibility private to keep a record in $HOME (uncommitted).
has_flag --visibility "$@" || SCOPE_ARGS+=(--visibility public)
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
fi
fi

Expand Down
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,45 @@ All notable changes to this project will be documented in this file.
Format follows [Keep a Changelog](https://keepachangelog.com/). This project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2026-06-24

### Added

- **Memory-discipline "Conventions and workflow" section in `CLAUDE.md`** — a
per-task *recall-before / remember-after* convention (scope localized to this
repo's nick) so the vendored `remember` / `recall` skills are actually used,
not just present: `/recall` before non-trivial work to build on prior
decisions instead of re-deriving them, and `/remember` when a non-obvious
decision, constraint, fix-and-why, or hard-won gotcha surfaces. The section
documents this repo's memory as **in-repo and public** — records resolve to
`<repo-root>/.eidetic/memory` (committed, team- and mesh-shared). Inserted
idempotently (skipped if already present), slotted under an existing
"Conventions and workflow" heading when one exists, else appended.

### Changed

- **Refreshed the `remember` + `recall` wrappers from eidetic-cli 0.10.0**
(cite-don't-import) — picks up eidetic's **project-local store default**: the
files backend now resolves per record by visibility — PUBLIC records inside a
git repo go to `<repo-root>/.eidetic/memory` (committed, team-shared), PRIVATE
records (or any record outside a repo) go to `$HOME/.eidetic/memory` (never
committed), an explicit `EIDETIC_DATA_DIR` still wins, and recall reads both
stores and merges. Also carries the 0.9.3 hardening (interactive-stdin guard,
`help` as a search term, SIGPIPE-safe suffix parsing). **Recipe policy
override (the wrappers here are NOT byte-verbatim):** the injected default
visibility is flipped from eidetic's `private` to **`public`**, so a plain
`/remember` lands the note in `./.eidetic/memory` in this repo, kept as part
of the repo — pass `--visibility private` to route a record to `$HOME`
instead. `remember` drives `eidetic remember` (idempotent upsert of one JSON
record or an NDJSON batch on stdin); `recall` drives `eidetic recall` with
four search modes (exact / approximate / keyword / hybrid). Each `SKILL.md` is
localized only in the illustrative `--scope <nick>` examples (Provenance keeps
"First-party to eidetic-cli"). Runtime dep: the `eidetic` CLI on PATH (else a
local eidetic-cli checkout with `uv`) — **`eidetic >= 0.10.0`** for the
in-repo routing; on an older CLI the public records still work but are stored
in `$HOME/.eidetic/memory` instead of in-repo. Propagated by rollout-cli's
`eidetic-memory` recipe.

## [0.3.0] - 2026-06-23

### Added
Expand Down
28 changes: 28 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,31 @@ Until you run `/init`, `refactor-cli` satisfies the `steward doctor`
`prompt-file-present` and `backend-consistency` invariants (a `CLAUDE.md`
exists and `culture.yaml` declares `backend: claude`) but the prompt is not
yet tailored to this agent's domain.

## Conventions and workflow

**Memory discipline — recall before, remember after.** This repo keeps its
eidetic memory **in-repo and public**: records resolve to
`<repo-root>/.eidetic/memory` — committed, and shared with the team and mesh
peers (the `claude` and `colleague` backends both read the same
`refactor-cli` scope), so memory travels with the repo, not a private
home-dir store. Make it a per-task habit:

- **`/recall` before you start.** Search the store for the area you're about
to touch — prior decisions, gotchas, "have we done this before?" — so you
build on what's already known instead of re-deriving it. Do this before
non-trivial tasks, not just when asked.
- **`/remember` when something worth keeping surfaces.** A non-obvious
decision and its rationale, a constraint, a fix and *why* it was needed, a
gotcha that cost time, a fact the next session would otherwise re-learn.
Capture it as it happens, not at the end when it's faded.

A plain `/remember` lands the note in `./.eidetic/memory` in this repo — no
flag needed (the wrappers here default to `--visibility public`; in-repo
routing needs `eidetic >= 0.10.0`, older CLIs keep records in `$HOME`). Keep
something out of the committed store only by passing `--visibility private`
(routes to `$HOME/.eidetic/memory`, never committed); `/recall` reads both
stores and merges. Don't store what the repo already records (code structure,
git history, what's already in this file or `CHANGELOG.md`) — store what you'd
have to re-derive. These are the `recall`/`remember` skills (`.claude/skills/`),
backed by the `eidetic` store.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "refactor-cli"
version = "0.3.0"
version = "0.4.0"
description = "Atomic in-repo transformation engine — composable, behavior-preserving refactor actions that don't change what the code does."
readme = "README.md"
license = "MIT"
Expand Down
Loading