@hir4ta/mneme 0.20.0 → 0.20.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import{i as zt,r as ot,R as Di,c as On,a as En,b as $i,o as ji,m as zn,d as Rn,g as Fi}from"./index-CFkxnncE.js";function Ui(e,n){let r=0;for(let i of e)(i=+i)&&(r+=i);return r}var In=180/Math.PI,Xe={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Pr(e,n,r,i,o,a){var s,u,f;return(s=Math.sqrt(e*e+n*n))&&(e/=s,n/=s),(f=e*r+n*i)&&(r-=e*f,i-=n*f),(u=Math.sqrt(r*r+i*i))&&(r/=u,i/=u,f/=u),e*i<n*r&&(e=-e,n=-n,f=-f,s=-s),{translateX:o,translateY:a,rotate:Math.atan2(n,e)*In,skewX:Math.atan(f)*In,scaleX:s,scaleY:u}}var fe;function Li(e){const n=new(typeof DOMMatrix=="function"?DOMMatrix:WebKitCSSMatrix)(e+"");return n.isIdentity?Xe:Pr(n.a,n.b,n.c,n.d,n.e,n.f)}function qi(e){return e==null||(fe||(fe=document.createElementNS("http://www.w3.org/2000/svg","g")),fe.setAttribute("transform",e),!(e=fe.transform.baseVal.consolidate()))?Xe:(e=e.matrix,Pr(e.a,e.b,e.c,e.d,e.e,e.f))}function Or(e,n,r,i){function o(l){return l.length?l.pop()+" ":""}function a(l,h,c,d,p,y){if(l!==c||h!==d){var m=p.push("translate(",null,n,null,r);y.push({i:m-4,x:zt(l,c)},{i:m-2,x:zt(h,d)})}else(c||d)&&p.push("translate("+c+n+d+r)}function s(l,h,c,d){l!==h?(l-h>180?h+=360:h-l>180&&(l+=360),d.push({i:c.push(o(c)+"rotate(",null,i)-2,x:zt(l,h)})):h&&c.push(o(c)+"rotate("+h+i)}function u(l,h,c,d){l!==h?d.push({i:c.push(o(c)+"skewX(",null,i)-2,x:zt(l,h)}):h&&c.push(o(c)+"skewX("+h+i)}function f(l,h,c,d,p,y){if(l!==c||h!==d){var m=p.push(o(p)+"scale(",null,",",null,")");y.push({i:m-4,x:zt(l,c)},{i:m-2,x:zt(h,d)})}else(c!==1||d!==1)&&p.push(o(p)+"scale("+c+","+d+")")}return function(l,h){var c=[],d=[];return l=e(l),h=e(h),a(l.translateX,l.translateY,h.translateX,h.translateY,c,d),s(l.rotate,h.rotate,c,d),u(l.skewX,h.skewX,c,d),f(l.scaleX,l.scaleY,h.scaleX,h.scaleY,c,d),l=h=null,function(p){for(var y=-1,m=d.length,_;++y<m;)c[(_=d[y]).i]=_.x(p);return c.join("")}}}var Hi=Or(Li,"px, ","px)","deg)"),Vi=Or(qi,", ",")",")"),Gi=1e-12;function Nn(e){return((e=Math.exp(e))+1/e)/2}function Wi(e){return((e=Math.exp(e))-1/e)/2}function Bi(e){return((e=Math.exp(2*e))-1)/(e+1)}const Xi=(function e(n,r,i){function o(a,s){var u=a[0],f=a[1],l=a[2],h=s[0],c=s[1],d=s[2],p=h-u,y=c-f,m=p*p+y*y,_,g;if(m<Gi)g=Math.log(d/l)/n,_=function(S){return[u+S*p,f+S*y,l*Math.exp(n*S*g)]};else{var w=Math.sqrt(m),C=(d*d-l*l+i*m)/(2*l*r*w),b=(d*d-l*l-i*m)/(2*d*r*w),x=Math.log(Math.sqrt(C*C+1)-C),A=Math.log(Math.sqrt(b*b+1)-b);g=(A-x)/n,_=function(S){var T=S*g,R=Nn(x),O=l/(r*w)*(R*Bi(n*T+x)-Wi(x));return[u+O*p,f+O*y,l*R/Nn(n*T+x)]}}return _.duration=g*1e3*n/Math.SQRT2,_}return o.rho=function(a){var s=Math.max(.001,+a),u=s*s,f=u*u;return e(s,u,f)},o})(Math.SQRT2,2,4);function Yi(e,n){var r=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(r!=null){var i,o,a,s,u=[],f=!0,l=!1;try{if(a=(r=r.call(e)).next,n===0){if(Object(r)!==r)return;f=!1}else for(;!(f=(i=a.call(r)).done)&&(u.push(i.value),u.length!==n);f=!0);}catch(h){l=!0,o=h}finally{try{if(!f&&r.return!=null&&(s=r.return(),Object(s)!==s))return}finally{if(l)throw o}}return u}}function Zi(e,n,r){return n=io(n),n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function Dn(e,n){return Ji(e)||Yi(e,n)||Er(e,n)||no()}function Ki(e){return Qi(e)||to(e)||Er(e)||eo()}function Qi(e){if(Array.isArray(e))return Ye(e)}function Ji(e){if(Array.isArray(e))return e}function to(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function Er(e,n){if(e){if(typeof e=="string")return Ye(e,n);var r=Object.prototype.toString.call(e).slice(8,-1);if(r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set")return Array.from(e);if(r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Ye(e,n)}}function Ye(e,n){(n==null||n>e.length)&&(n=e.length);for(var r=0,i=new Array(n);r<n;r++)i[r]=e[r];return i}function eo(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
1
+ import{i as zt,r as ot,R as Di,c as On,a as En,b as $i,o as ji,m as zn,d as Rn,g as Fi}from"./index-CeHiZXwl.js";function Ui(e,n){let r=0;for(let i of e)(i=+i)&&(r+=i);return r}var In=180/Math.PI,Xe={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function Pr(e,n,r,i,o,a){var s,u,f;return(s=Math.sqrt(e*e+n*n))&&(e/=s,n/=s),(f=e*r+n*i)&&(r-=e*f,i-=n*f),(u=Math.sqrt(r*r+i*i))&&(r/=u,i/=u,f/=u),e*i<n*r&&(e=-e,n=-n,f=-f,s=-s),{translateX:o,translateY:a,rotate:Math.atan2(n,e)*In,skewX:Math.atan(f)*In,scaleX:s,scaleY:u}}var fe;function Li(e){const n=new(typeof DOMMatrix=="function"?DOMMatrix:WebKitCSSMatrix)(e+"");return n.isIdentity?Xe:Pr(n.a,n.b,n.c,n.d,n.e,n.f)}function qi(e){return e==null||(fe||(fe=document.createElementNS("http://www.w3.org/2000/svg","g")),fe.setAttribute("transform",e),!(e=fe.transform.baseVal.consolidate()))?Xe:(e=e.matrix,Pr(e.a,e.b,e.c,e.d,e.e,e.f))}function Or(e,n,r,i){function o(l){return l.length?l.pop()+" ":""}function a(l,h,c,d,p,y){if(l!==c||h!==d){var m=p.push("translate(",null,n,null,r);y.push({i:m-4,x:zt(l,c)},{i:m-2,x:zt(h,d)})}else(c||d)&&p.push("translate("+c+n+d+r)}function s(l,h,c,d){l!==h?(l-h>180?h+=360:h-l>180&&(l+=360),d.push({i:c.push(o(c)+"rotate(",null,i)-2,x:zt(l,h)})):h&&c.push(o(c)+"rotate("+h+i)}function u(l,h,c,d){l!==h?d.push({i:c.push(o(c)+"skewX(",null,i)-2,x:zt(l,h)}):h&&c.push(o(c)+"skewX("+h+i)}function f(l,h,c,d,p,y){if(l!==c||h!==d){var m=p.push(o(p)+"scale(",null,",",null,")");y.push({i:m-4,x:zt(l,c)},{i:m-2,x:zt(h,d)})}else(c!==1||d!==1)&&p.push(o(p)+"scale("+c+","+d+")")}return function(l,h){var c=[],d=[];return l=e(l),h=e(h),a(l.translateX,l.translateY,h.translateX,h.translateY,c,d),s(l.rotate,h.rotate,c,d),u(l.skewX,h.skewX,c,d),f(l.scaleX,l.scaleY,h.scaleX,h.scaleY,c,d),l=h=null,function(p){for(var y=-1,m=d.length,_;++y<m;)c[(_=d[y]).i]=_.x(p);return c.join("")}}}var Hi=Or(Li,"px, ","px)","deg)"),Vi=Or(qi,", ",")",")"),Gi=1e-12;function Nn(e){return((e=Math.exp(e))+1/e)/2}function Wi(e){return((e=Math.exp(e))-1/e)/2}function Bi(e){return((e=Math.exp(2*e))-1)/(e+1)}const Xi=(function e(n,r,i){function o(a,s){var u=a[0],f=a[1],l=a[2],h=s[0],c=s[1],d=s[2],p=h-u,y=c-f,m=p*p+y*y,_,g;if(m<Gi)g=Math.log(d/l)/n,_=function(S){return[u+S*p,f+S*y,l*Math.exp(n*S*g)]};else{var w=Math.sqrt(m),C=(d*d-l*l+i*m)/(2*l*r*w),b=(d*d-l*l-i*m)/(2*d*r*w),x=Math.log(Math.sqrt(C*C+1)-C),A=Math.log(Math.sqrt(b*b+1)-b);g=(A-x)/n,_=function(S){var T=S*g,R=Nn(x),O=l/(r*w)*(R*Bi(n*T+x)-Wi(x));return[u+O*p,f+O*y,l*R/Nn(n*T+x)]}}return _.duration=g*1e3*n/Math.SQRT2,_}return o.rho=function(a){var s=Math.max(.001,+a),u=s*s,f=u*u;return e(s,u,f)},o})(Math.SQRT2,2,4);function Yi(e,n){var r=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(r!=null){var i,o,a,s,u=[],f=!0,l=!1;try{if(a=(r=r.call(e)).next,n===0){if(Object(r)!==r)return;f=!1}else for(;!(f=(i=a.call(r)).done)&&(u.push(i.value),u.length!==n);f=!0);}catch(h){l=!0,o=h}finally{try{if(!f&&r.return!=null&&(s=r.return(),Object(s)!==s))return}finally{if(l)throw o}}return u}}function Zi(e,n,r){return n=io(n),n in e?Object.defineProperty(e,n,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[n]=r,e}function Dn(e,n){return Ji(e)||Yi(e,n)||Er(e,n)||no()}function Ki(e){return Qi(e)||to(e)||Er(e)||eo()}function Qi(e){if(Array.isArray(e))return Ye(e)}function Ji(e){if(Array.isArray(e))return e}function to(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function Er(e,n){if(e){if(typeof e=="string")return Ye(e,n);var r=Object.prototype.toString.call(e).slice(8,-1);if(r==="Object"&&e.constructor&&(r=e.constructor.name),r==="Map"||r==="Set")return Array.from(e);if(r==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Ye(e,n)}}function Ye(e,n){(n==null||n>e.length)&&(n=e.length);for(var r=0,i=new Array(n);r<n;r++)i[r]=e[r];return i}function eo(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
2
2
  In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function no(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
3
3
  In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function ro(e,n){if(typeof e!="object"||e===null)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var i=r.call(e,n);if(typeof i!="object")return i;throw new TypeError("@@toPrimitive must return a primitive value.")}return(n==="string"?String:Number)(e)}function io(e){var n=ro(e,"string");return typeof n=="symbol"?n:String(n)}var oo=function(n,r){var i=new Set(r);return Object.assign.apply(Object,[{}].concat(Ki(Object.entries(n).filter(function(o){var a=Dn(o,1),s=a[0];return!i.has(s)}).map(function(o){var a=Dn(o,2),s=a[0],u=a[1];return Zi({},s,u)}))))};function Ze(e,n){(n==null||n>e.length)&&(n=e.length);for(var r=0,i=Array(n);r<n;r++)i[r]=e[r];return i}function ao(e){if(Array.isArray(e))return e}function so(e){if(Array.isArray(e))return Ze(e)}function uo(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function lo(e,n){var r=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(r!=null){var i,o,a,s,u=[],f=!0,l=!1;try{if(a=(r=r.call(e)).next,n!==0)for(;!(f=(i=a.call(r)).done)&&(u.push(i.value),u.length!==n);f=!0);}catch(h){l=!0,o=h}finally{try{if(!f&&r.return!=null&&(s=r.return(),Object(s)!==s))return}finally{if(l)throw o}}return u}}function fo(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
4
4
  In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function co(){throw new TypeError(`Invalid attempt to spread non-iterable instance.
@@ -6,7 +6,7 @@
6
6
  <title>mneme - Dashboard</title>
7
7
  <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32-max.png" />
8
8
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
9
- <script type="module" crossorigin src="/assets/index-CFkxnncE.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-CeHiZXwl.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-t_srr1OD.css">
11
11
  </head>
12
12
  <body>
@@ -30200,7 +30200,9 @@ async function parseTranscript(transcriptPath) {
30200
30200
  crlfDelay: Number.POSITIVE_INFINITY
30201
30201
  });
30202
30202
  const entries = [];
30203
+ let totalLines = 0;
30203
30204
  for await (const line of rl) {
30205
+ totalLines++;
30204
30206
  if (line.trim()) {
30205
30207
  try {
30206
30208
  entries.push(JSON.parse(line));
@@ -30210,6 +30212,7 @@ async function parseTranscript(transcriptPath) {
30210
30212
  }
30211
30213
  const userMessages = entries.filter((e) => {
30212
30214
  if (e.type !== "user" || e.message?.role !== "user") return false;
30215
+ if (e.isMeta === true) return false;
30213
30216
  const content = e.message?.content;
30214
30217
  if (typeof content !== "string") return false;
30215
30218
  if (content.startsWith("<local-command-stdout>")) return false;
@@ -30286,7 +30289,8 @@ async function parseTranscript(transcriptPath) {
30286
30289
  userMessages: userMessages.length,
30287
30290
  assistantResponses: assistantMessages.length,
30288
30291
  thinkingBlocks: assistantMessages.filter((a) => a.thinking).length
30289
- }
30292
+ },
30293
+ totalLines
30290
30294
  };
30291
30295
  }
30292
30296
  async function saveInteractions(claudeSessionId, mnemeSessionId) {
@@ -30424,6 +30428,34 @@ async function saveInteractions(claudeSessionId, mnemeSessionId) {
30424
30428
  clearBackupStmt.run(sessionId);
30425
30429
  } catch {
30426
30430
  }
30431
+ try {
30432
+ const lastTimestamp = finalInteractions.length > 0 ? finalInteractions[finalInteractions.length - 1].timestamp : null;
30433
+ const checkStmt = database.prepare(
30434
+ "SELECT 1 FROM session_save_state WHERE claude_session_id = ?"
30435
+ );
30436
+ const exists = checkStmt.get(claudeSessionId);
30437
+ if (exists) {
30438
+ const updateStmt = database.prepare(`
30439
+ UPDATE session_save_state
30440
+ SET last_saved_line = ?, last_saved_timestamp = ?, updated_at = datetime('now')
30441
+ WHERE claude_session_id = ?
30442
+ `);
30443
+ updateStmt.run(parsed.totalLines, lastTimestamp, claudeSessionId);
30444
+ } else {
30445
+ const insertStmt2 = database.prepare(`
30446
+ INSERT INTO session_save_state (claude_session_id, mneme_session_id, project_path, last_saved_line, last_saved_timestamp)
30447
+ VALUES (?, ?, ?, ?, ?)
30448
+ `);
30449
+ insertStmt2.run(
30450
+ claudeSessionId,
30451
+ sessionId,
30452
+ projectPath,
30453
+ parsed.totalLines,
30454
+ lastTimestamp
30455
+ );
30456
+ }
30457
+ } catch {
30458
+ }
30427
30459
  return {
30428
30460
  success: true,
30429
30461
  savedCount: insertedCount,
@@ -130,7 +130,21 @@ if [ -n "$session_file" ] && [ -f "$session_file" ]; then
130
130
  echo "[mneme] Session ended without /mneme:save - cleaned up ${deleted_count} interactions" >&2
131
131
  fi
132
132
  fi
133
- echo "[mneme] Session completed (not saved): ${session_file}" >&2
133
+
134
+ # Also delete the empty session JSON file (no summary = not saved)
135
+ if [ -f "$session_file" ]; then
136
+ rm -f "$session_file"
137
+ echo "[mneme] Deleted empty session file: ${session_file}" >&2
138
+ fi
139
+
140
+ # Delete session-link file if exists
141
+ link_file="${session_links_dir}/${session_short_id}.json"
142
+ if [ -f "$link_file" ]; then
143
+ rm -f "$link_file"
144
+ echo "[mneme] Deleted session-link file: ${link_file}" >&2
145
+ fi
146
+
147
+ echo "[mneme] Session completed (not saved, cleaned up)" >&2
134
148
  else
135
149
  echo "[mneme] Session completed: ${session_file}" >&2
136
150
  fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hir4ta/mneme",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "Long-term memory plugin for Claude Code - automated session saving, recording technical decisions, and web dashboard",
5
5
  "keywords": [
6
6
  "claude",
@@ -479,6 +479,7 @@ interface ParsedTranscript {
479
479
  assistantResponses: number;
480
480
  thinkingBlocks: number;
481
481
  };
482
+ totalLines: number;
482
483
  }
483
484
 
484
485
  async function parseTranscript(
@@ -509,8 +510,10 @@ async function parseTranscript(
509
510
  }
510
511
 
511
512
  const entries: TranscriptEntry[] = [];
513
+ let totalLines = 0;
512
514
 
513
515
  for await (const line of rl) {
516
+ totalLines++;
514
517
  if (line.trim()) {
515
518
  try {
516
519
  entries.push(JSON.parse(line));
@@ -520,10 +523,12 @@ async function parseTranscript(
520
523
  }
521
524
  }
522
525
 
523
- // Extract user messages (text only, exclude tool results and local command outputs)
526
+ // Extract user messages (text only, exclude tool results, local command outputs, and skill expansions)
524
527
  const userMessages = entries
525
528
  .filter((e) => {
526
529
  if (e.type !== "user" || e.message?.role !== "user") return false;
530
+ // Skip skill expansions (isMeta: true) to avoid duplicates
531
+ if ((e as { isMeta?: boolean }).isMeta === true) return false;
527
532
  const content = e.message?.content;
528
533
  if (typeof content !== "string") return false;
529
534
  if (content.startsWith("<local-command-stdout>")) return false;
@@ -641,6 +646,7 @@ async function parseTranscript(
641
646
  assistantResponses: assistantMessages.length,
642
647
  thinkingBlocks: assistantMessages.filter((a) => a.thinking).length,
643
648
  },
649
+ totalLines,
644
650
  };
645
651
  }
646
652
 
@@ -827,6 +833,43 @@ async function saveInteractions(
827
833
  // Ignore
828
834
  }
829
835
 
836
+ // Update session_save_state to prevent incremental-save from re-inserting
837
+ // This is critical to avoid duplicate interactions
838
+ try {
839
+ const lastTimestamp =
840
+ finalInteractions.length > 0
841
+ ? finalInteractions[finalInteractions.length - 1].timestamp
842
+ : null;
843
+
844
+ const checkStmt = database.prepare(
845
+ "SELECT 1 FROM session_save_state WHERE claude_session_id = ?",
846
+ );
847
+ const exists = checkStmt.get(claudeSessionId);
848
+
849
+ if (exists) {
850
+ const updateStmt = database.prepare(`
851
+ UPDATE session_save_state
852
+ SET last_saved_line = ?, last_saved_timestamp = ?, updated_at = datetime('now')
853
+ WHERE claude_session_id = ?
854
+ `);
855
+ updateStmt.run(parsed.totalLines, lastTimestamp, claudeSessionId);
856
+ } else {
857
+ const insertStmt = database.prepare(`
858
+ INSERT INTO session_save_state (claude_session_id, mneme_session_id, project_path, last_saved_line, last_saved_timestamp)
859
+ VALUES (?, ?, ?, ?, ?)
860
+ `);
861
+ insertStmt.run(
862
+ claudeSessionId,
863
+ sessionId,
864
+ projectPath,
865
+ parsed.totalLines,
866
+ lastTimestamp,
867
+ );
868
+ }
869
+ } catch {
870
+ // Ignore session_save_state errors
871
+ }
872
+
830
873
  return {
831
874
  success: true,
832
875
  savedCount: insertedCount,