Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a7ebc35ee0 | |||
| 9063c5d8a7 | |||
| 3b34feb779 | |||
| ad58fdf8fc |
@@ -10,7 +10,7 @@
|
||||
"plugins": [
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "10.7.2",
|
||||
"version": "11.0.0",
|
||||
"source": "./plugin",
|
||||
"description": "Persistent memory system for Claude Code - context compression across sessions"
|
||||
}
|
||||
|
||||
+9
-3
@@ -4,10 +4,16 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [10.7.1] - 2026-
|
||||
## [10.7.2] - 2026-
|
||||
✅ CHANGELOG.md generated successfully!
|
||||
219 releases processed
|
||||
stall simplification in v10.7.0 (commit 21b10b46) over-applied scope and replaced the entire `runInstallCommand` with just two `claude` CLI commands. This gutted the interactive IDE multi-select, `--ide` flag, and all 13 IDE-specific setup dispatchers.
|
||||
220 releases processed
|
||||
m error to debug (#1606) — reduces noise in logs by treating routine concept-type cleanup as debug-level rather than error-level logging.
|
||||
|
||||
## [10.7.1] - 2026-04-05
|
||||
|
||||
## Bug Fix
|
||||
|
||||
**Restore full interactive installer** — the install simplification in v10.7.0 (commit 21b10b46) over-applied scope and replaced the entire `runInstallCommand` with just two `claude` CLI commands. This gutted the interactive IDE multi-select, `--ide` flag, and all 13 IDE-specific setup dispatchers.
|
||||
|
||||
### What changed
|
||||
- **Claude Code**: now uses native `claude plugin marketplace add` + `claude plugin install` (the intended simplification)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "10.7.2",
|
||||
"version": "11.0.0",
|
||||
"description": "Memory compression system for Claude Code - persist context across sessions",
|
||||
"keywords": [
|
||||
"claude",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem",
|
||||
"version": "10.7.2",
|
||||
"version": "11.0.0",
|
||||
"description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions",
|
||||
"author": {
|
||||
"name": "Alex Newman"
|
||||
|
||||
@@ -87,8 +87,8 @@
|
||||
"system_identity": "You are a Claude-Mem, a specialized observer tool for creating searchable memory FOR FUTURE SESSIONS.\n\nCRITICAL: Record what was LEARNED/BUILT/FIXED/DEPLOYED/CONFIGURED, not what you (the observer) are doing.\n\nYou do not have access to tools. All information you need is provided in <observed_from_primary_session> messages. Create observations from what you observe - no investigation needed.",
|
||||
"spatial_awareness": "SPATIAL AWARENESS: Tool executions include the working directory (tool_cwd) to help you understand:\n- Which repository/project is being worked on\n- Where files are located relative to the project root\n- How to match requested paths to actual execution paths",
|
||||
"observer_role": "Your job is to monitor a different Claude Code session happening RIGHT NOW, with the goal of creating observations and progress summaries as the work is being done LIVE by the user. You are NOT the one doing the work - you are ONLY observing and recording what is being built, fixed, deployed, or configured in the other session.",
|
||||
"recording_focus": "WHAT TO RECORD\n--------------\nFocus on deliverables and capabilities:\n- What the system NOW DOES differently (new capabilities)\n- What shipped to users/production (features, fixes, configs, docs)\n- Changes in technical domains (auth, data, UI, infra, DevOps, docs)\n\nUse verbs like: implemented, fixed, deployed, configured, migrated, optimized, added, refactored\n\n✅ GOOD EXAMPLES (describes what was built):\n- \"Authentication now supports OAuth2 with PKCE flow\"\n- \"Deployment pipeline runs canary releases with auto-rollback\"\n- \"Database indexes optimized for common query patterns\"\n\n❌ BAD EXAMPLES (describes observation process - DO NOT DO THIS):\n- \"Analyzed authentication implementation and stored findings\"\n- \"Tracked deployment steps and logged outcomes\"\n- \"Monitored database performance and recorded metrics\"",
|
||||
"skip_guidance": "WHEN TO SKIP\n------------\nSkip routine operations:\n- Empty status checks\n- Package installations with no errors\n- Simple file listings\n- Repetitive operations you've already documented\n- If file related research comes back as empty or not found\n- **No output necessary if skipping.**",
|
||||
"recording_focus": "WHAT TO RECORD\n--------------\nFocus on durable technical signal:\n- What the system NOW DOES differently (new capabilities)\n- What shipped to users/production (features, fixes, configs, docs)\n- Changes in technical domains (auth, data, UI, infra, DevOps, docs)\n- Concrete debugging or investigative findings from logs, traces, queue state, database rows, and code-path inspection\n\nUse verbs like: implemented, fixed, deployed, configured, migrated, optimized, added, refactored, discovered, confirmed, traced\n\n✅ GOOD EXAMPLES (describes what was built or learned):\n- \"Authentication now supports OAuth2 with PKCE flow\"\n- \"Deployment pipeline runs canary releases with auto-rollback\"\n- \"Database indexes optimized for common query patterns\"\n- \"Observation queue for claude-mem session timed out waiting for an agent pool slot\"\n- \"Fallback processing abandoned pending messages after Gemini and OpenRouter returned 404\"\n\n❌ BAD EXAMPLES (describes observation process - DO NOT DO THIS):\n- \"Analyzed authentication implementation and stored findings\"\n- \"Tracked deployment steps and logged outcomes\"\n- \"Monitored database performance and recorded metrics\"",
|
||||
"skip_guidance": "WHEN TO SKIP\n------------\nSkip routine operations:\n- Empty status checks\n- Package installations with no errors\n- Simple file listings with no follow-on finding\n- Repetitive operations you've already documented\n- File related research that comes back empty or not found\n\nIf skipping, return an empty response only. Do not explain the skip in prose.",
|
||||
"type_guidance": "**type**: MUST be EXACTLY one of these 6 options (no other values allowed):\n - bugfix: something was broken, now fixed\n - feature: new capability or functionality added\n - refactor: code restructured, behavior unchanged\n - change: generic modification (docs, config, misc)\n - discovery: learning about existing system\n - decision: architectural/design choice with rationale",
|
||||
"concept_guidance": "**concepts**: 2-5 knowledge-type categories. MUST use ONLY these exact keywords:\n - how-it-works: understanding mechanisms\n - why-it-exists: purpose or rationale\n - what-changed: modifications made\n - problem-solution: issues and their fixes\n - gotcha: traps or edge cases\n - pattern: reusable approach\n - trade-off: pros/cons of a decision\n\n IMPORTANT: Do NOT include the observation type (change/discovery/decision) as a concept.\n Types and concepts are separate dimensions.",
|
||||
"field_guidance": "**facts**: Concise, self-contained statements\nEach fact is ONE piece of information\n No pronouns - each fact must stand alone\n Include specific details: filenames, functions, values\n\n**files**: All files touched (full paths from project root)",
|
||||
@@ -122,4 +122,4 @@
|
||||
"summary_format_instruction": "Respond in this XML format:",
|
||||
"summary_footer": "IMPORTANT! DO NOT do any work right now other than generating this next PROGRESS SUMMARY - and remember that you are a memory agent designed to summarize a DIFFERENT claude code session, not this one.\n\nNever reference yourself or your own actions. Do not output anything other than the summary content formatted in the XML structure above. All other output is ignored by the system, and the system has been designed to be smart about token usage. Please spend your tokens wisely on useful summary content.\n\nThank you, this summary will be very useful for keeping track of our progress!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mem-plugin",
|
||||
"version": "10.7.2",
|
||||
"version": "11.0.0",
|
||||
"private": true,
|
||||
"description": "Runtime dependencies for claude-mem bundled hooks",
|
||||
"type": "module",
|
||||
|
||||
@@ -114,7 +114,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
||||
${c}`}var bP=new Set([".js",".jsx",".ts",".tsx",".mjs",".cjs",".py",".pyw",".go",".rs",".rb",".java",".cs",".cpp",".c",".h",".hpp",".swift",".kt",".php",".vue",".svelte"]),xP=new Set(["node_modules",".git","dist","build",".next","__pycache__",".venv","venv","env",".env","target","vendor",".cache",".turbo","coverage",".nyc_output",".claude",".smart-file-read"]),kP=512*1024;async function*n$(e,t,r=20){if(r<=0)return;let n;try{n=await(0,Sn.readdir)(e,{withFileTypes:!0})}catch{return}for(let o of n){if(o.name.startsWith(".")&&o.name!=="."||xP.has(o.name))continue;let i=(0,pi.join)(e,o.name);if(o.isDirectory())yield*n$(i,t,r-1);else if(o.isFile()){let a=o.name.slice(o.name.lastIndexOf("."));bP.has(a)&&(yield i)}}}async function SP(e){try{let t=await(0,Sn.stat)(e);if(t.size>kP||t.size===0)return null;let r=await(0,Sn.readFile)(e,"utf-8");return r.slice(0,1e3).includes("\0")?null:r}catch{return null}}async function o$(e,t,r={}){let n=r.maxResults||20,o=t.toLowerCase(),i=o.split(/[\s_\-./]+/).filter(h=>h.length>0),a=[];for await(let h of n$(e,e)){if(r.filePattern&&!(0,pi.relative)(e,h).toLowerCase().includes(r.filePattern.toLowerCase()))continue;let _=await SP(h);_&&a.push({absolutePath:h,relativePath:(0,pi.relative)(e,h),content:_})}let s=e$(a),c=[],u=[],l=0;for(let[h,_]of s){l+=wP(_);let E=Os(h.toLowerCase(),i)>0,I=[],A=(j,Le)=>{for(let de of j){let Wt=0,Qe="",Kt=Os(de.name.toLowerCase(),i);Kt>0&&(Wt+=Kt*3,Qe="name match"),de.signature.toLowerCase().includes(o)&&(Wt+=2,Qe=Qe?`${Qe} + signature`:"signature match"),de.jsdoc&&de.jsdoc.toLowerCase().includes(o)&&(Wt+=1,Qe=Qe?`${Qe} + jsdoc`:"jsdoc match"),Wt>0&&(E=!0,I.push({filePath:h,symbolName:Le?`${Le}.${de.name}`:de.name,kind:de.kind,signature:de.signature,jsdoc:de.jsdoc,lineStart:de.lineStart,lineEnd:de.lineEnd,matchReason:Qe})),de.children&&A(de.children,de.name)}};A(_.symbols),E&&(c.push(_),u.push(...I))}u.sort((h,_)=>{let b=Os(h.symbolName.toLowerCase(),i);return Os(_.symbolName.toLowerCase(),i)-b});let d=u.slice(0,n),m=new Set(d.map(h=>h.filePath)),p=c.filter(h=>m.has(h.filePath)).slice(0,n),g=p.reduce((h,_)=>h+_.foldedTokenEstimate,0);return{foldedFiles:p,matchingSymbols:d,totalFilesScanned:a.length,totalSymbolsFound:l,tokenEstimate:g}}function Os(e,t){let r=0;for(let n of t)if(e===n)r+=10;else if(e.includes(n))r+=5;else{let o=0,i=0;for(let a of n){let s=e.indexOf(a,o);s!==-1&&(i++,o=s+1)}i===n.length&&(r+=1)}return r}function wP(e){let t=e.symbols.length;for(let r of e.symbols)r.children&&(t+=r.children.length);return t}function i$(e,t){let r=[];if(r.push(`\u{1F50D} Smart Search: "${t}"`),r.push(` Scanned ${e.totalFilesScanned} files, found ${e.totalSymbolsFound} symbols`),r.push(` ${e.matchingSymbols.length} matches across ${e.foldedFiles.length} files (~${e.tokenEstimate} tokens for folded view)`),r.push(""),e.matchingSymbols.length===0)return r.push(" No matching symbols found."),r.join(`
|
||||
`);r.push("\u2500\u2500 Matching Symbols \u2500\u2500"),r.push("");for(let n of e.matchingSymbols){if(r.push(` ${n.kind} ${n.symbolName} (${n.filePath}:${n.lineStart+1})`),r.push(` ${n.signature}`),n.jsdoc){let o=n.jsdoc.split(`
|
||||
`).find(i=>i.replace(/^[\s*/]+/,"").trim().length>0);o&&r.push(` \u{1F4AC} ${o.replace(/^[\s*/]+/,"").trim()}`)}r.push("")}r.push("\u2500\u2500 Folded File Views \u2500\u2500"),r.push("");for(let n of e.foldedFiles)r.push(kn(n)),r.push("");return r.push("\u2500\u2500 Actions \u2500\u2500"),r.push(" To see full implementation: use smart_unfold with file path and symbol name"),r.join(`
|
||||
`)}var Of=require("node:fs/promises"),js=require("node:path"),zP="10.7.2";console.log=(...e)=>{ve.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:e})};var a$={search:"/api/search",timeline:"/api/timeline"};async function s$(e,t){ve.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:e,params:t});try{let r=new URLSearchParams;for(let[a,s]of Object.entries(t))s!=null&&r.append(a,String(s));let n=`${e}?${r}`,o=await Ts(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return ve.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:e}),i}catch(r){return ve.error("SYSTEM","\u2190 Worker API error",{endpoint:e},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function IP(e,t){ve.debug("HTTP","Worker API request (POST)",void 0,{endpoint:e});try{let r=await Ts(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return ve.debug("HTTP","Worker API success (POST)",void 0,{endpoint:e}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){return ve.error("HTTP","Worker API error (POST)",{endpoint:e},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function EP(){try{return(await Ts("/api/health")).ok}catch(e){return ve.debug("SYSTEM","Worker health check failed",{},e),!1}}var c$=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
|
||||
`)}var Of=require("node:fs/promises"),js=require("node:path"),zP="11.0.0";console.log=(...e)=>{ve.error("CONSOLE","Intercepted console output (MCP protocol protection)",void 0,{args:e})};var a$={search:"/api/search",timeline:"/api/timeline"};async function s$(e,t){ve.debug("SYSTEM","\u2192 Worker API",void 0,{endpoint:e,params:t});try{let r=new URLSearchParams;for(let[a,s]of Object.entries(t))s!=null&&r.append(a,String(s));let n=`${e}?${r}`,o=await Ts(n);if(!o.ok){let a=await o.text();throw new Error(`Worker API error (${o.status}): ${a}`)}let i=await o.json();return ve.debug("SYSTEM","\u2190 Worker API success",void 0,{endpoint:e}),i}catch(r){return ve.error("SYSTEM","\u2190 Worker API error",{endpoint:e},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function IP(e,t){ve.debug("HTTP","Worker API request (POST)",void 0,{endpoint:e});try{let r=await Ts(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)});if(!r.ok){let o=await r.text();throw new Error(`Worker API error (${r.status}): ${o}`)}let n=await r.json();return ve.debug("HTTP","Worker API success (POST)",void 0,{endpoint:e}),{content:[{type:"text",text:JSON.stringify(n,null,2)}]}}catch(r){return ve.error("HTTP","Worker API error (POST)",{endpoint:e},r),{content:[{type:"text",text:`Error calling Worker API: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async function EP(){try{return(await Ts("/api/health")).ok}catch(e){return ve.debug("SYSTEM","Worker health check failed",{},e),!1}}var c$=[{name:"__IMPORTANT",description:`3-LAYER WORKFLOW (ALWAYS FOLLOW):
|
||||
1. search(query) \u2192 Get index with IDs (~50-100 tokens/result)
|
||||
2. timeline(anchor=ID) \u2192 Get context around interesting results
|
||||
3. get_observations([IDs]) \u2192 Fetch full details ONLY for filtered IDs
|
||||
|
||||
File diff suppressed because one or more lines are too long
+6
-2
@@ -116,7 +116,11 @@ export function buildObservationPrompt(obs: Observation): string {
|
||||
<occurred_at>${new Date(obs.created_at_epoch).toISOString()}</occurred_at>${obs.cwd ? `\n <working_directory>${obs.cwd}</working_directory>` : ''}
|
||||
<parameters>${JSON.stringify(toolInput, null, 2)}</parameters>
|
||||
<outcome>${JSON.stringify(toolOutput, null, 2)}</outcome>
|
||||
</observed_from_primary_session>`;
|
||||
</observed_from_primary_session>
|
||||
|
||||
Return either one or more <observation>...</observation> blocks, or an empty response if this tool use should be skipped.
|
||||
Concrete debugging findings from logs, queue state, database rows, session routing, or code-path inspection count as durable discoveries and should be recorded.
|
||||
Never reply with prose such as "Skipping", "No substantive tool executions", or any explanation outside XML. Non-XML text is discarded.`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,4 +239,4 @@ ${mode.prompts.format_examples}
|
||||
${mode.prompts.footer}
|
||||
|
||||
${mode.prompts.header_memory_continued}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,19 @@ export async function processAgentResponse(
|
||||
const observations = parseObservations(text, session.contentSessionId);
|
||||
const summary = parseSummary(text, session.sessionDbId);
|
||||
|
||||
if (
|
||||
text.trim() &&
|
||||
observations.length === 0 &&
|
||||
!summary &&
|
||||
!/<observation>|<summary>|<skip_summary\b/.test(text)
|
||||
) {
|
||||
const preview = text.length > 200 ? `${text.slice(0, 200)}...` : text;
|
||||
logger.warn('PARSER', `${agentName} returned non-XML response; observation content was discarded`, {
|
||||
sessionId: session.sessionDbId,
|
||||
preview
|
||||
});
|
||||
}
|
||||
|
||||
// Convert nullable fields to empty strings for storeSummary (if summary exists)
|
||||
const summaryForStore = normalizeSummaryForStorage(summary);
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import { buildObservationPrompt } from '../../src/sdk/prompts.js';
|
||||
|
||||
describe('buildObservationPrompt', () => {
|
||||
it('instructs the observer to avoid prose skip responses', () => {
|
||||
const prompt = buildObservationPrompt({
|
||||
id: 1,
|
||||
tool_name: 'exec_command',
|
||||
tool_input: JSON.stringify({ cmd: 'pwd' }),
|
||||
tool_output: JSON.stringify({ output: '/repo' }),
|
||||
created_at_epoch: Date.now(),
|
||||
cwd: '/repo',
|
||||
});
|
||||
|
||||
expect(prompt).toContain('Return either one or more <observation>...</observation> blocks, or an empty response');
|
||||
expect(prompt).toContain('Concrete debugging findings from logs, queue state, database rows, session routing, or code-path inspection');
|
||||
expect(prompt).toContain('Never reply with prose such as "Skipping", "No substantive tool executions"');
|
||||
});
|
||||
});
|
||||
@@ -212,6 +212,36 @@ describe('ResponseProcessor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('non-XML observer responses', () => {
|
||||
it('warns when the observer returns prose that will be discarded', async () => {
|
||||
const session = createMockSession();
|
||||
const responseText = 'Skipping — repeated log scan with no new findings.';
|
||||
|
||||
await processAgentResponse(
|
||||
responseText,
|
||||
session,
|
||||
mockDbManager,
|
||||
mockSessionManager,
|
||||
mockWorker,
|
||||
100,
|
||||
null,
|
||||
'TestAgent'
|
||||
);
|
||||
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
'PARSER',
|
||||
'TestAgent returned non-XML response; observation content was discarded',
|
||||
expect.objectContaining({
|
||||
sessionId: 1,
|
||||
preview: responseText
|
||||
})
|
||||
);
|
||||
const [, , observations, summary] = mockStoreObservations.mock.calls[0];
|
||||
expect(observations).toHaveLength(0);
|
||||
expect(summary).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsing summary from XML response', () => {
|
||||
it('should parse summary from response', async () => {
|
||||
const session = createMockSession();
|
||||
|
||||
Reference in New Issue
Block a user