forked from jason/echo
ver 1.3 and 1.3.1
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# ECHO — Obsidian Local REST API Reference
|
||||
|
||||
Server: `https://echoapi.alwisp.com` (reverse proxy → backend Obsidian Local REST API)
|
||||
Auth header: `Authorization: Bearer $ECHO_KEY` — `export ECHO_KEY=<token>` before running these recipes (`echo.py` resolves it automatically: `ECHO_KEY` → `~/.echo-memory/credentials` → deprecated baked-in fallback). The literal key is never stored in docs.
|
||||
The endpoint has a **valid TLS certificate** — `-k` is not required. Paths address the vault at its **root**.
|
||||
Server: the configured endpoint — the `endpoint` from `~/.claude/echo-memory/config.json` (overridable with `ECHO_BASE`), a reverse proxy → backend Obsidian Local REST API. Examples below use `$ECHO_BASE` (or the neutral placeholder `https://obsidian.example.com`).
|
||||
Auth header: `Authorization: Bearer $ECHO_KEY` — `export ECHO_KEY=<token>` before running these recipes, or let `echo.py` resolve it automatically (per-field, first wins: env override `ECHO_KEY` → the `key` in `~/.claude/echo-memory/config.json`, resolved via the `echo_config` module). The literal key is never stored in docs.
|
||||
A configured endpoint should present a **valid TLS certificate** — `-k` is not required. Paths address the vault at its **root**.
|
||||
|
||||
> **Prefer `scripts/echo.py` over the raw recipes below.** It wraps every verb with auth, status checking, retry, idempotent append, and frontmatter patches, and runs on any platform with a Python interpreter. The `curl` recipes here are the underlying mechanics and a **\*nix/bash fallback** (they use heredocs and `--data-binary`); on Windows, prefer `echo.py` or an equivalent. **If you call `curl` directly, check the HTTP status** — add `-o /dev/null -w "%{http_code}"` and branch on it. A `PATCH` to a non-existent heading returns `400 invalid-target` (errorCode 40080) and the write is *silently lost*; a bare `curl` that ignores status will report success anyway. `GET` returns `404` for a missing file. Treat any `>= 400` as a failed operation, surface it, and do not continue as if it succeeded.
|
||||
|
||||
@@ -29,7 +29,7 @@ For a vault large enough that even the concurrent pass approaches the tool timeo
|
||||
# Read any file by vault path
|
||||
curl -s \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
"https://echoapi.alwisp.com/vault/_agent/context/current-context.md"
|
||||
"$ECHO_BASE/vault/_agent/context/current-context.md"
|
||||
```
|
||||
|
||||
Returns raw file content (text/markdown). On 404, the file does not exist.
|
||||
@@ -38,7 +38,7 @@ Returns raw file content (text/markdown). On 404, the file does not exist.
|
||||
# Read a specific heading's content only
|
||||
curl -s \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
"https://echoapi.alwisp.com/vault/_agent/memory/semantic/operator-preferences.md/heading/Operator"
|
||||
"$ECHO_BASE/vault/_agent/memory/semantic/operator-preferences.md/heading/Operator"
|
||||
```
|
||||
|
||||
Nested headings: separate levels with `::` (URL-encode spaces as `%20`):
|
||||
@@ -54,7 +54,7 @@ Nested headings: separate levels with `::` (URL-encode spaces as `%20`):
|
||||
# List contents of a directory (trailing slash required)
|
||||
curl -s \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
"https://echoapi.alwisp.com/vault/_agent/sessions/"
|
||||
"$ECHO_BASE/vault/_agent/sessions/"
|
||||
```
|
||||
|
||||
Returns JSON: `{ "files": [...], "folders": [...] }`.
|
||||
@@ -74,7 +74,7 @@ curl -s -X POST \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
-H "Content-Type: text/markdown" \
|
||||
--data-binary @/tmp/obs_entry.md \
|
||||
"https://echoapi.alwisp.com/vault/inbox/captures/inbox.md"
|
||||
"$ECHO_BASE/vault/inbox/captures/inbox.md"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -105,7 +105,7 @@ curl -s -X PUT \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
-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"
|
||||
"$ECHO_BASE/vault/_agent/sessions/2026-06-05-1430-my-session.md"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -120,7 +120,7 @@ curl -s -X PUT \
|
||||
|
||||
```bash
|
||||
cat > /tmp/obs_patch.md << 'OBSEOF'
|
||||
Jason prefers concise status updates — lead with the decision.
|
||||
The operator prefers concise status updates — lead with the decision.
|
||||
OBSEOF
|
||||
|
||||
curl -s -X PATCH \
|
||||
@@ -130,7 +130,7 @@ curl -s -X PATCH \
|
||||
-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"
|
||||
"$ECHO_BASE/vault/_agent/memory/semantic/operator-preferences.md"
|
||||
```
|
||||
|
||||
### Discover heading / block / frontmatter targets
|
||||
@@ -141,7 +141,7 @@ When unsure of the exact heading path, GET the note with the document-map Accept
|
||||
curl -s \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
-H "Accept: application/vnd.olrapi.document-map+json" \
|
||||
"https://echoapi.alwisp.com/vault/_agent/memory/semantic/operator-preferences.md"
|
||||
"$ECHO_BASE/vault/_agent/memory/semantic/operator-preferences.md"
|
||||
```
|
||||
|
||||
Returns `{ "headings": [...], "blocks": [...], "frontmatterFields": [...] }`. Copy the heading string verbatim into `Target`.
|
||||
@@ -164,7 +164,7 @@ curl -s -X PATCH \
|
||||
-H "Target: updated" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data '"2026-06-05"' \
|
||||
"https://echoapi.alwisp.com/vault/projects/active/vault-foundation.md"
|
||||
"$ECHO_BASE/vault/projects/active/vault-foundation.md"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -174,7 +174,7 @@ curl -s -X PATCH \
|
||||
```bash
|
||||
curl -s -X POST \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
"https://echoapi.alwisp.com/search/simple/?query=weekly+review"
|
||||
"$ECHO_BASE/search/simple/?query=weekly+review"
|
||||
```
|
||||
|
||||
Returns an array of `{ filename, score, matches: [{ context, match }] }`.
|
||||
@@ -186,7 +186,7 @@ Returns an array of `{ filename, score, matches: [{ context, match }] }`.
|
||||
```bash
|
||||
curl -s -X DELETE \
|
||||
-H "Authorization: Bearer $ECHO_KEY" \
|
||||
"https://echoapi.alwisp.com/vault/inbox/imports/old-note.md"
|
||||
"$ECHO_BASE/vault/inbox/imports/old-note.md"
|
||||
```
|
||||
|
||||
Only on explicit operator request. Deletion is destructive.
|
||||
|
||||
@@ -20,8 +20,8 @@ python3 "$SCRIPTS/migrate.py" --apply # perform the migration (moves/delet
|
||||
`bootstrap.py` writes through `echo.py`, so every step is status-checked and the marker is written last. The scripts are pure Python (only an interpreter required — no bash, no platform-specific `date`), so they run identically on Windows, macOS, and Linux. The manual steps below document what the script does (and serve as a fallback if the script can't run).
|
||||
|
||||
```
|
||||
AUTH="Authorization: Bearer $ECHO_KEY" # export ECHO_KEY first; echo.py resolves it automatically
|
||||
BASE="https://echoapi.alwisp.com"
|
||||
AUTH="Authorization: Bearer $ECHO_KEY" # export ECHO_KEY first; echo.py resolves it automatically via echo_config
|
||||
BASE="$ECHO_BASE" # the endpoint from ~/.claude/echo-memory/config.json (override: ECHO_BASE)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -138,5 +138,5 @@ Run the same steps 1–5, but GET-probe each path first and **only create what i
|
||||
When the marker's `schema_version` is older than the plugin's, apply the migration steps for each intervening version, then PATCH the marker's `schema_version` frontmatter to the new value.
|
||||
|
||||
- **0 → 1** (control-docs-in-plugin): the vault previously carried root control docs (`CLAUDE.md`, `BOOTSTRAP.md`, `STRUCTURE.md`, `index.md`). Back them up outside the vault, DELETE them, PUT the thin `scaffold/README.vault.md` over the old verbose `README.md`, write the `_agent/echo-vault.md` marker, and scrub now-dangling `[[CLAUDE]]`/`[[BOOTSTRAP]]`/`[[STRUCTURE]]`/`[[index]]` links from the `## Related` sections of `operator-preferences.md` and `current-context.md` (leave historical session logs alone). Confirm with the operator before deleting.
|
||||
- **1 → 2** (reviews-folded-into-journal): the `reviews/` tree is retired. (a) For each note under `reviews/weekly/` and `reviews/monthly/`, MOVE it into `journal/weekly/` (rename `YYYY-Www-review.md` → `YYYY-Www.md`) and `journal/monthly/` respectively, preserving the earliest `created:`. (b) Move any `reviews/monthly/YYYY-MM-vault-health.md` to `_agent/health/`. (c) Move `reviews/quarterly|annual/` artifacts to `journal/quarterly|annual/`. (d) Update inbound `[[reviews/...]]` wikilinks in `## Related` sections to the new paths. (e) DELETE the now-empty `reviews/` tree. Confirm with the operator before deleting; leave historical session logs alone. *(Jason's live vault was hand-migrated for the one existing `2026-W24` artifact on 2026-06-10; this step covers any vault bootstrapped under schema 1.)*
|
||||
- **1 → 2** (reviews-folded-into-journal): the `reviews/` tree is retired. (a) For each note under `reviews/weekly/` and `reviews/monthly/`, MOVE it into `journal/weekly/` (rename `YYYY-Www-review.md` → `YYYY-Www.md`) and `journal/monthly/` respectively, preserving the earliest `created:`. (b) Move any `reviews/monthly/YYYY-MM-vault-health.md` to `_agent/health/`. (c) Move `reviews/quarterly|annual/` artifacts to `journal/quarterly|annual/`. (d) Update inbound `[[reviews/...]]` wikilinks in `## Related` sections to the new paths. (e) DELETE the now-empty `reviews/` tree. Confirm with the operator before deleting; leave historical session logs alone. *(This step covers any vault bootstrapped under schema 1.)*
|
||||
- **2 → 3** (entity index + cross-linking): adds the `_agent/index/` folder for the machine-maintained entity registry. `migrate.py` creates the folder; the **data back-fill is `sweep.py`** — after migrating, run `python3 scripts/sweep.py --apply` to build `_agent/index/entities.json` from the notes already in the vault and to symmetrize existing `## Related` cross-links (it adds only the missing reciprocal direction, never invents links). `sweep.py` is idempotent and dry-run by default; re-run it any time `/echo-health` reports index drift.
|
||||
|
||||
@@ -21,7 +21,7 @@ Views of the same truth: `scripts/routing.json` is the **machine-readable canoni
|
||||
| `inbox/imports/<slug>.md` | Raw external material dropped in wholesale (export, paste, dump) | The raw artifact, unedited | Holds un-triaged *bulk*, vs `captures` which holds single lines | PUT |
|
||||
| `inbox/processing-log/YYYY-MM-DD.md` | An inbox item is routed to its real home | One line: `<original> → <destination path>` | Audit trail of moves — never holds memory itself, only the record of routing | POST |
|
||||
|
||||
Captures and imports are temporary by contract. Triage drains them into the homes below and logs the move; the original is left until Jason okays deletion.
|
||||
Captures and imports are temporary by contract. Triage drains them into the homes below and logs the move; the original is left until the operator okays deletion.
|
||||
|
||||
## journal/ — the one append-only time-series stream
|
||||
|
||||
@@ -53,7 +53,7 @@ Lifecycle folders; `status:` frontmatter MUST equal the folder name (the linter
|
||||
|
||||
| Path | Trigger | What lands | Distinct because | Method |
|
||||
|------|---------|------------|------------------|--------|
|
||||
| `areas/<domain>/<slug>.md` | Ongoing domain Jason maintains indefinitely (`<domain>`: business/personal/learning/systems) | Area note | No end state — the one thing that disqualifies it from `projects/` | PUT |
|
||||
| `areas/<domain>/<slug>.md` | Ongoing domain the operator maintains indefinitely (`<domain>`: business/personal/learning/systems) | Area note | No end state — the one thing that disqualifies it from `projects/` | PUT |
|
||||
|
||||
## resources/ — reference material about the world
|
||||
|
||||
@@ -61,7 +61,7 @@ Lifecycle folders; `status:` frontmatter MUST equal the folder name (the linter
|
||||
|------|---------|------------|------------------|--------|
|
||||
| `resources/people/<name>.md` | A fact about a specific person | Person note (kebab-case slug) | Keyed to a person, not a topic or event | PUT / PATCH |
|
||||
| `resources/companies/<slug>.md` | A fact about an organization (client, vendor, partner, employer) | Company note (kebab-case slug) | Keyed to an organization, not an individual (`people/`) or an external source (`references/`) | PUT / PATCH |
|
||||
| `resources/concepts/<slug>.md` | A reusable concept/idea Jason wants on file | Concept note | An idea, vs a `reference` which is an external source | PUT |
|
||||
| `resources/concepts/<slug>.md` | A reusable concept/idea the operator wants on file | Concept note | An idea, vs a `reference` which is an external source | PUT |
|
||||
| `resources/references/<slug>.md` | An external source/link worth keeping | Reference note | Points outward (a source), vs `concepts` (an idea) | PUT |
|
||||
| `resources/meetings/YYYY-MM-DD-<slug>.md` | Notes/recap tied to a specific meeting or call | Meeting note; mirror decisions to `decisions/by-date/`, commitments to the project | Event-anchored to a meeting, vs a project's ongoing thread | PUT |
|
||||
|
||||
@@ -80,15 +80,15 @@ Lifecycle folders; `status:` frontmatter MUST equal the folder name (the linter
|
||||
|------|---------|------------|------------------|--------|
|
||||
| `_agent/echo-vault.md` | Bootstrap / schema migration only | Marker: `schema_version`, bootstrap date | Plugin-owned probe; never hand-edited | GET / PUT |
|
||||
| `_agent/context/current-context.md` | Active scope changes; task focus shifts | `## Scope`, `## Scope History`, priorities | Single *live* scope pointer, vs episodic which is a *past* record | PATCH / PUT |
|
||||
| `_agent/memory/semantic/operator-preferences.md` | A preference/pattern about Jason | Append under `## Observations`; promote to `## Fact / Pattern` when stable | The one curated profile; distinct from ad-hoc semantic notes | PATCH |
|
||||
| `_agent/memory/semantic/<slug>.md` | A durable fact/pattern that isn't a Jason-preference | Semantic note | Timeless fact, vs episodic (time-stamped event) and working (transient) | PUT |
|
||||
| `_agent/memory/semantic/operator-preferences.md` | A preference/pattern about the operator | Append under `## Observations`; promote to `## Fact / Pattern` when stable | The one curated profile; distinct from ad-hoc semantic notes | PATCH |
|
||||
| `_agent/memory/semantic/<slug>.md` | A durable fact/pattern that isn't an operator-preference | Semantic note | Timeless fact, vs episodic (time-stamped event) and working (transient) | PUT |
|
||||
| `_agent/memory/episodic/<slug>.md` | A record of *what happened, when* | Episodic note | Anchored to an event in time; not a standing fact | PUT |
|
||||
| `_agent/memory/working/<slug>.md` | Short-lived state needed only for the current effort | Working note | Explicitly transient/time-boxed; safe to go stale | PUT |
|
||||
| `_agent/sessions/YYYY-MM-DD-HHMM-<slug>.md` | A substantive session ends (decisions/artifacts/commitments) | Session log (see template) | Per-session record; the unit loading Step 4 scans | PUT |
|
||||
| `_agent/health/YYYY-MM-vault-health.md` | First substantive session of a month (Vault Health pass) | Health-audit findings | Agent self-maintenance about vault integrity — NOT Jason's work narrative, so not in `journal/` | PUT |
|
||||
| `_agent/health/YYYY-MM-vault-health.md` | First substantive session of a month (Vault Health pass) | Health-audit findings | Agent self-maintenance about vault integrity — NOT the operator's work narrative, so not in `journal/` | PUT |
|
||||
| `_agent/heartbeat/last-session.md` | End of every session, after the session log is written | One line: `<session-log-path> @ <ISO-timestamp>` | O(1) orientation pointer read first at load (Step 4); overwritten, never grows | PUT |
|
||||
| `_agent/templates/` | Bootstrap only (seeded from plugin masters) | Canonical note templates | Holds templates, not memory; never a runtime routing target | PUT (seed) |
|
||||
| `_agent/skills/active/<slug>.md` | Jason authors/installs a skill and wants it catalogued | Skill capability entry | Catalogs a *capability*, vs `projects/` which tracks the *build effort* | PUT |
|
||||
| `_agent/skills/active/<slug>.md` | The operator authors/installs a skill and wants it catalogued | Skill capability entry | Catalogs a *capability*, vs `projects/` which tracks the *build effort* | PUT |
|
||||
| `_agent/skills/archived/<slug>.md` | A catalogued skill is retired | Skill entry (moved from `active/`) | Terminal state of the skill catalog | PUT |
|
||||
| `_agent/locks/vault.lock` | Advisory multi-writer lock acquire/release | One line: `<owner> @ <ISO-timestamp>` | Concurrency coordination, not memory; managed only by `echo.py lock/unlock` | PUT / DELETE |
|
||||
| `_agent/index/entities.json` | Rebuilt on every `capture` and on `sweep` | Slug→{path, kind, title, aliases, last_seen} registry | Machine-maintained routing/recall index, not a note; the one JSON in the vault | PUT |
|
||||
@@ -120,7 +120,7 @@ Listed so they are recognised as dead and never recreated. Any one of these appe
|
||||
## Routing decision tree (the calls that get mis-made)
|
||||
|
||||
1. **Destination unknown right now?** → `inbox/captures/`. Known? → route directly; never park a known fact in the inbox.
|
||||
2. **Is it about Jason's work over time?** → `journal/` (pick the grain by cadence). **About the vault's mechanical health?** → `_agent/health/`. These two look similar monthly but answer different questions.
|
||||
2. **Is it about the operator's work over time?** → `journal/` (pick the grain by cadence). **About the vault's mechanical health?** → `_agent/health/`. These two look similar monthly but answer different questions.
|
||||
3. **Does the effort have an end state?** → `projects/` (folder = `status:`). **No finish line?** → `areas/`.
|
||||
4. **A fact about the world:** timeless → `semantic/`; a thing that happened → `episodic/`; needed only for now → `working/`. A fact about a *person* → `resources/people/`; a fact about an *organization* → `resources/companies/`.
|
||||
5. **A decision:** always `decisions/by-date/`; mirror into a project only if one already exists.
|
||||
|
||||
@@ -111,14 +111,14 @@ not rewrite frontmatter — the append goes after existing content. To change
|
||||
|
||||
The profile analog. Canonical headings:
|
||||
|
||||
- `## Operator` — who Jason is (one paragraph)
|
||||
- `## Operator` — who the vault owner is (one paragraph)
|
||||
- `## Fact / Pattern` — **promoted, deduped rules.** No date prefix. Timeless.
|
||||
- `## Observations` — **timestamped raw observations.** Date-prefixed lines (`- 2026-06-06: ...`). Default landing zone for new evidence.
|
||||
- `## Evidence` — citations/links supporting the rules
|
||||
- `## Recommendation or Implication` — how the rules should shape behavior
|
||||
- `## Review Notes` — confidence / last review date
|
||||
|
||||
Append observed facts under `## Observations` by default. Promote to `## Fact / Pattern` (dropping the date) once a pattern stabilizes. "The operator" is Jason — he is both operator and architect of this vault.
|
||||
Append observed facts under `## Observations` by default. Promote to `## Fact / Pattern` (dropping the date) once a pattern stabilizes. "The operator" is the vault owner — refer to them generically and in the third person (the display name is the runtime `owner` config value).
|
||||
|
||||
### projects/active/\<slug\>.md
|
||||
|
||||
@@ -173,11 +173,11 @@ ADR-style: Context → Decision → Consequences. If the decision belongs to an
|
||||
|
||||
### people/\<name\>.md
|
||||
|
||||
`type: person`. Use lowercase kebab-case for the slug (e.g. `jason-stedwell.md`).
|
||||
`type: person`. Use lowercase kebab-case for the slug (e.g. `robert-smith.md`).
|
||||
|
||||
### companies/\<slug\>.md
|
||||
|
||||
`type: company`. An organization Jason works with — client, vendor, partner, or employer (e.g. `gillig.md`, `mpm.md`). Distinct from `people/` (individuals, who *belong to* companies) and `references/` (external sources). Lowercase kebab-case slug.
|
||||
`type: company`. An organization the vault owner works with — client, vendor, partner, or employer (e.g. `acme.md`). Distinct from `people/` (individuals, who *belong to* companies) and `references/` (external sources). Lowercase kebab-case slug.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user