@augmnt-sh/mindcache 0.2.1 → 0.3.0
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/cli.js +148 -150
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1,199 +1,197 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {existsSync,mkdirSync,writeFileSync,readFileSync,watch
|
|
3
|
-
`),t=false,
|
|
4
|
-
`).trim():null}function
|
|
5
|
-
`),
|
|
6
|
-
`)}function M(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
`)
|
|
11
|
-
`);r.
|
|
12
|
-
`),
|
|
13
|
-
`);return
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
2
|
+
import {StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import {McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import {mkdir,readdir,stat,readFile,writeFile,appendFile,unlink}from'fs/promises';import {existsSync,mkdirSync,writeFileSync,readFileSync,watch}from'fs';import {dirname,join,relative,basename,extname,resolve}from'path';import st from'yaml';import {z as z$1}from'zod';import {fileURLToPath}from'url';import {homedir}from'os';import {createInterface}from'readline';function P(a,e){let c=a.split(`
|
|
3
|
+
`),t=false,n=0,o=[];for(let r of c){let i=r.match(/^(#{1,6})\s+(.+)$/);if(i){let s=i[1].length,d=i[2].trim();if(t){if(s<=n)break;o.push(r);}else d.toLowerCase()===e.toLowerCase()&&(t=true,n=s,o.push(r));}else t&&o.push(r);}return o.length>0?o.join(`
|
|
4
|
+
`).trim():null}function V(a,e,c){let t=a.split(`
|
|
5
|
+
`),n=[],o=false,r=0;for(let i of t){let s=i.match(/^(#{1,6})\s+(.+)$/);if(s){let d=s[1].length,l=s[2].trim();o&&d<=r?(o=false,n.push(i)):l.toLowerCase()===e.toLowerCase()?(o=true,r=d,n.push(i),n.push(""),n.push(c),n.push("")):n.push(i);}else o||n.push(i);}return n.join(`
|
|
6
|
+
`)}function M(a){let e=a.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);if(!e)return {frontmatter:{},body:a};let c={};try{let t=st.parse(e[1]);t&&typeof t=="object"&&!Array.isArray(t)&&(c=t);}catch{}return {frontmatter:c,body:e[2]}}function U(a,e){return `---
|
|
7
|
+
${st.stringify(a,{lineWidth:0}).trim()}
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
${e}`}function C(a){return [...a.matchAll(/\[\[([^\]|]+)(?:\|[^\]]+)?\]\]/g)].map(c=>c[1])}function b(a){let e=a.matchAll(/(?:^|\s)#([a-zA-Z0-9_/-]+)/g);return [...new Set([...e].map(c=>c[1]))]}function L(a){let e=[],c=a.split(`
|
|
11
|
+
`);for(let t=0;t<c.length;t++){let n=c[t].match(/^[\s]*-\s+\[([ xX])\]\s+(.+)$/);n&&e.push({text:n[2],done:n[1]!==" ",line:t+1});}return e}function it(a){let{body:e}=M(a);return e.split(/\s+/).filter(c=>c.length>0).length}function at(a){let e=[],c=[],t=null,n=null,o=a.replace(/"([^"]+)"/g,(i,s)=>(e.push(s),""));return o=o.replace(/\bpath:(\S+)/gi,(i,s)=>(t=s,"")),o=o.replace(/\bfile:(\S+)/gi,(i,s)=>(n=s,"")),o=o.replace(/\btag:#?(\S+)/gi,(i,s)=>(c.push(s),"")),{text:o.replace(/\s+/g," ").trim(),phrases:e,pathFilter:t,fileFilter:n,tagFilters:c}}var O=class{vaultPath;fileCache=null;fileCacheTime=0;CACHE_TTL=5e3;watcher=null;index=new Map;indexReady=false;locks=new Map;constructor(e){this.vaultPath=e.vault,this.vaultPath&&existsSync(this.vaultPath)&&this.startWatcher();}async withLock(e,c){let t=e,n=this.locks.get(t),o,r=new Promise(i=>{o=i;});this.locks.set(t,(n??Promise.resolve()).then(()=>r));try{return n&&await n,await c()}finally{o(),this.locks.get(t)===r&&this.locks.delete(t);}}startWatcher(){try{this.watcher=watch(this.vaultPath,{recursive:!0},(e,c)=>{c&&!c.startsWith(".")&&(this.fileCache=null,c.endsWith(".md")&&this.index.delete(c));}),this.watcher.on("error",()=>{});}catch{}}normalizePath(e){return e.endsWith(".md")?e:`${e}.md`}resolve(e){let c=this.normalizePath(e),t=join(this.vaultPath,c);if(!t.startsWith(this.vaultPath))throw new Error("Path traversal not allowed");return t}async ensureDir(e){let c=dirname(e);existsSync(c)||await mkdir(c,{recursive:true});}async walkDir(e){let c=[],t;try{t=await readdir(e,{withFileTypes:!0});}catch{return c}for(let n of t){if(n.name.startsWith(".")||n.name==="node_modules")continue;let o=join(e,n.name);if(n.isDirectory())c.push(...await this.walkDir(o));else try{let r=await stat(o);c.push({fullPath:o,mtime:r.mtimeMs});}catch{c.push({fullPath:o,mtime:0});}}return c}tokenize(e){return e.toLowerCase().split(/\s+/).filter(c=>c.length>1)}idf(e){if(!this.indexReady||this.index.size===0)return 1;let c=this.index.size,t=0;for(let n of this.index.values())n.tokens.has(e)&&t++;return t===0?1:Math.log(c/t)+1}async buildIndex(){let c=(await this.listFiles()).filter(t=>t.extension==="md");for(let t of c)if(!this.index.has(t.path))try{let n=await readFile(this.resolve(t.path),"utf-8"),o=n.toLowerCase().replace(/[^\w\s]/g," "),r=new Set(o.split(/\s+/).filter(d=>d.length>1)),i=b(n),s=C(n);this.index.set(t.path,{tokens:r,tags:i,links:s,mtime:t.mtime});}catch{}for(let t of this.index.keys())c.some(n=>n.path===t)||this.index.delete(t);this.indexReady=true;}scoreMatch(e,c,t,n,o=80){let r=c.toLowerCase(),i=e.toLowerCase(),s=this.tokenize(e),d=[],l=0;if(r.indexOf(i)>=0){l+=100;let h=0,x=0;for(;(h=r.indexOf(i,h))!==-1&&x<5;){let w=Math.max(0,h-o),y=Math.min(c.length,h+e.length+o);d.push({start:h,end:h+e.length,context:c.slice(w,y).replace(/\n/g," ").trim()}),h+=i.length,x++;}l+=x*10;}if(s.length>1){let h=0;for(let w of s)if(r.includes(w)){h++;let y=0,k=0;for(;(y=r.indexOf(w,y))!==-1;)k++,y+=w.length;let S=this.idf(w);l+=k*S*3;}let x=h/s.length;if(l+=x*50,d.length===0&&h>0)for(let w of s){let y=r.indexOf(w);if(y>=0){let k=Math.max(0,y-o),S=Math.min(c.length,y+w.length+o);d.push({start:y,end:y+w.length,context:c.slice(k,S).replace(/\n/g," ").trim()});break}}}let u=t.toLowerCase();if(u.includes(i))l+=80;else for(let h of s)u.includes(h)&&(l+=25);let m=(Date.now()-n)/(1e3*60*60*24);return m<1?l+=20:m<7?l+=10:m<30&&(l+=5),{score:l,matchPositions:d}}async ping(){return existsSync(this.vaultPath)}async search(e,c){let t=at(e);return this.searchParsed(t,c)}async searchParsed(e,c){let t=c?.limit??50,n=c?.contextLength??80,r=(await this.listFiles()).filter(l=>l.extension==="md");if(e.pathFilter){let l=e.pathFilter.toLowerCase();r=r.filter(f=>f.path.toLowerCase().includes(l));}if(e.fileFilter){let l=e.fileFilter.toLowerCase();r=r.filter(f=>f.name.toLowerCase().includes(l));}if(e.tagFilters.length>0&&this.indexReady){let l=e.tagFilters.map(f=>f.toLowerCase());r=r.filter(f=>{let u=this.index.get(f.path);if(!u)return true;let p=u.tags.map(m=>m.toLowerCase());return l.every(m=>p.includes(m))});}let i=[e.text,...e.phrases].filter(Boolean).join(" "),s=i?this.tokenize(i):[];s.length>0&&this.indexReady&&(r=r.filter(l=>{let f=this.index.get(l.path);return f?s.some(u=>f.tokens.has(u)):true}));let d=[];for(let l of r)try{let f=await readFile(this.resolve(l.path),"utf-8");if(e.tagFilters.length>0&&!this.index.has(l.path)){let w=b(f).map(k=>k.toLowerCase());if(!e.tagFilters.every(k=>w.includes(k.toLowerCase())))continue}if(!i){d.push({filename:l.path,score:1,matches:[]});continue}let{score:u,matchPositions:p}=this.scoreMatch(i,f,l.name,l.mtime,n),m=0,h=f.toLowerCase();for(let w of e.phrases)h.includes(w.toLowerCase())&&(m+=100);let x=u+m;x>0&&(p.length>0||m>0)&&d.push({filename:l.path,score:x,matches:p.map(w=>({match:{start:w.start,end:w.end},context:w.context}))});}catch{}return d.sort((l,f)=>f.score-l.score).slice(0,t)}async readNote(e){let c=this.resolve(e);try{return await readFile(c,"utf-8")}catch{throw new Error(`Note not found: ${e}`)}}async writeNote(e,c){let t=this.resolve(e);await this.withLock(t,async()=>{await this.ensureDir(t),await writeFile(t,c,"utf-8");}),this.fileCache=null;let n=this.normalizePath(e);this.index.delete(n);}async appendToNote(e,c){let t=this.resolve(e);await this.withLock(t,async()=>{try{await readFile(t,"utf-8");}catch{throw new Error(`Note not found: ${e}`)}await appendFile(t,c,"utf-8");});let n=this.normalizePath(e);this.index.delete(n);}async patchNote(e,c,t){let n=this.resolve(e);await this.withLock(n,async()=>{let r=await readFile(n,"utf-8");if(t?.targetHeading){if(!P(r,t.targetHeading))throw new Error(`Heading "${t.targetHeading}" not found`);if(t.operation==="replace"){let s=V(r,t.targetHeading,c);await writeFile(n,s,"utf-8");}else {let s=r.split(`
|
|
12
|
+
`),d=t.targetHeading.toLowerCase(),l=-1;for(let f=0;f<s.length;f++){let u=s[f].match(/^(#{1,6})\s+(.+)$/);if(u&&u[2].trim().toLowerCase()===d){let p=u[1].length;l=f+1;for(let m=f+1;m<s.length;m++){let h=s[m].match(/^(#{1,6})\s/);if(h&&h[1].length<=p)break;l=m+1;}break}}if(l>=0)s.splice(l,0,c),await writeFile(n,s.join(`
|
|
13
|
+
`),"utf-8");else throw new Error(`Heading "${t.targetHeading}" not found`)}}else await appendFile(n,c,"utf-8");});let o=this.normalizePath(e);this.index.delete(o);}async deleteNote(e){let c=this.resolve(e);await this.withLock(c,async()=>{try{await stat(c);}catch{throw new Error(`Note not found: ${e}`)}await unlink(c);}),this.fileCache=null;let t=this.normalizePath(e);this.index.delete(t);}async updateBacklinks(e,c){let n=(await this.listFiles()).filter(i=>i.extension==="md"),o=0,r=new RegExp(`\\[\\[${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(\\|[^\\]]*)?\\]\\]`,"g");for(let i of n)try{let s=this.resolve(i.path),d=await readFile(s,"utf-8");if(!r.test(d))continue;r.lastIndex=0;let l=d.replace(r,(f,u)=>`[[${c}${u??""}]]`);l!==d&&(await this.withLock(s,async()=>{await writeFile(s,l,"utf-8");}),o++);}catch{}return o}async listFiles(){let e=Date.now();if(this.fileCache&&e-this.fileCacheTime<this.CACHE_TTL)return this.fileCache;let c=await this.walkDir(this.vaultPath);return this.fileCache=c.map(({fullPath:t,mtime:n})=>{let o=relative(this.vaultPath,t),r=basename(o,extname(o)),i=extname(o).slice(1);return {path:o,name:r,extension:i,mtime:n}}).sort((t,n)=>n.mtime-t.mtime),this.fileCacheTime=e,this.fileCache}async searchWithContext(e,c=100){return this.search(e,{contextLength:c})}async searchBatch(e,c){let t=c?.limit??20,o=(await this.listFiles()).filter(i=>i.extension==="md"),r=new Map;for(let i of e)r.set(i,[]);for(let i of o)try{let s=await readFile(this.resolve(i.path),"utf-8");for(let d of e){let{score:l,matchPositions:f}=this.scoreMatch(d,s,i.name,i.mtime);l>0&&f.length>0&&r.get(d).push({filename:i.path,score:l,matches:f.map(u=>({match:{start:u.start,end:u.end},context:u.context}))});}}catch{}for(let[i,s]of r)r.set(i,s.sort((d,l)=>l.score-d.score).slice(0,t));return r}async health(){if(!this.vaultPath)return {ok:false,error:"No vault path configured. Run: mindcache init"};if(!existsSync(this.vaultPath))return {ok:false,error:`Vault not found at: ${this.vaultPath}`};try{return {ok:!0,noteCount:(await this.listFiles()).filter(t=>t.extension==="md").length}}catch(e){return {ok:false,error:`Cannot read vault: ${e instanceof Error?e.message:String(e)}`}}}close(){this.watcher&&(this.watcher.close(),this.watcher=null);}};function ft(a){let e=a.getFullYear(),c=String(a.getMonth()+1).padStart(2,"0"),t=String(a.getDate()).padStart(2,"0");return `${e}-${c}-${t}`}function $(){return ft(new Date)}function Z(a=new Date){let e=new Date(a),c=e.getDay(),t=e.getDate()-c+(c===0?-6:1);return e.setDate(t),e.setHours(0,0,0,0),e}function mt(a=new Date){let e=Z(a),c=[];for(let t=0;t<7;t++){let n=new Date(e);n.setDate(e.getDate()+t),c.push(ft(n));}return c}function pt(a){let e=new Date,c=a.toLowerCase().trim();if(c==="today"){let r=new Date(e);r.setHours(0,0,0,0);let i=new Date(e);return i.setHours(23,59,59,999),{from:r,to:i}}if(c==="yesterday"){let r=new Date(e);r.setDate(r.getDate()-1),r.setHours(0,0,0,0);let i=new Date(r);return i.setHours(23,59,59,999),{from:r,to:i}}if(c==="this week")return {from:Z(e),to:e};if(c==="last week"){let r=Z(e),i=new Date(r);i.setDate(i.getDate()-7);let s=new Date(r);return s.setDate(s.getDate()-1),s.setHours(23,59,59,999),{from:i,to:s}}if(c==="this month")return {from:new Date(e.getFullYear(),e.getMonth(),1),to:e};if(c==="last month"){let r=new Date(e.getFullYear(),e.getMonth()-1,1),i=new Date(e.getFullYear(),e.getMonth(),0,23,59,59,999);return {from:r,to:i}}let t=c.match(/(\d{4}-\d{2}-\d{2})\s+to\s+(\d{4}-\d{2}-\d{2})/);if(t)return {from:new Date(t[1]),to:new Date(t[2])};let n=c.match(/last\s+(\d+)\s+days?/);if(n){let r=new Date(e);return r.setDate(r.getDate()-parseInt(n[1])),r.setHours(0,0,0,0),{from:r,to:e}}let o=new Date(e);return o.setDate(o.getDate()-7),o.setHours(0,0,0,0),{from:o,to:e}}function ht(a,e,c){a.tool("search",'Full-text search across your Obsidian vault. Supports operators: "exact phrase" for exact matching, path:folder to filter by path, tag:#tag to filter by tag, file:name to filter by filename. Returns matching notes with context snippets.',{query:z$1.string().max(500).describe('Search query \u2014 supports operators: "phrase", path:, tag:#, file:')},async({query:t})=>{try{let n=await e.search(t,{contextLength:200});if(n.length===0)return {content:[{type:"text",text:`No results found for "${t}"`}]};let o=n.slice(0,20).map((r,i)=>{let s=r.matches.slice(0,3).map(d=>` > ${d.context.trim()}`).join(`
|
|
14
|
+
`);return `${i+1}. **${r.filename}** (score: ${r.score.toFixed(1)})
|
|
15
|
+
${s}`}).join(`
|
|
16
|
+
|
|
17
|
+
`);return {content:[{type:"text",text:`Found ${n.length} results for "${t}":
|
|
18
|
+
|
|
19
|
+
${o}`}]}}catch(n){return {content:[{type:"text",text:`Search error: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("ask","Ask a natural language question about your vault. Searches for relevant notes and returns their content so you can synthesize an answer. Use this when you need to understand what the user knows about a topic.",{question:z$1.string().max(500).describe("Natural language question to answer from vault knowledge")},async({question:t})=>{try{let n=await e.searchWithContext(t,300);if(n.length===0)return {content:[{type:"text",text:`No relevant notes found for: "${t}"`}]};let o=n.slice(0,5),r=[];for(let i of o)try{let s=await e.readNote(i.filename),{frontmatter:d}=M(s),l=b(s),f=C(s),u=s.length>2e3?s.slice(0,2e3)+`
|
|
20
|
+
...(truncated)`:s;r.push(`---
|
|
21
|
+
**${i.filename}**`+(l.length>0?` | Tags: ${l.map(p=>`#${p}`).join(", ")}`:"")+(f.length>0?` | Links: ${f.map(p=>`[[${p}]]`).join(", ")}`:"")+(d.type?` | Type: ${String(d.type)}`:"")+`
|
|
22
|
+
|
|
23
|
+
${u}`);}catch{}return {content:[{type:"text",text:`Found ${n.length} relevant notes for "${t}". Here are the top ${r.length}:
|
|
23
24
|
|
|
24
25
|
${r.join(`
|
|
25
26
|
|
|
26
|
-
`)}`}]}}catch(
|
|
27
|
+
`)}`}]}}catch(n){return {content:[{type:"text",text:`Ask error: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("find_related","Find notes related to a given note via backlinks, shared tags, or shared wikilinks. Returns notes that are connected in the knowledge graph.",{path:z$1.string().describe('Path to the note (e.g., "Projects/My Project.md")')},async({path:t})=>{try{let n=await e.readNote(t),o=C(n),r=b(n),i=new Map,s=[],d=[];for(let m of o)s.push(`file:${m}`),d.push({type:"link",label:m});for(let m of r.slice(0,5))s.push(`tag:#${m}`),d.push({type:"tag",label:m});let l=t.replace(/\.md$/,"").split("/").pop()??t;s.push(`"[[${l}]]"`),d.push({type:"backlink",label:l});let f=await e.searchBatch(s,{limit:5});for(let m=0;m<s.length;m++){let h=f.get(s[m])??[],x=d[m];for(let w of h){if(w.filename===t)continue;let y=i.get(w.filename),k=x.type==="backlink"?3:x.type==="link"?2:1,S=x.type==="backlink"?"Links to this note":x.type==="link"?`Linked from this note: [[${x.label}]]`:`Shared tag: #${x.label}`;i.set(w.filename,{reason:y?`${y.reason}; ${S}`:S,score:(y?.score??0)+k});}}let u=[...i.entries()].sort((m,h)=>h[1].score-m[1].score).slice(0,15);if(u.length===0)return {content:[{type:"text",text:`No related notes found for "${t}"`}]};let p=u.map(([m,h],x)=>`${x+1}. **${m}** \u2014 ${h.reason}`).join(`
|
|
27
28
|
`);return {content:[{type:"text",text:`Related notes for "${t}":
|
|
28
29
|
|
|
29
|
-
${
|
|
30
|
-
`);return {content:[{type:"text",text:`Notes tagged ${t.map(
|
|
30
|
+
${p}`}]}}catch(n){return {content:[{type:"text",text:`Find related error: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("find_by_tag","Find all notes with specific tags. Supports multiple tags (all must match). Use without # prefix.",{tags:z$1.array(z$1.string()).max(20).describe('Tags to search for (without # prefix, e.g., ["project", "active"])'),limit:z$1.number().optional().default(20).describe("Maximum number of results")},async({tags:t,limit:n})=>{try{let o=t.map(s=>`tag:#${s}`).join(" "),r=await e.search(o);if(r.length===0)return {content:[{type:"text",text:`No notes found with tags: ${t.map(s=>`#${s}`).join(", ")}`}]};let i=r.slice(0,n).map((s,d)=>`${d+1}. ${s.filename}`).join(`
|
|
31
|
+
`);return {content:[{type:"text",text:`Notes tagged ${t.map(s=>`#${s}`).join(", ")} (${r.length} total):
|
|
31
32
|
|
|
32
|
-
${
|
|
33
|
-
`);return {content:[{type:"text",text:`Notes from ${
|
|
33
|
+
${i}`}]}}catch(o){return {content:[{type:"text",text:`Tag search error: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("find_by_date",'Find notes created or modified within a date range. Supports natural language like "today", "this week", "last month", "last 7 days", or explicit "YYYY-MM-DD to YYYY-MM-DD".',{range:z$1.string().describe('Date range \u2014 "today", "this week", "last month", "last 30 days", or "YYYY-MM-DD to YYYY-MM-DD"'),limit:z$1.number().optional().default(20).describe("Maximum results")},async({range:t,limit:n})=>{try{let{from:o,to:r}=pt(t),s=(await e.listFiles()).filter(h=>h.extension==="md"),d=o.getTime(),l=r.getTime()+864e5,f=o.toISOString().split("T")[0],u=r.toISOString().split("T")[0],m=s.filter(h=>h.mtime>=d&&h.mtime<=l).slice(0,n).map((h,x)=>{let w=new Date(h.mtime).toISOString().split("T")[0];return `${x+1}. ${h.path} \u2014 modified ${w}`}).join(`
|
|
34
|
+
`);return {content:[{type:"text",text:`Notes modified from ${f} to ${u}:
|
|
34
35
|
|
|
35
|
-
`+(
|
|
36
|
+
`+(m||"No notes found in this range.")+`
|
|
36
37
|
|
|
37
|
-
Total markdown files in vault: ${
|
|
38
|
-
`);return {content:[{type:"text",text:`Recently modified notes (${
|
|
38
|
+
Total markdown files in vault: ${s.length}`}]}}catch(o){return {content:[{type:"text",text:`Date search error: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("find_recent","Get recently modified notes in the vault, sorted by modification time (newest first).",{limit:z$1.number().optional().default(10).describe("Number of recent notes to return")},async({limit:t})=>{try{let o=(await e.listFiles()).filter(s=>s.extension==="md"),i=o.slice(0,t).map((s,d)=>{let l=Date.now()-s.mtime,f=Math.floor(l/6e4),u=Math.floor(f/60),p=Math.floor(u/24),m=p>0?`${p}d ago`:u>0?`${u}h ago`:`${f}m ago`;return `${d+1}. ${s.path} \u2014 ${m}`}).join(`
|
|
39
|
+
`);return {content:[{type:"text",text:`Recently modified notes (${o.length} total):
|
|
39
40
|
|
|
40
|
-
${
|
|
41
|
-
> ${
|
|
41
|
+
${i}`}]}}catch(n){return {content:[{type:"text",text:`Recent notes error: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("find_mentions","Find all notes that mention a specific term, concept, or person. Searches for exact text matches across the vault.",{term:z$1.string().max(200).describe("Term or concept to search for"),limit:z$1.number().optional().default(20).describe("Maximum results")},async({term:t,limit:n})=>{try{let o=await e.searchWithContext(t,150);if(o.length===0)return {content:[{type:"text",text:`"${t}" is not mentioned in any notes.`}]};let r=o.slice(0,n).map((i,s)=>{let d=i.matches[0]?.context?.trim()??"";return `${s+1}. **${i.filename}**
|
|
42
|
+
> ${d}`}).join(`
|
|
42
43
|
|
|
43
|
-
`);return {content:[{type:"text",text:`"${t}" mentioned in ${
|
|
44
|
+
`);return {content:[{type:"text",text:`"${t}" mentioned in ${o.length} notes:
|
|
44
45
|
|
|
45
|
-
${r}`}]}}catch(
|
|
46
|
-
${Object.entries(
|
|
47
|
-
`)}`:null,"","---","",
|
|
48
|
-
`)}]}}catch(
|
|
46
|
+
${r}`}]}}catch(o){return {content:[{type:"text",text:`Mentions search error: ${o instanceof Error?o.message:String(o)}`}],isError:true}}});}function gt(a,e,c){a.tool("read_note","Read the full content of a note by its path. Returns the complete markdown including frontmatter.",{path:z$1.string().describe('Path to the note (e.g., "Projects/My Project.md")')},async({path:t})=>{try{return {content:[{type:"text",text:await e.readNote(t)}]}}catch(n){return {content:[{type:"text",text:`Could not read "${t}": ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("read_section","Read a specific section from a note, identified by its heading. Returns content from the heading to the next heading of the same or higher level.",{path:z$1.string().describe("Path to the note"),heading:z$1.string().describe('Heading text to extract (e.g., "Key Points", "Action Items")')},async({path:t,heading:n})=>{try{let o=await e.readNote(t),r=P(o,n);return r?{content:[{type:"text",text:r}]}:{content:[{type:"text",text:`No section "${n}" found in "${t}"`}]}}catch(o){return {content:[{type:"text",text:`Could not read section: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("read_summary","Get a note with its metadata: frontmatter properties, tags, wikilinks, and word count. Useful for understanding a note without reading it fully.",{path:z$1.string().describe("Path to the note")},async({path:t})=>{try{let n=await e.readNote(t),{frontmatter:o}=M(n),r=b(n),i=C(n),s=it(n);return {content:[{type:"text",text:[`**${t}**`,"",`**Words:** ${s}`,r.length>0?`**Tags:** ${r.map(l=>`#${l}`).join(", ")}`:null,i.length>0?`**Links:** ${i.map(l=>`[[${l}]]`).join(", ")}`:null,Object.keys(o).length>0?`**Properties:**
|
|
47
|
+
${Object.entries(o).map(([l,f])=>` - ${l}: ${typeof f=="object"?JSON.stringify(f):String(f)}`).join(`
|
|
48
|
+
`)}`:null,"","---","",n].filter(Boolean).join(`
|
|
49
|
+
`)}]}}catch(n){return {content:[{type:"text",text:`Could not read summary: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("read_properties","Read only the YAML frontmatter properties of a note. Fast way to check metadata without reading the full content.",{path:z$1.string().describe("Path to the note")},async({path:t})=>{try{let n=await e.readNote(t),{frontmatter:o}=M(n);if(Object.keys(o).length===0)return {content:[{type:"text",text:`No frontmatter found in "${t}"`}]};let r=Object.entries(o).map(([i,s])=>`${i}: ${typeof s=="object"?JSON.stringify(s):String(s)}`).join(`
|
|
49
50
|
`);return {content:[{type:"text",text:`Properties of "${t}":
|
|
50
51
|
|
|
51
|
-
${r}`}]}}catch(
|
|
52
|
+
${r}`}]}}catch(n){return {content:[{type:"text",text:`Could not read properties: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("read_today","Read today's daily note. Returns the content of today's daily note based on your configured daily notes folder and format.",{},async()=>{try{let t=`${c.dailyNotes.folder}/${$()}.md`,n=await e.readNote(t);return {content:[{type:"text",text:`**Today's Daily Note (${$()}):**
|
|
52
53
|
|
|
53
|
-
${
|
|
54
|
+
${n}`}]}}catch{return {content:[{type:"text",text:`No daily note found for today (${$()}). Expected at: ${c.dailyNotes.folder}/${$()}.md`}]}}}),a.tool("read_this_week","Read all daily notes from the current week (Monday to Sunday). Useful for getting a weekly overview of activity.",{},async()=>{try{let t=mt(),n=[];for(let o of t){let r=`${c.dailyNotes.folder}/${o}.md`;try{let i=await e.readNote(r);n.push(`## ${o}
|
|
54
55
|
|
|
55
|
-
${
|
|
56
|
+
${i}`);}catch{}}return n.length===0?{content:[{type:"text",text:`No daily notes found for this week (${t[0]} to ${t[6]}).`}]}:{content:[{type:"text",text:`**This Week's Daily Notes:**
|
|
56
57
|
|
|
57
|
-
${
|
|
58
|
+
${n.join(`
|
|
58
59
|
|
|
59
60
|
---
|
|
60
61
|
|
|
61
|
-
`)}`}]}}catch(t){return {content:[{type:"text",text:`Could not read weekly notes: ${t instanceof Error?t.message:String(t)}`}],isError:true}}}),
|
|
62
|
+
`)}`}]}}catch(t){return {content:[{type:"text",text:`Could not read weekly notes: ${t instanceof Error?t.message:String(t)}`}],isError:true}}}),a.tool("extract_links","Extract all links from a note \u2014 both [[wikilinks]] to other vault notes and external URLs. Useful for seeing what a note references.",{path:z$1.string().describe("Path to the note")},async({path:t})=>{try{let n=await e.readNote(t),o=C(n),r=/https?:\/\/[^\s)>\]]+/g,i=[...n.matchAll(r)].map(d=>d[0]);if(o.length===0&&i.length===0)return {content:[{type:"text",text:`No links found in "${t}".`}]};let s=`**Links in "${t}":**
|
|
63
|
+
`;return o.length>0&&(s+=`
|
|
64
|
+
**Wikilinks (${o.length}):**
|
|
65
|
+
`,s+=o.map(d=>`- [[${d}]]`).join(`
|
|
66
|
+
`)),i.length>0&&(s+=`
|
|
62
67
|
|
|
63
|
-
${
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
${
|
|
68
|
+
**URLs (${i.length}):**
|
|
69
|
+
`,s+=i.map(d=>`- ${d}`).join(`
|
|
70
|
+
`)),{content:[{type:"text",text:s}]}}catch(n){return {content:[{type:"text",text:`Could not extract links: ${n instanceof Error?n.message:String(n)}`}],isError:true}}});}var Ht=dirname(fileURLToPath(import.meta.url)),qt=join(Ht,"..","templates"),tt=new Map;function Vt(a){if(tt.has(a))return tt.get(a);let e=join(qt,`${a}.md`);if(!existsSync(e))throw new Error(`Template "${a}" not found at ${e}`);let c=readFileSync(e,"utf-8");return tt.set(a,c),c}function Bt(a,e){let c=a,n={...{date:$(),timestamp:new Date().toISOString(),year:String(new Date().getFullYear()),month:String(new Date().getMonth()+1).padStart(2,"0"),day:String(new Date().getDate()).padStart(2,"0")},...e};for(let[o,r]of Object.entries(n))c=c.replaceAll(`{{${o}}}`,r??"");return c=c.replace(/\{\{[^}]+\}\}/g,""),c}function D(a,e={}){let c=Vt(a);return Bt(c,e)}async function W(a,e){let c=e,t=1;for(;;)try{await a.readNote(c);let n=e.endsWith(".md")?".md":"";c=`${e.replace(/\.md$/,"")} ${t}${n}`,t++;}catch{return c}}function xt(a,e,c){a.tool("remember","Quick capture \u2014 save a thought, note, or piece of information to the vault. Auto-saves to the inbox folder. Use this for quick, unstructured captures.",{content:z$1.string().max(1e5).describe("The content to save"),title:z$1.string().max(200).optional().describe("Optional title for the note. If omitted, generates from content."),tags:z$1.array(z$1.string()).max(50).optional().describe("Optional tags to add (without # prefix)"),folder:z$1.string().optional().describe("Optional folder to save to (defaults to inbox)")},async({content:t,title:n,tags:o,folder:r})=>{try{let i=n??`Quick Note - ${$()} ${new Date().toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit"})}`,d=`${r??c.inbox.folder}/${i}.md`,l=o&&o.length>0?`tags:
|
|
71
|
+
${o.map(u=>` - ${u}`).join(`
|
|
67
72
|
`)}`:`tags:
|
|
68
|
-
- inbox`,
|
|
69
|
-
date: ${
|
|
70
|
-
${
|
|
73
|
+
- inbox`,f=`---
|
|
74
|
+
date: ${$()}
|
|
75
|
+
${l}
|
|
71
76
|
---
|
|
72
77
|
|
|
73
78
|
${t}
|
|
74
|
-
`;return await
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
${
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
`;return await e.writeNote(d,f),{content:[{type:"text",text:`Saved to "${d}"`}]}}catch(i){return {content:[{type:"text",text:`Could not save note: ${i instanceof Error?i.message:String(i)}`}],isError:true}}}),a.tool("remember_meeting","Create a structured meeting note with attendees, agenda, notes, and action items. Uses the meeting template.",{title:z$1.string().describe("Meeting title"),attendees:z$1.string().describe("Comma-separated list of attendees"),agenda:z$1.string().optional().default("").describe("Meeting agenda"),notes:z$1.string().optional().default("").describe("Meeting notes"),action_items:z$1.string().optional().default("").describe("Action items from the meeting"),follow_up:z$1.string().optional().default("").describe("Follow-up items"),folder:z$1.string().optional().describe("Folder to save to (defaults to inbox)")},async({title:t,attendees:n,agenda:o,notes:r,action_items:i,follow_up:s,folder:d})=>{try{let l=D("meeting",{title:t,attendees:n,agenda:o,notes:r,action_items:i,follow_up:s}),f=d??c.inbox.folder,u=await W(e,`${f}/${t}.md`);return await e.writeNote(u,l),{content:[{type:"text",text:`Meeting note created at "${u}"`}]}}catch(l){return {content:[{type:"text",text:`Could not create meeting note: ${l instanceof Error?l.message:String(l)}`}],isError:true}}}),a.tool("remember_decision","Log a decision using the ADR (Architecture Decision Record) format. Captures the context, decision, consequences, and alternatives considered.",{title:z$1.string().describe("Decision title"),context:z$1.string().describe("Why this decision needed to be made"),decision:z$1.string().describe("What was decided"),consequences:z$1.string().optional().default("").describe("What happens as a result"),alternatives:z$1.string().optional().default("").describe("Other options that were considered"),related:z$1.string().optional().default("").describe("Related notes or concepts"),folder:z$1.string().optional().describe("Folder to save to")},async({title:t,context:n,decision:o,consequences:r,alternatives:i,related:s,folder:d})=>{try{let l=D("decision",{title:t,context:n,decision:o,consequences:r,alternatives:i,related:s}),f=d??c.inbox.folder,u=await W(e,`${f}/${t}.md`);return await e.writeNote(u,l),{content:[{type:"text",text:`Decision logged at "${u}"`}]}}catch(l){return {content:[{type:"text",text:`Could not log decision: ${l instanceof Error?l.message:String(l)}`}],isError:true}}}),a.tool("remember_idea","Capture an idea with context on why it matters and potential next steps.",{title:z$1.string().describe("Idea title"),idea:z$1.string().describe("Description of the idea"),why:z$1.string().optional().default("").describe("Why this idea matters"),next_steps:z$1.string().optional().default("").describe("Potential next steps"),related:z$1.string().optional().default("").describe("Related notes or concepts"),tags:z$1.array(z$1.string()).optional().describe("Additional tags"),folder:z$1.string().optional().describe("Folder to save to")},async({title:t,idea:n,why:o,next_steps:r,related:i,tags:s,folder:d})=>{try{let l=D("idea",{title:t,idea:n,why:o,next_steps:r,related:i,tags:s?s.map(p=>` - ${p}`).join(`
|
|
80
|
+
`):""}),f=d??c.inbox.folder,u=await W(e,`${f}/${t}.md`);return await e.writeNote(u,l),{content:[{type:"text",text:`Idea captured at "${u}"`}]}}catch(l){return {content:[{type:"text",text:`Could not capture idea: ${l instanceof Error?l.message:String(l)}`}],isError:true}}}),a.tool("remember_learning","Save something new you learned \u2014 a concept, technique, insight, or skill with examples and application notes.",{title:z$1.string().describe("What was learned"),concept:z$1.string().describe("The concept or insight"),key_points:z$1.string().optional().default("").describe("Key points to remember"),examples:z$1.string().optional().default("").describe("Examples or illustrations"),application:z$1.string().optional().default("").describe("How to apply this learning"),source:z$1.string().optional().default("").describe("Where you learned this"),related:z$1.string().optional().default("").describe("Related notes"),folder:z$1.string().optional().describe("Folder to save to")},async({title:t,concept:n,key_points:o,examples:r,application:i,source:s,related:d,folder:l})=>{try{let f=D("learning",{title:t,concept:n,key_points:o,examples:r,application:i,source:s,related:d}),u=l??c.inbox.folder,p=await W(e,`${u}/${t}.md`);return await e.writeNote(p,f),{content:[{type:"text",text:`Learning saved at "${p}"`}]}}catch(f){return {content:[{type:"text",text:`Could not save learning: ${f instanceof Error?f.message:String(f)}`}],isError:true}}}),a.tool("remember_person","Create or update a note about a person \u2014 their role, organization, context, and interactions.",{name:z$1.string().describe("Person's name"),role:z$1.string().optional().default("").describe("Their role or title"),organization:z$1.string().optional().default("").describe("Their organization"),contact:z$1.string().optional().default("").describe("Contact information"),context:z$1.string().optional().default("").describe("How you know them or relevant context"),interaction:z$1.string().optional().default("").describe("Notes from recent interaction"),notes:z$1.string().optional().default("").describe("Additional notes"),folder:z$1.string().optional().describe("Folder to save to")},async({name:t,role:n,organization:o,contact:r,context:i,interaction:s,notes:d,folder:l})=>{try{let f=D("person",{name:t,role:n,organization:o,contact:r,context:i,interaction:s,notes:d}),u=l??c.inbox.folder,p=await W(e,`${u}/${t}.md`);return await e.writeNote(p,f),{content:[{type:"text",text:`Person note created at "${p}"`}]}}catch(f){return {content:[{type:"text",text:`Could not create person note: ${f instanceof Error?f.message:String(f)}`}],isError:true}}}),a.tool("remember_reference","Bookmark a URL or resource with a summary, tags, and personal notes. Great for saving articles, videos, tools, or documentation.",{title:z$1.string().describe("Title of the resource"),url:z$1.string().url().describe("URL of the resource"),source:z$1.string().optional().default("").describe('Source (e.g., "Hacker News", "Twitter")'),summary:z$1.string().optional().default("").describe("Brief summary"),takeaways:z$1.string().optional().default("").describe("Key takeaways"),thoughts:z$1.string().optional().default("").describe("Your personal thoughts"),related:z$1.string().optional().default("").describe("Related notes"),tags:z$1.array(z$1.string()).optional().describe("Tags for categorization"),folder:z$1.string().optional().describe("Folder to save to")},async({title:t,url:n,source:o,summary:r,takeaways:i,thoughts:s,related:d,tags:l,folder:f})=>{try{let u=D("article",{title:t,url:n,source:o,summary:r,takeaways:i,thoughts:s,related:d,tags:l?l.map(h=>` - ${h}`).join(`
|
|
81
|
+
`):""}),p=f??c.inbox.folder,m=await W(e,`${p}/${t}.md`);return await e.writeNote(m,u),{content:[{type:"text",text:`Reference saved at "${m}"`}]}}catch(u){return {content:[{type:"text",text:`Could not save reference: ${u instanceof Error?u.message:String(u)}`}],isError:true}}});}function T(a){for(let e=a.length-1;e>0;e--){let c=Math.floor(Math.random()*(e+1));[a[e],a[c]]=[a[c],a[e]];}return a}function wt(a,e,c){a.tool("log","Append an entry to today's daily note. Creates the daily note if it doesn't exist. Adds entries under a configurable heading with timestamps.",{entry:z$1.string().describe("The entry to add to the daily note"),heading:z$1.string().optional().default("Log").describe('Heading to add the entry under (default: "Log")')},async({entry:t,heading:n})=>{try{let o=`${c.dailyNotes.folder}/${$()}.md`,i=`
|
|
82
|
+
- ${new Date().toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit"})} \u2014 ${t}`,s=null;try{s=await e.readNote(o);}catch{}if(s!==null)P(s,n)?await e.patchNote(o,i,{operation:"append",targetHeading:n}):await e.appendToNote(o,`
|
|
83
|
+
## ${n}
|
|
84
|
+
${i}
|
|
85
|
+
`);else {let d=`---
|
|
86
|
+
date: ${$()}
|
|
80
87
|
tags:
|
|
81
88
|
- daily
|
|
82
89
|
---
|
|
83
90
|
|
|
84
|
-
# ${
|
|
91
|
+
# ${$()}
|
|
85
92
|
|
|
86
|
-
## ${
|
|
87
|
-
${
|
|
88
|
-
`;await
|
|
89
|
-
- [ ] ${t}
|
|
90
|
-
## ${
|
|
93
|
+
## ${n}
|
|
94
|
+
${i}
|
|
95
|
+
`;await e.writeNote(o,d);}return {content:[{type:"text",text:`Logged to daily note (${$()}): ${t}`}]}}catch(o){return {content:[{type:"text",text:`Could not log entry: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("log_task","Add a task (checkbox item) to today's daily note. Creates the daily note if it doesn't exist.",{task:z$1.string().describe("The task description"),heading:z$1.string().optional().default("Tasks").describe('Heading to add the task under (default: "Tasks")')},async({task:t,heading:n})=>{try{let o=`${c.dailyNotes.folder}/${$()}.md`,r=`
|
|
96
|
+
- [ ] ${t}`,i=null;try{i=await e.readNote(o);}catch{}if(i!==null)P(i,n)?await e.patchNote(o,r,{operation:"append",targetHeading:n}):await e.appendToNote(o,`
|
|
97
|
+
## ${n}
|
|
91
98
|
${r}
|
|
92
|
-
`);
|
|
93
|
-
date: ${
|
|
99
|
+
`);else {let s=`---
|
|
100
|
+
date: ${$()}
|
|
94
101
|
tags:
|
|
95
102
|
- daily
|
|
96
103
|
---
|
|
97
104
|
|
|
98
|
-
# ${
|
|
105
|
+
# ${$()}
|
|
99
106
|
|
|
100
|
-
## ${
|
|
107
|
+
## ${n}
|
|
101
108
|
${r}
|
|
102
|
-
`;await
|
|
109
|
+
`;await e.writeNote(o,s);}return {content:[{type:"text",text:`Task added to daily note: ${t}`}]}}catch(o){return {content:[{type:"text",text:`Could not add task: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("reflect","Generate reflection prompts based on recent vault activity. Reads today's and recent daily notes to suggest areas for reflection.",{},async()=>{try{let t="";try{let d=`${c.dailyNotes.folder}/${$()}.md`;t=await e.readNote(d);}catch{}let n=[];if(t){let d=L(t),l=d.filter(p=>!p.done),f=d.filter(p=>p.done),u=b(t);l.length>0&&n.push(`You have ${l.length} open task(s). Which one deserves your focus next, and why?`),f.length>0&&n.push(`You completed ${f.length} task(s) today. What did you learn from that work?`),u.length>0&&n.push(`Today's note touches on ${u.map(p=>`#${p}`).join(", ")}. How are these topics connected?`);}let o=["What was the most important thing you learned today?","What decision did you make that you want to remember?","What are you most proud of from today's work?","What would you do differently next time?","What's one thing you want to explore further?","Who helped you today, and how?","What's blocking you right now?","What's one thing you're grateful for today?"],r=Math.max(1,4-n.length),i=[...n.slice(0,3),...T([...o]).slice(0,r)],s=`**Reflection Prompts for ${$()}:**
|
|
103
110
|
|
|
104
|
-
`;return
|
|
105
|
-
`),t
|
|
111
|
+
`;return s+=i.map((d,l)=>`${l+1}. ${d}`).join(`
|
|
112
|
+
`),t?s+=`
|
|
106
113
|
|
|
107
114
|
---
|
|
108
115
|
|
|
109
116
|
**Today's note preview:**
|
|
110
|
-
${t.slice(0,500)}
|
|
117
|
+
${t.slice(0,500)}`:s+=`
|
|
111
118
|
|
|
112
119
|
---
|
|
113
120
|
|
|
114
|
-
|
|
115
|
-
${
|
|
116
|
-
`),{content:[{type:"text",text:`Content appended to "${t}"`}]}}catch(
|
|
117
|
-
- [[${
|
|
118
|
-
- [[${
|
|
121
|
+
No daily note yet for today. Use \`log\` to start one.`,s+="\n\nTip: Use `log` to add your reflections to today's daily note.",{content:[{type:"text",text:s}]}}catch(t){return {content:[{type:"text",text:`Reflection error: ${t instanceof Error?t.message:String(t)}`}],isError:true}}});}function $t(a,e,c){a.tool("create_note","Create a new note in the vault. Supports optional templates, folder routing, and frontmatter. Will not overwrite existing notes unless explicitly requested.",{path:z$1.string().max(500).describe('Path for the new note (e.g., "Projects/My Project.md")'),content:z$1.string().max(1e5).describe("Markdown content for the note"),template:z$1.enum(["decision","meeting","learning","idea","person","project","book","article","weekly-review","session"]).optional().describe("Optional template to use"),variables:z$1.record(z$1.string()).optional().describe("Template variables as key-value pairs"),overwrite:z$1.boolean().optional().default(false).describe("Whether to overwrite if note already exists")},async({path:t,content:n,template:o,variables:r,overwrite:i})=>{try{if(!i)try{return await e.readNote(t),{content:[{type:"text",text:`Note already exists at "${t}". Set overwrite=true to replace it.`}]}}catch{}let s=n;return o&&(s=D(o,{...r,content:n})),await e.writeNote(t,s),{content:[{type:"text",text:`Note created at "${t}"`}]}}catch(s){return {content:[{type:"text",text:`Could not create note: ${s instanceof Error?s.message:String(s)}`}],isError:true}}}),a.tool("append_to_note","Append content to the end of an existing note. The note must already exist.",{path:z$1.string().max(500).describe("Path to the note to append to"),content:z$1.string().max(1e5).describe("Content to append")},async({path:t,content:n})=>{try{return await e.appendToNote(t,`
|
|
122
|
+
${n}
|
|
123
|
+
`),{content:[{type:"text",text:`Content appended to "${t}"`}]}}catch(o){return {content:[{type:"text",text:`Could not append to note: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("update_section","Replace the content under a specific heading in a note. Useful for updating a section without modifying the rest of the note.",{path:z$1.string().describe("Path to the note"),heading:z$1.string().describe("Heading text of the section to replace"),content:z$1.string().describe("New content for the section")},async({path:t,heading:n,content:o})=>{try{let r=await e.readNote(t),i=V(r,n,o);return await e.writeNote(t,i),{content:[{type:"text",text:`Section "${n}" updated in "${t}"`}]}}catch(r){return {content:[{type:"text",text:`Could not update section: ${r instanceof Error?r.message:String(r)}`}],isError:true}}}),a.tool("delete_note","Permanently delete a note from the vault. This cannot be undone.",{path:z$1.string().max(500).describe("Path to the note to delete"),confirm:z$1.boolean().describe("Must be true to confirm deletion")},async({path:t,confirm:n})=>{if(!n)return {content:[{type:"text",text:`Deletion not confirmed. Set confirm=true to delete "${t}".`}]};try{return await e.deleteNote(t),{content:[{type:"text",text:`Deleted "${t}"`}]}}catch(o){return {content:[{type:"text",text:`Could not delete note: ${o instanceof Error?o.message:String(o)}`}],isError:true}}});}function bt(a,e,c){a.tool("add_link",'Add a [[wikilink]] to a note, connecting it to another note in the vault. Appends the link under a "Related" heading or at the end of the note.',{path:z$1.string().describe("Path of the note to add the link to"),target:z$1.string().describe("Name of the note to link to (without .md extension)"),context:z$1.string().optional().describe("Optional context for why these notes are related")},async({path:t,target:n,context:o})=>{try{let r=await e.readNote(t);if(C(r).includes(n))return {content:[{type:"text",text:`"${t}" already links to [[${n}]]`}]};let s=o?`
|
|
124
|
+
- [[${n}]] \u2014 ${o}`:`
|
|
125
|
+
- [[${n}]]`;return P(r,"Related")?await e.patchNote(t,s,{operation:"append",targetHeading:"Related"}):await e.appendToNote(t,`
|
|
119
126
|
## Related
|
|
120
|
-
${
|
|
121
|
-
`)
|
|
122
|
-
${
|
|
127
|
+
${s}
|
|
128
|
+
`),{content:[{type:"text",text:`Added link to [[${n}]] in "${t}"`}]}}catch(r){return {content:[{type:"text",text:`Could not add link: ${r instanceof Error?r.message:String(r)}`}],isError:true}}}),a.tool("suggest_connections","Analyze a note and suggest other notes that could be linked to it. Uses shared tags, mentions, and content similarity to find connections.",{path:z$1.string().describe("Path to the note to analyze"),limit:z$1.number().optional().default(10).describe("Maximum suggestions to return")},async({path:t,limit:n})=>{try{let o=await e.readNote(t),r=C(o),i=b(o),s=new Map,d=[],l=[];for(let y of i.slice(0,5))d.push(`tag:#${y}`),l.push({type:"tag",label:y});let f=new Set(["about","above","after","again","against","being","below","between","could","during","every","further","having","itself","might","other","ought","shall","should","their","there","these","those","through","under","until","where","which","while","would","your"]),u=[...new Set(o.replace(/[^\w\s]/g," ").split(/\s+/).map(y=>y.toLowerCase()).filter(y=>y.length>5&&!f.has(y)))],p=Math.max(1,Math.floor(u.length/5)),m=[];for(let y=0;y<u.length&&m.length<5;y+=p)m.push(u[y]);for(let y of m.slice(0,3))d.push(y),l.push({type:"word",label:y});let h=await e.searchBatch(d,{limit:5});for(let y=0;y<d.length;y++){let k=h.get(d[y])??[],S=l[y];for(let q of k){if(q.filename===t||r.includes(q.filename.replace(/\.md$/,"")))continue;let X=s.get(q.filename)??[],rt=S.type==="tag"?`Shared tag: #${S.label}`:`Mentions: "${S.label}"`;X.includes(rt)||(X.push(rt),s.set(q.filename,X));}}let x=[...s.entries()].sort((y,k)=>k[1].length-y[1].length).slice(0,n);if(x.length===0)return {content:[{type:"text",text:`No connection suggestions found for "${t}". The note may be well-connected already!`}]};let w=x.map(([y,k],S)=>`${S+1}. **${y}**
|
|
129
|
+
${k.join(", ")}`).join(`
|
|
123
130
|
|
|
124
131
|
`);return {content:[{type:"text",text:`Suggested connections for "${t}":
|
|
125
132
|
|
|
126
|
-
${
|
|
133
|
+
${w}
|
|
127
134
|
|
|
128
|
-
Use \`add_link\` to connect any of these notes.`}]}}catch(
|
|
135
|
+
Use \`add_link\` to connect any of these notes.`}]}}catch(o){return {content:[{type:"text",text:`Could not suggest connections: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("find_gaps","Find broken links in the vault \u2014 [[wikilinks]] that point to notes that don't exist. These represent knowledge gaps or notes waiting to be written.",{limit:z$1.number().optional().default(20).describe("Maximum gaps to return")},async({limit:t})=>{try{let o=(await e.listFiles()).filter(u=>u.extension==="md"),r=new Set;for(let u of o)r.add(u.name.toLowerCase()),r.add(u.path.replace(/\.md$/,"").toLowerCase());let i=new Map,s=o.length<=500?o:T([...o]).slice(0,200),d=s.length;for(let u of s)try{let p=await e.readNote(u.path),m=C(p);for(let h of m)if(!r.has(h.toLowerCase())){let x=i.get(h)??[];x.push(u.path),i.set(h,x);}}catch{}let l=[...i.entries()].sort((u,p)=>p[1].length-u[1].length).slice(0,t);return l.length===0?{content:[{type:"text",text:`No broken links found (sampled ${d} of ${o.length} notes).`}]}:{content:[{type:"text",text:`Knowledge gaps (broken links) found in vault:
|
|
129
136
|
|
|
130
|
-
${
|
|
137
|
+
${l.map(([u,p],m)=>`${m+1}. **[[${u}]]** \u2014 referenced by ${p.length} note(s): ${p.slice(0,3).join(", ")}${p.length>3?"...":""}`).join(`
|
|
131
138
|
`)}
|
|
132
139
|
|
|
133
|
-
These are concepts referenced but not yet written about. Use \`create_note\` to fill them in.`}]}}catch(
|
|
140
|
+
These are concepts referenced but not yet written about. Use \`create_note\` to fill them in.`}]}}catch(n){return {content:[{type:"text",text:`Could not find gaps: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("find_orphans","Find orphan notes \u2014 notes with no incoming or outgoing links. These are disconnected from the knowledge graph and might need connecting.",{limit:z$1.number().optional().default(20).describe("Maximum orphans to return")},async({limit:t})=>{try{let o=(await e.listFiles()).filter(u=>u.extension==="md"),r=new Set,i=o.length<=500?o:T([...o]).slice(0,200),s=i.length,d=new Map;for(let u of o)d.set(u.name.toLowerCase(),u.path),d.set(u.path.replace(/\.md$/,"").toLowerCase(),u.path);for(let u of i)try{let p=await e.readNote(u.path),m=C(p);if(m.length>0){r.add(u.path);for(let h of m){let x=d.get(h.toLowerCase());x&&r.add(x);}}}catch{}let l=i.filter(u=>!r.has(u.path)).slice(0,t);return l.length===0?{content:[{type:"text",text:`No orphan notes found (sampled ${s} notes). Your vault is well-connected!`}]}:{content:[{type:"text",text:`Orphan notes (no links in or out):
|
|
134
141
|
|
|
135
|
-
${l.map((u,
|
|
142
|
+
${l.map((u,p)=>`${p+1}. ${u.path}`).join(`
|
|
136
143
|
`)}
|
|
137
144
|
|
|
138
|
-
Consider linking these to related notes using \`add_link\` or \`suggest_connections\`.`}]}}catch(
|
|
139
|
-
`),u=Object.entries(r).filter(([d])=>d!=="tags").map(([d,p])=>`${d}: ${p}`).join(`
|
|
140
|
-
`),f=`---
|
|
141
|
-
${u}${u?`
|
|
142
|
-
`:""}tags:
|
|
143
|
-
${m}
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
${c}`;return await o.writeNote(t,f),{content:[{type:"text",text:`Added tags ${e.map(d=>`#${d}`).join(", ")} to "${t}"`}]}}catch(n){return {content:[{type:"text",text:`Could not add tags: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),s.tool("update_properties","Update YAML frontmatter properties of a note. Can add new properties or modify existing ones.",{path:z$1.string().describe("Path to the note"),properties:z$1.record(z$1.string()).describe('Properties to set as key-value pairs (e.g., {"status": "done", "priority": "high"})')},async({path:t,properties:e})=>{try{let n=await o.readNote(t),{frontmatter:r,body:c}=M(n),a={...r,...e},m=`---
|
|
147
|
-
${Object.entries(a).map(([f,d])=>`${f}: ${d}`).join(`
|
|
148
|
-
`)}
|
|
149
|
-
---
|
|
145
|
+
Consider linking these to related notes using \`add_link\` or \`suggest_connections\`.`}]}}catch(n){return {content:[{type:"text",text:`Could not find orphans: ${n instanceof Error?n.message:String(n)}`}],isError:true}}});}function kt(a,e,c){a.tool("add_tag","Add one or more tags to a note. Tags are added to the YAML frontmatter. If the note has no frontmatter, it will be created.",{path:z$1.string().describe("Path to the note"),tags:z$1.array(z$1.string()).max(50).describe("Tags to add (without # prefix)")},async({path:t,tags:n})=>{try{let o=await e.readNote(t),{frontmatter:r,body:i}=M(o),s=r.tags,d=Array.isArray(s)?s.map(String):typeof s=="string"&&s.length>0?[s]:[],l=[...new Set([...d,...n])];r.tags=l;let f=U(r,i);return await e.writeNote(t,f),{content:[{type:"text",text:`Added tags ${n.map(u=>`#${u}`).join(", ")} to "${t}"`}]}}catch(o){return {content:[{type:"text",text:`Could not add tags: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("update_properties","Update YAML frontmatter properties of a note. Can add new properties or modify existing ones.",{path:z$1.string().describe("Path to the note"),properties:z$1.record(z$1.string()).describe('Properties to set as key-value pairs (e.g., {"status": "done", "priority": "high"})')},async({path:t,properties:n})=>{try{let o=await e.readNote(t),{frontmatter:r,body:i}=M(o),s={...r,...n},d=U(s,i);await e.writeNote(t,d);let l=Object.entries(n).map(([f,u])=>`${f}: ${u}`).join(", ");return {content:[{type:"text",text:`Updated properties in "${t}": ${l}`}]}}catch(o){return {content:[{type:"text",text:`Could not update properties: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("rename_note","Rename a note by creating a copy at the new path and deleting the original. Note: Obsidian will update backlinks if the app is running.",{old_path:z$1.string().describe("Current path of the note"),new_path:z$1.string().describe("New path for the note")},async({old_path:t,new_path:n})=>{try{let o=await e.readNote(t);try{return await e.readNote(n),{content:[{type:"text",text:`A note already exists at "${n}". Choose a different name.`}]}}catch{}await e.writeNote(n,o);let r=t.replace(/\.md$/,"").split("/").pop()??t,i=n.replace(/\.md$/,"").split("/").pop()??n,s=0;r!==i&&(s=await e.updateBacklinks(r,i)),await e.deleteNote(t);let d=s>0?` Updated ${s} backlink(s).`:"";return {content:[{type:"text",text:`Renamed "${t}" to "${n}".${d}`}]}}catch(o){return {content:[{type:"text",text:`Could not rename note: ${o instanceof Error?o.message:String(o)}`}],isError:true}}}),a.tool("move_note","Move a note to a different folder in the vault. Preserves the filename.",{path:z$1.string().describe("Current path of the note"),destination:z$1.string().describe('Destination folder (e.g., "Projects", "Archive")')},async({path:t,destination:n})=>{try{let o=await e.readNote(t),r=t.split("/").pop()??t,i=`${n}/${r}`;try{return await e.readNote(i),{content:[{type:"text",text:`A note already exists at "${i}". Rename the note first.`}]}}catch{}await e.writeNote(i,o);let s=t.replace(/\.md$/,"").split("/").pop()??t,d=i.replace(/\.md$/,"").split("/").pop()??i,l=0;s!==d&&(l=await e.updateBacklinks(s,d)),await e.deleteNote(t);let f=l>0?` Updated ${l} backlink(s).`:"";return {content:[{type:"text",text:`Moved "${t}" to "${i}".${f}`}]}}catch(o){return {content:[{type:"text",text:`Could not move note: ${o instanceof Error?o.message:String(o)}`}],isError:true}}});}function Ct(a,e,c){a.tool("vault_overview","Get an overview of your Obsidian vault: total notes, folder structure, tag distribution, and general statistics.",{},async()=>{try{let t=await e.listFiles(),n=t.filter(p=>p.extension==="md"),o=t.filter(p=>p.extension!=="md"),r=new Map;for(let p of n){let m=p.path.split("/"),h=m.length>1?m.slice(0,-1).join("/"):"(root)";r.set(h,(r.get(h)??0)+1);}let i=[...r.entries()].sort((p,m)=>m[1]-p[1]).slice(0,15),s=new Map,d=n.length<=500?n:T([...n]).slice(0,200),l=d.length;for(let p of d)try{let m=await e.readNote(p.path),h=b(m);for(let x of h)s.set(x,(s.get(x)??0)+1);}catch{}let f=[...s.entries()].sort((p,m)=>m[1]-p[1]).slice(0,15),u=`**Vault Overview**
|
|
150
146
|
|
|
151
|
-
|
|
147
|
+
`;return u+=`- **Total notes:** ${n.length}
|
|
148
|
+
`,u+=`- **Other files:** ${o.length} (images, PDFs, etc.)
|
|
149
|
+
`,u+=`- **Folders:** ${r.size}
|
|
152
150
|
|
|
153
|
-
|
|
154
|
-
`,
|
|
155
|
-
|
|
151
|
+
`,u+=`**Top Folders:**
|
|
152
|
+
`,u+=i.map(([p,m])=>` - ${p}: ${m} notes`).join(`
|
|
153
|
+
`),f.length>0&&(u+=`
|
|
156
154
|
|
|
157
|
-
|
|
158
|
-
`,f
|
|
159
|
-
`),u.length
|
|
155
|
+
**Top Tags** (from ${l} notes):
|
|
156
|
+
`,u+=f.map(([p,m])=>` - #${p}: ${m}`).join(`
|
|
157
|
+
`)),{content:[{type:"text",text:u}]}}catch(t){return {content:[{type:"text",text:`Could not get vault overview: ${t instanceof Error?t.message:String(t)}`}],isError:true}}}),a.tool("list_tags","List all tags used in the vault with their occurrence counts. Samples notes to build the tag list.",{limit:z$1.number().optional().default(50).describe("Maximum tags to return")},async({limit:t})=>{try{let o=(await e.listFiles()).filter(f=>f.extension==="md"),r=new Map,i=o.length<=500?o:T([...o]).slice(0,300),s=i.length;for(let f of i)try{let u=await e.readNote(f.path),p=b(u);for(let m of p)r.set(m,(r.get(m)??0)+1);}catch{}let d=[...r.entries()].sort((f,u)=>u[1]-f[1]).slice(0,t);if(d.length===0)return {content:[{type:"text",text:"No tags found in vault."}]};let l=d.map(([f,u])=>`#${f} (${u})`).join(`
|
|
158
|
+
`);return {content:[{type:"text",text:`**Tags in vault** (sampled ${s} of ${o.length} notes):
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
`)),{content:[{type:"text",text:f}]}}catch(t){return {content:[{type:"text",text:`Could not get vault overview: ${t instanceof Error?t.message:String(t)}`}],isError:true}}}),s.tool("list_tags","List all tags used in the vault with their occurrence counts. Samples notes to build the tag list.",{limit:z$1.number().optional().default(50).describe("Maximum tags to return")},async({limit:t})=>{try{let n=(await o.listFiles()).filter(u=>u.extension==="md"),r=new Map,c=Math.min(n.length,200),a=n.slice(0,c);for(let u of a)try{let f=await o.readNote(u.path),d=$(f);for(let p of d)r.set(p,(r.get(p)??0)+1);}catch{}let l=[...r.entries()].sort((u,f)=>f[1]-u[1]).slice(0,t);if(l.length===0)return {content:[{type:"text",text:"No tags found in vault."}]};let m=l.map(([u,f])=>`#${u} (${f})`).join(`
|
|
164
|
-
`);return {content:[{type:"text",text:`**Tags in vault** (sampled ${c} of ${n.length} notes):
|
|
160
|
+
${l}`}]}}catch(n){return {content:[{type:"text",text:`Could not list tags: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("get_vault_structure","Get the folder structure of the vault as a tree with note counts per folder. Useful for understanding vault organization.",{},async()=>{try{let n=(await e.listFiles()).filter(s=>s.extension==="md"),o=new Map;for(let s of n){let d=s.path.split("/");if(d.length>1)for(let l=1;l<d.length;l++){let f=d.slice(0,l).join("/");l===d.length-1?o.set(f,(o.get(f)??0)+1):o.has(f)||o.set(f,0);}else o.set("(root)",(o.get("(root)")??0)+1);}let i=[...o.entries()].sort((s,d)=>s[0].localeCompare(d[0])).map(([s,d])=>{let l=s.split("/").length-1,f=" ".repeat(l),u=s.split("/").pop()??s;return `${f}${u}/ (${d} notes)`}).join(`
|
|
161
|
+
`);return {content:[{type:"text",text:`**Vault Structure** (${n.length} total notes):
|
|
165
162
|
|
|
166
|
-
${
|
|
167
|
-
|
|
163
|
+
${i}`}]}}catch(t){return {content:[{type:"text",text:`Could not get vault structure: ${t instanceof Error?t.message:String(t)}`}],isError:true}}});}function vt(a,e,c){a.tool("find_tasks","Find all checkbox tasks across the vault. Filter by status (open, done, or all). Returns tasks with their source note and line number.",{status:z$1.enum(["open","done","all"]).optional().default("open").describe("Filter by task status"),query:z$1.string().optional().describe("Optional search query to filter tasks by text"),limit:z$1.number().optional().default(30).describe("Maximum tasks to return")},async({status:t,query:n,limit:o})=>{try{let i=(await e.listFiles()).filter(u=>u.extension==="md"),s=[],d=i.length<=500?i:T([...i]).slice(0,200);for(let u of d)try{let p=await e.readNote(u.path),m=L(p);for(let h of m)n&&!h.text.toLowerCase().includes(n.toLowerCase())||t==="open"&&h.done||t==="done"&&!h.done||s.push({text:h.text,done:h.done,file:u.path,line:h.line});}catch{}if(s.length===0)return {content:[{type:"text",text:`No ${t==="all"?"":t+" "}tasks found${n?` matching "${n}"`:""}.`}]};let l=s.slice(0,o),f=l.map((u,p)=>`${p+1}. [${u.done?"x":" "}] ${u.text}
|
|
164
|
+
*${u.file}:${u.line}*`).join(`
|
|
168
165
|
|
|
169
|
-
|
|
170
|
-
*${d.file}:${d.line}*`).join(`
|
|
166
|
+
`);return {content:[{type:"text",text:`**${t==="all"?"All":t==="open"?"Open":"Completed"} Tasks** (${s.length} found, showing ${l.length}):
|
|
171
167
|
|
|
172
|
-
`)
|
|
168
|
+
${f}`}]}}catch(r){return {content:[{type:"text",text:`Could not find tasks: ${r instanceof Error?r.message:String(r)}`}],isError:true}}}),a.tool("complete_task","Mark a specific task as done by replacing [ ] with [x] at the given line in a note.",{path:z$1.string().describe("Path to the note containing the task"),line:z$1.number().describe("Line number of the task (1-based)")},async({path:t,line:n})=>{try{let r=(await e.readNote(t)).split(`
|
|
169
|
+
`);if(n<1||n>r.length)return {content:[{type:"text",text:`Line ${n} is out of range (note has ${r.length} lines)`}],isError:!0};let i=r[n-1],s=i.match(/^(\s*-\s+)\[[ ]\](\s+.+)$/);return s?(r[n-1]=`${s[1]}[x]${s[2]}`,await e.writeNote(t,r.join(`
|
|
170
|
+
`)),{content:[{type:"text",text:`Completed task: "${s[2].trim()}" in "${t}"`}]}):{content:[{type:"text",text:`Line ${n} is not an open task: "${i.trim()}"`}],isError:!0}}catch(o){return {content:[{type:"text",text:`Could not complete task: ${o instanceof Error?o.message:String(o)}`}],isError:true}}});}function St(a,e,c){a.tool("bulk_tag","Add or remove tags across multiple notes matching a search query. Useful for vault-wide tag management and organization.",{query:z$1.string().max(500).describe("Search query to find notes to tag (same operators as search tool)"),add:z$1.array(z$1.string()).optional().describe("Tags to add (without # prefix)"),remove:z$1.array(z$1.string()).optional().describe("Tags to remove (without # prefix)"),limit:z$1.number().optional().default(50).describe("Maximum notes to modify")},async({query:t,add:n,remove:o,limit:r})=>{if(!n?.length&&!o?.length)return {content:[{type:"text",text:"Specify at least one tag to add or remove."}],isError:true};try{let i=await e.search(t,{limit:r});if(i.length===0)return {content:[{type:"text",text:`No notes matched "${t}".`}]};let s=0;for(let l of i)try{let f=await e.readNote(l.filename),{frontmatter:u,body:p}=M(f),m=u.tags,h=Array.isArray(m)?m.map(String):typeof m=="string"&&m.length>0?[m]:[],x=h.length;if(n?.length&&(h=[...new Set([...h,...n])]),o?.length){let w=new Set(o);h=h.filter(y=>!w.has(y));}if(h.length!==x||n?.some(w=>!h.includes(w))===!1){u.tags=h.length>0?h:void 0;let w=U(Object.fromEntries(Object.entries(u).filter(([y,k])=>k!==void 0)),p);await e.writeNote(l.filename,w),s++;}}catch{}let d=[];return n?.length&&d.push(`added ${n.map(l=>`#${l}`).join(", ")}`),o?.length&&d.push(`removed ${o.map(l=>`#${l}`).join(", ")}`),{content:[{type:"text",text:`${d.join(" and ")} \u2014 modified ${s} of ${i.length} matching notes.`}]}}catch(i){return {content:[{type:"text",text:`Bulk tag error: ${i instanceof Error?i.message:String(i)}`}],isError:true}}}),a.tool("daily_notes","List all daily notes in the vault with stats (entry count, task counts). Useful for reviewing journaling history and activity.",{limit:z$1.number().optional().default(30).describe("Maximum daily notes to return")},async({limit:t})=>{try{let n=await e.listFiles(),o=c.dailyNotes.folder,r=n.filter(s=>s.extension==="md"&&s.path.startsWith(o+"/")).slice(0,t);if(r.length===0)return {content:[{type:"text",text:`No daily notes found in "${o}/".`}]};let i=[];for(let s of r)try{let d=await e.readNote(s.path),l=L(d),f=l.filter(h=>!h.done).length,u=l.filter(h=>h.done).length,m=`${d.split(`
|
|
171
|
+
`).filter(h=>h.startsWith("- ")).length} entries`;l.length>0&&(m+=`, ${u}/${l.length} tasks done`,f>0&&(m+=` (${f} open)`)),i.push(`- **${s.name}** \u2014 ${m}`);}catch{i.push(`- **${s.name}** \u2014 (unreadable)`);}return {content:[{type:"text",text:`**Daily Notes** (${r.length} found):
|
|
173
172
|
|
|
174
|
-
${
|
|
175
|
-
`)
|
|
176
|
-
`)),{content:[{type:"text",text:`Completed task: "${a[2].trim()}" in "${t}"`}]}):{content:[{type:"text",text:`Line ${e} is not an open task: "${c.trim()}"`}],isError:!0}}catch(n){return {content:[{type:"text",text:`Could not complete task: ${n instanceof Error?n.message:String(n)}`}],isError:true}}});}function mt(s,o,i){s.resource("today","vault://today",{description:"Today's daily note content. Auto-updates with the current day's note.",mimeType:"text/markdown"},async t=>{try{let e=`${i.dailyNotes.folder}/${x()}.md`,n=await o.readNote(e);return {contents:[{uri:t.href,text:`# Today's Daily Note (${x()})
|
|
173
|
+
${i.join(`
|
|
174
|
+
`)}`}]}}catch(n){return {content:[{type:"text",text:`Could not list daily notes: ${n instanceof Error?n.message:String(n)}`}],isError:true}}}),a.tool("export_note","Export a note as clean markdown (frontmatter stripped) or as structured JSON with metadata. Useful for sharing or processing notes externally.",{path:z$1.string().max(500).describe("Path to the note"),format:z$1.enum(["markdown","json"]).optional().default("markdown").describe("Export format")},async({path:t,format:n})=>{try{let o=await e.readNote(t),{frontmatter:r,body:i}=M(o),s=b(o),d=C(o),l=L(o);if(n==="json"){let f={path:t,title:typeof r.title=="string"?r.title:t.replace(/\.md$/,"").split("/").pop(),frontmatter:r,tags:s,links:d,tasks:l.map(u=>({text:u.text,done:u.done})),body:i.trim(),wordCount:i.split(/\s+/).filter(u=>u.length>0).length,exportedAt:new Date().toISOString()};return {content:[{type:"text",text:JSON.stringify(f,null,2)}]}}return {content:[{type:"text",text:i.trim()}]}}catch(o){return {content:[{type:"text",text:`Could not export note: ${o instanceof Error?o.message:String(o)}`}],isError:true}}});}function Mt(a,e,c){a.resource("today","vault://today",{description:"Today's daily note content. Auto-updates with the current day's note.",mimeType:"text/markdown"},async t=>{try{let n=`${c.dailyNotes.folder}/${$()}.md`,o=await e.readNote(n);return {contents:[{uri:t.href,text:`# Today's Daily Note (${$()})
|
|
177
175
|
|
|
178
|
-
${
|
|
176
|
+
${o}`,mimeType:"text/markdown"}]}}catch{return {contents:[{uri:t.href,text:`No daily note for today (${$()}).`,mimeType:"text/plain"}]}}}),a.resource("recent","vault://recent",{description:"Summary of recently modified notes in the vault.",mimeType:"text/markdown"},async t=>{try{let r=(await e.listFiles()).filter(i=>i.extension==="md").slice(0,10).map((i,s)=>{let d=Date.now()-i.mtime,l=Math.floor(d/6e4),f=Math.floor(l/60),u=Math.floor(f/24),p=u>0?`${u}d ago`:f>0?`${f}h ago`:l>0?`${l}m ago`:"just now";return `${s+1}. ${i.path} \u2014 ${p}`}).join(`
|
|
179
177
|
`);return {contents:[{uri:t.href,text:`# Recent Notes
|
|
180
178
|
|
|
181
|
-
${r}`,mimeType:"text/markdown"}]}}catch{return {contents:[{uri:t.href,text:"Could not fetch recent notes.",mimeType:"text/plain"}]}}}),
|
|
179
|
+
${r}`,mimeType:"text/markdown"}]}}catch{return {contents:[{uri:t.href,text:"Could not fetch recent notes.",mimeType:"text/plain"}]}}}),a.resource("tags","vault://tags",{description:"Tag cloud with counts from the vault.",mimeType:"text/markdown"},async t=>{try{let o=(await e.listFiles()).filter(l=>l.extension==="md"),r=new Map,i=o.length<=500?o:T([...o]).slice(0,200);for(let l of i)try{let f=await e.readNote(l.path);for(let u of b(f))r.set(u,(r.get(u)??0)+1);}catch{}let d=[...r.entries()].sort((l,f)=>f[1]-l[1]).slice(0,30).map(([l,f])=>`- #${l} (${f})`).join(`
|
|
182
180
|
`);return {contents:[{uri:t.href,text:`# Vault Tags
|
|
183
181
|
|
|
184
|
-
${
|
|
182
|
+
${d||"No tags found."}`,mimeType:"text/markdown"}]}}catch{return {contents:[{uri:t.href,text:"Could not fetch tags.",mimeType:"text/plain"}]}}}),a.resource("context","vault://context",{description:"Contextual notes relevant to the current working directory or project. Auto-infers which notes may be useful.",mimeType:"text/markdown"},async t=>{try{let o=process.cwd().split("/").pop()??"",r=[];if(o)try{r=(await e.search(o)).slice(0,5).map(d=>d.filename);}catch{}if(r.length===0)return {contents:[{uri:t.href,text:`# Context
|
|
185
183
|
|
|
186
|
-
No vault notes found related to the current project "${
|
|
187
|
-
`);return {contents:[{uri:t.href,text:`# Context for "${
|
|
184
|
+
No vault notes found related to the current project "${o}".`,mimeType:"text/markdown"}]};let i=r.map((s,d)=>`${d+1}. ${s}`).join(`
|
|
185
|
+
`);return {contents:[{uri:t.href,text:`# Context for "${o}"
|
|
188
186
|
|
|
189
187
|
Relevant vault notes:
|
|
190
188
|
|
|
191
|
-
${
|
|
192
|
-
`);}function
|
|
193
|
-
`);}function
|
|
194
|
-
`);}var
|
|
195
|
-
`)){let
|
|
196
|
-
MindCache v${
|
|
189
|
+
${i}`,mimeType:"text/markdown"}]}}catch{return {contents:[{uri:t.href,text:"Could not determine context.",mimeType:"text/plain"}]}}});}var et="mindcache";function N(a){process.stderr.write(`[${et}] ${a}
|
|
190
|
+
`);}function z(a){process.stderr.write(`[${et}] ERROR: ${a}
|
|
191
|
+
`);}function Tt(a){process.stderr.write(`[${et}] WARN: ${a}
|
|
192
|
+
`);}var I="0.3.0";function Nt(a){let e=new McpServer({name:"mindcache",version:I},{instructions:["MindCache connects your Obsidian vault to AI.","Use the search and ask tools to find information in the user's personal knowledge base.","Use the remember tools to capture decisions, meetings, ideas, learnings, and references.","Use the log tool to add entries to the daily note.","Use the connect tools to build links between related notes.","Always check the vault before asking the user to repeat information they may have already documented.","When creating notes, use appropriate templates and add relevant tags."].join(" ")}),c=new O(a);return N("Registering tools..."),ht(e,c),gt(e,c,a),xt(e,c,a),wt(e,c,a),$t(e,c),bt(e,c),kt(e,c),Ct(e,c),vt(e,c),St(e,c,a),N("Registering resources..."),Mt(e,c,a),a.vault&&c.buildIndex().then(()=>{N("Search index built");}).catch(()=>{N("WARN: Could not build search index \u2014 falling back to full scan");}),N("Server configured with 44 tools and 4 resources"),{server:e,client:c}}var Q={vault:"",dailyNotes:{folder:"Daily",format:"YYYY-MM-DD"},templates:{folder:"Templates/MindCache"},inbox:{folder:"MindCache/Inbox",requireReview:false}};function Jt(a){let e={},c="";for(let t of a.split(`
|
|
193
|
+
`)){let n=t.replace(/#.*$/,"").trimEnd();if(!n.trim())continue;let o=n.length-n.trimStart().length,r=n.trim(),i=r.indexOf(":");if(i<0)continue;let s=r.slice(0,i).trim(),d=r.slice(i+1).trim();o===0&&!d?(c=s,e[c]||(e[c]={})):o>0&&c?e[c][s]=d:e[s]=d;}return e}function Xt(){let a=[resolve(process.cwd(),".mindcache.yml"),resolve(process.cwd(),".mindcache.yaml"),join(homedir(),".config","mindcache","config.yml"),join(homedir(),".config","mindcache","config.yaml"),join(homedir(),".mindcache.yml")];for(let e of a)if(existsSync(e))return e;return null}function H(){let a={vault:Q.vault,dailyNotes:{...Q.dailyNotes},templates:{...Q.templates},inbox:{...Q.inbox}};process.env.MINDCACHE_VAULT&&(a.vault=process.env.MINDCACHE_VAULT);let e=process.env.MINDCACHE_CONFIG||Xt();if(e&&existsSync(e)){N(`Loading config from ${e}`);try{let c=readFileSync(e,"utf-8"),t=Jt(c);typeof t.vault=="string"&&(a.vault=t.vault);let n=t.daily_notes??t.dailyNotes;n&&(n.folder&&(a.dailyNotes.folder=n.folder),n.format&&(a.dailyNotes.format=n.format));let o=t.templates;o&&o.folder&&(a.templates.folder=o.folder);let r=t.inbox;r&&(r.folder&&(a.inbox.folder=r.folder),r.require_review!==void 0&&(a.inbox.requireReview=String(r.require_review)==="true"));}catch(c){Tt(`Failed to parse config: ${c instanceof Error?c.message:String(c)}`);}}return a.vault.startsWith("~")&&(a.vault=a.vault.replace("~",homedir())),a}var re=`
|
|
194
|
+
MindCache v${I} \u2014 Your Obsidian vault, connected to AI.
|
|
197
195
|
|
|
198
196
|
Usage:
|
|
199
197
|
mindcache Start the MCP server (default)
|
|
@@ -213,18 +211,18 @@ Config file locations (checked in order):
|
|
|
213
211
|
~/.mindcache.yml
|
|
214
212
|
|
|
215
213
|
Learn more: https://usemindcache.com
|
|
216
|
-
`;async function
|
|
214
|
+
`;async function Dt(a){let e=createInterface({input:process.stdin,output:process.stderr});return new Promise(c=>{e.question(a,t=>{e.close(),c(t.trim());});})}async function se(){process.stderr.write(`
|
|
217
215
|
MindCache Setup
|
|
218
216
|
`),process.stderr.write(`===============
|
|
219
217
|
|
|
220
218
|
`),process.stderr.write(`MindCache reads your Obsidian vault directly from the filesystem.
|
|
221
219
|
`),process.stderr.write(`No plugins required \u2014 just point it at your vault folder.
|
|
222
220
|
|
|
223
|
-
`);let
|
|
224
|
-
vault: ${
|
|
221
|
+
`);let a=join(homedir(),"Documents","Obsidian Vault"),e=await Dt(`Obsidian vault path [${a}]: `)||a,c=await Dt("Daily notes folder [Daily]: ")||"Daily",t=`# MindCache Configuration
|
|
222
|
+
vault: ${e}
|
|
225
223
|
|
|
226
224
|
daily_notes:
|
|
227
|
-
folder: ${
|
|
225
|
+
folder: ${c}
|
|
228
226
|
format: YYYY-MM-DD
|
|
229
227
|
|
|
230
228
|
templates:
|
|
@@ -233,50 +231,50 @@ templates:
|
|
|
233
231
|
inbox:
|
|
234
232
|
folder: MindCache/Inbox
|
|
235
233
|
require_review: false
|
|
236
|
-
`,
|
|
237
|
-
Config saved to: ${
|
|
234
|
+
`,n=join(homedir(),".config","mindcache");existsSync(n)||mkdirSync(n,{recursive:true});let o=join(n,"config.yml");writeFileSync(o,t),process.stderr.write(`
|
|
235
|
+
Config saved to: ${o}
|
|
238
236
|
|
|
239
237
|
`),process.stderr.write(`Checking vault...
|
|
240
|
-
`);let r=
|
|
238
|
+
`);let r=H(),i=new O(r),s=await i.health();i.close(),s.ok?process.stderr.write(` Found ${s.noteCount} notes in vault
|
|
241
239
|
|
|
242
|
-
`):(process.stderr.write(` ${
|
|
240
|
+
`):(process.stderr.write(` ${s.error}
|
|
243
241
|
`),process.stderr.write(` Check that the vault path is correct.
|
|
244
242
|
|
|
245
243
|
`)),process.stderr.write(`Add MindCache to Claude Code:
|
|
246
244
|
`),process.stderr.write(` claude mcp add --scope user mindcache -- npx @augmnt-sh/mindcache
|
|
247
245
|
|
|
248
|
-
`),process.exit(0);}async function
|
|
249
|
-
MindCache Doctor v${
|
|
246
|
+
`),process.exit(0);}async function ie(){process.stderr.write(`
|
|
247
|
+
MindCache Doctor v${I}
|
|
250
248
|
`),process.stderr.write(`==========================
|
|
251
249
|
|
|
252
|
-
`);let
|
|
253
|
-
`),
|
|
250
|
+
`);let a=H();process.stderr.write(`Checking configuration...
|
|
251
|
+
`),a.vault?process.stderr.write(` [OK] Vault: ${a.vault}
|
|
254
252
|
`):process.stderr.write(` [WARN] No vault path configured. Run: mindcache init
|
|
255
|
-
`),process.stderr.write(` [OK] Daily notes: ${
|
|
256
|
-
`),process.stderr.write(` [OK] Templates: ${
|
|
257
|
-
`),process.stderr.write(` [OK] Inbox: ${
|
|
253
|
+
`),process.stderr.write(` [OK] Daily notes: ${a.dailyNotes.folder}/
|
|
254
|
+
`),process.stderr.write(` [OK] Templates: ${a.templates.folder}/
|
|
255
|
+
`),process.stderr.write(` [OK] Inbox: ${a.inbox.folder}/
|
|
258
256
|
`),process.stderr.write(`
|
|
259
257
|
Checking vault access...
|
|
260
|
-
`);let
|
|
261
|
-
`),process.stderr.write(` [OK] Found ${
|
|
262
|
-
`)):(process.stderr.write(` [FAIL] ${
|
|
258
|
+
`);let e=new O(a),c=await e.health();e.close(),c.ok?(process.stderr.write(` [OK] Vault readable
|
|
259
|
+
`),process.stderr.write(` [OK] Found ${c.noteCount} markdown notes
|
|
260
|
+
`)):(process.stderr.write(` [FAIL] ${c.error}
|
|
263
261
|
`),process.stderr.write(`
|
|
264
262
|
Troubleshooting:
|
|
265
263
|
`),process.stderr.write(` 1. Check that the vault path is correct
|
|
266
264
|
`),process.stderr.write(` 2. Check file permissions
|
|
267
265
|
`),process.stderr.write(` 3. Run: mindcache init
|
|
268
266
|
`)),process.stderr.write(`
|
|
269
|
-
`),process.exit(
|
|
267
|
+
`),process.exit(c.ok?0:1);}function ae(){let a=H();process.stderr.write(`
|
|
270
268
|
MindCache Configuration
|
|
271
269
|
`),process.stderr.write(`=======================
|
|
272
270
|
|
|
273
|
-
`),process.stderr.write(`Vault: ${
|
|
274
|
-
`),process.stderr.write(`Daily folder: ${
|
|
275
|
-
`),process.stderr.write(`Daily format: ${
|
|
276
|
-
`),process.stderr.write(`Templates: ${
|
|
277
|
-
`),process.stderr.write(`Inbox: ${
|
|
278
|
-
`),process.stderr.write(`Review mode: ${
|
|
271
|
+
`),process.stderr.write(`Vault: ${a.vault||"(not set)"}
|
|
272
|
+
`),process.stderr.write(`Daily folder: ${a.dailyNotes.folder}
|
|
273
|
+
`),process.stderr.write(`Daily format: ${a.dailyNotes.format}
|
|
274
|
+
`),process.stderr.write(`Templates: ${a.templates.folder}
|
|
275
|
+
`),process.stderr.write(`Inbox: ${a.inbox.folder}
|
|
276
|
+
`),process.stderr.write(`Review mode: ${a.inbox.requireReview?"on":"off"}
|
|
279
277
|
`),process.stderr.write(`
|
|
280
|
-
`),process.exit(0);}async function
|
|
281
|
-
`),process.exit(0);break;case "--help":case "-h":process.stderr.write(
|
|
278
|
+
`),process.exit(0);}async function ce(){let a=process.version;parseInt(a.slice(1))<18&&(z(`Node.js >= 18 required, found ${a}`),process.exit(1)),N(`mindcache v${I} | Node ${a} | ${process.platform}`);let c=H();c.vault||N('WARN: No vault path configured. Run "mindcache init" for setup.');let{server:t,client:n}=Nt(c),o=new StdioServerTransport,r=async()=>{N("Shutting down...");try{n.close(),await t.close();}catch(i){z(`Shutdown error: ${i instanceof Error?i.message:String(i)}`),process.exit(1);}process.exit(0);};process.on("SIGINT",r),process.on("SIGTERM",r),await t.connect(o);}var le=process.argv[2];switch(le){case "init":se().catch(a=>{z(a instanceof Error?a.message:String(a)),process.exit(1);});break;case "doctor":ie().catch(a=>{z(a instanceof Error?a.message:String(a)),process.exit(1);});break;case "config":ae();break;case "--version":case "-v":process.stderr.write(`mindcache v${I}
|
|
279
|
+
`),process.exit(0);break;case "--help":case "-h":process.stderr.write(re),process.exit(0);break;default:ce().catch(a=>{z(`Fatal: ${a instanceof Error?a.message:String(a)}`),process.exit(1);});}//# sourceMappingURL=cli.js.map
|
|
282
280
|
//# sourceMappingURL=cli.js.map
|