agentsclimarketplace

Session

Skill jayantak/vaultmem/skills/session

Agent memory over an Obsidian vault — bash + ripgrep, no database. A restricted-TOML vault registry, a curated Agent Index, a wikilink graph, and a Projects→Sessions lifecycle. Ships the vaultmem CLI + memory skills as a Claude Code plugin.

Install
npx -y skills add jayantak/vaultmem --skill session

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • 27 days oldThe repository was created 27 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Always-active cross-session working memory. Auto-activates every conversation to offer resuming or starting a session. Also triggers on "start a session", "resume", "park this", "save this", "wrapping up", or multi-session/multi-repo work. Routes work to the correct Obsidian vault, groups sessions under a parent Project (epic), and distills durable insights up into the Project note and MOCs at stopping points.

SKILL.md

18.9 KB, as published. Nobody here has run it

<!-- CANONICAL SOURCE: this repo (jayantak/vaultmem). The dotfiles copy under agents-source/skills/ is synced FROM here — edit this file, not that one. -->

session — cross-session working memory

The hot layer above the durable Obsidian vaults. Sessions are per-thread worklogs that survive /clear. The conversation is disposable; the session file is the state. Pairs with vaultmem (search/registry/router), vault-recall (finding what's already written), and vault-capture (durable capture).

The vault layout, frontmatter fields, status vocabulary, and glyph convention this skill writes are the normative contract in SCHEMA.md (run vaultmem vaults for the live registry). The templates below are the operational shapes; SCHEMA.md is the reference when they disagree.

Always-on activation

The SessionStart hook prints a picker line (vaultmem sessions) ending in an AGENT DIRECTIVE — the trailing prompt that tells the agent what to do with the picker. Its wording is configurable (the registry's directive_file); the default asks the agent to route session handling through this skill. That directive is binding: invoke this skill FIRST, every conversation, before any other skill, tool call, or reply. Memory lookups (vault-recall, vaultmem) do NOT substitute — even when they read the same session file, route through this skill first, then continue. Be concise; never block the user's actual task. If the user skips, do NOT re-ask this conversation.

Trivial one-shot exception: if the first user message is a quick question or a single read-only lookup answerable in one reply, skip this skill silently and just answer. The moment the thread turns into real work (edits, a task, a second substantive turn), invoke the skill then. Never announce the skip.

Vault routing (never hardcode a vault)

Always resolve the vault through the router:

  • Guess: vaultmem which (prints the routed vault id).
  • Confirm with one line: "New session in the <vault> vault — ok? (or say the other)".
  • Registry facts (paths, sessions root, default MOC): vaultmem vaults.

Resolve <vault_path> with vaultmem path <vault> (bare path on stdout — use it directly in scripts as V="$(vaultmem path <vault>)"; vaultmem vaults still prints the full registry).

A session is one task, not one project

A session = one focused unit of work (a feature, a bug, an investigation). The tier above a session is a Project (Projects/<name>.md) — that is where cross-session state accumulates. When the unit of work closes, park the session and open a fresh one under the same Project; do not keep one session open as a stand-in for the whole project (that is what makes _index.md balloon). Tiers: MOC (domain map) → Project (epic, owns repos + cross-session state) → Session (one thread).

List projects with vaultmem projects; see one with vaultmem project <name> (its repos, linear pointer, MOC, and sessions by status).

Create a session

  1. Route + confirm vault (above).
  2. Attach to a Project. Resolve the parent epic:
    • Run vaultmem which for the vault, then vaultmem projects to see candidates. If the cwd is a repo, prefer the Project whose repos: lists it.
    • If exactly one fits, use it. If ambiguous or none, ask one line: "Which project does this belong to — <a> / <b> / new / none?".
    • If a new Project is needed, create Projects/🟢 <name>.md from the Project shape (below) with type: project, status: active, repos:, and aliases: ["<name>"] (glyph on filename + H1; see Status glyphs below).
  3. Pick a short kebab <thread> from the task.
  4. Write <vault_path>/Sessions/<thread>/_index.md from the template, stamping project:, aliases: [<thread>] (so the Project's back-link resolves), + inheriting repos: from the Project, and auto-linking MOCs/repos/people inferred from the opening context.
  5. Register the session on the Project. Append a line to the Project note's ## Sessions index: - [[<thread>]] — <one-line> (status: active) (+ the tracker issue id if the project has one, e.g. (PROJ-1234, active)). This [[<thread>]] only resolves because step 4 gave the session an aliases: [<thread>] entry.

_index.md template

---
thread: <thread>
project: <project-or-blank>
vault: <vault-id>
status: active
aliases: [<thread>]   # REQUIRED — so [[<thread>]] back-links resolve (the file is _index.md, basename ≠ thread)
updated: <YYYY-MM-DD HH:MM>
---
# 🟢 <thread>
**Goal:** <one line>   **Project:** [[<project>]]   **MOCs:** [[<moc>]]   **People:** [[<person>]]
**Repos:** [[<repo>]] (branch <branch>)   **Related:** [[<prior-session>]]

## Bookmark
Last: <what just happened> · Next: <next action> · Open: <unresolved>

## Pinned

## Work log

## Decisions

## Git state
| Repo | Branch / worktree | PR | State |
|---|---|---|---|

Parent Project. project: is the session's parent epic — a Projects/<name>.md note. The picker groups sessions by it, and distill promotes durable bits up to it. Leave it blank only for genuinely one-off work with no home. repos: default- inherit from the Project note; restate them in the header (override only if this session touches a different repo).

The spine is fixed; topical sections are free-form. Bookmark, Pinned, Work log, Decisions, Git state are the required skeleton. Add your own ## <topic> working sections as the task needs them (an analysis in progress, a decomposed plan, a verdict) — real sessions grow these, and distill collapses them. Don't force everything into the work log.

## Pinned = load-bearing constants that must survive distill. The handful of facts you'd be annoyed to re-derive: the stage/env gotcha, the one binding constraint, the non-obvious command, the canonical file to edit, the worktree layout. Keep it short (≤6 lines); it is re-read first on resume. Distill never strips it — correct it in place when a constant changes. (Leave the heading empty until you have a constant worth pinning.)

Link targets must resolve. The [[<moc>]] / [[<repo>]] / [[<person>]] placeholders are not free text — a wikilink works only if a note's exact basename or alias matches. Link MOCs by filename [[MOC - <Topic>]] (or a declared alias like [[Payments]]); for a repo/person, link only if a real note exists (vaultmem mocs / index to check), else use plain text. Never wikilink repo artifacts (ADR IDs, file paths, PR numbers). Full rules: vault-capture § Linking Rules.

The Project note (Projects/<name>.md)

The long-lived epic. Frontmatter:

---
type: project
status: active            # active | parked | done
vault: <vault-id>
repos: [<repo>]           # sessions inherit this
linear: <url>             # optional, if the project tracks tickets (read for context; never a worklog)
moc: "[[MOC - <Topic>]]"  # optional — the domain map above this project
updated: <YYYY-MM-DD>
---

Body spine: ## Sessions (the index, grouped by status) · ## Pinned (cross-session load-bearing constants) · ## Decisions (durable cross-session decisions) · free-form topical sections. Keep it lean the same way _index.md is — distill (below) is what keeps it that way.

Status glyphs (sidebar self-sorts by state)

Every Project and Session carries a status glyph so the Obsidian file-tree sidebar shows state at a glance. The glyph maps 1:1 to the status: field:

statusglyph
active🟢
parked💤
done / shipped
  • Session (_index.md): glyph goes on the H1 only (# 🟢 <thread>). NEVER glyph the session folder name — vaultmem keys sessions by folder basename == the thread: field, so a glyphed folder desyncs them and breaks the picker/groom. Sessions churn fast and their status shows in the picker anyway, so the H1 glyph is enough.
  • Project (Projects/<name>.md): glyph goes on both the filename and the H1 (🟢 <name>.md / # 🟢 <name>) — the filename is what the sidebar sorts. Renaming a flat project file is safe as long as you first add the plain name as a frontmatter aliases: entry (aliases: ["<name>"]) so every existing [[<name>]] link keeps resolving. vaultmem strips the leading glyph when matching a project to its sessions, so sessions keep the plain project: name — never write the glyph into a session's project:.
  • Keep it in sync on every transition. When a status flips (active→parked at park, →done at retire), update the glyph in the same write: the H1 for a session; the H1 and the filename for a project (re-mv + keep the alias). A stale glyph is a lie the sidebar tells — treat it like a stale ## Bookmark.
  • Non-status notes (references, reviews, 2026 Review) take no status glyph.

Indexed mode (auto-persist, no prompts)

Once a session is active, after every few tool calls (or whenever you produce durable content — decisions, findings, plans):

First edit of the conversation: Read _index.md before editing it (a section read via offset/limit satisfies this). The Edit tool rejects writes to never-read files — this is the top recorded tool error in past sessions. The same applies to the Project note and MEMORY.md.

vaultmem cat/bookmark do NOT satisfy that precondition. They are separate processes; the Edit tool only counts a file as read when you read it with the Read tool. Use them for cheap inspection (resume, re-anchoring, checking a section before deciding), and Read the file itself before the first edit.

  1. Append a timestamped bullet to ## Work log. Lead it with a status marker when it helps scanning — DONE / BLOCKED / DECISION / NEXT.
  2. Rewrite ## Bookmark (Last / Next / Open) — always current, never stale.
  3. If git state moved (branch, PR, worktree, merge/push), refresh ## Git state in the same write. The work log is the source of truth for what happened; the table is just the at-a-glance index — never let it contradict the log.
  4. Add or correct a ## Pinned constant if this turn surfaced one (a gotcha, a binding decision, the canonical command/path).
  5. Keep header wikilinks current ([[MOC]], [[repo]], [[person]], [[prior-session]]); bump updated:.

Never keep durable results only in conversation memory. The write is atomic: content + Bookmark (+ Git state if it moved), same turn. Corrections edit the existing section in place rather than appending.

Re-anchor periodically. On a long thread, every ~5–6 substantive turns re-read your own ## Bookmark + ## Pinned (vaultmem bookmark <thread>) before deciding the next move — it keeps you on the task instead of drifting with the conversation, and it surfaces when the file has bloated enough to checkpoint (below).

Resume

Run vaultmem bookmark <thread> — it prints exactly the ## Bookmark + ## Pinned blocks, not the whole file. Those two are the resumable state (what just happened / what's next / the load-bearing constants). Read further sections only if the next action needs them, with vaultmem cat <note> --section '## <Heading>'. The picker's number/name selects the thread; resolve its _index.md path.

If the bookmark already declares completion (Next: none / done / shipped, or the only Open item is a tracker link), don't resume it as if there's work left — flip status: done + ✅ right then and tell the user, rather than reopening a finished unit.

Checkpoint (distill in place, keep going)

Checkpoint is the pressure-release valve that keeps _index.md lean without needing a stopping point. Trigger it on the FIRST of these — don't wait for the user to ask:

  • the user says "checkpoint" / "distill but keep going"; OR
  • _index.md drifts past ~150 lines (the work log is dense — it bloats fast); OR
  • the conversation has run long / context is filling AND you've produced durable content (a decision, a finished analysis, a shipped change) since the last distill — checkpoint at the next turn boundary rather than riding context to the edge. Catching it early beats an emergency compaction that loses the trail.

Then load references/distill.md and run the checkpoint flow: promote durable bits to the vault, collapse the promoted work-log entries to → promoted to [[note]] pointers, leave ## Pinned intact, and keep status: active. No _meta.md event, no clear — the conversation continues. It is the lighter alternative to park when there's no stopping point yet. A one-line confirm is enough; don't interrupt the user's flow to ask permission for an in-place checkpoint.

Park / end + distill

When the user says "park", "wrapping up", "let's clear", OR you detect a natural stopping point (task list drained AND a coherent unit closed AND context growing), load references/distill.md and run the park flow (checkpoint + _meta.md event + clear-safe). Offer: "Natural stopping point — distill durable bits into the vault and clear?"

At park time, decide done vs parked — don't default to parked. If the unit of work is complete (the bookmark says shipped/done/nothing next, or the only remainder is tracked on GitHub/Linear), set status: done + ✅ immediately in the same write, not "parked" as a placeholder for later cleanup. parked/💤 is ONLY for work that will genuinely resume — a real next action you expect to pick up. Sessions are not ticket trackers: an open PR review, a follow-up issue, or a "waiting on review" state tracked on GitHub/Linear does not keep a session alive on its own — leave a pointer to the tracker item and retire the session as done.

Lifecycle & grooming

A session moves active → parked → done, then is archivedarchived is a location, not a status: archived sessions live under Sessions/_archive/<thread>/ and drop out of every listing surface (picker, projects, project). Wikilinks resolve by basename, so [[<thread>]] keeps working after the move. The picker shows active + parked only (resumable work); done is hidden, awaiting groom. On each transition, update the session H1 glyph (🟢→💤→✅) in the same write — and when a Project's own status changes, re-mv its file to the new glyph (💤 when parked, ✅ when shipped) and fix its H1, keeping the plain-name alias.

The SessionStart picker appends a ⚠ … run vaultmem groom nudge when a vault has done sessions (ready to archive), parked sessions untouched past OBSIDIAN_SESSION_COLD_DAYS (default 21), or active sessions untouched past VAULTMEM_STALE_ACTIVE_DAYS (default 7). vaultmem status surfaces the same nudge in short form. When you see it:

  • vaultmem groom mechanically moves every done session into _archive/ and flips its parent Project's ## Sessions line to archived. Safe to run anytime (done was already distilled at park). Scope with -v <vault>. It also archives every done Project into Projects/_archive/ — unless a session outside _archive/ still points at it, in which case groom prints a warning naming the blocking session(s) instead of moving it. Clear those first (archive or reassign the session) and re-run groom.
  • It also lists cold-parked sessions for triage — it never auto-retires them. For each, drive the decision with the user: resume (open it, status back to active), or retire. To retire: confirm it was distilled at its last park (it has a _meta park event / → promoted pointers — cold means untouched since park, so normally yes); if not, run the distill loop (references/distill.md) first; then set status: done so the next groom archives it.
  • It also lists stale-active sessions for triage — an active session that has gone untouched this long likely stalled. Triage it the same way as cold-parked, just from the other side of the fence: either the work actually finished (a bookmark that was never flipped — set status: done per the park-time rule above, so the next groom archives it), or it is genuinely still live (park it if paused but not abandoned, or touch updated: and keep it active). A stale-active entry is usually the symptom this whole rule set exists to catch — the park-time done/parked decision above is what prevents it from recurring.

Project retirement

Projects get the same active → done → archived lifecycle as sessions, with one added precondition: a Project can only retire once its goal is met and every session under it is done or already archived — a Project with a live session is not done, no matter how old it looks (groom enforces this too: it refuses to archive a done Project a live session still points at, and names the blockers). When that holds:

  1. Distill any remaining durable content (open ## Pinned constants, unresolved ## Decisions) up to the vault's MOC tree, same as a session distill — the Project note disappears from active listings after this, so nothing should be left only there.
  2. Set status: done in frontmatter.
  3. Glyph both the filename and the H1 (✅), keeping the plain-name aliases: entry so existing [[<name>]] links keep resolving — same rule as any other Project status transition (see Status glyphs above).
  4. vaultmem groom moves it into Projects/_archive/ on its next run. Archived is a location, not a status, exactly like sessions — wikilinks keep resolving via basename/alias after the move.

Frugality rule

Never read a full file when a section will do — vaultmem cat <note> --section '## <Heading>' extracts one heading block without guessing offsets, and vaultmem bookmark <thread> is the pre-cut Bookmark+Pinned pair (measured on a real vault: 2568 bytes vs 11950 for the whole _index.md, 4.7x cheaper). Both are read-only inspection — they do not satisfy the Edit tool's read-before-write precondition (see Indexed mode). The _index.md IS the state; keep it ~100–150 lines — when it grows past that, checkpoint (see above) to promote and collapse rather than letting it bloat. Search across everything with vaultmem <query> — run 2–3 pattern variants (exact term, synonym, adjacent concept), and treat match lines as leads: read the note (or its section) before answering, never answer from search output alone. Follow [[links]] between notes with vaultmem links / backlinks (see vault-recall § Researching by following wikilinks) instead of reading whole folders.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.