# Session Notes

> Use when significant work wraps up — a feature shipped, a bug fixed, a research thread finished, before the user steps away — and also mid-task as a checkpoint before a long run risks context compaction (it updates the same session note + handoff, never spawns a duplicate). Also on 'записать сессию', 'сохрани сессию', 'отложи сессию в память', 'сессию в обсидиан', 'закругляйся с сессией', 'sessio…

- **Type:** Skill
- **Install:** `agentstack add skill-jojoprison-mnemo-session-notes`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [jojoprison](https://agentstack.voostack.com/s/jojoprison)
- **Installs:** 0
- **Category:** [Productivity](https://agentstack.voostack.com/c/productivity)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [jojoprison](https://github.com/jojoprison)
- **Source:** https://github.com/jojoprison/mnemo/tree/main/plugins/mnemo/skills/session-notes

## Install

```sh
agentstack add skill-jojoprison-mnemo-session-notes
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# mnemo:session — Session Notes to Obsidian

Create a human-readable session summary note in Obsidian after significant work.

## Prerequisites & config

Obsidian must be open. Config at `~/.mnemo/config.json` — reads `vault`, `taxonomy.session`, `links_section`, `handoff_note`. Schema in `${CLAUDE_PLUGIN_ROOT}/references/config-schema.md`.

Tool-routing (MCP for writes, CLI for reads/search) in `${CLAUDE_PLUGIN_ROOT}/references/tool-routing.md`. Frontmatter template in `${CLAUDE_PLUGIN_ROOT}/assets/session-template.md`.

## When to Trigger

- After completing a feature / PR / fix
- After significant research session
- Manually via `/mn:session`
- Do NOT trigger for trivial tasks (typo fix, one-liner). Research / exploration / personal-curiosity sessions are **NOT** trivial — they get a note even with zero code. When unsure, create: a session note is cheap, a lost session is not.

## Workflow

### Step 1: Summarize Current Session

**Mid-task checkpoint vs end-of-session note.** If this is a mid-task checkpoint (a long run risks compaction and you're saving progress, not wrapping up) **and** a note for *this same session* already exists — match by the `session_id` you'd write in frontmatter (or the filename derived earlier this session) — **update that note in place** (`mcp__obsidian__str_replace` to refresh the summary and append new progress) plus refresh the handoff (Step 5). Do **not** create a second note for the same session — that fragments the record. Only a genuinely new session or a distinct topic gets a new note.

Analyze the conversation: what was done, key decisions, commits/PRs created, findings.

**Ground the summary in facts — don't rely on conversation memory alone** (a note that claims "shipped X" when git shows no such commit is worse than no note). Before writing "what was done", cross-check against reality: `git log --oneline -15` + `git status --short` for real commits/changes, and — when the script is reachable — the session's actual tool/skill activity:

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/session-scan.py" 2>/dev/null | head -30 \
  || python3 "$(ls -d "$HOME/.claude/plugins/cache/"*"/mnemo/"*"/plugins/mnemo/scripts/session-scan.py" 2>/dev/null | head -1)" 2>/dev/null | head -30
```

Reconcile claimed outcomes with these before persisting — the same grounding `/mn:review` does, applied to the note `/mn:session` writes directly.

Derive a **planned filename**: `{session_prefix}{YYYY-MM-DD} {short descriptive topic}`. Topic should be specific enough to disambiguate from other sessions the same day (include PR number, Linear ticket, branch name, or primary keyword).

**Naming:** the topic must NOT contain `#`, `.`, or `/` — they break wikilinks (`#`→heading anchor) or the CLI (`.`→truncation). Write `PR 387`, not `PR #387`. See `${CLAUDE_PLUGIN_ROOT}/references/tool-routing.md` (naming rules).

### Step 2: Duplicate Check (two-level, parallel)

**Run Level 1 and Level 2 in parallel — single assistant message with two Bash tool uses.** ~185ms total instead of ~370ms sequential.

**Level 1 — exact filename:**

```bash
obsidian read file="{planned-filename}" vault="{vault}" 2>/dev/null | head -5
```

If the read returns content → a note with this EXACT filename already exists. Do NOT silently skip and do NOT auto-overwrite. Offer **append / overwrite / rename**, leading with append/continuation. To append, use `mcp__obsidian__insert` (at the file's last line) or `mcp__obsidian__str_replace` to add a `## part 2` / `## continuation` section — NOT `mcp__obsidian__create` (it only creates new files and will fail or clobber an existing one). Overwrite only if the user is clearly regenerating the same session.

**Level 2 — related same-day sessions (informational only):**

```bash
obsidian search query="{session_prefix}{YYYY-MM-DD}" vault="{vault}"
```

These are NOT duplicates — same day, different topics. **Doing many sessions in one day is normal: each distinct topic gets its OWN note. A Level-2 same-day match is context, NOT a reason to skip creation or to assume the note already exists** — that mistake silently loses sessions. Show the list to the user so they can:
- Decide if this session should be merged into an existing one
- Remember to cross-link related sessions via `## Связи` / `## Links`

Do not block creation on Level 2 matches — they're context, not conflicts.

### Step 3: Create Session Note (MCP — primary; Codex fallback below)

**In Claude, always use `mcp__obsidian__create` for creation.** Never CLI with inline `content=` (shell-injection). In Codex, where the Obsidian MCP isn't available, take the **Codex fallback** at the end of this step instead — the inline-`content=` ban still holds there.

First, read the template (provides the exact structure to follow):

```bash
cat "${CLAUDE_PLUGIN_ROOT}/assets/session-template.md" 2>/dev/null \
  || cat "$(ls -d "$HOME/.claude/plugins/cache/"*"/mnemo/"*"/plugins/mnemo/assets/session-template.md" 2>/dev/null | head -1)" 2>/dev/null \
  || cat "$(ls -d "$HOME/.codex/plugins/cache/"*"/mnemo/"*"/assets/session-template.md" 2>/dev/null | head -1)"
```

Then create the note, filling the template placeholders (`{Session Title}`, `{YYYY-MM-DD}`, `{project}`, etc.) with the current session's context:

```
mcp__obsidian__create(
  path: "{planned-filename}.md",
  file_text: ""
)
```

Where `{session_prefix}` comes from `config.taxonomy.session.prefix`, `{links_section}` from `config.links_section`, and the session id from `{CLAUDE_SESSION_ID}` — or `{CODEX_SESSION_ID}` under Codex — in the environment (empty string if neither is available).

**Why MCP here:** frontmatter and body may contain any markdown — code blocks with backticks, `$(...)` samples, shell snippets. MCP passes `file_text` as a JSON parameter; no shell involved.

**Codex fallback (no Obsidian MCP, no safe CLI create):** the human-readable Obsidian note is a Claude-path deliverable, and there is no shell-safe way to create it with a markdown body from Codex. So instead of failing silently or running an unsafe inline `content=`, write the filled summary to the local fallback memory — `~/.codex/memories/session-{YYYY-MM-DD}-{HHMM}.md` (include the time so two sessions the same day don't overwrite each other) — and tell the user the full vault note needs a Claude/Obsidian session. This degrades gracefully and preserves the content.

### Step 4: Verify MOC Link

**Read MOC (CLI — safe, read-only):**

```bash
obsidian read file="{MOC name}" vault="{vault}"
```

Check if the new session note is listed.

**If missing — append link (MCP preferred):**

```
mcp__obsidian__str_replace(
  path: "{MOC name}.md",
  old_str: "{some stable anchor line near the session list}",
  new_str: "{same anchor}\n- [[{session note name}]]"
)
```

Alternatively, if the MOC has a predictable append point, use `mcp__obsidian__insert` with a line number.

**CLI fallback (only if link text has no backticks/`$()`):** `obsidian append file="{MOC}" content="- [[{name}]]"` — a plain wikilink is safe, but prefer MCP for consistency.

### Step 5: Update Session Handoff

**Read handoff (CLI — safe):**

```bash
obsidian read file="{handoff_note}" vault="{vault}"
```

**Update with MCP `str_replace` — targeted, not blind append:**

- Remove completed pending items from previous sessions
- Add new pending items from current session (if any)
- Update context carry-over section

```
mcp__obsidian__str_replace(
  path: "{handoff_note}.md",
  old_str: "{old section content}",
  new_str: "{updated section content}"
)
```

If handoff note doesn't exist, create it via `mcp__obsidian__create` (same structure as `initial-setup` Step 6).

**Size-guard — keep the handoff thin (run every session):**

The handoff is a LIVE index, not an archive. After updating it, run the archival helper so closed history doesn't accumulate into a multi-MB token bomb (it no-ops when the note is at/under `handoff.maxKB`):

```bash
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/handoff-archive.py" \
  --vault-path "$(bash "${CLAUDE_PLUGIN_ROOT}/scripts/get-vault-path.sh" "{vault}")" \
  --handoff "{handoff_note}" --max-kb {handoff.maxKB:-40} --keep-days {handoff.keepDays:-14} --execute
```

Keeps HOT: entries with an open `- [ ]` + the last `keepDays`. Moves CLOSED older entries verbatim to `{handoff_note} Archive` (cold, not read at session start; a `.bak-` is written for undo). Their durable detail already lives in the linked `Session — …` notes. Mirrors the MEMORY.md size-guard (thin hot index + cold archive + header pointer). If the vault path can't be resolved (Obsidian absent), skip — degrade gracefully.

### Step 6: Orphan Check

```bash
obsidian orphans vault="{vault}"
```

If the newly created note appears in orphans, it means no `## Связи` links or the MOC didn't get updated.

⚠️ **`obsidian orphans` caches & lags writes 1-5s** — a note created moments ago via MCP may show as orphan falsely. If it appears right after creation, wait 2-3s and re-run, or verify authoritatively via `obsidian eval` (`metadataCache.resolvedLinks`/`unresolvedLinks`). See `${CLAUDE_PLUGIN_ROOT}/references/gotchas.md`.

### Step 7: Confirm

Output summary:
- Note name
- MOC updated (yes/no)
- Handoff updated (yes/no)
- Orphan status (clean / flagged)

## Rules

- **MCP for any write with markdown body** — non-negotiable, shell-safety
- **CLI for read/search/index** — faster, indexed, unique functions
- **No inline `obsidian create content="..."` with markdown** — banned: zsh expands backticks / `$(...)` in the body (real incident: nearly ran `make deploy-back`)
- **Two-level duplicate check** — exact-read + same-day-search
- **Include session_id in frontmatter** — disambiguates same-day sessions
- **No session notes for trivial work** — but "trivial" = mechanical one-liners only (typo, single rename). A research / exploration / curiosity session counts as significant even with zero code; default to creating.
- **Branch field optional** — research sessions don't have branches
- **Handoff = thin live index, not an archive** — targeted `str_replace`, not blind append. Named ceiling: when it exceeds `handoff.maxKB` (default 40KB), `scripts/handoff-archive.py` (Step 5) rotates CLOSED blocks older than `handoff.keepDays` into `{handoff_note} Archive` (cold); open `- [ ]` + recent stay hot. Prevents multi-MB token-bomb accumulation.
- **Links section is mandatory** — at least one MOC link, else the note orphans (invisible to graph navigation)
- **Ghost notes generously** — wrap projects, technologies, people in `[[wikilinks]]`

## Gotchas

Common failures (IPC hung, shell injection) in `${CLAUDE_PLUGIN_ROOT}/references/gotchas.md`. Skill-specific rules:

- **MCP `create` signature**: `path` (not `name`), `file_text` (not `content`). Path is relative to vault root, include `.md` extension.
- **Always check duplicate before creating** — prevents clobbering same-day work. Two-level check in Step 2.
- **`str_replace` requires exact match** — copy the anchor text verbatim from read output, including whitespace.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [jojoprison](https://github.com/jojoprison)
- **Source:** [jojoprison/mnemo](https://github.com/jojoprison/mnemo)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-jojoprison-mnemo-session-notes
- Seller: https://agentstack.voostack.com/s/jojoprison
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
