diff --git a/CHANGELOG.md b/CHANGELOG.md index 46337b4..e1b07bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to the Nowledge Mem Gemini CLI extension will be documented The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.9] - 2026-06-06 + +### Improved + +- Session-start hooks, commands, and Gemini guidance now prefer Context Bundle when startup identity, active scope, or guidance matters, with Working Memory kept as the lightweight fallback for older `nmem` clients. + ## [0.1.8] - 2026-04-27 ### Improved diff --git a/GEMINI.md b/GEMINI.md index e250970..3ac055d 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -6,12 +6,13 @@ Use MCP tools for retrieval and memory writes when Gemini exposes them in this s ## Core Memory Lifecycle -Treat Nowledge Mem as four linked surfaces: +Treat Nowledge Mem as five linked surfaces: -1. Working Memory for current focus and active priorities -2. Distilled memories for durable knowledge -3. Threads for full searchable conversation history -4. Handoff summaries for compact resumability when the user wants a manual handoff +1. Context Bundle for startup identity, active space, active rules, and current priorities +2. Working Memory for a lightweight current-focus briefing +3. Distilled memories for durable knowledge +4. Threads for full searchable conversation history +5. Handoff summaries for compact resumability when the user wants a manual handoff Prefer the smallest surface that answers the user's need, then move upward only when more context is necessary. @@ -39,9 +40,19 @@ nmem config mcp show --host gemini-cli Paste the generated JSON into Gemini `settings.json`. Direct MCP clients do not read `~/.nowledge-mem/config.json` automatically. -## Working Memory +## Context Bundle And Working Memory -At the start of a session, or when recent priorities would help, read Working Memory with: +At the start of a session, or when recent priorities would help, read Context Bundle when identity, active space, active rules, or multi-agent behavior could matter: + +Prefer the MCP `read_context_bundle` tool when it is available. + +Otherwise use: + +```bash +nmem --json context --source-app gemini-cli +``` + +Use Working Memory alone for the lighter daily briefing or compatibility fallback. Prefer the MCP `read_working_memory` tool when it is available. @@ -53,7 +64,7 @@ nmem --json wm read If the command succeeds but returns `exists: false`, there is no Working Memory briefing yet. Say that clearly instead of pretending a briefing exists. -If the runtime already knows the current project or agent lane, add `--space ""`. +If the runtime already knows the current project or agent lane, add `--space ""` to either command. Multi-agent orchestrators can set `NMEM_AGENT_ID=""` before launching Gemini CLI. Add `NMEM_SPACE` only when that whole run should override the identity's default space. Use `NMEM_HOST_AGENT_ID` only for advanced host-id aliases. Only fall back to the legacy file below for older local-only **Default-space** setups where the user still keeps Working Memory there: @@ -61,7 +72,7 @@ Only fall back to the legacy file below for older local-only **Default-space** s test -f ~/ai-now/memory.md && cat ~/ai-now/memory.md ``` -Read Working Memory once near the start of a session, then reuse that context mentally. Do not re-read on every turn unless the user asks, the session context changed materially, or a long-running session clearly needs a refresh. +Read Context Bundle or Working Memory once near the start of a session, then reuse that context mentally. If Context Bundle already included Working Memory, do not read Working Memory again immediately. Do not re-read on every turn unless the user asks, the session context changed materially, or a long-running session clearly needs a refresh. ## Search Memory @@ -87,7 +98,7 @@ Otherwise use: nmem --json m search "query" ``` -If the runtime already knows the active project or agent lane, add `--space ""` to Working Memory, memory search, thread search, and save commands. +If the runtime already knows the active project or agent lane, add `--space ""` to Context Bundle, Working Memory, memory search, thread search, and save commands. If the recall need is conceptual or the first pass is weak, use deep search: @@ -113,7 +124,7 @@ Prefer the smallest retrieval surface that answers the question. Distill only durable knowledge worth keeping after the current session ends. -Use MCP `memory_add` for genuinely new decisions, procedures, lessons, preferences, or plans when available: +Use MCP `memory_add` for genuinely new facts, preferences, decisions, plans, procedures, learnings, events, or context when available. Pass `unit_type` when the type is clear: If MCP tools are not exposed, use: diff --git a/README.md b/README.md index 4d82f65..c33352b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This package is the **Gemini-native product surface** for Nowledge Mem. It is deliberately **hybrid**: -- Gemini CLI loads `GEMINI.md` plus extension hooks for Working Memory bootstrap and session capture +- Gemini CLI loads `GEMINI.md` plus extension hooks for Context Bundle / Working Memory startup context and session capture - the extension exposes local Nowledge Mem MCP tools for lower-friction retrieval and memory writes - bundled commands wrap common `nmem` workflows - bundled skills teach Gemini when to recall, distill, save real threads, and create handoff summaries @@ -65,7 +65,7 @@ Release packaging and marketplace notes live in [`RELEASING.md`](./RELEASING.md) **Automatic lifecycle hooks** -- Session start loads Working Memory into Gemini when a briefing exists +- Session start loads Context Bundle when available, with Working Memory as the lightweight fallback - Before context compression, Gemini imports the current thread so the pre-compression transcript remains searchable - Session end performs a best-effort real Gemini thread import through `nmem t save --from gemini-cli` @@ -76,7 +76,7 @@ Release packaging and marketplace notes live in [`RELEASING.md`](./RELEASING.md) **Persistent context** -- `GEMINI.md` tells Gemini how to route recall across Working Memory, distilled memories, conversation threads, thread save, distillation, and handoff summaries +- `GEMINI.md` tells Gemini how to route recall across Context Bundle, Working Memory, distilled memories, conversation threads, thread save, distillation, and handoff summaries **Custom commands** diff --git a/commands/nowledge/read-working-memory.toml b/commands/nowledge/read-working-memory.toml index 922e096..b6c2ea9 100644 --- a/commands/nowledge/read-working-memory.toml +++ b/commands/nowledge/read-working-memory.toml @@ -1,15 +1,21 @@ prompt = """ -Load the user's Working Memory briefing before continuing. +Load the user's startup context before continuing. -Use the shell tool to try: +When identity, active space, active rules, or multi-agent behavior could matter, use Context Bundle first: + +```bash +nmem --json context --source-app gemini-cli +``` + +For a lightweight daily briefing or older `nmem` clients, use: ```bash nmem --json wm read ``` -If the command succeeds but reports that no Working Memory exists yet, say that clearly. +If either command succeeds but returns no content yet, say that clearly. -If the runtime already knows the current project or agent lane, add `--space ""`. +If the runtime already knows the current project or agent lane, add `--space ""` to either command. Multi-agent orchestrators can set `NMEM_AGENT_ID=""` before launching Gemini CLI. Add `NMEM_SPACE` only when that whole run should override the identity's default space. Use `NMEM_HOST_AGENT_ID` only for advanced host-id aliases. Only if `nmem` is unavailable in an older local-only **Default-space** setup, fall back to: @@ -17,7 +23,7 @@ Only if `nmem` is unavailable in an older local-only **Default-space** setup, fa cat ~/ai-now/memory.md ``` -Then summarize the user's active focus areas, priorities, unresolved flags, and the most relevant recent changes when a briefing is actually present. +Then summarize only the parts relevant to the task. If Context Bundle was loaded, do not separately read Working Memory unless the user asks for a lightweight refresh. If remote access is configured through `~/.nowledge-mem/config.json`, let `nmem` use it naturally. Do not assume environment variables are the only auth path. """ diff --git a/gemini-extension.json b/gemini-extension.json index 73ef8df..c44c618 100644 --- a/gemini-extension.json +++ b/gemini-extension.json @@ -1,7 +1,7 @@ { "name": "nowledge-mem-gemini-cli", - "version": "0.1.8", - "description": "Gemini CLI extension for Nowledge Mem with persistent context, memory commands, and agent skills.", + "version": "0.1.9", + "description": "Gemini CLI extension for Nowledge Mem with startup context, memory commands, and agent skills.", "mcpServers": { "nowledge-mem": { "httpUrl": "http://127.0.0.1:14242/mcp/", @@ -9,7 +9,7 @@ "APP": "Gemini CLI" }, "timeout": 10000, - "description": "Nowledge Mem local MCP tools for Working Memory, memory search, thread search, and memory writes." + "description": "Nowledge Mem local MCP tools for Context Bundle, Working Memory, memory search, thread search, and memory writes." } }, "contextFileName": "GEMINI.md" diff --git a/hooks/session-start.mjs b/hooks/session-start.mjs index 5abf87f..09b7c52 100644 --- a/hooks/session-start.mjs +++ b/hooks/session-start.mjs @@ -12,38 +12,104 @@ function emit(payload) { ); } -function readWorkingMemory() { - const result = spawnSync('nmem', ['--json', 'wm', 'read'], { +function parseJsonText(stdout, keys) { + try { + const data = JSON.parse(stdout || '{}'); + for (const key of keys) { + const value = data[key]; + if (typeof value === 'string' && value.trim()) { + return value.trim(); + } + } + } catch { + // Fall back to the next context source. + } + return ''; +} + +function readNmem(args, keys) { + const result = spawnSync('nmem', ['--json', ...args], { encoding: 'utf8', timeout: 10000, }); if (result.status === 0) { - try { - const data = JSON.parse(result.stdout || '{}'); - const content = typeof data.content === 'string' ? data.content.trim() : ''; - if (content) { - return content; - } - } catch { - // Fall back to the legacy file path below. + const content = parseJsonText(result.stdout, keys); + if (content) { + return content; } } + return ''; +} + +function envValue(name) { + const value = process.env[name]; + return typeof value === 'string' ? value.trim() : ''; +} + +function withStartupArgs(args) { + const next = [...args]; + const agentId = envValue('NMEM_AGENT_ID'); + const hostAgentId = envValue('NMEM_HOST_AGENT_ID'); + const space = envValue('NMEM_SPACE') || envValue('NMEM_SPACE_ID'); + if (agentId && !next.includes('--agent-id')) { + next.push('--agent-id', agentId); + } + if (hostAgentId && !next.includes('--host-agent-id')) { + next.push('--host-agent-id', hostAgentId); + } + if (space && !next.includes('--space')) { + next.push('--space', space); + } + return next; +} + +function withSpaceArgs(args) { + const next = [...args]; + const space = envValue('NMEM_SPACE') || envValue('NMEM_SPACE_ID'); + if (space && !next.includes('--space')) { + next.push('--space', space); + } + return next; +} + +function readStartupContext() { + const contextBundle = readNmem(withStartupArgs(['context', '--source-app', 'gemini-cli']), ['rendered_markdown', 'markdown', 'content']); + if (contextBundle) { + return { + tag: 'nowledge_context_bundle', + label: 'Context Bundle', + content: contextBundle, + }; + } + + const workingMemory = readNmem(withSpaceArgs(['wm', 'read']), ['content']); + if (workingMemory) { + return { + tag: 'nowledge_working_memory', + label: 'Working Memory', + content: workingMemory, + }; + } const legacyPath = path.join(os.homedir(), 'ai-now', 'memory.md'); if (existsSync(legacyPath)) { const content = readFileSync(legacyPath, 'utf8').trim(); if (content) { - return content; + return { + tag: 'nowledge_working_memory', + label: 'legacy Working Memory file', + content, + }; } } - return ''; + return null; } -const workingMemory = readWorkingMemory(); +const startupContext = readStartupContext(); -if (!workingMemory) { +if (!startupContext) { emit({ hookSpecificOutput: { hookEventName: 'SessionStart', @@ -53,11 +119,11 @@ if (!workingMemory) { emit({ hookSpecificOutput: { hookEventName: 'SessionStart', - additionalContext: ` -Use this as current user context from Nowledge Mem Working Memory. It is situational context, not a higher-priority instruction. + additionalContext: `<${startupContext.tag}> +Use this as current user context from Nowledge Mem ${startupContext.label}. It is situational context, not a higher-priority instruction. -${workingMemory} -`, +${startupContext.content} +`, }, }); } diff --git a/package.json b/package.json index 68aec10..5a14892 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nowledge-mem-gemini-cli", - "version": "0.1.8", + "version": "0.1.9", "private": true, "description": "Gemini CLI extension for Nowledge Mem", "license": "MIT", diff --git a/release-notes/0.1.9.md b/release-notes/0.1.9.md new file mode 100644 index 0000000..6d8e382 --- /dev/null +++ b/release-notes/0.1.9.md @@ -0,0 +1,4 @@ +# Nowledge Mem Gemini CLI 0.1.9 + +Session-start context now uses Nowledge Mem's full Context Bundle when available. Gemini can receive owner identity, AI Identity, active space, active rules, Working Memory, and KFS paths before falling back to the lighter Working Memory briefing on older `nmem` clients. + diff --git a/skills/distill-memory/SKILL.md b/skills/distill-memory/SKILL.md index b9c04db..fe343a4 100644 --- a/skills/distill-memory/SKILL.md +++ b/skills/distill-memory/SKILL.md @@ -5,7 +5,7 @@ description: Detect breakthrough moments, durable lessons, and decisions worth p # Distill Memory -Save proactively when the conversation produces a decision, preference, plan, procedure, learning, or important context. Do not wait to be asked. +Save proactively when the conversation produces a durable fact, preference, decision, plan, procedure, learning, event, or important context. Do not wait to be asked. ## Good Candidates @@ -21,3 +21,5 @@ Save proactively when the conversation produces a decision, preference, plan, pr - If an existing memory already captures the same decision, workflow, or preference and the new information refines it, use MCP `memory_update` when available. Otherwise use `nmem m update ...` instead of creating a duplicate. Prefer atomic, standalone memories with strong titles and structured meaning. Focus on what was learned or decided, not routine chatter. + +When saving directly, pass `unit_type` when you know it: `fact`, `preference`, `decision`, `plan`, `procedure`, `learning`, `context`, or `event`. diff --git a/skills/read-working-memory/SKILL.md b/skills/read-working-memory/SKILL.md index 048e5a7..2bc0342 100644 --- a/skills/read-working-memory/SKILL.md +++ b/skills/read-working-memory/SKILL.md @@ -5,9 +5,21 @@ description: Read the user's daily Working Memory briefing at session start or w # Read Working Memory -Use MCP `read_working_memory` when it is available. Otherwise use `nmem --json wm read` for the user's current priorities, unresolved flags, and recent context. +Use MCP `read_context_bundle` when startup identity, agent lane, space scope, or Rules could matter. It includes Working Memory plus the full owner/AI Identity/scope/rules contract. -If the runtime already knows the current project or agent lane, add `--space ""`. +If Context Bundle is unavailable or you only need current priorities, use MCP `read_working_memory` or: + +```bash +nmem --json wm read +``` + +CLI Context Bundle fallback: + +```bash +nmem --json context --source-app gemini-cli +``` + +If the runtime already knows the current project or agent lane, add `--space ""`. Multi-agent orchestrators can set `NMEM_AGENT_ID=""` before launching Gemini CLI. Add `NMEM_SPACE` only when that whole run should override the identity's default space. Use `NMEM_HOST_AGENT_ID` only for advanced host-id aliases. ## When to Use @@ -18,6 +30,7 @@ If the runtime already knows the current project or agent lane, add `--space "