Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{
"name": "mnemo",
"description": "7 memory skills for Claude Code and Codex via Obsidian — vault audit, knowledge search, bounded cross-runtime recall, connection discovery, session handoff, memory routing, review orchestration, and setup",
"version": "1.2.9",
"version": "1.2.10",
"source": "./plugins/mnemo",
"category": "productivity",
"keywords": [
Expand Down
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [1.2.10] - 2026-07-23

### Added

- **`review --full` can auto-apply connect's links (opt-in, `review.full.autoConnect`, default false).** When the flag is on, the `--full` chain's `connect` step writes its suggested links without a per-suggestion prompt (connect Step 5.5) and reports every write — so closing a session no longer stops on "apply these? (y/N)". The user typing `--full` **and** setting the flag is the consent, mirroring `review.lint.autoStampReviewed`: it's the second, default-off exception to the non-destructive principle (`docs/design-decisions.md`). A standalone `/mn:connect` / `$mnemo:connect` never auto-applies regardless of the flag, and the default install is byte-for-byte unchanged (connect stays suggest-only). Dual-runtime by construction — the behavior lives in the shared `SKILL.md` prose that both Claude Code and Codex read; the Claude-only `model:` frontmatter is untouched. `test-skill-write-contracts.py` pins the new invariants (flag default false, standalone never auto-applies, verify pass still never links).

## [1.2.9] - 2026-07-21

### Changed
Expand Down Expand Up @@ -933,7 +939,8 @@ Frontmatter now includes `session_id: {CLAUDE_SESSION_ID}` — disambiguates sam
- `config.example.json`
- MIT License

[Unreleased]: https://github.com/jojoprison/mnemo/compare/v1.2.9...HEAD
[Unreleased]: https://github.com/jojoprison/mnemo/compare/v1.2.10...HEAD
[1.2.10]: https://github.com/jojoprison/mnemo/compare/v1.2.9...v1.2.10
[1.2.9]: https://github.com/jojoprison/mnemo/compare/v1.2.8...v1.2.9
[1.2.8]: https://github.com/jojoprison/mnemo/compare/v1.2.7...v1.2.8
[1.2.7]: https://github.com/jojoprison/mnemo/compare/v1.2.6...v1.2.7
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ You work → mnemo remembers → Your vault grows → You find things later
| **session** | `/mn:session` | Session summary note + cross-session handoff for the next session |
| **review** | `/mn:review` | End-of-session orchestrator — audits the session, recommends save + session + the rest, asks before running anything |
| **ask** | `/mn:ask` | Search the vault + active runtime memory (+ optional verified counterpart memory), synthesize a cited answer, date sources, and ground current-state answers in git history |
| **connect** | `/mn:connect` | Discover hidden connections between notes — suggests, never auto-applies |
| **connect** | `/mn:connect` | Discover hidden connections between notes — suggests; standalone never auto-applies (`review --full` can, opt-in `review.full.autoConnect`) |
| **health** | `/mn:health` | Vault audit: orphans, broken links, type-aware review candidates (+ optional LLM lint), growth stats |
| **setup** | `/mn:setup` | Interactive onboarding — vault, taxonomy, language, optional cross-runtime recall |

Expand Down
6 changes: 3 additions & 3 deletions docs/connect.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Overview

Analyzes a note and discovers connections to other notes you'd never think of. Shows suggestions with explanations — you confirm before applying.
Analyzes a note and discovers connections to other notes you'd never think of. Shows suggestions with explanations — you confirm before applying. (One exception: `/mn:review --full` with `review.full.autoConnect` turned on applies them for you — the standalone command never does.)

## Usage

Expand All @@ -18,7 +18,7 @@ Analyzes a note and discovers connections to other notes you'd never think of. S
3. Searches vault for each concept
4. Compares found notes vs already linked notes
5. Suggests new connections with reasoning
6. Applies only what you approve
6. Applies only what you approve (the `review --full` chain can auto-apply when `review.full.autoConnect` is on)

## Example Output

Expand All @@ -41,7 +41,7 @@ Apply these? (y/N, or pick numbers: 1,2)
## Important Notes

- **Max 5-7 suggestions** — won't overwhelm you
- **Never auto-applies** — you confirm each connection
- **Standalone never auto-applies** — you confirm each connection; only `review --full` with `review.full.autoConnect` (default off) applies them for you
- **Ignores generic matches** — "both mention Claude" is not a meaningful connection
- **Won't suggest orphans** — they need their own fixing first

Expand Down
2 changes: 1 addition & 1 deletion docs/design-decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Why mnemo is shaped the way it is, and which tempting features it deliberately d
Three consequences:

- **Human-authored** — the atomic claims are written by you (or by the agent, in-conversation, on your behalf). mnemo does not generate vault content from a document corpus. The agent "was there" when the insight happened and pins the discrete claim. Contrast: an LLM that ingests a folder of PDFs and auto-explodes them into pages.
- **Non-destructive** — skills report and suggest; they never auto-delete, overwrite, or author content. The single, opt-in exception is the `reviewed:` snooze stamp (`review.lint.autoStampReviewed`), and only on notes the lint judged still-valid.
- **Non-destructive** — skills report and suggest; they never auto-delete, overwrite, or author content. There are two **opt-in, default-off** exceptions, each a config flag the user must flip: the `reviewed:` snooze stamp (`review.lint.autoStampReviewed`), written only on notes the lint judged still-valid; and `review --full` auto-linking (`review.full.autoConnect`), where the chain's `connect` step writes its suggested links without a per-suggestion prompt. Both stay suggest-only until the user opts in — a standalone `/mn:connect` never auto-applies, and the default install writes neither.
- **In-agent** — mnemo runs inside Claude Code / Codex. The harness already injects the `memory/MEMORY.md` index and the live conversation as hot context, so mnemo never has to re-create the agent's working memory.

## Cross-runtime recall is federation, not synchronization (v1.2.3)
Expand Down
2 changes: 1 addition & 1 deletion plugins/mnemo/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mn",
"displayName": "mnemo",
"version": "1.2.9",
"version": "1.2.10",
"description": "Persistent memory layer for Claude Code and Codex via Obsidian — 7 skills for vault health, knowledge search, verified cross-runtime project recall, connection discovery, session continuity, memory routing, skill-aware session review, and interactive setup. Bundled JSON-stdin atomic vault writer, argv-safe indexed reads, dual-runtime helpers, hybrid model routing, parallel CLI calls, SessionStart prewarm, incremental JSONL parsing, shared references, and semantic taxonomy roles.",
"author": {
"name": "jojoprison",
Expand Down
2 changes: 1 addition & 1 deletion plugins/mnemo/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mnemo",
"version": "1.2.9",
"version": "1.2.10",
"description": "Persistent memory layer for Codex and Claude Code via Obsidian: vault health, knowledge search, verified cross-runtime project recall, link discovery, session notes, memory routing, review orchestration, and setup.",
"author": {
"name": "jojoprison",
Expand Down
4 changes: 3 additions & 1 deletion plugins/mnemo/references/config-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Path: `~/.mnemo/config.json`. Created by `setup` skill on first install. All oth
},

"review": {
"full": { "autoConnect": false },
"staleDays": {
"default": 30,
"atom": 60,
Expand Down Expand Up @@ -98,7 +99,7 @@ Path: `~/.mnemo/config.json`. Created by `setup` skill on first install. All oth
}
```

The whole `review` section is **optional** — if absent, `health` falls back to a uniform 30-day staleness threshold (the legacy behavior) and the content-lint pass stays off. Add it only when you want type-aware cadence or the LLM lint.
The whole `review` section is **optional** — if absent, `health` falls back to a uniform 30-day staleness threshold (the legacy behavior), the content-lint pass stays off, and `review.full.autoConnect` defaults to **false** so `review --full`'s connect step stays suggest-only. Add it only when you want type-aware cadence, the LLM lint, or one-command auto-linking.

The `recall` section is optional and ships off. `recall.codeGraph` (default `null`) is a seam for `/mn:ask` Step 4c: set it to a code-knowledge-graph backend you have installed — `"graphify"` (reads its `graph.json` / `GRAPH_REPORT.md`) or an MCP server (`"sourcegraph"` / `"ast-grep"` / `"tree-sitter-analyzer"`) — and recall gains structural "what's where" context. With no backend it's a no-op. (The project-repo **git-log** grounding in Step 4c runs regardless whenever `/mn:ask` is invoked inside a git project — it needs no config.)

Expand Down Expand Up @@ -133,6 +134,7 @@ The `recall` section is optional and ships off. `recall.codeGraph` (default `nul
| `memory.indexWarnKB` | Early loaded-content byte warning for Claude `MEMORY.md`. Current loader limits are 200 lines or 25,000 bytes after stripping leading YAML frontmatter and block-level HTML comments; health always reports either hard-limit breach independently of this threshold. Default **22** | health |
| `handoff.maxKB` | Size ceiling (KB) before `vault-write.py archive-handoff` rotates CLOSED old blocks into `<handoff_note> Archive` (cold). The handoff is a live index, not a store; un-rotated it becomes a token bomb read every session. Default **40** | session |
| `handoff.keepDays` | Blocks newer than this stay hot regardless of status; older **and** closed (no open `- [ ]`) move to the archive. Default **14** | session |
| `review.full.autoConnect` | In `review --full`, the internal `connect` step **applies** its link suggestions without a per-suggestion prompt instead of only showing them (connect Step 5.5, review Step 9A). The user typing `--full` **and** setting this flag is the consent — mirroring `review.lint.autoStampReviewed`, the opt-in that lets a skill write. A standalone `/mn:connect` is unaffected and never auto-applies. Default **false** (non-destructive default; connect stays suggest-only for everyone who doesn't opt in) | review, connect |
| `review.staleDays.default` | Days before a note becomes a review candidate when its type has no specific entry. Default **30**. (`review.staleDays` may also be a bare integer — a single uniform threshold for every type.) | health |
| `review.staleDays.{type}` | Per-type staleness cadence (key = a taxonomy `type` you actually use: `atom`/`molecule`/`source`/`session`/`moc`). A fast-moving fact ages quicker than an architectural decision | health |
| `review.lint.enabled` | Run the content-lint deep pass (LLM re-reads candidates, emits still-valid/update-needed/contradicts verdicts). Default **false** — it reads note bodies and costs tokens | health |
Expand Down
2 changes: 1 addition & 1 deletion plugins/mnemo/references/depth-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Step 0b, applied across the whole close-out.
| A cross-note synthesis (≥2 notes) | `save` → insight role | non-trivial insight, `cites:` the sources |
| An actionable never-X / always-Y rule tied to code | `save` Step 3.5 → `.claude/rules/` | path-scoped auto-inject, not recall |
| The session narrative (what happened, the arc, decisions-in-context) | `session` | one note + handoff, never a duplicate of today's |
| Connections between the new notes (incl. non-obvious) | `connect` | suggest-only links, no orphans |
| Connections between the new notes (incl. non-obvious) | `connect` | suggest-only links (auto-applied only under `review.full.autoConnect`), no orphans |
| A future-useful thread not finished | `session` handoff `- [ ]` pending | thin live index |

The business-logic / pains / mental-model layer is the crown material — it routes to `save`'s typed
Expand Down
10 changes: 8 additions & 2 deletions plugins/mnemo/skills/connect/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,15 @@ Apply these? (y/N, or pick numbers: 1,3)

Links are applied **with their one-line context** (the «Why»), not as bare `[[wikilinks]]` (Luhmann: state why you linked).

### Step 5.5: Auto-apply mode (the `review --full` chain only)

**Fires only when the invoking payload carries an explicit auto-apply directive** — the `review --full` chain sends one after reading `review.full.autoConnect: true` from `~/.mnemo/config.json` (see `<mnemo-root>/references/config-schema.md`). A standalone `/mn:connect` / `$mnemo:connect` — anything a user typed, or any invocation without that directive — **never** auto-applies; it renders Step 5 and waits. This is the same shape as the other `--full` writes: the user opting into `--full` *and* flipping the config flag is the consent, exactly as `review.lint.autoStampReviewed` gates the one write `health` makes.

When the directive is present, skip the Step 5 confirmation prompt and go straight to **Step 6** for the ranked, deduplicated, backlink-excluded suggestions this run produced — then report every link written (tension links included and clearly marked) so nothing is applied invisibly. The suggestions are still the same high-quality set (max 5-7, meaningful-not-generic, orphans excluded); auto-apply removes the keystroke, not the quality bar. If the config flag is off, or the directive is absent, this step does not run — fall through to Step 5.

### Step 6: Apply on Confirmation

If user confirms:
If the user confirms (Step 5) — or auto-apply mode is active (Step 5.5):
1. Read the target again, choose one unique stable anchor in `{links_section}`, then add new links **with a one-line context** (Luhmann — state why connected): `- [[Name]] — {why, ≤10 words}`, not a bare `[[Name]]`. Tension links get the marker: `- [[Name]] #contradiction — {what disagrees}`.
2. Apply the confirmed block through the bundled writer:

Expand Down Expand Up @@ -133,4 +139,4 @@ Common failures in `<mnemo-root>/references/gotchas.md`. Tool-routing rationale
- **Don't suggest links to orphan notes** — they need their own fixing first (run `/mn:health` if interested).
- **Ghost notes are normal** — `[[Technology]]` pointing to a non-existent note enables entity discovery. Not "unresolved" in the bad sense.
- **Avoid generic connections** — "both mention Claude" is noise. A connection is meaningful if it shares a concept, approach, or unresolved question.
- **Never auto-apply** — always ask the user before writing new wikilinks.
- **Never auto-apply on the standalone path** — a user-typed `/mn:connect` / `$mnemo:connect`, or any invocation without the explicit `review --full` auto-apply directive, always asks before writing new wikilinks (Step 5). The **only** exception is Step 5.5: the `review --full` chain with `review.full.autoConnect: true` — there the user opted into `--full` and flipped the config flag, so the links apply without a per-suggestion `y` and every write is reported. Default config keeps the flag off, so the default connect behavior is unchanged.
5 changes: 3 additions & 2 deletions plugins/mnemo/skills/review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ Codex: read `<mnemo-root>/skills/save/SKILL.md` completely, then follow it with
1. **save** — persist the Step 7 candidates (decisions, findings, and any `principle` / `pain` / `stance` material routed by `depth-contract.md` into typed atoms, never the narrative). Actionable rules still route to `.claude/rules/` (save Step 3.5).
2. **session** — write the single narrative note + handoff (never a duplicate of today's).
3. **[focus / extras]** — apply any focus text left in the invocation after `--full` (Step 0 item 6) here, between session and connect.
4. **connect** — discover genuine links on the notes just parked, including non-obvious ones; suggest-only, never auto-applied.
4. **connect** — discover genuine links on the notes just parked, including non-obvious ones. Read `review.full.autoConnect` from `~/.mnemo/config.json` (default **false**): when **true**, delegate to `connect` with an explicit **auto-apply directive** so it writes the links without a per-suggestion prompt (connect Step 5.5) and reports every write; when **false/absent**, delegate **suggest-only** (connect renders its Step 5 offer and stops — the v1.2.8 default, unchanged). The user typing `--full` *and* setting the flag is the consent; a standalone `/mn:connect` never auto-applies regardless of the flag. Discover genuinely — including non-obvious links — never manufacture links to hit a count.

`health` is **excluded** — heavy and manual; surface it as a recommendation in the report, don't run it. Recommendations flow **down** from this audit into the chain; `connect` never reaches back up to re-drive the list.

Expand Down Expand Up @@ -284,5 +284,6 @@ All-green → "🏛 palace in order". Otherwise emit the residual-gap list (advi
- **`--full` = consent, not autorun** — the explicit flag chains save → session → connect without per-skill `y`; plain `/mn:review` still never auto-runs (v0.16.0 intact). Only the user typing `--full` triggers the chain
- **`health` stays manual** — never in the `--full` chain (heavy); recommend it, don't run it
- **Verify grounds externally, never self-grades** — every Step 9 check cites git / orphans / `session-scan.py` / the Step-0 snapshot, never the agent's own assertion
- **Verify never links** — an orphan is delegated to `connect`; Step 9 adds no link itself, and link *count* is never a green signal
- **Verify never links** — an orphan is delegated to `connect`; the Step 9 **verify** pass adds no link itself, and link *count* is never a green signal
- **`connect` auto-applies only under the opt-in flag** — the Step 9A **chain** step (distinct from the verify pass above) writes links without a prompt only when `review.full.autoConnect: true` (default false); otherwise it stays suggest-only, and a standalone `/mn:connect` never auto-applies regardless of the flag
- **memory-not-CI** — `--full` REPORTS a missing prod/e2e verification as a gap; it never runs tests, hits prod, or fires a trigger
17 changes: 17 additions & 0 deletions scripts/test-skill-write-contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,23 @@ def test_verify_is_grounded_binary_and_never_links(self) -> None:
self.assertIn("never add a link here", review)
self.assertIn("memory-not-CI", review) # prod/e2e reported, never run

def test_auto_connect_is_opt_in_and_standalone_never_auto_applies(self) -> None:
review = skill("review")
connect = skill("connect")
schema = (REPO_ROOT / "plugins/mnemo/references/config-schema.md").read_text()
# v1.2.10 flag: documented in review, connect, and the schema, and default-off.
self.assertIn("review.full.autoConnect", review)
self.assertIn("review.full.autoConnect", connect)
self.assertIn("review.full.autoConnect", schema)
self.assertIn('"full": { "autoConnect": false }', schema) # default off in the sample schema
# connect gained an auto-apply mode gated on the --full directive...
self.assertIn("Auto-apply mode", connect)
self.assertIn("auto-apply directive", connect)
# ...but the standalone path still never auto-applies.
self.assertIn("Never auto-apply on the standalone path", connect)
# review's chain step reads the flag; the verify pass still never links.
self.assertIn("auto-applies only under the opt-in flag", review)

def test_depth_contract_routes_mental_model_to_save_not_narrative(self) -> None:
contract = (REPO_ROOT / "plugins/mnemo/references/depth-contract.md").read_text()
self.assertIn("one kind → one home", contract)
Expand Down
Loading