mcp-memory-bucket 0.5.4 → 0.5.5
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.
- package/dist/client/assets/{index-6BCcM6pL.js → index-DEOIrixV.js} +67 -52
- package/dist/client/index.html +1 -1
- package/dist/src/memory/repository.js +36 -2
- package/dist/src/memory/tools.js +12 -1
- package/dist/src/skills/builtin/memory-bucket-authoring/SKILL.md +30 -0
- package/dist/src/skills/repository.js +2 -2
- package/dist/src/store/db.js +30 -4
- package/dist/src/store/search.js +25 -2
- package/dist/src/store/sync.js +1 -1
- package/dist/src/web/routes.js +26 -9
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* Copyright 2017 Google LLC
|
|
18
18
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
19
19
|
*/const we=globalThis;let _=class extends F{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){const e=super.createRenderRoot();return this.renderOptions.renderBefore??=e.firstChild,e}update(e){const t=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(e),this._$Do=Gt(t,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return D}};_._$litElement$=!0,_.finalized=!0,we.litElementHydrateSupport?.({LitElement:_});const Jt=we.litElementPolyfillSupport;Jt?.({LitElement:_});(we.litElementVersions??=[]).push("4.2.2");var gt;let pe=0;const Ke=new Set;function Xt(r){pe++;try{r()}finally{pe--,pe===0&&es()}}function es(){const r=new Set(Ke);Ke.clear();for(const e of r)e()}function ts(r,e){return r!=r?e==e:r!==e||r&&typeof r=="object"||typeof r=="function"}function ss(r){return r&&typeof r.deref=="function"}class _e{static#e=0;#t;#s;#r=new Set;#i=gt.#e++;name;constructor(e,t){this.#t=e,this.#s=e,this.name=t}_get(){return u.activeConsumer&&u.activeConsumer.track(this),this.#t}_set(e){return ts(e,this.#t)?(this.#s=this.#t,this.#t=e,this._notify(),!0):!1}subscribe(e,t=!1){let s=e;return t&&(s=new WeakRef(e)),this.#r.add(s),()=>this.#r.delete(s)}_notify(){const e=[...this.#r];for(const t of e){let s;if(ss(t)){if(s=t.deref(),!s){this.#r.delete(t);continue}}else s=t;s()}}get id(){return this.#i}get previousValue(){return this.#s}toString(){return this.name?`${this.constructor.name}(${this.name})`:`${this.constructor.name}#${this.id}`}}gt=_e;class u extends _e{static#e=[];constructor(e,t){super(e,t)}static get activeConsumer(){return this.#e[this.#e.length-1]}static push(e){this.#e.push(e)}static pop(){this.#e.pop()}get(){return this._get()}set(e){Xt(()=>{this._set(e)})}get value(){return this.get()}set value(e){this.set(e)}update(e){this.set(e(this.get()))}}class is extends _e{#e;#t=!0;#s=!1;#r=new Map;_debugParent;#i;#d=()=>{this.#t||(this.#t=!0,this._notify())};constructor(e,t,s){super(void 0,t),this.#e=e,this.#i=s?.weak??!0}track(e){if(this.#r.has(e))return;const t=e.subscribe(this.#d,this.#i);this.#r.set(e,t)}dispose(){this.#a()}#a(){for(const e of this.#r.values())e();this.#r.clear()}get(){if(u.activeConsumer&&u.activeConsumer.track(this),!this.#t)return this._get();if(this.#s)throw new Error(`Cycle detected in ${this}
|
|
20
|
-
↳ while computing ${this._debugParent??"root"}`);this.#s=!0,this.#a(),u.push(this);try{const e=this.#e();this._set(e)}finally{u.pop(),this.#t=!1,this.#s=!1}return this._get()}get value(){return this.get()}set value(e){throw new Error(`Cannot set value of Computed ${this}. Computed values are read-only.`)}subscribe(e,t=!1){const s=super.subscribe(e,t);return u.activeConsumer||this.get(),s}}class oe{#e;#t=new Map;_debugParent;constructor(e){this.#e=e,e.addController(this);const t=e,s=t.update,i=this;t.update=function(n){u.push(i);try{s.call(this,n)}finally{u.pop()}}}track(e){this.#t.has(e)||this.#t.set(e,e.subscribe(this.#s,!0))}#s=()=>{this.#e.requestUpdate()};hostUpdate(){for(const e of this.#t.values())e();this.#t.clear()}hostDisconnected(){for(const e of this.#t.values())e();this.#t.clear()}toString(){const e=this.#e;return`SignalWatcher(${e?.localName??e?.tagName??e?.constructor?.name??"unknown"})`}}const rs=new Intl.DateTimeFormat("en-CA",{year:"numeric",month:"2-digit",day:"2-digit"});function ns(r){if(!r)return"unknown age";const e=new Date(r);return Number.isNaN(e.getTime())?"unknown age":rs.format(e)}
|
|
20
|
+
↳ while computing ${this._debugParent??"root"}`);this.#s=!0,this.#a(),u.push(this);try{const e=this.#e();this._set(e)}finally{u.pop(),this.#t=!1,this.#s=!1}return this._get()}get value(){return this.get()}set value(e){throw new Error(`Cannot set value of Computed ${this}. Computed values are read-only.`)}subscribe(e,t=!1){const s=super.subscribe(e,t);return u.activeConsumer||this.get(),s}}class oe{#e;#t=new Map;_debugParent;constructor(e){this.#e=e,e.addController(this);const t=e,s=t.update,i=this;t.update=function(n){u.push(i);try{s.call(this,n)}finally{u.pop()}}}track(e){this.#t.has(e)||this.#t.set(e,e.subscribe(this.#s,!0))}#s=()=>{this.#e.requestUpdate()};hostUpdate(){for(const e of this.#t.values())e();this.#t.clear()}hostDisconnected(){for(const e of this.#t.values())e();this.#t.clear()}toString(){const e=this.#e;return`SignalWatcher(${e?.localName??e?.tagName??e?.constructor?.name??"unknown"})`}}const rs=new Intl.DateTimeFormat("en-CA",{year:"numeric",month:"2-digit",day:"2-digit"});function ns(r){if(!r)return"unknown age";const e=new Date(r);return Number.isNaN(e.getTime())?"unknown age":rs.format(e)}function as(r){const e=[],t=new Map,s=[];for(const n of r){if(n._table!=="memory_docs"){s.push(n);continue}t.has(n.name)||(t.set(n.name,[]),e.push(n.name)),t.get(n.name).push(n)}const i=e.map(n=>({key:n,items:t.get(n)}));return s.length>0&&i.push({key:null,items:s}),i}class os extends _{static{this.properties={results:{attribute:!1},showFolder:{attribute:!1},onSelect:{attribute:!1},selectedIds:{attribute:!1},onToggleSelect:{attribute:!1}}}static{this.styles=O`
|
|
21
21
|
:host { display: block; }
|
|
22
22
|
.count-header {
|
|
23
23
|
padding: 8px 14px;
|
|
@@ -28,6 +28,18 @@
|
|
|
28
28
|
background: var(--bg);
|
|
29
29
|
border-bottom: 1px solid var(--border);
|
|
30
30
|
}
|
|
31
|
+
.key-group-header {
|
|
32
|
+
padding: 6px 14px;
|
|
33
|
+
font-size: 11px;
|
|
34
|
+
font-weight: 700;
|
|
35
|
+
opacity: 0.7;
|
|
36
|
+
background: var(--bg-subtle);
|
|
37
|
+
border-bottom: 1px solid var(--border);
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
gap: 6px;
|
|
41
|
+
}
|
|
42
|
+
.key-group-count { opacity: 0.6; font-weight: 400; }
|
|
31
43
|
.row { padding: 10px 14px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; gap: 8px; }
|
|
32
44
|
.row:hover { background: var(--hover); }
|
|
33
45
|
.row.deprecated { opacity: 0.55; }
|
|
@@ -57,38 +69,41 @@
|
|
|
57
69
|
.empty { padding: 24px; opacity: 0.6; font-size: 13px; }
|
|
58
70
|
`}render(){return!this.results||this.results.length===0?h`<div class="empty">No results.</div>`:h`
|
|
59
71
|
<div class="count-header">${this.results.length} result${this.results.length===1?"":"s"}</div>
|
|
60
|
-
${this.results.map(e=>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
</
|
|
72
|
+
${as(this.results).map(e=>h`
|
|
73
|
+
${e.key?h`<div class="key-group-header">🔑 ${e.key} <span class="key-group-count">(${e.items.length})</span></div>`:""}
|
|
74
|
+
${e.items.map(t=>this.#e(t))}
|
|
75
|
+
`)}
|
|
76
|
+
`}#e(e){const t=e._table==="skills"&&e.folder==="builtin";return h`
|
|
77
|
+
<div class="row ${e.deprecated?"deprecated":""} ${e.paused?"paused":""}" @click=${()=>this.onSelect(e)}>
|
|
78
|
+
<input
|
|
79
|
+
type="checkbox"
|
|
80
|
+
class="row-checkbox"
|
|
81
|
+
.checked=${this.selectedIds?.has(e.id)??!1}
|
|
82
|
+
.disabled=${t}
|
|
83
|
+
title=${t?"Builtin skills can't be bulk-deprecated, paused, or deleted":""}
|
|
84
|
+
@click=${s=>{s.stopPropagation(),t||this.onToggleSelect(e)}}
|
|
85
|
+
/>
|
|
86
|
+
<div class="row-body">
|
|
87
|
+
<div class="top">
|
|
88
|
+
<span class="name">${e.name}</span>
|
|
89
|
+
<span class="meta">${ns(e.created_at)} · ${e.owner??"—"} · ${e.status}</span>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="desc">${e.description}</div>
|
|
92
|
+
<div class="tags">
|
|
93
|
+
<span class="type-badge">${e._table==="skills"?"skill":"memory"}</span>
|
|
94
|
+
${this.showFolder&&e.folder?h`<span class="folder-badge">📁 ${e.folder}</span>`:""}
|
|
95
|
+
${e.deprecated?h`<span class="deprecated-badge">deprecated</span>`:""}
|
|
96
|
+
${e.paused?h`<span class="paused-badge">paused</span>`:""}
|
|
97
|
+
${e.tags.map(s=>h`<span class="tag">${s}</span>`)}
|
|
84
98
|
</div>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
`}}customElements.define("result-list",os);function Se(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}var M=Se();function ft(r){M=r}var z={exec:()=>null};function N(r){let e=[];return t=>{let s=Math.max(0,Math.min(3,t-1)),i=e[s];return i||(i=r(s),e[s]=i),i}}function f(r,e=""){let t=typeof r=="string"?r:r.source,s={replace:(i,n)=>{let a=typeof n=="string"?n:n.source;return a=a.replace(v.caret,"$1"),t=t.replace(i,a),s},getRegex:()=>new RegExp(t,e)};return s}var ls=((r="")=>{try{return!!new RegExp("(?<=1)(?<!1)"+r)}catch{return!1}})(),v={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] +\S/,listReplaceTask:/^\[[ xX]\] +/,listTaskCheckbox:/\[[ xX]\]/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^<a /i,endATag:/^<\/a>/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^</,endAngleBracket:/>$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:r=>new RegExp(`^( {0,3}${r})((?:[ ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:N(r=>new RegExp(`^ {0,${r}}(?:[*+-]|\\d{1,9}[.)])((?:[ ][^\\n]*)?(?:\\n|$))`)),hrRegex:N(r=>new RegExp(`^ {0,${r}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`)),fencesBeginRegex:N(r=>new RegExp(`^ {0,${r}}(?:\`\`\`|~~~)`)),headingBeginRegex:N(r=>new RegExp(`^ {0,${r}}#`)),htmlBeginRegex:N(r=>new RegExp(`^ {0,${r}}<(?:[a-z].*>|!--)`,"i")),blockquoteBeginRegex:N(r=>new RegExp(`^ {0,${r}}>`))},cs=/^(?:[ \t]*(?:\n|$))+/,hs=/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,ps=/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,G=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,ds=/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,Ae=/ {0,3}(?:[*+-]|\d{1,9}[.)])/,bt=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,mt=f(bt).replace(/bull/g,Ae).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}(?:\s|$)/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),us=f(bt).replace(/bull/g,Ae).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}(?:\s|$)/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),Te=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table|[ \t]+\n)[^\n]+)*)/,gs=/^[^\n]+/,Ce=/(?!\s*\])(?:\\[\s\S]|[^\[\]\\])+/,fs=f(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",Ce).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),bs=f(/^(bull)([ \t][^\n]*?)?(?:\n|$)/).replace(/bull/g,Ae).getRegex(),le="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",Ee=/<!--(?:-?>|[\s\S]*?(?:-->|$))/,ms=f("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n*|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n*|$)|<![A-Z][\\s\\S]*?(?:>[^\\n]*\\n*|$)|<!\\[CDATA\\[[\\s\\S]*?(?:\\]\\]>[^\\n]*\\n*|$)|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$)|</(?!script|pre|style|textarea)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ ]*)+\\n|$))","i").replace("comment",Ee).replace("tag",le).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),xt=r=>f(Te).replace("hr",G).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list",r).replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",le).getRegex(),xs=xt(/ {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]/),ks=xt(/ {0,3}(?:[*+-]|\d{1,9}[.)])(?:[ \t]|\n|$)/),vs=f(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",ks).getRegex(),Re={blockquote:vs,code:hs,def:fs,fences:ps,heading:ds,hr:G,html:ms,lheading:mt,list:bs,newline:cs,paragraph:xs,table:z,text:gs},Qe=f("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",G).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3} )[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",le).getRegex(),$s={...Re,lheading:us,table:Qe,paragraph:f(Te).replace("hr",G).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",Qe).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*(?:\\n|$))|~~~)[^\\n]*(?:\\n|$)").replace("list"," {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]").replace("html","</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|textarea|!--)").replace("tag",le).getRegex()},ys={...Re,html:f(`^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)|<tag(?:"[^"]*"|'[^']*'|\\s[^'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))`).replace("comment",Ee).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:z,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:f(Te).replace("hr",G).replace("heading",` *#{1,6} *[^
|
|
102
|
+
]`).replace("lheading",mt).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},ws=/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,_s=/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,kt=/^( {2,}|\\)\n(?!\s*$)/,Ss=/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*_]|\b_|$)|[^ ](?= {2,}\n)))/,T=/[\p{P}\p{S}]/u,U=/[\s\p{P}\p{S}]/u,J=/[^\s\p{P}\p{S}]/u,As=f(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,U).getRegex(),Ts=/[\p{Pi}\p{Ps}"']/u,vt=/(?!~)[\p{P}\p{S}]/u,Cs=/(?!~)[\s\p{P}\p{S}]/u,Es=/(?:[^\s\p{P}\p{S}]|~)/u,Rs=f(/link|precode-code|html/,"g").replace("link",/\[(?:[^\[\]`]|(?<a>`+)[^`]+\k<a>(?!`))*?\]\((?:\\[\s\S]|[^\\\(\)]|\((?:\\[\s\S]|[^\\\(\)])*\))*\)/).replace("precode-",ls?"(?<!`)()":"(^^|[^`])").replace("code",/(?<b>`+)[^`]+\k<b>(?!`)/).replace("html",/<(?! )[^<>]*?>/).getRegex(),$t=/^(?:\*+(?:((?!\*)punct)|([^\s*]))?)|^_+(?:((?!_)punct)|([^\s_]))?/,zs=f($t,"u").replace(/punct/g,T).getRegex(),Ps=f($t,"u").replace(/punct/g,vt).getRegex(),Ls=/^(?:\*+(?:((?!\*)(?!openQuote)punct)|([^\s*]))?)|^_+(?:((?!_)(?!openQuote)punct)|([^\s_]))?/,Ds=f(Ls,"u").replace(/openQuote/g,Ts).replace(/punct/g,T).getRegex(),yt="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",Is=f(yt,"gu").replace(/notPunctSpace/g,J).replace(/punctSpace/g,U).replace(/punct/g,T).getRegex(),Os=f(yt,"gu").replace(/notPunctSpace/g,Es).replace(/punctSpace/g,Cs).replace(/punct/g,vt).getRegex(),Ms="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)[\\s](\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|(?:(?!\\*)punct|notPunctSpace)(\\*+)(?!\\*)(?=notPunctSpace)",Ns=f(Ms,"gu").replace(/notPunctSpace/g,J).replace(/punctSpace/g,U).replace(/punct/g,T).getRegex(),Fs=f("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,J).replace(/punctSpace/g,U).replace(/punct/g,T).getRegex(),qs="^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)[\\s](_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)|(?:(?!_)punct|notPunctSpace)(_+)(?!_)(?=notPunctSpace)",Us=f(qs,"gu").replace(/notPunctSpace/g,J).replace(/punctSpace/g,U).replace(/punct/g,T).getRegex(),Hs=f(/^~~?(?:((?!~)punct)|[^\s~])/,"u").replace(/punct/g,T).getRegex(),Bs="^[^~]+(?=[^~])|(?!~)punct(~~?)(?=[\\s]|$)|notPunctSpace(~~?)(?!~)(?=punctSpace|$)|(?!~)punctSpace(~~?)(?=notPunctSpace)|[\\s](~~?)(?!~)(?=punct)|(?!~)punct(~~?)(?!~)(?=punct)|notPunctSpace(~~?)(?=notPunctSpace)",js=f(Bs,"gu").replace(/notPunctSpace/g,J).replace(/punctSpace/g,U).replace(/punct/g,T).getRegex(),Zs=f(/\\(punct)/,"gu").replace(/punct/g,T).getRegex(),Ks=f(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),Qs=f(Ee).replace("(?:-->|$)","-->").getRegex(),Ws=f("^comment|^</[a-zA-Z][\\w:-]*\\s*>|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^<![a-zA-Z]+\\s[\\s\\S]*?>|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>").replace("comment",Qs).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),se=/(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+(?!`)[^`]*?`+(?!`)|``+(?=\])|[^\[\]\\`])*?/,Vs=f(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/).replace("label",se).replace("href",/<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]+|(?=\))/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),wt=f(/^!?\[(label)\]\[(ref)\]/).replace("label",se).replace("ref",Ce).getRegex(),_t=f(/^!?\[(ref)\](?:\[\])?/).replace("ref",Ce).getRegex(),Ys=f("reflink|nolink(?!\\()","g").replace("reflink",wt).replace("nolink",_t).getRegex(),We=/[hH][tT][tT][pP][sS]?|[fF][tT][pP]/,ze={_backpedal:z,anyPunctuation:Zs,autolink:Ks,blockSkip:Rs,br:kt,code:_s,del:z,delLDelim:z,delRDelim:z,emStrongLDelim:zs,emStrongRDelimAst:Is,emStrongRDelimUnd:Fs,escape:ws,link:Vs,nolink:_t,punctuation:As,reflink:wt,reflinkSearch:Ys,tag:Ws,text:Ss,url:z},Gs={...ze,emStrongLDelim:Ds,emStrongRDelimAst:Ns,emStrongRDelimUnd:Us,link:f(/^!?\[(label)\]\((.*?)\)/).replace("label",se).getRegex(),reflink:f(/^!?\[(label)\]\s*\[([^\]]*)\]/).replace("label",se).getRegex()},fe={...ze,emStrongRDelimAst:Os,emStrongLDelim:Ps,delLDelim:Hs,delRDelim:js,url:f(/^((?:protocol):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/).replace("protocol",We).replace("email",/[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/).getRegex(),_backpedal:/(?:[^?!.,:;*_'"~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_'"~)]+(?!$))+/,del:/^(~~?)(?=[^\s~])((?:\\[\s\S]|[^\\])*?(?:\\[\s\S]|[^\s~\\]))\1(?=[^~]|$)/,text:f(/^(`+|~+|[^`~])(?:(?=[`~])|(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|protocol:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/).replace("protocol",We).getRegex()},Js={...fe,br:f(kt).replace("{2,}","*").getRegex(),text:f(fe.text).replace("\\b_","\\b_| {2,}\\n").replace(/\{2,\}/g,"*").getRegex()},X={normal:Re,gfm:$s,pedantic:ys},Z={normal:ze,gfm:fe,breaks:Js,pedantic:Gs},Xs={"&":"&","<":"<",">":">",'"':""","'":"'"},Ve=r=>Xs[r];function A(r,e){if(e){if(v.escapeTest.test(r))return r.replace(v.escapeReplace,Ve)}else if(v.escapeTestNoEncode.test(r))return r.replace(v.escapeReplaceNoEncode,Ve);return r}function Ye(r){try{r=encodeURI(r).replace(v.percentDecode,"%")}catch{return null}return r}function Ge(r,e){let t=r.replace(v.findPipe,(n,a,o)=>{let l=!1,c=a;for(;--c>=0&&o[c]==="\\";)l=!l;return l?"|":" |"}),s=t.split(v.splitPipe),i=0;if(s[0].trim()||s.shift(),s.length>0&&!s.at(-1)?.trim()&&s.pop(),e)if(s.length>e)s.splice(e);else for(;s.length<e;)s.push("");for(;i<s.length;i++)s[i]=s[i].trim().replace(v.slashPipe,"|");return s}function C(r,e,t){let s=r.length;if(s===0)return"";let i=0;for(;i<s&&r.charAt(s-i-1)===e;)i++;return r.slice(0,s-i)}function Je(r){let e=r.split(`
|
|
88
103
|
`),t=e.length-1;for(;t>=0&&v.blankLine.test(e[t]);)t--;return e.length-t<=2?r:e.slice(0,t+1).join(`
|
|
89
|
-
`)}function
|
|
104
|
+
`)}function ei(r,e){if(r.indexOf(e[1])===-1)return-1;let t=0;for(let s=0;s<r.length;s++)if(r[s]==="\\")s++;else if(r[s]===e[0])t++;else if(r[s]===e[1]&&(t--,t<0))return s;return t>0?-2:-1}function ti(r,e=0){let t=e,s="";for(let i of r)if(i===" "){let n=4-t%4;s+=" ".repeat(n),t+=n}else s+=i,t++;return s}function Xe(r,e,t,s,i){let n=e.href,a=e.title||null,o=r[1].replace(i.other.outputLinkReplace,"$1");s.state.inLink=!0;let l={type:r[0].charAt(0)==="!"?"image":"link",raw:t,href:n,title:a,text:o,tokens:s.inlineTokens(o)};return s.state.inLink=!1,l}function si(r,e,t){let s=r.match(t.other.indentCodeCompensation);if(s===null)return e;let i=s[1];return e.split(`
|
|
90
105
|
`).map(n=>{let a=n.match(t.other.beginningSpace);if(a===null)return n;let[o]=a;return o.length>=i.length?n.slice(i.length):n}).join(`
|
|
91
|
-
`)}var ie=class{options;rules;lexer;constructor(r){this.options=r||M}space(r){let e=this.rules.block.newline.exec(r);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(r){let e=this.rules.block.code.exec(r);if(e){let t=this.options.pedantic?e[0]:Je(e[0]),s=t.replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t,codeBlockStyle:"indented",text:s}}}fences(r){let e=this.rules.block.fences.exec(r);if(e){let t=e[0],s=
|
|
106
|
+
`)}var ie=class{options;rules;lexer;constructor(r){this.options=r||M}space(r){let e=this.rules.block.newline.exec(r);if(e&&e[0].length>0)return{type:"space",raw:e[0]}}code(r){let e=this.rules.block.code.exec(r);if(e){let t=this.options.pedantic?e[0]:Je(e[0]),s=t.replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t,codeBlockStyle:"indented",text:s}}}fences(r){let e=this.rules.block.fences.exec(r);if(e){let t=e[0],s=si(t,e[3]||"",this.rules);return{type:"code",raw:t,lang:e[2]?e[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):e[2],text:s}}}heading(r){let e=this.rules.block.heading.exec(r);if(e){let t=e[2].trim();if(this.rules.other.endingHash.test(t)){let s=C(t,"#");(this.options.pedantic||!s||this.rules.other.endingSpaceChar.test(s))&&(t=s.trim())}return{type:"heading",raw:C(e[0],`
|
|
92
107
|
`),depth:e[1].length,text:t,tokens:this.lexer.inline(t)}}}hr(r){let e=this.rules.block.hr.exec(r);if(e)return{type:"hr",raw:C(e[0],`
|
|
93
108
|
`)}}blockquote(r){let e=this.rules.block.blockquote.exec(r);if(e){let t=C(e[0],`
|
|
94
109
|
`).split(`
|
|
@@ -102,7 +117,7 @@ ${d}`:d;let p=this.lexer.state.top;if(this.lexer.state.top=!0,this.lexer.blockTo
|
|
|
102
117
|
${b}`,i=i.substring(0,i.length-k.text.length)+H.text;break}else if(g?.type==="list"){let k=g,b=k.raw+`
|
|
103
118
|
`+t.join(`
|
|
104
119
|
`),S=this.list(b);n[n.length-1]=S,s=s.substring(0,s.length-g.raw.length)+S.raw,i=i.substring(0,i.length-k.raw.length)+S.raw,t=b.substring(n.at(-1).raw.length).split(`
|
|
105
|
-
`);continue}}return{type:"blockquote",raw:s,tokens:n,text:i}}}list(r){let e=this.rules.block.list.exec(r);if(e){let t=e[1].trim(),s=t.length>1,i={type:"list",raw:"",ordered:s,start:s?+t.slice(0,-1):"",loose:!1,items:[]};t=s?`\\d{1,9}\\${t.slice(-1)}`:`\\${t}`,this.options.pedantic&&(t=s?t:"[*+-]");let n=this.rules.other.listItemRegex(t),a=!1;for(;r;){let l=!1,c="",d="";if(!(e=n.exec(r))||this.rules.block.hr.test(r))break;c=e[0],r=r.substring(c.length);let p=
|
|
120
|
+
`);continue}}return{type:"blockquote",raw:s,tokens:n,text:i}}}list(r){let e=this.rules.block.list.exec(r);if(e){let t=e[1].trim(),s=t.length>1,i={type:"list",raw:"",ordered:s,start:s?+t.slice(0,-1):"",loose:!1,items:[]};t=s?`\\d{1,9}\\${t.slice(-1)}`:`\\${t}`,this.options.pedantic&&(t=s?t:"[*+-]");let n=this.rules.other.listItemRegex(t),a=!1;for(;r;){let l=!1,c="",d="";if(!(e=n.exec(r))||this.rules.block.hr.test(r))break;c=e[0],r=r.substring(c.length);let p=ti(e[2].split(`
|
|
106
121
|
`,1)[0],e[1].length),g=r.split(`
|
|
107
122
|
`,1)[0],k=!p.trim(),b=0;if(this.options.pedantic?(b=2,d=p.trimStart()):k?b=e[1].length+1:(b=p.search(this.rules.other.nonSpaceChar),b=b>4?1:b,d=p.slice(b),b+=e[1].length),k&&this.rules.other.blankLine.test(g)&&(c+=g+`
|
|
108
123
|
`,r=r.substring(g.length+1),l=!0),!l){let S=this.rules.other.nextBulletRegex(b),H=this.rules.other.hrRegex(b),Le=this.rules.other.fencesBeginRegex(b),De=this.rules.other.headingBeginRegex(b),Et=this.rules.other.htmlBeginRegex(b),Rt=this.rules.other.blockquoteBeginRegex(b);for(;r;){let ce=r.split(`
|
|
@@ -114,7 +129,7 @@ ${b}`,i=i.substring(0,i.length-k.text.length)+H.text;break}else if(g?.type==="li
|
|
|
114
129
|
`):[],n={type:"table",raw:C(e[0],`
|
|
115
130
|
`),header:[],align:[],rows:[]};if(t.length===s.length){for(let a of s)this.rules.other.tableAlignRight.test(a)?n.align.push("right"):this.rules.other.tableAlignCenter.test(a)?n.align.push("center"):this.rules.other.tableAlignLeft.test(a)?n.align.push("left"):n.align.push(null);for(let a=0;a<t.length;a++)n.header.push({text:t[a],tokens:this.lexer.inline(t[a]),header:!0,align:n.align[a]});for(let a of i)n.rows.push(Ge(a,n.header.length).map((o,l)=>({text:o,tokens:this.lexer.inline(o),header:!1,align:n.align[l]})));return n}}lheading(r){let e=this.rules.block.lheading.exec(r);if(e){let t=e[1].trim();return{type:"heading",raw:C(e[0],`
|
|
116
131
|
`),depth:e[2].charAt(0)==="="?1:2,text:t,tokens:this.lexer.inline(t)}}}paragraph(r){let e=this.rules.block.paragraph.exec(r);if(e){let t=e[1].charAt(e[1].length-1)===`
|
|
117
|
-
`?e[1].slice(0,-1):e[1];return{type:"paragraph",raw:e[0],text:t,tokens:this.lexer.inline(t)}}}text(r){let e=this.rules.block.text.exec(r);if(e)return{type:"text",raw:e[0],text:e[0],tokens:this.lexer.inline(e[0])}}escape(r){let e=this.rules.inline.escape.exec(r);if(e)return{type:"escape",raw:e[0],text:e[1]}}tag(r){let e=this.rules.inline.tag.exec(r);if(e)return!this.lexer.state.inLink&&this.rules.other.startATag.test(e[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(e[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(e[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(e[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:e[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:e[0]}}link(r){let e=this.rules.inline.link.exec(r);if(e){let t=e[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(t)){if(!this.rules.other.endAngleBracket.test(t))return;let n=C(t.slice(0,-1),"\\");if((t.length-n.length)%2===0)return}else{let n=
|
|
132
|
+
`?e[1].slice(0,-1):e[1];return{type:"paragraph",raw:e[0],text:t,tokens:this.lexer.inline(t)}}}text(r){let e=this.rules.block.text.exec(r);if(e)return{type:"text",raw:e[0],text:e[0],tokens:this.lexer.inline(e[0])}}escape(r){let e=this.rules.inline.escape.exec(r);if(e)return{type:"escape",raw:e[0],text:e[1]}}tag(r){let e=this.rules.inline.tag.exec(r);if(e)return!this.lexer.state.inLink&&this.rules.other.startATag.test(e[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(e[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(e[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(e[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:e[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:e[0]}}link(r){let e=this.rules.inline.link.exec(r);if(e){let t=e[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(t)){if(!this.rules.other.endAngleBracket.test(t))return;let n=C(t.slice(0,-1),"\\");if((t.length-n.length)%2===0)return}else{let n=ei(e[2],"()");if(n===-2)return;if(n>-1){let a=(e[0].indexOf("!")===0?5:4)+e[1].length+n;e[2]=e[2].substring(0,n),e[0]=e[0].substring(0,a).trim(),e[3]=""}}let s=e[2],i="";if(this.options.pedantic){let n=this.rules.other.pedanticHrefTitle.exec(s);n&&(s=n[1],i=n[3])}else i=e[3]?e[3].slice(1,-1):"";return s=s.trim(),this.rules.other.startAngleBracket.test(s)&&(this.options.pedantic&&!this.rules.other.endAngleBracket.test(t)?s=s.slice(1):s=s.slice(1,-1)),Xe(e,{href:s&&s.replace(this.rules.inline.anyPunctuation,"$1"),title:i&&i.replace(this.rules.inline.anyPunctuation,"$1")},e[0],this.lexer,this.rules)}}reflink(r,e){let t;if((t=this.rules.inline.reflink.exec(r))||(t=this.rules.inline.nolink.exec(r))){let s=(t[2]||t[1]).replace(this.rules.other.multipleSpaceGlobal," "),i=e[s.toLowerCase()];if(!i){let n=t[0].charAt(0);return{type:"text",raw:n,text:n}}return Xe(t,i,t[0],this.lexer,this.rules)}}emStrong(r,e,t=""){let s=this.rules.inline.emStrongLDelim.exec(r);if(!(!s||!s[1]&&!s[2]&&!s[3]&&!s[4]||s[4]&&t.match(this.rules.other.unicodeAlphaNumeric))&&(!(s[1]||s[3])||!t||this.rules.inline.punctuation.exec(t))){let i=[...s[0]].length-1,n,a,o=i,l=0,c=s[0][0],d=t===c,p=c==="*"?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(p.lastIndex=0,e=e.slice(-1*r.length+i);(s=p.exec(e))!==null;){if(n=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!n)continue;if(a=[...n].length,s[3]||s[4]){o+=a;continue}else if(s[5]||s[6]){if(i%3&&!((i+a)%3)){l+=a;continue}if(d)break}if(o-=a,o>0)continue;a=Math.min(a,a+o+l);let g=[...s[0]][0].length,k=r.slice(0,i+s.index+g+a);if(Math.min(i,a)%2){let S=k.slice(1,-1);return{type:"em",raw:k,text:S,tokens:this.lexer.inlineTokens(S)}}let b=k.slice(2,-2);return{type:"strong",raw:k,text:b,tokens:this.lexer.inlineTokens(b)}}}}codespan(r){let e=this.rules.inline.code.exec(r);if(e){let t=e[2].replace(this.rules.other.newLineCharGlobal," "),s=this.rules.other.nonSpaceChar.test(t),i=this.rules.other.startingSpaceChar.test(t)&&this.rules.other.endingSpaceChar.test(t);return s&&i&&(t=t.substring(1,t.length-1)),{type:"codespan",raw:e[0],text:t}}}br(r){let e=this.rules.inline.br.exec(r);if(e)return{type:"br",raw:e[0]}}del(r,e,t=""){let s=this.rules.inline.delLDelim.exec(r);if(s&&(!s[1]||!t||this.rules.inline.punctuation.exec(t))){let i=[...s[0]].length-1,n,a,o=i,l=this.rules.inline.delRDelim;for(l.lastIndex=0,e=e.slice(-1*r.length+i);(s=l.exec(e))!==null;){if(n=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!n||(a=[...n].length,a!==i))continue;if(s[3]||s[4]){o+=a;continue}if(o-=a,o>0)continue;a=Math.min(a,a+o);let c=[...s[0]][0].length,d=r.slice(0,i+s.index+c+a),p=d.slice(i,-i);return{type:"del",raw:d,text:p,tokens:this.lexer.inlineTokens(p)}}}}autolink(r){let e=this.rules.inline.autolink.exec(r);if(e){let t,s;return e[2]==="@"?(t=e[1],s="mailto:"+t):(t=e[1],s=t),{type:"link",raw:e[0],text:t,href:s,tokens:[{type:"text",raw:t,text:t}]}}}url(r){let e;if(e=this.rules.inline.url.exec(r)){let t,s;if(e[2]==="@")t=e[0],s="mailto:"+t;else{let i;do i=e[0],e[0]=this.rules.inline._backpedal.exec(e[0])?.[0]??"";while(i!==e[0]);t=e[0],e[1]==="www."?s="http://"+e[0]:s=e[0]}return{type:"link",raw:e[0],text:t,href:s,tokens:[{type:"text",raw:t,text:t}]}}}inlineText(r){let e=this.rules.inline.text.exec(r);if(e){let t=this.lexer.state.inRawBlock;return{type:"text",raw:e[0],text:e[0],escaped:t}}}},y=class be{tokens;options;state;inlineQueue;tokenizer;constructor(e){this.tokens=[],this.tokens.links=Object.create(null),this.options=e||M,this.options.tokenizer=this.options.tokenizer||new ie,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};let t={other:v,block:X.normal,inline:Z.normal};this.options.pedantic?(t.block=X.pedantic,t.inline=Z.pedantic):this.options.gfm&&(t.block=X.gfm,this.options.breaks?t.inline=Z.breaks:t.inline=Z.gfm),this.tokenizer.rules=t}static get rules(){return{block:X,inline:Z}}static lex(e,t){return new be(t).lex(e)}static lexInline(e,t){return new be(t).inlineTokens(e)}lex(e){e=e.replace(v.carriageReturn,`
|
|
118
133
|
`),this.blockTokens(e,this.tokens);for(let t=0;t<this.inlineQueue.length;t++){let s=this.inlineQueue[t];this.inlineTokens(s.src,s.tokens)}return this.inlineQueue=[],this.tokens}blockTokens(e,t=[],s=!1){this.tokenizer.lexer=this,this.options.pedantic&&(e=e.replace(v.tabCharGlobal," ").replace(v.spaceLine,""));let i=1/0;for(;e;){if(e.length<i)i=e.length;else{this.infiniteLoopError(e.charCodeAt(0));break}let n;if(this.options.extensions?.block?.some(o=>(n=o.call({lexer:this},e,t))?(e=e.substring(n.raw.length),t.push(n),!0):!1))continue;if(n=this.tokenizer.space(e)){e=e.substring(n.raw.length);let o=t.at(-1);n.raw.length===1&&o!==void 0?o.raw+=`
|
|
119
134
|
`:t.push(n);continue}if(n=this.tokenizer.code(e)){e=e.substring(n.raw.length);let o=t.at(-1);o?.type==="paragraph"||o?.type==="text"?(o.raw+=(o.raw.endsWith(`
|
|
120
135
|
`)?"":`
|
|
@@ -146,16 +161,16 @@ ${this.parser.parse(r)}</blockquote>
|
|
|
146
161
|
`}tablerow({text:r}){return`<tr>
|
|
147
162
|
${r}</tr>
|
|
148
163
|
`}tablecell(r){let e=this.parser.parseInline(r.tokens),t=r.header?"th":"td";return(r.align?`<${t} align="${r.align}">`:`<${t}>`)+e+`</${t}>
|
|
149
|
-
`}strong({tokens:r}){return`<strong>${this.parser.parseInline(r)}</strong>`}em({tokens:r}){return`<em>${this.parser.parseInline(r)}</em>`}codespan({text:r}){return`<code>${A(r,!0)}</code>`}br(r){return"<br>"}del({tokens:r}){return`<del>${this.parser.parseInline(r)}</del>`}link({href:r,title:e,tokens:t}){let s=this.parser.parseInline(t),i=Ye(r);if(i===null)return s;r=i;let n='<a href="'+r+'"';return e&&(n+=' title="'+A(e)+'"'),n+=">"+s+"</a>",n}image({href:r,title:e,text:t,tokens:s}){s&&(t=this.parser.parseInline(s,this.parser.textRenderer));let i=Ye(r);if(i===null)return A(t);r=i;let n=`<img src="${r}" alt="${A(t)}"`;return e&&(n+=` title="${A(e)}"`),n+=">",n}text(r){return"tokens"in r&&r.tokens?this.parser.parseInline(r.tokens):"escaped"in r&&r.escaped?r.text:A(r.text)}},Pe=class{strong({text:r}){return r}em({text:r}){return r}codespan({text:r}){return r}del({text:r}){return r}html({text:r}){return r}text({text:r}){return r}link({text:r}){return""+r}image({text:r}){return""+r}br(){return""}checkbox({raw:r}){return r}},w=class me{options;renderer;textRenderer;constructor(e){this.options=e||M,this.options.renderer=this.options.renderer||new re,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new Pe}static parse(e,t){return new me(t).parse(e)}static parseInline(e,t){return new me(t).parseInline(e)}parse(e){this.renderer.parser=this;let t="";for(let s=0;s<e.length;s++){let i=e[s];if(this.options.extensions?.renderers?.[i.type]){let a=i,o=this.options.extensions.renderers[a.type].call({parser:this},a);if(o!==!1||!["space","hr","heading","code","table","blockquote","list","checkbox","html","def","paragraph","text"].includes(a.type)){t+=o||"";continue}}let n=i;switch(n.type){case"space":{t+=this.renderer.space(n);break}case"hr":{t+=this.renderer.hr(n);break}case"heading":{t+=this.renderer.heading(n);break}case"code":{t+=this.renderer.code(n);break}case"table":{t+=this.renderer.table(n);break}case"blockquote":{t+=this.renderer.blockquote(n);break}case"list":{t+=this.renderer.list(n);break}case"checkbox":{t+=this.renderer.checkbox(n);break}case"html":{t+=this.renderer.html(n);break}case"def":{t+=this.renderer.def(n);break}case"paragraph":{t+=this.renderer.paragraph(n);break}case"text":{t+=this.renderer.text(n);break}default:{let a='Token with "'+n.type+'" type was not found.';if(this.options.silent)return console.error(a),"";throw new Error(a)}}}return t}parseInline(e,t=this.renderer){this.renderer.parser=this;let s="";for(let i=0;i<e.length;i++){let n=e[i];if(this.options.extensions?.renderers?.[n.type]){let o=this.options.extensions.renderers[n.type].call({parser:this},n);if(o!==!1||!["escape","html","link","image","checkbox","strong","em","codespan","br","del","text"].includes(n.type)){s+=o||"";continue}}let a=n;switch(a.type){case"escape":{s+=t.text(a);break}case"html":{s+=t.html(a);break}case"link":{s+=t.link(a);break}case"image":{s+=t.image(a);break}case"checkbox":{s+=t.checkbox(a);break}case"strong":{s+=t.strong(a);break}case"em":{s+=t.em(a);break}case"codespan":{s+=t.codespan(a);break}case"br":{s+=t.br(a);break}case"del":{s+=t.del(a);break}case"text":{s+=t.text(a);break}default:{let o='Token with "'+a.type+'" type was not found.';if(this.options.silent)return console.error(o),"";throw new Error(o)}}}return s}},K=class{options;block;constructor(r){this.options=r||M}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens","emStrongMask"]);static passThroughHooksRespectAsync=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(r){return r}postprocess(r){return r}processAllTokens(r){return r}emStrongMask(r){return r}provideLexer(r=this.block){return r?y.lex:y.lexInline}provideParser(r=this.block){return r?w.parse:w.parseInline}},
|
|
150
|
-
Please report this to https://github.com/markedjs/marked.`,r){let s="<p>An error occurred:</p><pre>"+A(t.message+"",!0)+"</pre>";return e?Promise.resolve(s):s}if(e)return Promise.reject(t);throw t}}},I=new
|
|
164
|
+
`}strong({tokens:r}){return`<strong>${this.parser.parseInline(r)}</strong>`}em({tokens:r}){return`<em>${this.parser.parseInline(r)}</em>`}codespan({text:r}){return`<code>${A(r,!0)}</code>`}br(r){return"<br>"}del({tokens:r}){return`<del>${this.parser.parseInline(r)}</del>`}link({href:r,title:e,tokens:t}){let s=this.parser.parseInline(t),i=Ye(r);if(i===null)return s;r=i;let n='<a href="'+r+'"';return e&&(n+=' title="'+A(e)+'"'),n+=">"+s+"</a>",n}image({href:r,title:e,text:t,tokens:s}){s&&(t=this.parser.parseInline(s,this.parser.textRenderer));let i=Ye(r);if(i===null)return A(t);r=i;let n=`<img src="${r}" alt="${A(t)}"`;return e&&(n+=` title="${A(e)}"`),n+=">",n}text(r){return"tokens"in r&&r.tokens?this.parser.parseInline(r.tokens):"escaped"in r&&r.escaped?r.text:A(r.text)}},Pe=class{strong({text:r}){return r}em({text:r}){return r}codespan({text:r}){return r}del({text:r}){return r}html({text:r}){return r}text({text:r}){return r}link({text:r}){return""+r}image({text:r}){return""+r}br(){return""}checkbox({raw:r}){return r}},w=class me{options;renderer;textRenderer;constructor(e){this.options=e||M,this.options.renderer=this.options.renderer||new re,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new Pe}static parse(e,t){return new me(t).parse(e)}static parseInline(e,t){return new me(t).parseInline(e)}parse(e){this.renderer.parser=this;let t="";for(let s=0;s<e.length;s++){let i=e[s];if(this.options.extensions?.renderers?.[i.type]){let a=i,o=this.options.extensions.renderers[a.type].call({parser:this},a);if(o!==!1||!["space","hr","heading","code","table","blockquote","list","checkbox","html","def","paragraph","text"].includes(a.type)){t+=o||"";continue}}let n=i;switch(n.type){case"space":{t+=this.renderer.space(n);break}case"hr":{t+=this.renderer.hr(n);break}case"heading":{t+=this.renderer.heading(n);break}case"code":{t+=this.renderer.code(n);break}case"table":{t+=this.renderer.table(n);break}case"blockquote":{t+=this.renderer.blockquote(n);break}case"list":{t+=this.renderer.list(n);break}case"checkbox":{t+=this.renderer.checkbox(n);break}case"html":{t+=this.renderer.html(n);break}case"def":{t+=this.renderer.def(n);break}case"paragraph":{t+=this.renderer.paragraph(n);break}case"text":{t+=this.renderer.text(n);break}default:{let a='Token with "'+n.type+'" type was not found.';if(this.options.silent)return console.error(a),"";throw new Error(a)}}}return t}parseInline(e,t=this.renderer){this.renderer.parser=this;let s="";for(let i=0;i<e.length;i++){let n=e[i];if(this.options.extensions?.renderers?.[n.type]){let o=this.options.extensions.renderers[n.type].call({parser:this},n);if(o!==!1||!["escape","html","link","image","checkbox","strong","em","codespan","br","del","text"].includes(n.type)){s+=o||"";continue}}let a=n;switch(a.type){case"escape":{s+=t.text(a);break}case"html":{s+=t.html(a);break}case"link":{s+=t.link(a);break}case"image":{s+=t.image(a);break}case"checkbox":{s+=t.checkbox(a);break}case"strong":{s+=t.strong(a);break}case"em":{s+=t.em(a);break}case"codespan":{s+=t.codespan(a);break}case"br":{s+=t.br(a);break}case"del":{s+=t.del(a);break}case"text":{s+=t.text(a);break}default:{let o='Token with "'+a.type+'" type was not found.';if(this.options.silent)return console.error(o),"";throw new Error(o)}}}return s}},K=class{options;block;constructor(r){this.options=r||M}static passThroughHooks=new Set(["preprocess","postprocess","processAllTokens","emStrongMask"]);static passThroughHooksRespectAsync=new Set(["preprocess","postprocess","processAllTokens"]);preprocess(r){return r}postprocess(r){return r}processAllTokens(r){return r}emStrongMask(r){return r}provideLexer(r=this.block){return r?y.lex:y.lexInline}provideParser(r=this.block){return r?w.parse:w.parseInline}},ii=class{defaults=Se();options=this.setOptions;parse=this.parseMarkdown(!0);parseInline=this.parseMarkdown(!1);Parser=w;Renderer=re;TextRenderer=Pe;Lexer=y;Tokenizer=ie;Hooks=K;constructor(...r){this.use(...r)}walkTokens(r,e){let t=[];for(let s of r)switch(t=t.concat(e.call(this,s)),s.type){case"table":{let i=s;for(let n of i.header)t=t.concat(this.walkTokens(n.tokens,e));for(let n of i.rows)for(let a of n)t=t.concat(this.walkTokens(a.tokens,e));break}case"list":{let i=s;t=t.concat(this.walkTokens(i.items,e));break}default:{let i=s;this.defaults.extensions?.childTokens?.[i.type]?this.defaults.extensions.childTokens[i.type].forEach(n=>{let a=i[n].flat(1/0);t=t.concat(this.walkTokens(a,e))}):i.tokens&&(t=t.concat(this.walkTokens(i.tokens,e)))}}return t}use(...r){let e=this.defaults.extensions||{renderers:{},childTokens:{}};return r.forEach(t=>{let s={...t};if(s.async=this.defaults.async||s.async||!1,t.extensions&&(t.extensions.forEach(i=>{if(!i.name)throw new Error("extension name required");if("renderer"in i){let n=e.renderers[i.name];n?e.renderers[i.name]=function(...a){let o=i.renderer.apply(this,a);return o===!1&&(o=n.apply(this,a)),o}:e.renderers[i.name]=i.renderer}if("tokenizer"in i){if(!i.level||i.level!=="block"&&i.level!=="inline")throw new Error("extension level must be 'block' or 'inline'");let n=e[i.level];n?n.unshift(i.tokenizer):e[i.level]=[i.tokenizer],i.start&&(i.level==="block"?e.startBlock?e.startBlock.push(i.start):e.startBlock=[i.start]:i.level==="inline"&&(e.startInline?e.startInline.push(i.start):e.startInline=[i.start]))}"childTokens"in i&&i.childTokens&&(e.childTokens[i.name]=i.childTokens)}),s.extensions=e),t.renderer){let i=this.defaults.renderer||new re(this.defaults);for(let n in t.renderer){if(!(n in i))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;let a=n,o=t.renderer[a],l=i[a];i[a]=(...c)=>{let d=o.apply(i,c);return d===!1&&(d=l.apply(i,c)),d||""}}s.renderer=i}if(t.tokenizer){let i=this.defaults.tokenizer||new ie(this.defaults);for(let n in t.tokenizer){if(!(n in i))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;let a=n,o=t.tokenizer[a],l=i[a];i[a]=(...c)=>{let d=o.apply(i,c);return d===!1&&(d=l.apply(i,c)),d}}s.tokenizer=i}if(t.hooks){let i=this.defaults.hooks||new K;for(let n in t.hooks){if(!(n in i))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;let a=n,o=t.hooks[a],l=i[a];K.passThroughHooks.has(n)?i[a]=c=>{if(this.defaults.async&&K.passThroughHooksRespectAsync.has(n))return(async()=>{let p=await o.call(i,c);return l.call(i,p)})();let d=o.call(i,c);return l.call(i,d)}:i[a]=(...c)=>{if(this.defaults.async)return(async()=>{let p=await o.apply(i,c);return p===!1&&(p=await l.apply(i,c)),p})();let d=o.apply(i,c);return d===!1&&(d=l.apply(i,c)),d}}s.hooks=i}if(t.walkTokens){let i=this.defaults.walkTokens,n=t.walkTokens;s.walkTokens=function(a){let o=[];return o.push(n.call(this,a)),i&&(o=o.concat(i.call(this,a))),o}}this.defaults={...this.defaults,...s}}),this}setOptions(r){return this.defaults={...this.defaults,...r},this}lexer(r,e){return y.lex(r,e??this.defaults)}parser(r,e){return w.parse(r,e??this.defaults)}parseMarkdown(r){return(e,t)=>{let s={...t},i={...this.defaults,...s},n=this.onError(!!i.silent,!!i.async);if(this.defaults.async===!0&&s.async===!1)return n(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(typeof e>"u"||e===null)return n(new Error("marked(): input parameter is undefined or null"));if(typeof e!="string")return n(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(e)+", string expected"));if(i.hooks&&(i.hooks.options=i,i.hooks.block=r),i.async)return(async()=>{let a=i.hooks?await i.hooks.preprocess(e):e,o=await(i.hooks?await i.hooks.provideLexer(r):r?y.lex:y.lexInline)(a,i),l=i.hooks?await i.hooks.processAllTokens(o):o;i.walkTokens&&await Promise.all(this.walkTokens(l,i.walkTokens));let c=await(i.hooks?await i.hooks.provideParser(r):r?w.parse:w.parseInline)(l,i);return i.hooks?await i.hooks.postprocess(c):c})().catch(n);try{i.hooks&&(e=i.hooks.preprocess(e));let a=(i.hooks?i.hooks.provideLexer(r):r?y.lex:y.lexInline)(e,i);i.hooks&&(a=i.hooks.processAllTokens(a)),i.walkTokens&&this.walkTokens(a,i.walkTokens);let o=(i.hooks?i.hooks.provideParser(r):r?w.parse:w.parseInline)(a,i);return i.hooks&&(o=i.hooks.postprocess(o)),o}catch(a){return n(a)}}}onError(r,e){return t=>{if(t.message+=`
|
|
165
|
+
Please report this to https://github.com/markedjs/marked.`,r){let s="<p>An error occurred:</p><pre>"+A(t.message+"",!0)+"</pre>";return e?Promise.resolve(s):s}if(e)return Promise.reject(t);throw t}}},I=new ii;function x(r,e){return I.parse(r,e)}x.options=x.setOptions=function(r){return I.setOptions(r),x.defaults=I.defaults,ft(x.defaults),x};x.getDefaults=Se;x.defaults=M;function ri(...r){return I.use(...r),x.defaults=I.defaults,ft(x.defaults),x}x.use=ri;x.walkTokens=function(r,e){return I.walkTokens(r,e)};x.parseInline=I.parseInline;x.Parser=w;x.parser=w.parse;x.Renderer=re;x.TextRenderer=Pe;x.Lexer=y;x.lexer=y.lex;x.Tokenizer=ie;x.Hooks=K;x.parse=x;x.options;x.setOptions;x.walkTokens;x.parseInline;w.parse;y.lex;/**
|
|
151
166
|
* @license
|
|
152
167
|
* Copyright 2017 Google LLC
|
|
153
168
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
154
|
-
*/const
|
|
169
|
+
*/const ni={CHILD:2},ai=r=>(...e)=>({_$litDirective$:r,values:e});class oi{constructor(e){}get _$AU(){return this._$AM._$AU}_$AT(e,t,s){this._$Ct=e,this._$AM=t,this._$Ci=s}_$AS(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}}/**
|
|
155
170
|
* @license
|
|
156
171
|
* Copyright 2017 Google LLC
|
|
157
172
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
158
|
-
*/class xe extends
|
|
173
|
+
*/class xe extends oi{constructor(e){if(super(e),this.it=m,e.type!==ni.CHILD)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(e){if(e===m||e==null)return this._t=void 0,this.it=e;if(e===D)return e;if(typeof e!="string")throw Error(this.constructor.directiveName+"() called with a non-string value");if(e===this.it)return this._t;this.it=e;const t=[e];return t.raw=t,this._t={_$litType$:this.constructor.resultType,strings:t,values:[]}}}xe.directiveName="unsafeHTML",xe.resultType=1;const li=ai(xe);let et=0;class ci extends _{static{this.properties={tags:{attribute:!1},active:{attribute:!1},onToggle:{attribute:!1},allowCreate:{attribute:!1},width:{attribute:!1}}}#e=new u("");#t=new u(!1);#s=new u(-1);#r;#i;static{this.styles=O`
|
|
159
174
|
:host {
|
|
160
175
|
display: inline-block;
|
|
161
176
|
position: relative;
|
|
@@ -334,7 +349,7 @@ Please report this to https://github.com/markedjs/marked.`,r){let s="<p>An error
|
|
|
334
349
|
</li>
|
|
335
350
|
`:h`<li class="empty">${this.allowCreate?"Type to create a tag":"No matches"}</li>`}
|
|
336
351
|
</ul>
|
|
337
|
-
`}}customElements.define("tag-multiselect",
|
|
352
|
+
`}}customElements.define("tag-multiselect",ci);let tt=0;class hi extends _{static{this.properties={options:{attribute:!1},value:{attribute:!1},onChange:{attribute:!1}}}#e=new u("");#t=new u(!1);#s=new u(-1);#r;#i;static{this.styles=O`
|
|
338
353
|
:host {
|
|
339
354
|
display: inline-block;
|
|
340
355
|
position: relative;
|
|
@@ -479,7 +494,7 @@ Please report this to https://github.com/markedjs/marked.`,r){let s="<p>An error
|
|
|
479
494
|
`:""}
|
|
480
495
|
${!t.length&&!this.#f?h`<li class="empty">No options</li>`:""}
|
|
481
496
|
</ul>
|
|
482
|
-
`}}customElements.define("status-select",
|
|
497
|
+
`}}customElements.define("status-select",hi);let pi=0;const di={top:"top: auto; bottom: anchor(top); left: anchor(center); right: auto; translate: -50% 0; margin-top: 0; margin-bottom: 6px;",bottom:"top: anchor(bottom); bottom: auto; left: anchor(center); right: auto; translate: -50% 0; margin-top: 6px; margin-bottom: 0;",left:"top: anchor(center); bottom: auto; left: auto; right: anchor(left); translate: 0 -50%; margin-right: 6px;",right:"top: anchor(center); bottom: auto; left: anchor(right); right: auto; translate: 0 -50%; margin-left: 6px;"},ui=["top","bottom","left","right"];class gi extends _{static{this.properties={text:{attribute:!1},placement:{attribute:!1}}}#e;#t=null;static{this.styles=O`
|
|
483
498
|
:host {
|
|
484
499
|
display: inline-flex;
|
|
485
500
|
position: relative;
|
|
@@ -563,7 +578,7 @@ Please report this to https://github.com/markedjs/marked.`,r){let s="<p>An error
|
|
|
563
578
|
translate: 0 -50%;
|
|
564
579
|
margin: 0 0 0 6px;
|
|
565
580
|
}
|
|
566
|
-
`}constructor(){super(),this.text="",this.placement="top",this.#e=`--help-tooltip-${++
|
|
581
|
+
`}constructor(){super(),this.text="",this.placement="top",this.#e=`--help-tooltip-${++pi}`}get#s(){const e=ui.filter(t=>t!==this.placement);return[this.placement,...e].map(t=>`--bubble-${t}`).join(", ")}disconnectedCallback(){this.#t&&clearTimeout(this.#t),super.disconnectedCallback()}#r(){this.#t&&(clearTimeout(this.#t),this.#t=null);const e=this.renderRoot.querySelector(".bubble");e&&!e.matches(":popover-open")&&e.showPopover()}#i(){this.#t=setTimeout(()=>{this.renderRoot.querySelector(".bubble")?.hidePopover()},100)}render(){return h`
|
|
567
582
|
<button
|
|
568
583
|
type="button"
|
|
569
584
|
class="icon"
|
|
@@ -579,13 +594,13 @@ Please report this to https://github.com/markedjs/marked.`,r){let s="<p>An error
|
|
|
579
594
|
role="tooltip"
|
|
580
595
|
class="bubble"
|
|
581
596
|
popover="manual"
|
|
582
|
-
style=${`position-anchor: ${this.#e}; position-try-fallbacks: ${this.#s}; ${
|
|
597
|
+
style=${`position-anchor: ${this.#e}; position-try-fallbacks: ${this.#s}; ${di[this.placement]}`}
|
|
583
598
|
@mouseenter=${()=>this.#r()}
|
|
584
599
|
@mouseleave=${()=>this.#i()}
|
|
585
600
|
>
|
|
586
601
|
${this.text}
|
|
587
602
|
</div>
|
|
588
|
-
`}}customElements.define("help-tooltip",
|
|
603
|
+
`}}customElements.define("help-tooltip",gi);const st="mem-bucket-detail-view-mode";function fi(r){return new Intl.DateTimeFormat("en-CA",{year:"numeric",month:"2-digit",day:"2-digit"}).format(new Date(r))}const bi=["stable","beta","unreviewed"],mi=["active","shipped","abandoned"],it=["plan","spec","sql","testing-todo","discovery","session-summary","other"],xi=["ticket","freeform"],$={description:"What this doc is / does, shown in search results and list views. For skills, this is also the only thing an agent sees before deciding whether to load the full body — so state both what it does and when to use it.",tags:"Free-form keywords used to filter and search. Not the only thing matched during full-text search, but the fastest way to narrow a list view.",status:`Lifecycle state — stable/beta/unreviewed for skills, active/shipped/abandoned for memory docs. Skills default to "unreviewed" if left unset, so low-trust content doesn't read as equivalent to a reviewed pattern.`,name:"The skill's identifier — becomes its containing folder name on disk. Lowercase letters, numbers, and hyphens only.",owner:"Who authored or maintains this skill — a person or team name, free text.",extends:"Name of another skill this one builds on or specializes, if any. Leave blank if it doesn't extend anything.",trigger_phrases:"Extra keyword phrases beyond the description that should surface this skill in search — phrasings a user might type that don't literally appear in the description text.",key:"The lookup handle for this memory doc — usually a ticket ID, sometimes a free-form name. One key can have several docs attached (a plan, a spec, session notes) all retrievable together.",key_type:'"ticket" if the key is a real ticket ID, "freeform" for a name like "Spot Chart Design".',doc_type:"What kind of content this is — shapes how it should be read (a plan reads differently than raw SQL or a session summary).",related_to:"Another key this doc relates to, if any — free text, not validated against existing keys."};function rt(r){return{description:r.description??"",tags:r.tags??[],status:r.status??"",name:r.name??"",owner:r.owner??"",extends:r.extends??"",trigger_phrases:r.trigger_phrases??[],key:r.key??"",key_type:r.key_type??"",doc_type:r.doc_type??"",related_to:r.related_to??""}}class ki extends _{constructor(){super(...arguments),this._viewMode=localStorage.getItem(st)??"markdown",this._editing=!1,this._draft=null,this._addingFrontmatter=!1,this._renaming=!1,this._renameValue=""}static{this.properties={selected:{attribute:!1},onChanged:{attribute:!1},facets:{attribute:!1},onTagClick:{attribute:!1},onFolderClick:{attribute:!1},onDateClick:{attribute:!1},_doc:{state:!0},_viewMode:{state:!0},_editing:{state:!0},_draft:{state:!0},_addingFrontmatter:{state:!0},_renaming:{state:!0},_renameValue:{state:!0}}}static{this.styles=O`
|
|
589
604
|
:host { display: block; padding: 16px; }
|
|
590
605
|
h2 { margin: 0 0 4px; font-size: 16px; }
|
|
591
606
|
.title-row {
|
|
@@ -875,7 +890,7 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
875
890
|
.value=${t.key_type}
|
|
876
891
|
@change=${i=>this.#n({key_type:i.target.value})}
|
|
877
892
|
>
|
|
878
|
-
${
|
|
893
|
+
${xi.map(i=>h`<option value=${i} ?selected=${i===t.key_type}>${i}</option>`)}
|
|
879
894
|
</select>
|
|
880
895
|
</div>
|
|
881
896
|
</div>
|
|
@@ -908,7 +923,7 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
908
923
|
<div class="field">
|
|
909
924
|
<label>status <help-tooltip .text=${$.status}></help-tooltip></label>
|
|
910
925
|
<status-select
|
|
911
|
-
.options=${[...new Set([...e?
|
|
926
|
+
.options=${[...new Set([...e?bi:mi,...s?.statuses??[]])]}
|
|
912
927
|
.value=${t.status}
|
|
913
928
|
.onChange=${i=>this.#n({status:i})}
|
|
914
929
|
></status-select>
|
|
@@ -963,7 +978,7 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
963
978
|
📁 ${e.folder}
|
|
964
979
|
</button>
|
|
965
980
|
</div>
|
|
966
|
-
`}#y(e,t){const s=e.created_at?
|
|
981
|
+
`}#y(e,t){const s=e.created_at?fi(e.created_at):null;return h`
|
|
967
982
|
<dl class="info-grid">
|
|
968
983
|
<dt>Description</dt>
|
|
969
984
|
<dd class="description">${e.description||"—"}</dd>
|
|
@@ -1051,9 +1066,9 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
1051
1066
|
Raw
|
|
1052
1067
|
</button>
|
|
1053
1068
|
</div>
|
|
1054
|
-
${this._viewMode==="markdown"?h`<div class="markdown-body">${
|
|
1069
|
+
${this._viewMode==="markdown"?h`<div class="markdown-body">${li(x.parse(e.body??"",{async:!1}))}</div>`:h`<pre>${e.raw_file??e.body}</pre>`}
|
|
1055
1070
|
<div class="source-path" title="click to copy" @click=${()=>this.#t()}>${e.source_path}</div>
|
|
1056
|
-
`}}customElements.define("detail-panel",
|
|
1071
|
+
`}}customElements.define("detail-panel",ki);class vi extends _{static{this.properties={defaultKind:{attribute:!1},lockKind:{attribute:!1},onAdded:{attribute:!1},onCancel:{attribute:!1}}}#e=new u("skill");#t=new u("");#s=new u([]);#r=new u(null);#i=new u("");#d=new u("");#a=new u("");#o=new u(!1);#c=new u(!1);#h=new u(!1);#n=new u("");static{this.styles=O`
|
|
1057
1072
|
:host { display: block; }
|
|
1058
1073
|
.backdrop {
|
|
1059
1074
|
position: fixed; inset: 0; background: var(--overlay);
|
|
@@ -1213,7 +1228,7 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
1213
1228
|
</div>
|
|
1214
1229
|
</div>
|
|
1215
1230
|
</div>
|
|
1216
|
-
`}}customElements.define("add-folder-modal",
|
|
1231
|
+
`}}customElements.define("add-folder-modal",vi);const $i=[{value:"all",label:"All"},{value:"skill",label:"Skills"},{value:"memory",label:"Memories"}],nt=[{value:"all",label:"Show all"},{value:"hide",label:"Hide"},{value:"only",label:"Only"}],yi={tags:[],statuses:[],owners:[],doc_types:[],key_types:[],folders:[]},wi={skill:[],memory:[]},St="mem-bucket-split-pct",At=20,Tt=80;function _i(){const r=Number(localStorage.getItem(St));return Number.isFinite(r)&&r>=At&&r<=Tt?r:40}const Ct="mem-bucket-theme",de=["system","light","dark"],Si={system:"◐",light:"☀",dark:"☾"},Ai={system:"Auto",light:"Light",dark:"Dark"};function Ti(){const r=localStorage.getItem(Ct);return r==="light"||r==="dark"?r:"system"}function at(r){r==="system"?document.documentElement.removeAttribute("data-theme"):document.documentElement.setAttribute("data-theme",r)}function Ci(){const r=location.hash.slice(1);if(!r)return{};const e=new URLSearchParams(r),t={},s=e.get("q");s&&(t.query=s);const i=e.get("type");(i==="skill"||i==="memory"||i==="all")&&(t.typeFilter=i);const n=e.getAll("tag");n.length&&(t.activeTags=n);const a=e.getAll("folder");a.length&&(t.activeFolders=a);const o=e.get("sort");o&&(t.sort=o);const l=e.get("deprecated");l==="0"?t.deprecatedFilter="hide":l==="1"&&(t.deprecatedFilter="only");const c=e.get("paused");c==="0"?t.pausedFilter="hide":c==="1"&&(t.pausedFilter="only");const d=e.get("date_from");d&&(t.dateFrom=d);const p=e.get("date_to");return p&&(t.dateTo=p),t}class Ei extends _{static{this.styles=O`
|
|
1217
1232
|
:host { display: flex; flex-direction: column; height: 100vh; }
|
|
1218
1233
|
.theme-toggle {
|
|
1219
1234
|
position: fixed;
|
|
@@ -1418,14 +1433,14 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
1418
1433
|
cursor: pointer;
|
|
1419
1434
|
}
|
|
1420
1435
|
.bulk-bar button.danger { border-color: var(--danger); color: var(--danger); }
|
|
1421
|
-
`}#e=
|
|
1436
|
+
`}#e=Ci();#t=new u(this.#e.query??"");#s=new u(this.#e.typeFilter??"all");#r=new u(this.#e.activeTags??[]);#i=new u(this.#e.activeFolders??[]);#d=new u([]);#a=new u(yi);#o=new u(null);#c=new u(wi);#h=new u(!1);#n=new u("");#p=new u(!1);#u=new u(_i());#f=new u(!1);#b=new u(this.#e.sort??"mtime_desc");#m=new u(this.#e.deprecatedFilter??"all");#k=new u(this.#e.pausedFilter??"all");#x=new u(this.#e.dateFrom??"");#v=new u(this.#e.dateTo??"");#g=new u(new Set);#y=new u(Ti());#_=new u(!1);#T=e=>this.#M(e);#C=()=>this.#N();constructor(){super(),new oe(this),at(this.#y.value)}#L(e){this.#y.set(e),localStorage.setItem(Ct,e),at(e)}#D(){const e=de[(de.indexOf(this.#y.value)+1)%de.length];this.#L(e)}async#I(){if(!this.#_.value){this.#_.set(!0);try{await fetch("/api/rebuild-cache",{method:"POST"}),await Promise.all([this.#w(),this.#$(),this.#l()])}finally{this.#_.set(!1)}}}#O(e){e.preventDefault(),this.#f.set(!0),document.addEventListener("pointermove",this.#T),document.addEventListener("pointerup",this.#C)}#M(e){const t=this.renderRoot.querySelector(".body-region");if(!t)return;const s=t.getBoundingClientRect(),i=(e.clientX-s.left)/s.width*100;this.#u.set(Math.min(Tt,Math.max(At,i)))}#N(){this.#f.set(!1),document.removeEventListener("pointermove",this.#T),document.removeEventListener("pointerup",this.#C),localStorage.setItem(St,String(this.#u.value))}#F=new is(()=>{const e=new URLSearchParams;this.#s.value!=="all"&&e.set("type",this.#s.value),this.#t.value.trim()&&e.set("q",this.#t.value.trim());for(const t of this.#r.value)e.append("tag",t);for(const t of this.#i.value)e.append("folder",t);return this.#b.value!=="mtime_desc"&&e.set("sort",this.#b.value),this.#m.value==="hide"?e.set("deprecated","0"):this.#m.value==="only"&&e.set("deprecated","1"),this.#k.value==="hide"?e.set("paused","0"):this.#k.value==="only"&&e.set("paused","1"),this.#x.value&&e.set("date_from",this.#x.value),this.#v.value&&e.set("date_to",this.#v.value),e.toString()});#S=()=>this.#q();connectedCallback(){super.connectedCallback(),this.#l(),this.#$(),this.#w(),window.addEventListener("focus",this.#S),document.addEventListener("visibilitychange",this.#S)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("focus",this.#S),document.removeEventListener("visibilitychange",this.#S)}#q(){document.visibilityState==="visible"&&(this.#l(),this.#$(),this.#w())}async#l(){const e=this.#F.value;history.replaceState(null,"",e?`#${e}`:location.pathname+location.search);const t=await fetch(`/api/entries?${e}`);this.#d.set(await t.json()),this.renderRoot.querySelector("result-list")?.scrollTo(0,0)}async#$(){const e=this.#s.value==="all"?"":`?type=${this.#s.value}`,t=await fetch(`/api/facets${e}`);this.#a.set(await t.json())}async#w(){const e=await fetch("/api/folders");this.#c.set(await e.json()),this.#p.set(!0)}#U(e){const t=this.#i.value;this.#i.set(t.includes(e)?t.filter(s=>s!==e):[...t,e]),this.#l()}async#H(e,t){if(t.stopPropagation(),window.prompt(`Type "${e.name}" to remove this ${e.kind} folder (files on disk are untouched):`)===e.name){this.#n.set(e.name);try{await fetch(`/api/folders/${e.kind}/${encodeURIComponent(e.name)}`,{method:"DELETE"}),this.#i.set(this.#i.value.filter(i=>i!==e.name)),await Promise.all([this.#w(),this.#$(),this.#l()])}finally{this.#n.set("")}}}#E(){this.#h.set(!1),this.#w(),this.#$(),this.#l()}#B(){return[...this.#c.value.skill,...this.#c.value.memory]}#j(e){this.#s.set(e),this.#r.set([]),this.#$(),this.#l()}#Z(e){const t=this.#r.value;this.#r.set(t.includes(e)?t.filter(s=>s!==e):[...t,e]),this.#l()}#K(e){this.#r.value.includes(e)||this.#r.set([...this.#r.value,e]),this.#l()}#Q(e){this.#i.value.includes(e)||this.#i.set([...this.#i.value,e]),this.#l()}#W(e){this.#x.set(e),this.#v.set(e),this.#l()}#V(e){this.#t.set(e.target.value),this.#l()}#Y(){this.#t.set(""),this.#l()}#G(e){this.#o.set({table:e._table,id:e.id})}#J(e){this.#b.set(e.target.value),this.#l()}#X(e){this.#m.set(e.target.value),this.#l()}#ee(e){this.#k.set(e.target.value),this.#l()}#te(e){this.#x.set(e.target.value),this.#l()}#se(e){this.#v.set(e.target.value),this.#l()}#ie(){this.#x.set(""),this.#v.set(""),this.#l()}#re(e){const t=new Set(this.#g.value);t.has(e.id)?t.delete(e.id):t.add(e.id),this.#g.set(t)}#ne(){return new Map(this.#d.value.map(e=>[e.id,e]))}#A(){const e=new Map,t=this.#ne();for(const s of this.#g.value){const i=t.get(s);if(!i)continue;const n=e.get(i._table)??[];n.push(s),e.set(i._table,n)}return e}async#R(e){const t=this.#A();await Promise.all([...t.entries()].map(([s,i])=>fetch(`/api/entries/${s}/bulk/deprecated`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({ids:i,deprecated:e})}))),this.#g.set(new Set),await this.#l()}async#z(e){const t=this.#A();await Promise.all([...t.entries()].map(([s,i])=>fetch(`/api/entries/${s}/bulk/paused`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({ids:i,paused:e})}))),this.#g.set(new Set),await this.#l()}async#ae(){if(!window.confirm(`Delete ${this.#g.value.size} doc(s)? This can't be undone.`))return;const e=this.#A();await Promise.all([...e.entries()].map(([t,s])=>fetch(`/api/entries/${t}/bulk/delete`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({ids:s})}))),this.#g.set(new Set),await this.#l()}#P(){const e=this.#y.value;return h`
|
|
1422
1437
|
<button
|
|
1423
1438
|
class="theme-toggle"
|
|
1424
|
-
title=${`Theme: ${
|
|
1439
|
+
title=${`Theme: ${Ai[e]} (click to change)`}
|
|
1425
1440
|
aria-label="Toggle color theme"
|
|
1426
1441
|
@click=${()=>this.#D()}
|
|
1427
1442
|
>
|
|
1428
|
-
${
|
|
1443
|
+
${Si[e]}
|
|
1429
1444
|
</button>
|
|
1430
1445
|
`}#oe(){const e=this.#_.value;return h`
|
|
1431
1446
|
<button
|
|
@@ -1496,7 +1511,7 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
1496
1511
|
`:""}
|
|
1497
1512
|
</div>
|
|
1498
1513
|
<div class="row type-toggle">
|
|
1499
|
-
${
|
|
1514
|
+
${$i.map(s=>h`
|
|
1500
1515
|
<button
|
|
1501
1516
|
class=${this.#s.value===s.value?"active":""}
|
|
1502
1517
|
@click=${()=>this.#j(s.value)}
|
|
@@ -1595,4 +1610,4 @@ This removes the doc's key, tags, and status — it drops out of key-based looku
|
|
|
1595
1610
|
.onAdded=${()=>this.#E()}
|
|
1596
1611
|
.onCancel=${()=>this.#h.set(!1)}
|
|
1597
1612
|
></add-folder-modal>`:""}
|
|
1598
|
-
`}}customElements.define("mem-bucket-app",
|
|
1613
|
+
`}}customElements.define("mem-bucket-app",Ei);
|
package/dist/client/index.html
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
:root[data-theme='dark'] { color-scheme: dark; }
|
|
28
28
|
body { font-family: system-ui, sans-serif; margin: 0; background: var(--bg); color: var(--fg); }
|
|
29
29
|
</style>
|
|
30
|
-
<script type="module" crossorigin src="/assets/index-
|
|
30
|
+
<script type="module" crossorigin src="/assets/index-DEOIrixV.js"></script>
|
|
31
31
|
</head>
|
|
32
32
|
<body>
|
|
33
33
|
<mem-bucket-app></mem-bucket-app>
|
|
@@ -5,8 +5,13 @@ import { writeMarkdownFile } from '../store/markdown-file.js';
|
|
|
5
5
|
import { slugify } from '../store/slug.js';
|
|
6
6
|
import { resolveWithinBase } from '../store/safe-path.js';
|
|
7
7
|
import { upsertFile, removeFile, scanSingleFolder, unregisterFolder, memorySyncSpec } from '../store/sync.js';
|
|
8
|
-
import { SearchQueryError } from '../store/search.js';
|
|
8
|
+
import { SearchQueryError, sanitizeFtsQuery } from '../store/search.js';
|
|
9
9
|
import { normalizeKey } from '../types.js';
|
|
10
|
+
/** Uppercases and strips everything but letters/digits — used to compare keys that differ only in
|
|
11
|
+
* punctuation/whitespace formatting (e.g. `RMXS-15` and `RMXS15` strip to the same `RMXS15`). */
|
|
12
|
+
export function stripKey(s) {
|
|
13
|
+
return s.toUpperCase().replace(/[^A-Z0-9]/g, '');
|
|
14
|
+
}
|
|
10
15
|
function rowToDoc(row) {
|
|
11
16
|
return {
|
|
12
17
|
id: row.id,
|
|
@@ -104,7 +109,7 @@ export class MemoryRepository {
|
|
|
104
109
|
search(query, opts = {}) {
|
|
105
110
|
const { docType, status, folder, tag, limit = 20, offset = 0, includePaused = false } = opts;
|
|
106
111
|
const conditions = [];
|
|
107
|
-
const params = [query];
|
|
112
|
+
const params = [sanitizeFtsQuery(query)];
|
|
108
113
|
if (docType) {
|
|
109
114
|
conditions.push('m.doc_type = ?');
|
|
110
115
|
params.push(docType);
|
|
@@ -159,6 +164,35 @@ export class MemoryRepository {
|
|
|
159
164
|
.filter((r) => !prefix || r.key.startsWith(prefix))
|
|
160
165
|
.map((r) => ({ key: r.key, docCount: r.doc_count }));
|
|
161
166
|
}
|
|
167
|
+
/**
|
|
168
|
+
* Fuzzy key lookup for a partial/drifted key, comparing keys with all punctuation stripped so
|
|
169
|
+
* `RMXS15` and `RMXS-15` are treated as the same candidate. Ranked: exact stripped match first,
|
|
170
|
+
* then stripped-prefix, then stripped-substring; alphabetical tiebreak within each tier.
|
|
171
|
+
*/
|
|
172
|
+
suggestKeys(partial, limit = 5) {
|
|
173
|
+
const strippedPartial = stripKey(partial);
|
|
174
|
+
if (!strippedPartial)
|
|
175
|
+
return [];
|
|
176
|
+
const rows = this.db
|
|
177
|
+
.prepare(`SELECT key, COUNT(*) as doc_count FROM memory_docs GROUP BY key`)
|
|
178
|
+
.all();
|
|
179
|
+
return rows
|
|
180
|
+
.map((r) => ({ key: r.key, docCount: r.doc_count, stripped: stripKey(r.key) }))
|
|
181
|
+
.filter((r) => r.stripped.includes(strippedPartial))
|
|
182
|
+
.sort((a, b) => {
|
|
183
|
+
const aExact = a.stripped === strippedPartial ? 0 : 1;
|
|
184
|
+
const bExact = b.stripped === strippedPartial ? 0 : 1;
|
|
185
|
+
if (aExact !== bExact)
|
|
186
|
+
return aExact - bExact;
|
|
187
|
+
const aPrefix = a.stripped.startsWith(strippedPartial) ? 0 : 1;
|
|
188
|
+
const bPrefix = b.stripped.startsWith(strippedPartial) ? 0 : 1;
|
|
189
|
+
if (aPrefix !== bPrefix)
|
|
190
|
+
return aPrefix - bPrefix;
|
|
191
|
+
return a.key.localeCompare(b.key);
|
|
192
|
+
})
|
|
193
|
+
.slice(0, limit)
|
|
194
|
+
.map(({ key, docCount }) => ({ key, docCount }));
|
|
195
|
+
}
|
|
162
196
|
create(input) {
|
|
163
197
|
const normalizedKey = normalizeKey(input.key);
|
|
164
198
|
const id = `${slugify(normalizedKey)}-${slugify(input.description)}-${randomUUID().slice(0, 8)}`;
|
package/dist/src/memory/tools.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { stripKey } from './repository.js';
|
|
2
3
|
import { statusSchema } from '../shared/status.js';
|
|
4
|
+
import { normalizeKey } from '../types.js';
|
|
3
5
|
const MEMORY_DOC_TYPES = ['plan', 'spec', 'sql', 'testing-todo', 'discovery', 'session-summary', 'other'];
|
|
4
6
|
const MEMORY_KEY_TYPES = ['ticket', 'freeform'];
|
|
5
7
|
const MEMORY_STATUS_DEFAULTS = ['active', 'shipped', 'abandoned'];
|
|
@@ -66,8 +68,17 @@ export function registerMemoryTools(mcp, repo) {
|
|
|
66
68
|
...(multiFolder ? { folder: z.string().describe(`which configured memory folder to write into: ${folderNames}`) } : {}),
|
|
67
69
|
}, async ({ key, key_type, doc_type, description, body, tags, status, related_to, subfolder, folder }) => {
|
|
68
70
|
try {
|
|
71
|
+
const normalized = normalizeKey(key);
|
|
72
|
+
const strippedNew = stripKey(normalized);
|
|
73
|
+
const nearDuplicate = repo
|
|
74
|
+
.suggestKeys(key, 3)
|
|
75
|
+
.find((m) => stripKey(m.key) === strippedNew && m.key !== normalized);
|
|
69
76
|
const doc = repo.create({ key, key_type, doc_type, description, body, tags, status, related_to, subfolder, folder });
|
|
70
|
-
|
|
77
|
+
const result = { ...doc };
|
|
78
|
+
if (nearDuplicate) {
|
|
79
|
+
result.key_warning = `A similarly-formatted key "${nearDuplicate.key}" already exists with ${nearDuplicate.docCount} doc(s) — did you mean to use that key instead of "${normalized}"? This doc was still created under "${normalized}".`;
|
|
80
|
+
}
|
|
81
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
71
82
|
}
|
|
72
83
|
catch (err) {
|
|
73
84
|
return { content: [{ type: 'text', text: err.message }], isError: true };
|
|
@@ -251,6 +251,36 @@ body: >-
|
|
|
251
251
|
conversation, or ask if it's genuinely unclear.
|
|
252
252
|
|
|
253
253
|
|
|
254
|
+
**Tags are for cross-cutting labels, not grouping.** `key` is the one and
|
|
255
|
+
|
|
256
|
+
only grouping concept for memory docs — never add a doc's own key (or a
|
|
257
|
+
|
|
258
|
+
related doc's key) as a tag to link things together; that's what `key`
|
|
259
|
+
|
|
260
|
+
already does. Use `tags` for labels that cut across many keys (e.g.
|
|
261
|
+
|
|
262
|
+
`viz-team`, `datalab`), not as a second grouping axis.
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
**Before creating a doc under a `ticket`-type key, check for an existing
|
|
266
|
+
|
|
267
|
+
near-match first** — e.g. `memory_list("RMXS-15")` or `memory_search` —
|
|
268
|
+
|
|
269
|
+
rather than writing a fresh key that only differs in punctuation or
|
|
270
|
+
|
|
271
|
+
casing (`RMXS15` vs `RMXS-15` vs `Rmxs 15` are three different keys to
|
|
272
|
+
|
|
273
|
+
this system, even though they're clearly meant to be the same ticket).
|
|
274
|
+
|
|
275
|
+
Reuse the existing key's exact casing/format if one is found.
|
|
276
|
+
|
|
277
|
+
`memory_create`'s response includes a `key_warning` field if it detects a
|
|
278
|
+
|
|
279
|
+
likely near-duplicate after the fact — treat that as a signal to double
|
|
280
|
+
|
|
281
|
+
check, not to ignore.
|
|
282
|
+
|
|
283
|
+
|
|
254
284
|
### Saving a session
|
|
255
285
|
|
|
256
286
|
|
|
@@ -4,7 +4,7 @@ import { writeMarkdownFile } from '../store/markdown-file.js';
|
|
|
4
4
|
import { assertValidSkillName } from '../store/skill-name.js';
|
|
5
5
|
import { resolveWithinBase } from '../store/safe-path.js';
|
|
6
6
|
import { upsertFile, removeFile, scanSingleFolder, unregisterFolder, skillSyncSpec } from '../store/sync.js';
|
|
7
|
-
import { SearchQueryError } from '../store/search.js';
|
|
7
|
+
import { SearchQueryError, sanitizeFtsQuery } from '../store/search.js';
|
|
8
8
|
function rowToDoc(row) {
|
|
9
9
|
return {
|
|
10
10
|
name: row.id,
|
|
@@ -115,7 +115,7 @@ export class SkillRepository {
|
|
|
115
115
|
search(query, opts = {}) {
|
|
116
116
|
const { folder, status, owner, tag, limit = 20, offset = 0, includePaused = false } = opts;
|
|
117
117
|
const conditions = [];
|
|
118
|
-
const params = [query];
|
|
118
|
+
const params = [sanitizeFtsQuery(query)];
|
|
119
119
|
if (folder) {
|
|
120
120
|
conditions.push('s.folder = ?');
|
|
121
121
|
params.push(folder);
|
package/dist/src/store/db.js
CHANGED
|
@@ -46,6 +46,7 @@ export function openCache(dbPath) {
|
|
|
46
46
|
description,
|
|
47
47
|
body,
|
|
48
48
|
tags,
|
|
49
|
+
key,
|
|
49
50
|
tokenize = 'porter unicode61'
|
|
50
51
|
);
|
|
51
52
|
|
|
@@ -70,9 +71,34 @@ export function openCache(dbPath) {
|
|
|
70
71
|
['paused', 'INTEGER NOT NULL DEFAULT 0'],
|
|
71
72
|
['created_at', 'TEXT'],
|
|
72
73
|
]);
|
|
74
|
+
ensureSearchIndexHasKeyColumn(db);
|
|
73
75
|
backfillSearchIndex(db);
|
|
74
76
|
return db;
|
|
75
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* search_index is an FTS5 virtual table — existing cache files created before the `key` column
|
|
80
|
+
* existed need it added. FTS5 doesn't support ALTER TABLE ADD COLUMN reliably across versions, and
|
|
81
|
+
* search_index is a disposable cache (rebuilt from skills/memory_docs, not the source of truth), so
|
|
82
|
+
* the simplest safe migration is: drop and recreate with the new schema, then let backfillSearchIndex
|
|
83
|
+
* repopulate everything.
|
|
84
|
+
*/
|
|
85
|
+
function ensureSearchIndexHasKeyColumn(db) {
|
|
86
|
+
const cols = db.prepare(`PRAGMA table_info(search_index)`).all();
|
|
87
|
+
if (cols.length === 0 || cols.some((c) => c.name === 'key'))
|
|
88
|
+
return; // fresh table, or already migrated
|
|
89
|
+
db.exec(`DROP TABLE search_index`);
|
|
90
|
+
db.exec(`
|
|
91
|
+
CREATE VIRTUAL TABLE search_index USING fts5(
|
|
92
|
+
ref_table UNINDEXED,
|
|
93
|
+
ref_id UNINDEXED,
|
|
94
|
+
description,
|
|
95
|
+
body,
|
|
96
|
+
tags,
|
|
97
|
+
key,
|
|
98
|
+
tokenize = 'porter unicode61'
|
|
99
|
+
);
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
76
102
|
/** Migration for cache files created before a given column existed. Safe to call every startup. */
|
|
77
103
|
function ensureColumns(db, table, columns) {
|
|
78
104
|
const existing = new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name));
|
|
@@ -87,16 +113,16 @@ function backfillSearchIndex(db) {
|
|
|
87
113
|
if (indexed > 0)
|
|
88
114
|
return;
|
|
89
115
|
const skillRows = db.prepare(`SELECT id, description, body, tags FROM skills`).all();
|
|
90
|
-
const memoryRows = db.prepare(`SELECT id, description, body, tags FROM memory_docs`).all();
|
|
116
|
+
const memoryRows = db.prepare(`SELECT id, key, description, body, tags FROM memory_docs`).all();
|
|
91
117
|
if (skillRows.length === 0 && memoryRows.length === 0)
|
|
92
118
|
return;
|
|
93
|
-
const insert = db.prepare(`INSERT INTO search_index (ref_table, ref_id, description, body, tags) VALUES (?, ?, ?, ?, ?)`);
|
|
119
|
+
const insert = db.prepare(`INSERT INTO search_index (ref_table, ref_id, description, body, tags, key) VALUES (?, ?, ?, ?, ?, ?)`);
|
|
94
120
|
const insertAll = db.transaction(() => {
|
|
95
121
|
for (const row of skillRows) {
|
|
96
|
-
insert.run('skills', row.id, row.description, row.body, flattenTags(row.tags));
|
|
122
|
+
insert.run('skills', row.id, row.description, row.body, flattenTags(row.tags), '');
|
|
97
123
|
}
|
|
98
124
|
for (const row of memoryRows) {
|
|
99
|
-
insert.run('memory_docs', row.id, row.description, row.body, flattenTags(row.tags));
|
|
125
|
+
insert.run('memory_docs', row.id, row.description, row.body, flattenTags(row.tags), row.key);
|
|
100
126
|
}
|
|
101
127
|
});
|
|
102
128
|
insertAll();
|
package/dist/src/store/search.js
CHANGED
|
@@ -7,6 +7,27 @@ export class SearchQueryError extends Error {
|
|
|
7
7
|
this.name = 'SearchQueryError';
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
|
+
const FTS_BOOLEAN_KEYWORDS = new Set(['AND', 'OR', 'NOT']);
|
|
11
|
+
/**
|
|
12
|
+
* Auto-quotes bareword tokens that contain punctuation (e.g. a bare hyphenated ticket key like
|
|
13
|
+
* `RMXS-15`) so FTS5 doesn't interpret a bare `-` as its NOT/column-filter operator and silently
|
|
14
|
+
* return zero rows. Already-quoted phrases, `AND`/`OR`/`NOT` operators, and `prefix*` wildcards are
|
|
15
|
+
* left untouched so existing power-user query syntax keeps working exactly as before.
|
|
16
|
+
*/
|
|
17
|
+
export function sanitizeFtsQuery(query) {
|
|
18
|
+
const tokens = query.match(/"[^"]*"|\S+/g) ?? [];
|
|
19
|
+
return tokens
|
|
20
|
+
.map((token) => {
|
|
21
|
+
if (token.startsWith('"'))
|
|
22
|
+
return token; // already an explicit phrase
|
|
23
|
+
if (FTS_BOOLEAN_KEYWORDS.has(token.toUpperCase()))
|
|
24
|
+
return token; // boolean operator
|
|
25
|
+
if (/^[A-Za-z0-9_*]+$/.test(token))
|
|
26
|
+
return token; // plain word or prefix* wildcard — no punctuation
|
|
27
|
+
return `"${token.replace(/"/g, '""')}"`; // punctuation present (hyphen, colon, etc.) — quote it literally
|
|
28
|
+
})
|
|
29
|
+
.join(' ');
|
|
30
|
+
}
|
|
10
31
|
/**
|
|
11
32
|
* Full-text search over the shared FTS5 index, optionally scoped to one ref_table.
|
|
12
33
|
* `query` is passed through as raw FTS5 MATCH syntax — supports `AND`/`OR`/`NOT`,
|
|
@@ -16,6 +37,7 @@ export class SearchQueryError extends Error {
|
|
|
16
37
|
*/
|
|
17
38
|
export function searchIndex(db, query, opts = {}) {
|
|
18
39
|
const { table, limit = 20, offset = 0 } = opts;
|
|
40
|
+
const sanitized = sanitizeFtsQuery(query);
|
|
19
41
|
try {
|
|
20
42
|
const rows = db
|
|
21
43
|
.prepare(`SELECT ref_table, ref_id,
|
|
@@ -25,7 +47,7 @@ export function searchIndex(db, query, opts = {}) {
|
|
|
25
47
|
WHERE search_index MATCH ? ${table ? 'AND ref_table = ?' : ''}
|
|
26
48
|
ORDER BY bm25(search_index)
|
|
27
49
|
LIMIT ? OFFSET ?`)
|
|
28
|
-
.all(...(table ? [
|
|
50
|
+
.all(...(table ? [sanitized, table, limit, offset] : [sanitized, limit, offset]));
|
|
29
51
|
return rows;
|
|
30
52
|
}
|
|
31
53
|
catch (err) {
|
|
@@ -97,6 +119,7 @@ export function searchByDate(db, start, end, opts = {}) {
|
|
|
97
119
|
* per table); use skill_search/memory_search directly when filtering by those.
|
|
98
120
|
*/
|
|
99
121
|
export function searchCombined(db, query, limit = 20, offset = 0) {
|
|
122
|
+
const sanitized = sanitizeFtsQuery(query);
|
|
100
123
|
try {
|
|
101
124
|
return db
|
|
102
125
|
.prepare(`SELECT ref_table, ref_id AS id,
|
|
@@ -110,7 +133,7 @@ export function searchCombined(db, query, limit = 20, offset = 0) {
|
|
|
110
133
|
WHERE search_index MATCH ?
|
|
111
134
|
ORDER BY bm25(search_index)
|
|
112
135
|
LIMIT ? OFFSET ?`)
|
|
113
|
-
.all(
|
|
136
|
+
.all(sanitized, limit, offset);
|
|
114
137
|
}
|
|
115
138
|
catch (err) {
|
|
116
139
|
throw new SearchQueryError(query, err);
|
package/dist/src/store/sync.js
CHANGED
|
@@ -114,7 +114,7 @@ export function upsertFile(db, spec, filePath) {
|
|
|
114
114
|
db.prepare(`INSERT INTO ${spec.table} (${cols.join(', ')}) VALUES (${placeholders})
|
|
115
115
|
ON CONFLICT(id) DO UPDATE SET ${updateClause}`).run(...values);
|
|
116
116
|
db.prepare(`DELETE FROM search_index WHERE ref_table = ? AND ref_id = ?`).run(spec.table, id);
|
|
117
|
-
db.prepare(`INSERT INTO search_index (ref_table, ref_id, description, body, tags) VALUES (?, ?, ?, ?, ?)`).run(spec.table, id, String(row.description ?? ''), parsed.body, flattenTags(String(row.tags ?? '[]')));
|
|
117
|
+
db.prepare(`INSERT INTO search_index (ref_table, ref_id, description, body, tags, key) VALUES (?, ?, ?, ?, ?, ?)`).run(spec.table, id, String(row.description ?? ''), parsed.body, flattenTags(String(row.tags ?? '[]')), String(row.key ?? ''));
|
|
118
118
|
db.prepare(`DELETE FROM doc_dates WHERE ref_table = ? AND ref_id = ?`).run(spec.table, id);
|
|
119
119
|
const dates = new Set(extractDates(parsed.body));
|
|
120
120
|
// created_at is a UTC instant; convert to the OS-local calendar date so it
|
package/dist/src/web/routes.js
CHANGED
|
@@ -4,7 +4,9 @@ import path from 'node:path';
|
|
|
4
4
|
import express from 'express';
|
|
5
5
|
import matter from 'gray-matter';
|
|
6
6
|
import { saveFolder, removeFolder as removeFolderFromConfig, sanitizeFolderName } from '../config.js';
|
|
7
|
+
import { stripKey } from '../memory/repository.js';
|
|
7
8
|
import { initialScan } from '../store/sync.js';
|
|
9
|
+
import { sanitizeFtsQuery } from '../store/search.js';
|
|
8
10
|
function asArray(v) {
|
|
9
11
|
if (v === undefined)
|
|
10
12
|
return [];
|
|
@@ -16,7 +18,7 @@ function tagWhereClause(tags) {
|
|
|
16
18
|
const clauses = tags.map(() => `EXISTS (SELECT 1 FROM json_each(tags) WHERE value = ?)`);
|
|
17
19
|
return { clause: ` AND ${clauses.join(' AND ')}`, params: tags };
|
|
18
20
|
}
|
|
19
|
-
function queryEntries(db, req) {
|
|
21
|
+
function queryEntries(db, memoryRepo, req) {
|
|
20
22
|
const type = req.query.type ?? 'all';
|
|
21
23
|
const tags = asArray(req.query.tag);
|
|
22
24
|
const statuses = asArray(req.query.status);
|
|
@@ -31,10 +33,13 @@ function queryEntries(db, req) {
|
|
|
31
33
|
const paused = pausedParam === '0' || pausedParam === '1' ? pausedParam : undefined;
|
|
32
34
|
const dateFrom = req.query.date_from?.trim() || undefined;
|
|
33
35
|
const dateTo = req.query.date_to?.trim() || undefined;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// If the typed query matches an existing key (ignoring punctuation/case), short-circuit straight
|
|
37
|
+
// to "every doc under this key" for memory_docs — this bypasses FTS/bm25 ranking entirely, so a
|
|
38
|
+
// doc whose body never repeats the literal key text (e.g. a session summary) still shows up.
|
|
39
|
+
// Skills have no `key` concept, so they still go through the normal FTS path below when q is set.
|
|
40
|
+
const keyMatch = q ? memoryRepo.suggestKeys(q, 1).find((m) => stripKey(m.key) === stripKey(q)) : undefined;
|
|
41
|
+
const matchedIds = q && !keyMatch ? matchSearch(db, q) : null;
|
|
42
|
+
if (q && !keyMatch && matchedIds && matchedIds.skills.size === 0 && matchedIds.memory_docs.size === 0) {
|
|
38
43
|
return [];
|
|
39
44
|
}
|
|
40
45
|
const dateIds = dateFrom || dateTo ? matchDateRange(db, dateFrom, dateTo) : null;
|
|
@@ -43,10 +48,10 @@ function queryEntries(db, req) {
|
|
|
43
48
|
}
|
|
44
49
|
const results = [];
|
|
45
50
|
if (type === 'skill' || type === 'all') {
|
|
46
|
-
results.push(...queryTable(db, 'skills', { tags, statuses, owners, docTypes: [], keyTypes: [], folders, deprecated, paused }, intersectIds(matchedIds?.skills, dateIds?.skills)));
|
|
51
|
+
results.push(...queryTable(db, 'skills', { tags, statuses, owners, docTypes: [], keyTypes: [], folders, keys: [], deprecated, paused }, intersectIds(matchedIds?.skills, dateIds?.skills)));
|
|
47
52
|
}
|
|
48
53
|
if (type === 'memory' || type === 'all') {
|
|
49
|
-
results.push(...queryTable(db, 'memory_docs', { tags, statuses, owners: [], docTypes, keyTypes, folders, deprecated, paused }, intersectIds(matchedIds?.memory_docs, dateIds?.memory_docs)));
|
|
54
|
+
results.push(...queryTable(db, 'memory_docs', { tags, statuses, owners: [], docTypes, keyTypes, folders, deprecated, paused, keys: keyMatch ? [keyMatch.key] : [] }, keyMatch ? undefined : intersectIds(matchedIds?.memory_docs, dateIds?.memory_docs)));
|
|
50
55
|
}
|
|
51
56
|
const sort = req.query.sort ?? 'mtime_desc';
|
|
52
57
|
results.sort((a, b) => {
|
|
@@ -91,6 +96,10 @@ function queryTable(db, table, filters, restrictToIds) {
|
|
|
91
96
|
where += ` AND key_type IN (${filters.keyTypes.map(() => '?').join(', ')})`;
|
|
92
97
|
params.push(...filters.keyTypes);
|
|
93
98
|
}
|
|
99
|
+
if (table === 'memory_docs' && filters.keys.length > 0) {
|
|
100
|
+
where += ` AND key IN (${filters.keys.map(() => '?').join(', ')})`;
|
|
101
|
+
params.push(...filters.keys);
|
|
102
|
+
}
|
|
94
103
|
if (filters.folders.length > 0) {
|
|
95
104
|
where += ` AND folder IN (${filters.folders.map(() => '?').join(', ')})`;
|
|
96
105
|
params.push(...filters.folders);
|
|
@@ -186,7 +195,7 @@ function matchSearch(db, q) {
|
|
|
186
195
|
try {
|
|
187
196
|
rows = db
|
|
188
197
|
.prepare(`SELECT ref_table, ref_id FROM search_index WHERE search_index MATCH ? ORDER BY rank`)
|
|
189
|
-
.all(q);
|
|
198
|
+
.all(sanitizeFtsQuery(q));
|
|
190
199
|
}
|
|
191
200
|
catch {
|
|
192
201
|
// Bad FTS5 query syntax (e.g. a bare quote) — treat as no matches rather than 500ing.
|
|
@@ -271,7 +280,7 @@ export function buildWebRouter(db, config, skillRepo, memoryRepo, skillSpec, mem
|
|
|
271
280
|
}
|
|
272
281
|
});
|
|
273
282
|
router.get('/api/entries', (req, res) => {
|
|
274
|
-
res.json(queryEntries(db, req));
|
|
283
|
+
res.json(queryEntries(db, memoryRepo, req));
|
|
275
284
|
});
|
|
276
285
|
router.get('/api/entries/:table/:id', (req, res) => {
|
|
277
286
|
const { table, id } = req.params;
|
|
@@ -491,6 +500,14 @@ export function buildWebRouter(db, config, skillRepo, memoryRepo, skillSpec, mem
|
|
|
491
500
|
const type = req.query.type ?? 'all';
|
|
492
501
|
res.json(buildFacets(db, type));
|
|
493
502
|
});
|
|
503
|
+
router.get('/api/keys/suggest', (req, res) => {
|
|
504
|
+
const q = req.query.q?.trim();
|
|
505
|
+
if (!q) {
|
|
506
|
+
res.json([]);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
res.json(memoryRepo.suggestKeys(q, 8));
|
|
510
|
+
});
|
|
494
511
|
router.get('/api/health', (_req, res) => {
|
|
495
512
|
res.json(buildHealth(db));
|
|
496
513
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-memory-bucket",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server exposing skill_* (reusable coding patterns) and memory_* (point-in-time working context) tools over a markdown+frontmatter source, cached into SQLite at runtime.",
|
|
6
6
|
"repository": {
|