From 307c7f5e9b773d9dfad2caec3c72175fab6616ef Mon Sep 17 00:00:00 2001 From: jason Date: Sat, 6 Jun 2026 21:41:54 -0500 Subject: [PATCH] plugin 0.3.0: port search-first rule + 6 new SKILL.md improvements 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= 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-.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. --- .gitignore | 1 + echo-memory.plugin | Bin 15337 -> 16761 bytes .../.claude-plugin/plugin.json | 15 + echo-memory.plugin.src/README.md | 55 +++ .../skills/echo-memory/SKILL.md | 324 ++++++++++++++++++ .../echo-memory/references/api-reference.md | 207 +++++++++++ .../echo-memory/references/bootstrap.md | 37 ++ .../references/session-log-template.md | 108 ++++++ .../echo-memory/references/vault-layout.md | 154 +++++++++ 9 files changed, 901 insertions(+) create mode 100644 .gitignore create mode 100644 echo-memory.plugin.src/.claude-plugin/plugin.json create mode 100644 echo-memory.plugin.src/README.md create mode 100644 echo-memory.plugin.src/skills/echo-memory/SKILL.md create mode 100644 echo-memory.plugin.src/skills/echo-memory/references/api-reference.md create mode 100644 echo-memory.plugin.src/skills/echo-memory/references/bootstrap.md create mode 100644 echo-memory.plugin.src/skills/echo-memory/references/session-log-template.md create mode 100644 echo-memory.plugin.src/skills/echo-memory/references/vault-layout.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b0870c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.plugin-extracted/ diff --git a/echo-memory.plugin b/echo-memory.plugin index bcc39a767872fb231f0c4a63231a7f2724f92f65..931bea0eec4d11b239891edaf5ff367feebbcc57 100644 GIT binary patch literal 16761 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 literal 15337 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 N;O~*6MD{;E{U5D-t$F|e diff --git a/echo-memory.plugin.src/.claude-plugin/plugin.json b/echo-memory.plugin.src/.claude-plugin/plugin.json new file mode 100644 index 0000000..087d60f --- /dev/null +++ b/echo-memory.plugin.src/.claude-plugin/plugin.json @@ -0,0 +1,15 @@ +{ + "name": "echo-memory", + "version": "0.3.0", + "description": "Persistent memory via the ECHO Obsidian vault over the Local REST API. Reads and writes notes across Claude/CoWork sessions using direct REST calls \u2014 no MCP server required. Jason's personal memory vault.", + "author": { + "name": "Jason" + }, + "keywords": [ + "memory", + "obsidian", + "notes", + "persistence", + "echo" + ] +} \ No newline at end of file diff --git a/echo-memory.plugin.src/README.md b/echo-memory.plugin.src/README.md new file mode 100644 index 0000000..c1bf7da --- /dev/null +++ b/echo-memory.plugin.src/README.md @@ -0,0 +1,55 @@ +# echo-memory + +Persistent memory for Claude via the **ECHO** Obsidian vault, using the [Obsidian Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api). + +Reads and writes notes across Claude/CoWork sessions using direct REST calls — no MCP server required. Built for **Jason Stedwell** (Director of Technical Services / Systems Engineer, MPM / ALABAMA wISP), who is both the operator and the architect of this vault. Successor to the obsidian-memory system and sibling of the goldbrain vault (Bryan's). + +## What it does + +- Loads operator preferences, current context, and relevant project notes at the start of substantive conversations +- Writes facts, preferences, and decisions Jason asks Claude to remember +- Logs working sessions so future conversations can pick up where they left off +- Defers to the vault's own `BOOTSTRAP.md` to verify and repair structure + +## Configuration + +The plugin is hardcoded for: + +- **Server:** `https://echoapi.alwisp.com` (reverse proxy → backend Obsidian Local REST API) +- **API Key:** stored in the skill (personal plugin, not for distribution) + +The endpoint presents a **valid TLS certificate**, so `-k` is not required. The bearer key lives only in the plugin — never inside the vault (per the vault's own safety rules). + +## Vault layout (root-addressed) + +``` +/vault/ +├── CLAUDE.md STRUCTURE.md BOOTSTRAP.md spinup.md index.md README.md +├── inbox/ (captures, imports, processing-log) +├── journal/ (daily, weekly, monthly, templates) +├── projects/ (active, incubating, on-hold, archived) +├── areas/ (business, personal, learning, systems) +├── resources/ (concepts, references, people, meetings, source-material) +├── decisions/ (by-date, by-project) +├── reviews/ (weekly, monthly, quarterly, annual) +├── archive/ (notes, projects, imports) +└── _agent/ + ├── context/ ← task-scoped context bundles + ├── memory/ ← working / episodic / semantic + ├── sessions/ ← YYYY-MM-DD-HHMM-.md + ├── templates/ ← canonical note templates + ├── outputs/ ← briefs / drafts / summaries / synthesis + ├── skills/ ← active / archived + └── heartbeat/ +``` + +## Skills + +| Skill | Triggers | +|-------|----------| +| `echo-memory` | "remember that", "save to memory", "what do you know about me", "load my profile", "check my notes", "log this decision", "add to my inbox" — and proactively at the start of substantive conversations | + +## Requirements + +- Obsidian running on the backend with the [Local REST API plugin](https://github.com/coddingtonbear/obsidian-local-rest-api) enabled +- HTTPS access to `https://echoapi.alwisp.com` from the Claude/CoWork session environment diff --git a/echo-memory.plugin.src/skills/echo-memory/SKILL.md b/echo-memory.plugin.src/skills/echo-memory/SKILL.md new file mode 100644 index 0000000..0a7929e --- /dev/null +++ b/echo-memory.plugin.src/skills/echo-memory/SKILL.md @@ -0,0 +1,324 @@ +--- +name: echo-memory +description: 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:** + +```bash +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):** + +```bash +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:** + +```bash +# 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-.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: + +```bash +curl -s -X POST \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/search/simple/?query=" + +# Then read the match wherever it lives, e.g.: +curl -s -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/vault/projects//.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=` 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//.md`, `_agent/memory/semantic/.md`, `resources/people/.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. + +```bash +curl -s -X POST \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/search/simple/?query=" +``` + +If a match is found: +- In a non-active project subfolder (`on-hold/`, `incubating/`, `archived/`): **promote/merge** — PUT the merged content to `projects/active/.md` with `status: active`, then DELETE the old location. Preserve the earliest `created:` 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: + +```bash +cat > /tmp/obs_entry.md << 'OBSEOF' +- 2026-06-05: +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). + +```bash +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: + +```bash +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) + +```bash +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 + +```bash +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):** + +1. Try to GET `journal/daily/YYYY-MM-DD.md`. +2. If 404 — PUT a fresh daily note from the template, then PATCH. +3. If 200 but `## Agent Log` is missing — POST `\n\n## Agent Log\n` to the file to add the heading, then PATCH. +4. PATCH-append the entry under the target `::Agent Log` (the H1 of a daily note is the date, so that's the full target path). + +```bash +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}: +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/.md` | PUT | +| What happened (event record) | `_agent/memory/episodic/.md` | PUT | +| Short-lived working state | `_agent/memory/working/.md` | PUT | +| Task-scoped context / focus | `_agent/context/current-context.md` | PATCH / PUT | +| Working session ended with substance | `_agent/sessions/YYYY-MM-DD-HHMM-.md` | PUT | +| Long-running project state | `projects//.md` (see Project Lifecycle) | PUT + PATCH | +| Non-obvious decision (ADR) | `decisions/by-date/YYYY-MM-DD-.md` (see mirror note below) | PUT | +| Person context | `resources/people/.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-.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. + +```bash +cat > /tmp/obs_session.md << 'OBSEOF' + +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: true` on agent-generated notes and list `source_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. promoting `on-hold/` → `active/`), preserve the earliest `created:` and only bump `updated:`. +- **`source_notes` lists 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 `## Related` section 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 `## Related` section 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. diff --git a/echo-memory.plugin.src/skills/echo-memory/references/api-reference.md b/echo-memory.plugin.src/skills/echo-memory/references/api-reference.md new file mode 100644 index 0000000..8f782ff --- /dev/null +++ b/echo-memory.plugin.src/skills/echo-memory/references/api-reference.md @@ -0,0 +1,207 @@ +# ECHO — Obsidian Local REST API Reference + +Server: `https://echoapi.alwisp.com` (reverse proxy → backend Obsidian Local REST API) +Auth header: `Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab` +The endpoint has a **valid TLS certificate** — `-k` is not required. Paths address the vault at its **root**. + +--- + +## Reading Files + +```bash +# Read any file by vault path +curl -s \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/vault/_agent/context/current-context.md" +``` + +Returns raw file content (text/markdown). On 404, the file does not exist. + +```bash +# Read a specific heading's content only +curl -s \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/vault/_agent/memory/semantic/operator-preferences.md/heading/Operator" +``` + +Nested headings: separate levels with `::` (URL-encode spaces as `%20`): +``` +/vault/path/to/note.md/heading/Work%3A%3AMeetings +``` + +--- + +## Listing Directories + +```bash +# List contents of a directory (trailing slash required) +curl -s \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/vault/_agent/sessions/" +``` + +Returns JSON: `{ "files": [...], "folders": [...] }`. + +--- + +## Appending Content (POST) + +`POST` appends to the **end** of an existing file. Creates the file if it doesn't exist. + +```bash +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" +``` + +--- + +## Creating or Overwriting Files (PUT) + +`PUT` creates a new file or fully overwrites an existing one. Intermediate directories are created automatically. + +```bash +cat > /tmp/obs_file.md << 'OBSEOF' +--- +type: session-log +status: complete +created: 2026-06-05 +updated: 2026-06-05 +tags: [agent, session] +agent_written: true +source_notes: [] +--- + +# content here + +## Related +- [[projects/active/some-project]] +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/_agent/sessions/2026-06-05-1430-my-session.md" +``` + +--- + +## Patching a Specific Section (PATCH) + +`PATCH` edits inside a file without rewriting it. Target and operation are set via headers. + +### Append under a heading + +**Heading targets must be the FULL heading path, `::`-delimited from the top-level heading.** A bare subheading name returns `400 invalid-target` (errorCode 40080). Example: `## Fact / Pattern` nested under `# Operator Preferences` → `Target: Operator Preferences::Fact / Pattern`. Percent-encode non-ASCII characters (e.g. `H%C3%A9llo`); spaces are fine. + +```bash +cat > /tmp/obs_patch.md << 'OBSEOF' +Jason prefers concise status updates — 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" +``` + +### Discover heading / block / frontmatter targets + +When unsure of the exact heading path, GET the note with the document-map Accept header: + +```bash +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" +``` + +Returns `{ "headings": [...], "blocks": [...], "frontmatterFields": [...] }`. Copy the heading string verbatim into `Target`. + +### Replace a heading's content entirely + +Same call with `Operation: replace` — e.g. to refresh a project's `Project Name::Current status`. + +### Prepend under a heading + +Same call with `Operation: prepend`. + +### Patch a frontmatter field + +```bash +curl -s -X PATCH \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + -H "Operation: replace" \ + -H "Target-Type: frontmatter" \ + -H "Target: updated" \ + -H "Content-Type: application/json" \ + --data '"2026-06-05"' \ + "https://echoapi.alwisp.com/vault/projects/active/vault-foundation.md" +``` + +--- + +## Searching + +```bash +curl -s -X POST \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/search/simple/?query=weekly+review" +``` + +Returns an array of `{ filename, score, matches: [{ context, match }] }`. + +--- + +## Deleting Files + +```bash +curl -s -X DELETE \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/vault/archive/notes/old-note.md" +``` + +Only on explicit operator request. Deletion is destructive. + +--- + +## URL-Encoding Notes + +- Path separators (`/`) in the vault path are **literal** — do not encode them. +- Spaces in filenames or heading targets in a URL: use `%20`. +- Nested heading levels in a URL path: use `%3A%3A` for `::`. +- Heading text in the `Target:` header: the **full heading path** joined by `::` (e.g. `Operator Preferences::Fact / Pattern`), no `#`; spaces are fine; percent-encode non-ASCII. + +--- + +## Memory Routing Map + +| Situation | Vault path | Method | +|-----------|-----------|--------| +| Quick capture / unsorted | `inbox/captures/inbox.md` (date-prefixed line) | POST | +| Raw imported material | `inbox/imports/` | PUT | +| Operator preference / durable fact | `_agent/memory/semantic/operator-preferences.md` | PATCH | +| Other durable facts, patterns | `_agent/memory/semantic/.md` | PUT | +| Event record (what happened) | `_agent/memory/episodic/.md` | PUT | +| Short-lived, time-boxed state | `_agent/memory/working/.md` | PUT | +| Task-scoped context / focus | `_agent/context/current-context.md` | PATCH / PUT | +| Working-session log | `_agent/sessions/YYYY-MM-DD-HHMM-.md` | PUT | +| Long-running project state | `projects/active/.md` | PUT + PATCH | +| Non-obvious decision (ADR) | `decisions/by-date/YYYY-MM-DD-.md` (mirror only into an existing project note's Key Decisions; otherwise skip) | PUT | +| Person context | `resources/people/.md` | PUT / PATCH | +| Concept / reference note | `resources/concepts/` or `resources/references/` | PUT | +| Daily activity / Agent Log | `journal/daily/YYYY-MM-DD.md` | POST / PATCH | +| Periodic review | `reviews/{weekly,monthly,quarterly,annual}/` | PUT | + +**Slug rules:** kebab-case, ASCII, ~40 chars max. Every file carries canonical frontmatter (see `vault-layout.md`). diff --git a/echo-memory.plugin.src/skills/echo-memory/references/bootstrap.md b/echo-memory.plugin.src/skills/echo-memory/references/bootstrap.md new file mode 100644 index 0000000..9021d04 --- /dev/null +++ b/echo-memory.plugin.src/skills/echo-memory/references/bootstrap.md @@ -0,0 +1,37 @@ +# Bootstrap Procedure + +The ECHO vault ships its **own** `BOOTSTRAP.md` at the vault root — that file is the canonical preflight/repair manifest. This plugin defers to it rather than duplicating the logic. + +## Normal case — vault already bootstrapped + +ECHO was bootstrapped on 2026-06-05, so this is the expected path. At session start, read the in-vault manifest: + +```bash +curl -s \ + -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \ + "https://echoapi.alwisp.com/vault/BOOTSTRAP.md" +``` + +If it returns content (200), the vault is set up. Skim its preflight checklist, then read `CLAUDE.md` for the operating contract and proceed with the loading procedure in `SKILL.md`. The in-vault `BOOTSTRAP.md` describes how to repair any missing folders/files; follow it if something is absent. **Never overwrite an existing file** during repair — generate only what is missing. + +## Fresh-vault case — BOOTSTRAP.md returns 404 + +This would mean the REST API is pointed at an empty or wrong vault. Confirm with Jason once: + +> "The ECHO vault looks empty — there's no `BOOTSTRAP.md`. That's unexpected (it was bootstrapped 2026-06-05). Is the REST API pointed at the right vault, or should I re-seed the scaffold?" + +If a re-seed is wanted, create the minimum viable seed with `PUT` (the API creates intermediate directories automatically), then let the vault's own structure grow from there: + +1. `CLAUDE.md` — operating contract and session protocol +2. `BOOTSTRAP.md` — preflight/repair manifest +3. `STRUCTURE.md` — layout, taxonomy, frontmatter standard +4. `index.md` — navigation hub +5. `_agent/memory/semantic/operator-preferences.md` — operator profile (no fabricated facts) +6. `_agent/context/current-context.md` — empty context bundle +7. `inbox/captures/inbox.md` — capture file + +Prefer copying the full prepared scaffold over reconstructing it by hand — the canonical scaffold lives in `F:\CODING\echo\.references\goldbrain-obsidian\` (adapt operator references to Jason). + +## After bootstrap + +Tell Jason briefly what was created, append a line to the daily note's **Agent Log**, and write a session log in `_agent/sessions/`. Do not over-explain — he can browse the vault. diff --git a/echo-memory.plugin.src/skills/echo-memory/references/session-log-template.md b/echo-memory.plugin.src/skills/echo-memory/references/session-log-template.md new file mode 100644 index 0000000..c6807d3 --- /dev/null +++ b/echo-memory.plugin.src/skills/echo-memory/references/session-log-template.md @@ -0,0 +1,108 @@ +# Session Log Template + +Session logs go in: `_agent/sessions/YYYY-MM-DD-HHMM-.md` + +The slug describes what the session was about in 2–5 words, kebab-case. +Examples: `2026-06-05-1430-echo-plugin-build.md`, `2026-05-14-0900-q1-review-prep.md`. + +Keep logs focused. Capture the goal, what was read/done, decisions, outputs, open threads, and the next step. This matches the vault's `_agent/templates/session-log-template.md`. + +--- + +## Template + +```markdown +--- +type: session-log +status: complete +created: 2026-06-05T14:30 +updated: 2026-06-05T14:30 +tags: [agent, session] +agent_written: true +source_notes: [] +session_date: 2026-06-05 +client: claude-code +--- + +# Session Log + +## Goal +One line — what this session set out to do. + +## Notes Read +- [[note]] — why it was relevant +(omit if none) + +## Actions Taken +Brief narrative or bullets of what was done. + +## Decisions Made +- Decision — why +(omit section if none) + +## Outputs Created +- `path/or/filename` — what it is +(omit section if none) + +## Open Threads +- [ ] unresolved question or next step +(omit section if none) + +## Suggested Next Step +One sentence: what to do first next time on this topic. + +## Related +- [[wikilinks go here, in the body — never in frontmatter]] +``` + +--- + +## Example + +```markdown +--- +type: session-log +status: complete +created: 2026-06-05T14:30 +updated: 2026-06-05T14:30 +tags: [agent, session, plugin] +agent_written: true +source_notes: ["resources/references/obsidian-local-rest-api.md"] +session_date: 2026-06-05 +client: claude-code +--- + +# Session Log + +## Goal +Build and package the echo-memory CoWork plugin against the live vault. + +## Notes Read +- [[BOOTSTRAP]], [[STRUCTURE]], [[_agent/memory/semantic/operator-preferences]] + +## Actions Taken +Verified the REST API end-to-end, confirmed the scaffold copied into the live vault, +created missing empty folders, and built the plugin (SKILL + 4 reference files). + +## Decisions Made +- Vault addressed at root — ECHO is a dedicated vault. +- Key hardcoded in the plugin (not in the vault) — personal plugin, per the reference pattern. + +## Outputs Created +- `echo-memory.plugin` — installable CoWork plugin + +## Open Threads +- [ ] Validate Claude Code direct filesystem access to the vault host. + +## Suggested Next Step +Install the plugin and run a live load/write through it to confirm the skill triggers. + +## Related +- [[projects/active/vault-foundation]] +- [[resources/references/obsidian-local-rest-api]] +``` + +After writing the log, append a one-line entry to the daily note's **Agent Log** section. + +> **Reminder:** wiki links go in the body (e.g. this `## Related` section), never in YAML +> frontmatter. `source_notes` in frontmatter holds plain relative path strings, not `[[links]]`. diff --git a/echo-memory.plugin.src/skills/echo-memory/references/vault-layout.md b/echo-memory.plugin.src/skills/echo-memory/references/vault-layout.md new file mode 100644 index 0000000..ca512ba --- /dev/null +++ b/echo-memory.plugin.src/skills/echo-memory/references/vault-layout.md @@ -0,0 +1,154 @@ +# Vault Layout & Frontmatter Conventions + +Mirrors the canonical conventions defined in the vault's own `STRUCTURE.md` and `BOOTSTRAP.md`. If those change, they are the source of truth. + +## Folder Map (root-addressed) + +``` +/vault/ +├── CLAUDE.md ← operating contract + session protocol +├── STRUCTURE.md ← layout, taxonomy, frontmatter standard +├── BOOTSTRAP.md ← preflight/repair manifest (read first) +├── spinup.md index.md README.md +├── inbox/ +│ ├── captures/ ← quick captures (inbox.md), date-prefixed lines +│ ├── imports/ ← raw imported material +│ └── processing-log/ +├── journal/ +│ ├── daily/ ← YYYY-MM-DD.md (has an "Agent Log" section) +│ ├── weekly/ monthly/ +│ └── templates/ +├── projects/ ← lifecycle: incubating → active → on-hold/archived +│ ├── active/ ← current work (status: active) +│ ├── incubating/ ← idea captured, not yet started (status: incubating) +│ ├── on-hold/ ← paused but kept (status: on-hold) +│ ├── archived/ ← done / abandoned (status: archived) +│ └── project-template.md +├── areas/ ← business / personal / learning / systems +├── resources/ +│ ├── concepts/ references/ meetings/ source-material/ +│ └── people/ ← .md +├── decisions/ +│ ├── by-date/ ← YYYY-MM-DD-.md (ADR-style) +│ ├── by-project/ ← mirror by project +│ └── decision-template.md +├── reviews/ ← weekly / monthly / quarterly / annual +├── archive/ ← notes / projects / imports +└── _agent/ + ├── context/ ← current-context.md and task bundles + ├── memory/ + │ ├── working/ ← transient, time-boxed + │ ├── episodic/ ← what happened, when + │ └── semantic/ ← durable facts/patterns (operator-preferences.md) + ├── sessions/ ← YYYY-MM-DD-HHMM-.md + ├── templates/ ← canonical note templates + ├── outputs/ ← briefs / drafts / summaries / synthesis + ├── skills/ ← active / archived + └── heartbeat/ +``` + +**Slug rules:** kebab-case, ASCII only, truncate to ~40 chars. + +--- + +## Canonical Frontmatter + +Every note starts with this block. Fill what applies; leave the rest empty rather than guessing. + +```yaml +--- +type: # see Note Types below +status: # active | draft | done | archived | complete +created: # YYYY-MM-DD (or YYYY-MM-DDTHH:mm for sessions) +updated: # YYYY-MM-DD +tags: [] +agent_written: false +source_notes: [] # plain relative paths as strings — NEVER [[wikilinks]] +--- +``` + +`agent_written: true` + a populated `source_notes` is the key signal separating +agent-managed content from human-authored content. When appending with POST, do +not rewrite frontmatter — the append goes after existing content. To change +`updated:` or `status:`, use PATCH with `Target-Type: frontmatter`. + +**Frontmatter field semantics:** + +- `created:` is the **earliest known date** the entity was tracked in the vault — *not* "today". When merging notes (e.g. promoting an `on-hold/` project into `active/`), preserve the earliest `created:` from any merged source and only update `updated:`. +- `source_notes` is a **backward link** — the note(s) that triggered or supplied content for this one (e.g. the session log a project update came from). Forward links go in the `## Related` body section, never here. +- `status:` for a project MUST match its folder under `projects/` (`active`, `incubating`, `on-hold`, `archived`). Moving the file and updating `status:` are the same operation. + +> **No `[[wikilinks]]` in frontmatter.** YAML parses `[[...]]` as nested lists, so wiki +> links there break and never render as clickable links in reading view. Put all +> cross-references in a **`## Related`** section in the note **body** (bulleted `[[links]]`). +> Frontmatter holds scalar/string metadata only; `source_notes` values are plain relative +> paths, not links. + +## Note Types + +`daily-note`, `weekly-note`, `monthly-note`, `project`, `project-update`, `area`, +`concept`, `reference`, `person`, `meeting`, `decision`, `review`, `session-log`, +`working-memory`, `episodic-memory`, `semantic-memory`, `context-bundle`, `skill`, +`draft`, `inbox-item`. + +--- + +## File-Specific Conventions + +### operator-preferences.md (`_agent/memory/semantic/`) + +The profile analog. Headings include `Operator`, `Fact / Pattern`, `Evidence`, +`Recommendation or Implication`, `Review Notes`. Append observed facts under the +right heading via PATCH. "The operator" is Jason — he is both +operator and architect of this vault. + +### projects/active/\.md + +```markdown +--- +type: project +status: active +created: 2026-06-05 +updated: 2026-06-05 +tags: [] +agent_written: false +source_notes: [] +--- + +# Project Name + +## Current status +One paragraph, kept fresh via PATCH replace. + +## Decisions +- [[YYYY-MM-DD-decision-slug]] — one-line summary + +## Open threads +- [ ] unresolved thing + +## Log +- 2026-06-05: observation or update + +## Related +- [[areas/business/business-ops]] +``` + +### sessions/YYYY-MM-DD-HHMM-\.md + +See `session-log-template.md`. Note ECHO uses an **HHMM time component** in the +filename (unlike a date-only convention). + +### decisions/by-date/YYYY-MM-DD-\.md + +ADR-style: Context → Decision → Consequences. Mirror a link into `by-project/`. + +### people/\.md + +`type: person`. Use lowercase kebab-case for the slug (e.g. `jason-stedwell.md`). + +--- + +## Cross-References + +Use Obsidian wiki links freely: `[[note-name]]` or `[[folder/note]]`. The REST API +doesn't resolve them, but Obsidian does when the operator browses the vault.