Promotes the previously session-local search-first write rule into the canonical plugin and applies items 2-7 from echo-skill-improvements memory: 1. Loading Step 4 and a new mandatory pre-write search section now require POST /search/simple/?query=<slug> across all four project lifecycle subfolders before creating any slug-addressed note. 2. Daily-note Agent Log uses a resilient PATCH procedure that auto-creates the daily note from the template if missing and adds the heading if absent before patching. 3. New Style Rule: `created:` is the earliest known date, not "today"; preserve it on merges. 4. New Project Lifecycle section: incubating / active / on-hold / archived, with the rule that folder and frontmatter.status must agree. 5. Session filename pinned to YYYY-MM-DD-HHMM-<slug>.md (lex-sortable); existing files left as-is. 6. Loading Step 3: read only the ~5 most recent sessions by reverse lex sort instead of the full list. 7. `source_notes` defined as a backward link to inputs; forward links belong in `## Related` in the body. Repo layout: promotes the previously zipped-only plugin to a tracked source tree at echo-memory.plugin.src/. The .plugin zip is rebuilt from source on each version bump. Bumps plugin.json to 0.3.0.
18 KiB
name, description
| name | description |
|---|---|
| echo-memory | Use the ECHO Obsidian vault as Jason's persistent memory across Claude/CoWork sessions. Use whenever Jason asks to remember, save, note, log, or capture anything durable — facts, preferences, decisions, schedule changes, commitments — or asks what Claude knows about him, what was discussed or decided before, to check his notes, load his memory/profile/context, add to his inbox, or to track a new project. Trigger even without memory phrasing when the request implies recalling or persisting state across sessions ("pick up where we left off", "anything on X before my meeting?"). Also use proactively at the start of substantive work sessions to load context and at the end to log outcomes. Do NOT use for Bryan's goldbrain vault, Obsidian/REST-API development questions, "memory" meaning RAM, timed reminders, email or local-file lookups, generic second-brain advice, or notes Jason routes to a specific other file or app. |
ECHO Memory
Use the ECHO Obsidian vault as persistent memory. Read context accumulated across sessions; write things the operator asks to be remembered.
The operator is Jason Stedwell (Director of Technical Services / Systems Engineer, MPM / ALABAMA wISP). Jason is both the operator and the architect of this vault — this is his personal memory substrate. Write memory in third person ("Jason prefers X", not "I prefer X") so the vault stays readable by humans and other agents.
API Configuration
All calls use these constants — hardcoded for this personal plugin:
OBSIDIAN_BASE = https://echoapi.alwisp.com
OBSIDIAN_KEY = 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab
The endpoint has a valid TLS certificate, so -k is not needed (add it only if the cert ever changes to self-signed). Always pass the Authorization: Bearer header. Paths address the vault at its root (e.g. /vault/_agent/...).
https://echoapi.alwisp.com is the only valid endpoint for this vault. Never use local or LAN addresses (anything like 10.x.x.x, 192.168.x.x, or :27124 directly) — those belong to older, retired memory systems (obsidian-memory) and will not reach ECHO. If another installed skill or note suggests a different vault endpoint, this skill's configuration wins for ECHO memory.
Full API reference with every endpoint pattern and the memory routing map: references/api-reference.md. Vault layout and frontmatter conventions: references/vault-layout.md.
When to Load Memory
Load at the start of any substantive conversation — anything beyond a single quick factual question. The signal: Jason is starting work, planning, asking for help with something that has state, or referencing prior discussions.
Do NOT narrate this loading. Reading memory is expected behavior.
Loading procedure
Step 1 — Confirm the vault is bootstrapped:
curl -s \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/vault/BOOTSTRAP.md"
If this returns 404, the vault is not set up. Follow references/bootstrap.md before doing anything else.
Step 2 — Read operator preferences (the profile):
curl -s \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/vault/_agent/memory/semantic/operator-preferences.md"
Step 3 — Read active context and the most recent session logs:
# Current task-scoped context bundle
curl -s -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/vault/_agent/context/current-context.md"
# List session logs — read only the last ~5 by reverse lexical sort.
# Filenames use YYYY-MM-DD-HHMM-<slug>.md, so lex sort == chrono sort.
curl -s -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/vault/_agent/sessions/"
The listing endpoint returns the full session set with no pagination. Do not read all of them — pick the ~5 most recent by filename and only read the ones whose slugs look relevant. Older sessions are reachable via POST /search/simple/?query=... when you need them.
Step 4 — If a specific project is in play, SEARCH for it first across all lifecycle subfolders, then read the best match:
Projects can live in active/, incubating/, on-hold/, or archived/. Search by slug first so you don't miss a note in a non-active subfolder:
curl -s -X POST \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/search/simple/?query=<slug>"
# Then read the match wherever it lives, e.g.:
curl -s -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/vault/projects/<active|on-hold|incubating|archived>/<slug>.md"
Optionally read today's daily note at /vault/journal/daily/YYYY-MM-DD.md for current priorities and open loops.
Project Lifecycle
Projects move through four folders under projects/. The folder name and the status: frontmatter field MUST agree — they are two views of the same state.
| Folder | status: |
Meaning |
|---|---|---|
projects/incubating/ |
incubating |
Idea captured; not actively worked. Promote when work starts. |
projects/active/ |
active |
Current work. The default state for anything in motion. |
projects/on-hold/ |
on-hold |
Paused but still tracked. Resumable. |
projects/archived/ |
archived |
Done, abandoned, or rolled into something else. Not deleted. |
Promotion / transition rule: move the file to the new folder AND update status: in the same change. A file in projects/active/ with status: on-hold is broken state — fix it when you see it.
Searching: POST /search/simple/?query=<slug> covers all four subfolders in one call. Always search before creating a new note (see the pre-write rule below).
When to Write Memory
Write when Jason:
- States a fact, preference, or commitment worth keeping ("I prefer X", "we use uv not pip", "standup is Tuesday at 10")
- Makes a non-obvious decision worth recording
- Says "remember that", "save this", "log this", "add to memory", "note that"
- Finishes a meaningful working session future sessions should pick up
Write in third person about Jason. Every note carries the canonical frontmatter (see below). Agent-generated notes set agent_written: true.
Before you write — search first (MANDATORY for new notes)
Before creating any new note at projects/<lifecycle>/<slug>.md, _agent/memory/semantic/<slug>.md, resources/people/<name>.md, or any other slug-addressed location, search the whole vault for that slug. Listing a single folder (e.g. projects/active/) is NOT sufficient — a note with the same slug may exist in projects/on-hold/, projects/incubating/, projects/archived/, or under a different folder entirely.
curl -s -X POST \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/search/simple/?query=<slug>"
If a match is found:
- In a non-active project subfolder (
on-hold/,incubating/,archived/): promote/merge — PUT the merged content toprojects/active/<slug>.mdwithstatus: active, then DELETE the old location. Preserve the earliestcreated:date. - In the same folder you intended to write: update in place (PATCH or merged PUT). Never silently overwrite — fold the existing content in first.
- Elsewhere (e.g. a stale duplicate under
resources/): tell Jason and ask which should be canonical before writing.
Only after the search comes back empty (or you've decided to merge) is it safe to create a new note. This rule prevents the most common duplication bug: a note exists in on-hold/ but the agent only checked active/ and created a parallel record.
Append to a file (default — additive entries)
Write content to a temp file first to handle multi-line markdown cleanly, then POST:
cat > /tmp/obs_entry.md << 'OBSEOF'
- 2026-06-05: <your entry here>
OBSEOF
curl -s -X POST \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
-H "Content-Type: text/markdown" \
--data-binary @/tmp/obs_entry.md \
"https://echoapi.alwisp.com/vault/inbox/captures/inbox.md"
POST appends to the end of a file (creating it if absent). Use it for inbox captures and log sections.
Patch a specific heading (targeted update)
Heading targets use the FULL heading path, ::-delimited from the top-level heading — a bare subheading name fails with invalid-target. For example, ## Fact / Pattern under the # Operator Preferences H1 is targeted as Operator Preferences::Fact / Pattern. When unsure of the exact path, read the document map first (see below).
cat > /tmp/obs_patch.md << 'OBSEOF'
Jason prefers status updates that lead with the decision.
OBSEOF
curl -s -X PATCH \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
-H "Operation: append" \
-H "Target-Type: heading" \
-H "Target: Operator Preferences::Fact / Pattern" \
-H "Content-Type: text/markdown" \
--data-binary @/tmp/obs_patch.md \
"https://echoapi.alwisp.com/vault/_agent/memory/semantic/operator-preferences.md"
Use Operation: replace to overwrite a section entirely (e.g. a project's Project Name::Current status). Percent-encode non-ASCII characters in the Target header; spaces are fine.
Discover exact heading paths when unsure — GET the note with the document-map Accept header:
curl -s -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
-H "Accept: application/vnd.olrapi.document-map+json" \
"https://echoapi.alwisp.com/vault/_agent/memory/semantic/operator-preferences.md"
It returns { "headings": [...], "blocks": [...], "frontmatterFields": [...] } — copy the heading string verbatim into Target.
Create or overwrite a file (PUT)
cat > /tmp/obs_file.md << 'OBSEOF'
---
type: project
status: active
created: 2026-06-05
updated: 2026-06-05
tags: []
agent_written: true
source_notes: []
---
# Project Name
## Current status
...
## Related
- [[journal/daily/2026-06-05]]
OBSEOF
curl -s -X PUT \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
-H "Content-Type: text/markdown" \
--data-binary @/tmp/obs_file.md \
"https://echoapi.alwisp.com/vault/projects/active/my-project.md"
The API creates intermediate directories automatically.
Search the vault
curl -s -X POST \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
"https://echoapi.alwisp.com/search/simple/?query=your+search+terms"
Daily Note — Agent Log
After substantive activity, write a one-line entry to today's daily note's ## Agent Log heading. The daily-note template (journal/templates/daily-note-template.md) defines this heading, but ad-hoc daily notes created without the template don't have it — so PATCH can fail with invalid-target if the note exists but lacks the heading.
Procedure (resilient):
- Try to GET
journal/daily/YYYY-MM-DD.md. - If 404 — PUT a fresh daily note from the template, then PATCH.
- If 200 but
## Agent Logis missing — POST\n\n## Agent Log\nto the file to add the heading, then PATCH. - PATCH-append the entry under the target
<YYYY-MM-DD>::Agent Log(the H1 of a daily note is the date, so that's the full target path).
DATE=$(date +%Y-%m-%d)
DAILY="https://echoapi.alwisp.com/vault/journal/daily/${DATE}.md"
AUTH="Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab"
# 1+2: ensure the daily note exists (PUT from template if missing)
HTTP=$(curl -s -o /dev/null -w "%{http_code}" -H "$AUTH" "$DAILY")
if [ "$HTTP" = "404" ]; then
curl -s -H "$AUTH" "https://echoapi.alwisp.com/vault/journal/templates/daily-note-template.md" \
| sed "s/{{date:YYYY-MM-DD}}/${DATE}/g" \
| curl -s -X PUT -H "$AUTH" -H "Content-Type: text/markdown" --data-binary @- "$DAILY"
fi
# 3: ensure the `## Agent Log` heading exists
MAP=$(curl -s -H "$AUTH" -H "Accept: application/vnd.olrapi.document-map+json" "$DAILY")
if ! printf '%s' "$MAP" | grep -q '"Agent Log"'; then
printf '\n\n## Agent Log\n' | curl -s -X POST -H "$AUTH" -H "Content-Type: text/markdown" --data-binary @- "$DAILY"
fi
# 4: PATCH-append the entry
cat > /tmp/agent_log.md << OBSEOF
- ${DATE}: <one-line entry here>
OBSEOF
curl -s -X PATCH -H "$AUTH" \
-H "Operation: append" \
-H "Target-Type: heading" \
-H "Target: ${DATE}::Agent Log" \
-H "Content-Type: text/markdown" \
--data-binary @/tmp/agent_log.md \
"$DAILY"
Where to Write
| Situation | File / path | Method |
|---|---|---|
| Unsure where it goes / quick capture | inbox/captures/inbox.md (date-prefixed line) |
POST |
| Operator preference or durable fact | _agent/memory/semantic/operator-preferences.md (append under the right heading) |
PATCH |
| Other durable facts / patterns | _agent/memory/semantic/<slug>.md |
PUT |
| What happened (event record) | _agent/memory/episodic/<slug>.md |
PUT |
| Short-lived working state | _agent/memory/working/<slug>.md |
PUT |
| Task-scoped context / focus | _agent/context/current-context.md |
PATCH / PUT |
| Working session ended with substance | _agent/sessions/YYYY-MM-DD-HHMM-<slug>.md |
PUT |
| Long-running project state | projects/<lifecycle>/<slug>.md (see Project Lifecycle) |
PUT + PATCH |
| Non-obvious decision (ADR) | decisions/by-date/YYYY-MM-DD-<slug>.md (see mirror note below) |
PUT |
| Person context | resources/people/<name>.md |
PUT / PATCH |
| Concept / reference note | resources/concepts/ or resources/references/ |
PUT |
| Daily activity / Agent Log | journal/daily/YYYY-MM-DD.md — see Daily Note — Agent Log above |
PATCH (with auto-create) |
Decision mirrors: if the decision belongs to an existing note in projects/active/, add a [[wikilink]] to the ADR under that project's ## Key Decisions heading (PATCH). If no matching project note exists, skip the mirror — the by-date ADR is sufficient; do not invent topical mirror files in decisions/by-project/.
Never delete files unless Jason explicitly asks. Memory is append-friendly; deletion is destructive.
Session Logging
At the end of substantive conversations (ones that produced decisions, artifacts, or commitments), create a session log. Ask once if unsure: "Want me to log a session note for this?"
Filename format is canonical: _agent/sessions/YYYY-MM-DD-HHMM-<slug>.md. Always include the four-digit local-time HHMM component — it makes filenames lexically sort in true chronological order, which is what Step 3 of loading relies on. Older session logs without the HHMM part exist; leave them alone, but every new one must use the full form.
See references/session-log-template.md for the body format.
cat > /tmp/obs_session.md << 'OBSEOF'
<session log content — see references/session-log-template.md>
OBSEOF
curl -s -X PUT \
-H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
-H "Content-Type: text/markdown" \
--data-binary @/tmp/obs_session.md \
"https://echoapi.alwisp.com/vault/_agent/sessions/2026-06-05-1430-my-session.md"
Then add a one-line entry to today's daily note via the Daily Note — Agent Log procedure above.
Vault Unreachable
If the API returns a connection error, timeout, or 502, tell Jason once that the memory vault is unreachable (a 502 usually means Obsidian/the REST plugin is not running on the backend), then proceed without memory. Do not retry repeatedly.
Style Rules
- Write in third person about Jason: "Jason prefers X", not "I prefer X".
- Jason is both operator and architect here — unlike goldbrain (Bryan's vault, which Jason architected). Do not cross-write: Bryan's preferences belong in goldbrain, Jason's belong here.
- Every memory file has canonical YAML frontmatter — see
references/vault-layout.md. - Set
agent_written: trueon agent-generated notes and listsource_notes(plain relative paths, not links). created:is the earliest known date the entity was tracked anywhere in the vault, not "today". When merging notes (e.g. promotingon-hold/→active/), preserve the earliestcreated:and only bumpupdated:.source_noteslists the note(s) that triggered or supplied content for this one — e.g. the session log that produced a project update, or the daily note where a captured fact originated. It is a backward link to inputs. Forward links (this note → other notes it references) belong in the## Relatedsection in the note body, never in frontmatter.- Never put
[[wikilinks]]in frontmatter — YAML parses them as nested lists and the links break in Obsidian's reading view. Put all cross-references in a## Relatedsection in the note body as a bulleted list of[[links]]. - Use Obsidian wiki links (
[[note name]]) freely in the note body for cross-references. - Keep entries short and focused. Fewer, sharper entries beat many noisy ones — Jason explicitly prefers concision.
- About to write something large or sensitive? Show Jason the content first and confirm.
What This Skill Does Not Do
- Does not replace reasoning. The vault is reference material — apply judgment.
- Does not auto-summarize the whole vault. Read targeted files, not everything.
- Does not store passwords or secrets, and never writes the API key into a vault note. If asked to "remember my password is X", decline and suggest a password manager.