2.0.0 — packaging & structure major: skills-only plugin, release-based artifacts
Memory behavior unchanged; the major signals a distribution/install break. - Legacy commands/ directory DELETED (skills-only; the 1.6.0 skills were verified on desktop + CoWork and already took precedence). Pre-skills clients install 1.6.0 from the release history. - Artifact policy: the repo tracks only the echo-memory.plugin pointer; the 15 historical versioned zips leave the tree (git history retains them); versioned builds ship as Gitea release assets, one release per v<version> tag, starting v2.0.0. .gitignore: *.plugin except the pointer. - README: post-2.0 repository layout; "packaging for other agent runtimes" note (ports are build targets from the canonical tree, never a second source tree). - ROADMAP-2.0 items ticked; manifest -> 2.0.0. All suites green (25/25 unit + 4 e2e suites); artifact rebuilt skills-only (79 entries, no commands/). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "echo-memory",
|
||||
"version": "1.6.0",
|
||||
"version": "2.0.0",
|
||||
"homepage": "https://git.alwisp.com/jason/echo",
|
||||
"repository": "https://git.alwisp.com/jason/echo",
|
||||
"description": "Persistent memory via the ECHO Obsidian vault over the Obsidian Local REST API. Cross-platform Python client: one-call capture/resolve/recall/link/triage over an entity index, hybrid BM25 + graph recall spanning entities + sessions/journal (recency/status-aware), a pre-write duplicate gate, complete-frontmatter capture, session hooks that self-fire load/reflect, offline write-ahead queue, lock-guarded concurrency, linter-enforced routing, and /echo-* commands.",
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
description: Check ECHO readiness — Python, vault reachability, auth, bootstrap/schema, and key source
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to run a one-call readiness check before relying on memory.
|
||||
|
||||
```bash
|
||||
ECHO="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/echo.py"
|
||||
[ -f "$ECHO" ] || ECHO=$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/echo.py 2>/dev/null | head -1) # CoWork sandbox fallback
|
||||
python3 "$ECHO" doctor
|
||||
# Windows: use `python` or `py -3` if `python3` is not on PATH.
|
||||
```
|
||||
|
||||
It prints green/red for: Python version, vault reachability, auth accepted, vault
|
||||
bootstrapped (+ `schema_version`), and the **config source** for owner/endpoint/key
|
||||
(per-field: env override `ECHO_OWNER`/`ECHO_BASE`/`ECHO_KEY`, then the machine-local
|
||||
`~/.claude/echo-memory/config.json` — or `baked-in (plugin)` on a per-user baked build,
|
||||
which is authoritative and reported as such). Exits non-zero if anything is red — if the config is
|
||||
missing (or still the placeholder template), ask the operator for their echo-memory config file and install it with `echo.py config import <path>` (or `config set --owner … --endpoint … --key …`). For the full vault-invariant lint, run
|
||||
`/echo-health`.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
description: Run the ECHO vault-health linter and summarize any invariant violations
|
||||
allowed-tools: Bash(python3 *vault_lint.py*), Bash(python *vault_lint.py*), Bash(ls /sessions/*), Bash(dirname *)
|
||||
---
|
||||
|
||||
Run the bundled, read-only vault linter and report findings. Pass the conversation's current date (`ECHO_TODAY`) so stale/aging math uses the same clock the agent writes with:
|
||||
|
||||
```bash
|
||||
SDIR="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts"
|
||||
[ -d "$SDIR" ] || SDIR=$(dirname "$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/vault_lint.py 2>/dev/null | head -1)") # CoWork sandbox fallback
|
||||
ECHO_TODAY=<currentDate> python3 "$SDIR/vault_lint.py"
|
||||
# Windows: use `python` or `py -3` if `python3` is not on PATH.
|
||||
```
|
||||
|
||||
Exit codes: `0` clean · `1` violations (printed, grouped by check) · `2` vault unreachable · `3` vault not bootstrapped (run `bootstrap.py`).
|
||||
|
||||
Summarize the violations grouped by category and propose fixes, but **do not auto-fix** without the operator's go-ahead. If this is the first substantive session of a calendar month, offer to write the findings to `_agent/health/YYYY-MM-vault-health.md` (per the skill's **Vault Health** section).
|
||||
@@ -1,18 +0,0 @@
|
||||
---
|
||||
description: Load ECHO memory — cold-start context read (profile, scope, latest session, today, inbox)
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to load memory now. Run the cold-start **Loading procedure** from `SKILL.md`: the six orientation reads (marker, operator-preferences, current-context, heartbeat, today's daily note, inbox) in **one call**, then do the load-time **reconcile** (inbox-depth + scope-drift) and surface it in a single line.
|
||||
|
||||
```bash
|
||||
ECHO="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/echo.py"
|
||||
[ -f "$ECHO" ] || ECHO=$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/echo.py 2>/dev/null | head -1) # CoWork sandbox fallback
|
||||
python3 "$ECHO" load
|
||||
# Windows: use `python` or `py -3` if `python3` is not on PATH.
|
||||
```
|
||||
|
||||
`load` prints all six sections (404s on today's note / inbox are shown as absent, not errors) and flags an un-bootstrapped vault. Follow up with `echo.py scope show` if you need the sessions-since-switch count, and a project search if a specific project is in play.
|
||||
|
||||
**If `load` prints `NOT CONFIGURED` (exit 78)**, this machine has no usable key file yet. Don't proceed with memory — follow **First run** in `SKILL.md`: tell the operator ECHO isn't configured, ask for their echo-memory config file (owner/endpoint/key), install it with `echo.py config import <path>` (or `config set`), then re-run `load`.
|
||||
|
||||
Do not narrate the reads. End with a one-line orientation: who/what/where the active scope is, and any reconcile prompt.
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
description: Recall a topic from ECHO memory — matching notes plus their linked neighbourhood
|
||||
argument-hint: "[topic, person, or project]"
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to recall context for:
|
||||
|
||||
> $ARGUMENTS
|
||||
|
||||
Run the one-call recall — it resolves the term against the entity index, searches, and expands one hop along `## Related` links and `source_notes`, so you get the connected web (linked decisions, people, prior sessions), not an isolated note:
|
||||
|
||||
```bash
|
||||
ECHO="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/echo.py"
|
||||
[ -f "$ECHO" ] || ECHO=$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/echo.py 2>/dev/null | head -1) # CoWork sandbox fallback
|
||||
python3 "$ECHO" recall "$ARGUMENTS"
|
||||
# Windows: use `python` or `py -3` if `python3` is not on PATH.
|
||||
```
|
||||
|
||||
The corpus spans the entity graph **plus session logs and journal notes**, ranked by relevance × freshness × status — each hit shows its `updated:`/`status:`, so prefer fresh/active hits and treat stale ones as history. Add `--json` for structured hits (`path/score/type/updated/status/excerpt`) when you need to act on the results programmatically.
|
||||
|
||||
Then answer from the assembled context. If you only need the canonical path for one entity, use `echo.py resolve "<title>"` instead. Don't narrate the retrieval.
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
description: Reflect on this session — extract durable memories and propose them for one-tap capture
|
||||
argument-hint: "[optional focus, e.g. 'just decisions']"
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to run **session reflection** (H5). Scan this conversation for
|
||||
things worth remembering across sessions, then propose them — don't write blindly.
|
||||
|
||||
1. **Extract** durable items from the conversation: new facts, preferences, decisions,
|
||||
commitments, people/companies/projects introduced, and anything the operator said to remember.
|
||||
Skip the ephemeral. Anchor relative dates on the conversation's `currentDate`.
|
||||
2. **Emit a JSON array** of proposals (one per item), each:
|
||||
`{"title","kind","body","aliases","tags","sources","confidence"}` — `kind` ∈
|
||||
`person, company, concept, reference, meeting, project, area, semantic, episodic,
|
||||
working, skill, decision`; set `"inbox": true` when the home is genuinely unknown;
|
||||
`confidence` 0–1 (items below 0.6 are dropped — send those to the inbox instead).
|
||||
Write the array to a file with the Write tool (cross-platform; no heredoc).
|
||||
3. **Preview, then apply.** Dry-run first (writes nothing) and show the operator the plan; only
|
||||
apply after they confirm.
|
||||
|
||||
```bash
|
||||
ECHO="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/echo.py" # python3 (Windows: python / py -3)
|
||||
[ -f "$ECHO" ] || ECHO=$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/echo.py 2>/dev/null | head -1) # CoWork sandbox fallback
|
||||
python3 "$ECHO" reflect proposals.json # dry-run: validate + classify + preview
|
||||
python3 "$ECHO" reflect proposals.json --apply # write — routes each via capture (indexed, linked, logged)
|
||||
```
|
||||
|
||||
`reflect` dedups every proposal against the entity index (so it shows create-vs-update),
|
||||
skips below-confidence items, and routes `inbox` proposals to the capture inbox. Each
|
||||
applied item goes through the normal `capture` path — canonical frontmatter, auto-linking,
|
||||
and the recall index — and the writes are lock-guarded. $ARGUMENTS
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
description: Save to ECHO memory — route content to its canonical home (search-first, idempotent)
|
||||
argument-hint: "[what to remember]"
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to persist this to the ECHO vault:
|
||||
|
||||
> $ARGUMENTS
|
||||
|
||||
Prefer the one-call **`capture`** — it routes (via the entity index), derives the canonical path, stamps complete frontmatter (kind-default `status`, the kind seeded into `tags`), indexes, auto-links mentioned entities, and writes the Agent-Log line. Pick the `--kind` from the content (`person, company, concept, reference, meeting, project, area, semantic, episodic, working, skill, decision`); add `--tags` when obvious; use `--inbox` only when the home is genuinely unknown. Write multi-line bodies to a file with the Write tool (cross-platform, no heredoc) — updates to an existing entity preserve the whole body, so don't pre-trim it.
|
||||
|
||||
```bash
|
||||
ECHO="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/echo.py" # run with: python3 "$ECHO" ... (Windows: python / py -3)
|
||||
[ -f "$ECHO" ] || ECHO=$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/echo.py 2>/dev/null | head -1) # CoWork sandbox fallback
|
||||
python3 "$ECHO" capture "<title>" <bodyfile> --kind <kind> [--aliases a,b] [--tags t1,t2] [--source p1,p2]
|
||||
python3 "$ECHO" capture "<title>" --inbox # unknown home -> idempotent inbox line
|
||||
```
|
||||
|
||||
**If capture exits `76` (duplicate gate):** the title strongly resembles an existing entity — do NOT retry blindly. Show the operator the printed candidates and either update the existing note (`capture ... --merge-into <slug>`) or, only after they confirm it's genuinely distinct, re-run with `--force`.
|
||||
|
||||
Drop to the low-level verbs only for shapes `capture` doesn't model (a project `## Status` replace, an ADR mirror, a daily-note edit):
|
||||
|
||||
```bash
|
||||
python3 "$ECHO" put <routed/path>.md <bodyfile> # create/overwrite (verifies)
|
||||
python3 "$ECHO" patch <path>.md append heading "<H1::Sub>" <bodyfile> # targeted append
|
||||
```
|
||||
|
||||
Write in third person about the vault owner; never put `[[wikilinks]]` in frontmatter. `capture` handles `agent_written`, `source_notes`, indexing, and linking for you; if you write by hand, `resolve "<title>"` first to avoid duplicates and `bump` `updated:` only on substantive changes.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
description: Bring the ECHO vault up to the current feature spec (entity index + cross-links)
|
||||
allowed-tools: Bash(python3 *sweep.py*), Bash(python *sweep.py*), Bash(ls /sessions/*), Bash(dirname *)
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to bring the vault up to the current spec. Run the sweep **dry-run first**, show the operator the plan, and only `--apply` on their go-ahead:
|
||||
|
||||
```bash
|
||||
SDIR="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts"
|
||||
[ -d "$SDIR" ] || SDIR=$(dirname "$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/sweep.py 2>/dev/null | head -1)") # CoWork sandbox fallback
|
||||
python3 "$SDIR/sweep.py" # plan (read-only)
|
||||
python3 "$SDIR/sweep.py" --apply # write
|
||||
# Windows: use `python` or `py -3` if `python3` is not on PATH.
|
||||
```
|
||||
|
||||
The sweep (1) creates `_agent/index/`, (2) rebuilds the entity index from existing notes, (3) symmetrizes `## Related` cross-links (adds only the missing reciprocal direction — never invents new links), and (4) stamps the marker `schema_version`. It is idempotent and read-only without `--apply`. If `schema_version` is behind, run `migrate.py` first. After applying, run `/echo-health` to confirm invariants.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
description: Triage the ECHO inbox — route aging captures to their canonical homes and log the moves
|
||||
---
|
||||
|
||||
Use the **echo-memory** skill to run **one-tap Inbox Triage** via the `triage` verb (it reuses the reflect pipeline: classify against the entity index → preview → apply via `capture`, and writes the audit log for you).
|
||||
|
||||
```bash
|
||||
ECHO="${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/echo.py"
|
||||
[ -f "$ECHO" ] || ECHO=$(ls /sessions/*/mnt/.remote-plugins/*/skills/echo-memory/scripts/echo.py 2>/dev/null | head -1) # CoWork sandbox fallback
|
||||
python3 "$ECHO" triage --list --json
|
||||
# Windows: use `python` or `py -3` if `python3` is not on PATH.
|
||||
```
|
||||
|
||||
1. **List** — the JSON gives each capture as `{line, date, text, age_days}`. Surface the aging ones (≥ ~7 days) to the operator and ask which to route.
|
||||
2. **Propose** — for accepted items, write a proposals JSON with the Write tool (reflect schema: `{"title","kind","body",...}` **plus `"line"`: the original inbox line**, echoed into the audit log). Route per the map: preference/pattern → `semantic` (or a direct PATCH to `operator-preferences.md::Observations`); project idea → `project` with `--status incubating`; durable fact → `semantic`; person fact → `person`.
|
||||
3. **Preview, then apply** with the operator's go-ahead:
|
||||
|
||||
```bash
|
||||
python3 "$ECHO" triage proposals.json # dry-run: classify + preview, writes nothing
|
||||
python3 "$ECHO" triage proposals.json --apply # route via capture + log each move
|
||||
```
|
||||
|
||||
`--apply` records every move in `inbox/processing-log/YYYY-MM-DD.md` (`- <original> → <destination>`) automatically. Do **not** delete the original captures unless the operator explicitly asks — the processing log is the audit trail. A proposal that stops at the duplicate gate (exit note in the summary) should be re-proposed with the existing entity's title.
|
||||
Reference in New Issue
Block a user