@theglitchking/semantic-pages 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/embedder.ts","../src/core/graph.ts","../src/core/vector.ts","../src/core/search-text.ts","../src/core/crud.ts","../src/core/frontmatter.ts","../src/core/watcher.ts"],"sourcesContent":["import { pipeline, type FeatureExtractionPipeline } from \"@huggingface/transformers\";\nimport { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\n\nconst DEFAULT_MODEL = \"nomic-ai/nomic-embed-text-v1.5\";\nconst CACHE_DIR = join(homedir(), \".semantic-pages\", \"models\");\n\nexport class Embedder {\n private model: string;\n private extractor: FeatureExtractionPipeline | null = null;\n private dimensions = 0;\n\n constructor(model: string = DEFAULT_MODEL) {\n this.model = model;\n }\n\n async init(): Promise<void> {\n if (this.extractor) return;\n\n await mkdir(CACHE_DIR, { recursive: true });\n\n this.extractor = await pipeline(\"feature-extraction\", this.model, {\n cache_dir: CACHE_DIR,\n dtype: \"fp32\",\n });\n\n // Determine dimensions from a test embedding\n const test = await this.embed(\"test\");\n this.dimensions = test.length;\n }\n\n async embed(text: string): Promise<Float32Array> {\n if (!this.extractor) throw new Error(\"Embedder not initialized. Call init() first.\");\n\n const output = await this.extractor(text, { pooling: \"mean\", normalize: true });\n return new Float32Array(output.data as ArrayLike<number>);\n }\n\n async embedBatch(texts: string[]): Promise<Float32Array[]> {\n return Promise.all(texts.map((t) => this.embed(t)));\n }\n\n getDimensions(): number {\n return this.dimensions;\n }\n\n getModel(): string {\n return this.model;\n }\n\n async saveEmbeddings(\n embeddings: Map<string, Float32Array>,\n indexPath: string\n ): Promise<void> {\n const entries: Array<{ key: string; data: number[] }> = [];\n for (const [key, vec] of embeddings) {\n entries.push({ key, data: Array.from(vec) });\n }\n await writeFile(join(indexPath, \"embeddings.json\"), JSON.stringify(entries));\n }\n\n async loadEmbeddings(\n indexPath: string\n ): Promise<Map<string, Float32Array>> {\n const filePath = join(indexPath, \"embeddings.json\");\n if (!existsSync(filePath)) return new Map();\n\n const raw = await readFile(filePath, \"utf-8\");\n const entries: Array<{ key: string; data: number[] }> = JSON.parse(raw);\n const map = new Map<string, Float32Array>();\n for (const entry of entries) {\n map.set(entry.key, new Float32Array(entry.data));\n }\n return map;\n }\n}\n","import Graph from \"graphology\";\nimport { bfsFromNode } from \"graphology-traversal\";\nimport { bidirectional } from \"graphology-shortest-path\";\nimport type { IndexedDocument, GraphNode, GraphEdge, GraphStats } from \"./types.js\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nexport class GraphBuilder {\n private graph: Graph;\n\n constructor() {\n this.graph = new Graph({ type: \"directed\", multi: false });\n }\n\n buildFromDocuments(documents: IndexedDocument[]): void {\n this.graph.clear();\n\n // Add nodes\n for (const doc of documents) {\n this.graph.addNode(doc.path, {\n title: doc.title,\n tags: doc.tags,\n });\n }\n\n const pathLookup = new Map<string, string>();\n for (const doc of documents) {\n const nameNoExt = doc.path.replace(/\\.md$/, \"\");\n const basename = nameNoExt.split(\"/\").pop()!;\n pathLookup.set(basename.toLowerCase(), doc.path);\n pathLookup.set(nameNoExt.toLowerCase(), doc.path);\n }\n\n // Add wikilink edges\n for (const doc of documents) {\n for (const link of doc.wikilinks) {\n const target = pathLookup.get(link.toLowerCase());\n if (target && target !== doc.path && !this.graph.hasEdge(doc.path, target)) {\n this.graph.addEdge(doc.path, target, { type: \"wikilink\", weight: 1.0 });\n }\n }\n }\n\n // Add tag-based edges\n const tagToNotes = new Map<string, string[]>();\n for (const doc of documents) {\n for (const tag of doc.tags) {\n const notes = tagToNotes.get(tag) || [];\n notes.push(doc.path);\n tagToNotes.set(tag, notes);\n }\n }\n\n for (const [, notes] of tagToNotes) {\n for (let i = 0; i < notes.length; i++) {\n for (let j = i + 1; j < notes.length; j++) {\n if (!this.graph.hasEdge(notes[i], notes[j])) {\n this.graph.addEdge(notes[i], notes[j], { type: \"tag\", weight: 0.5 });\n }\n if (!this.graph.hasEdge(notes[j], notes[i])) {\n this.graph.addEdge(notes[j], notes[i], { type: \"tag\", weight: 0.5 });\n }\n }\n }\n }\n }\n\n backlinks(notePath: string): GraphNode[] {\n if (!this.graph.hasNode(notePath)) return [];\n return this.graph.inNeighbors(notePath).map((n) => this.nodeToGraphNode(n));\n }\n\n forwardlinks(notePath: string): GraphNode[] {\n if (!this.graph.hasNode(notePath)) return [];\n return this.graph.outNeighbors(notePath).map((n) => this.nodeToGraphNode(n));\n }\n\n findPath(from: string, to: string): string[] | null {\n if (!this.graph.hasNode(from) || !this.graph.hasNode(to)) return null;\n const path = bidirectional(this.graph, from, to);\n return path;\n }\n\n searchGraph(concept: string, maxDepth = 2): GraphNode[] {\n const startNodes = this.graph\n .nodes()\n .filter((n) => {\n const attrs = this.graph.getNodeAttributes(n);\n return (\n n.toLowerCase().includes(concept.toLowerCase()) ||\n attrs.title?.toLowerCase().includes(concept.toLowerCase()) ||\n attrs.tags?.some((t: string) => t.toLowerCase().includes(concept.toLowerCase()))\n );\n });\n\n const visited = new Set<string>();\n for (const start of startNodes) {\n let depth = 0;\n bfsFromNode(this.graph, start, (node) => {\n visited.add(node);\n depth++;\n return depth > maxDepth;\n });\n }\n\n return [...visited].map((n) => this.nodeToGraphNode(n));\n }\n\n statistics(): GraphStats {\n const nodes = this.graph.order;\n const edges = this.graph.size;\n const orphans = this.graph.nodes().filter(\n (n) => this.graph.degree(n) === 0\n );\n\n const connections = this.graph.nodes().map((n) => ({\n path: n,\n connections: this.graph.degree(n),\n }));\n connections.sort((a, b) => b.connections - a.connections);\n\n const maxPossibleEdges = nodes * (nodes - 1);\n const density = maxPossibleEdges > 0 ? edges / maxPossibleEdges : 0;\n\n return {\n totalNodes: nodes,\n totalEdges: edges,\n orphanCount: orphans.length,\n mostConnected: connections.slice(0, 10),\n density,\n };\n }\n\n private nodeToGraphNode(nodePath: string): GraphNode {\n const attrs = this.graph.getNodeAttributes(nodePath);\n return {\n path: nodePath,\n title: attrs.title || nodePath,\n tags: attrs.tags || [],\n linkCount: this.graph.outDegree(nodePath),\n backlinkCount: this.graph.inDegree(nodePath),\n };\n }\n\n async save(indexPath: string): Promise<void> {\n const data = this.graph.export();\n await writeFile(join(indexPath, \"graph.json\"), JSON.stringify(data));\n }\n\n async load(indexPath: string): Promise<boolean> {\n const filePath = join(indexPath, \"graph.json\");\n if (!existsSync(filePath)) return false;\n\n const raw = await readFile(filePath, \"utf-8\");\n const data = JSON.parse(raw);\n this.graph.import(data);\n return true;\n }\n\n getGraph(): Graph {\n return this.graph;\n }\n}\n","import hnswlib from \"hnswlib-node\";\nconst { HierarchicalNSW } = hnswlib;\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport type { SearchResult } from \"./types.js\";\n\ninterface ChunkMeta {\n docPath: string;\n chunkIndex: number;\n text: string;\n}\n\nexport class VectorIndex {\n private index: InstanceType<typeof HierarchicalNSW> | null = null;\n private dimensions: number;\n private chunkMeta: ChunkMeta[] = [];\n\n constructor(dimensions: number) {\n this.dimensions = dimensions;\n }\n\n build(\n embeddings: Float32Array[],\n meta: ChunkMeta[]\n ): void {\n if (embeddings.length === 0) {\n this.index = null;\n this.chunkMeta = [];\n return;\n }\n\n this.index = new HierarchicalNSW(\"cosine\", this.dimensions);\n this.index.initIndex(embeddings.length);\n\n for (let i = 0; i < embeddings.length; i++) {\n this.index.addPoint(Array.from(embeddings[i]), i);\n }\n\n this.chunkMeta = meta;\n }\n\n search(queryEmbedding: Float32Array, k: number = 10): SearchResult[] {\n if (!this.index || this.chunkMeta.length === 0) return [];\n\n const numResults = Math.min(k, this.chunkMeta.length);\n const result = this.index.searchKnn(Array.from(queryEmbedding), numResults);\n\n const seen = new Set<string>();\n const results: SearchResult[] = [];\n\n for (let i = 0; i < result.neighbors.length; i++) {\n const idx = result.neighbors[i];\n const meta = this.chunkMeta[idx];\n if (!meta || seen.has(meta.docPath)) continue;\n seen.add(meta.docPath);\n\n results.push({\n path: meta.docPath,\n title: meta.docPath,\n score: 1 - result.distances[i],\n snippet: meta.text.slice(0, 200),\n matchedChunk: meta.text,\n });\n }\n\n return results;\n }\n\n async save(indexPath: string): Promise<void> {\n if (!this.index) return;\n\n this.index.writeIndexSync(join(indexPath, \"hnsw.bin\"));\n await writeFile(\n join(indexPath, \"hnsw-meta.json\"),\n JSON.stringify(this.chunkMeta)\n );\n }\n\n async load(indexPath: string): Promise<boolean> {\n const hnswPath = join(indexPath, \"hnsw.bin\");\n const metaPath = join(indexPath, \"hnsw-meta.json\");\n\n if (!existsSync(hnswPath) || !existsSync(metaPath)) return false;\n\n const raw = await readFile(metaPath, \"utf-8\");\n this.chunkMeta = JSON.parse(raw);\n\n this.index = new HierarchicalNSW(\"cosine\", this.dimensions);\n this.index.initIndex(this.chunkMeta.length);\n this.index.readIndexSync(hnswPath);\n\n return true;\n }\n\n getChunkMeta(): ChunkMeta[] {\n return this.chunkMeta;\n }\n}\n","import type { IndexedDocument, SearchResult, SearchTextOptions } from \"./types.js\";\nimport { minimatch } from \"minimatch\";\n\nexport class TextSearch {\n private documents: IndexedDocument[] = [];\n\n setDocuments(documents: IndexedDocument[]): void {\n this.documents = documents;\n }\n\n search(options: SearchTextOptions): SearchResult[] {\n const { pattern, regex, caseSensitive, pathGlob, tagFilter, limit = 20 } = options;\n\n let matcher: (text: string) => { matched: boolean; index: number };\n\n if (regex) {\n const flags = caseSensitive ? \"g\" : \"gi\";\n const re = new RegExp(pattern, flags);\n matcher = (text) => {\n re.lastIndex = 0;\n const m = re.exec(text);\n return { matched: !!m, index: m?.index ?? -1 };\n };\n } else {\n const needle = caseSensitive ? pattern : pattern.toLowerCase();\n matcher = (text) => {\n const haystack = caseSensitive ? text : text.toLowerCase();\n const idx = haystack.indexOf(needle);\n return { matched: idx >= 0, index: idx };\n };\n }\n\n const results: SearchResult[] = [];\n\n for (const doc of this.documents) {\n // Path filter\n if (pathGlob && !minimatch(doc.path, pathGlob)) continue;\n\n // Tag filter\n if (tagFilter?.length) {\n const hasTag = tagFilter.some((t) => doc.tags.includes(t));\n if (!hasTag) continue;\n }\n\n const { matched, index } = matcher(doc.content);\n if (!matched) continue;\n\n const snippetStart = Math.max(0, index - 80);\n const snippetEnd = Math.min(doc.content.length, index + 120);\n const snippet = doc.content.slice(snippetStart, snippetEnd).trim();\n\n results.push({\n path: doc.path,\n title: doc.title,\n score: 1.0,\n snippet: (snippetStart > 0 ? \"...\" : \"\") + snippet + (snippetEnd < doc.content.length ? \"...\" : \"\"),\n });\n\n if (results.length >= limit) break;\n }\n\n return results;\n }\n}\n","import { readFile, writeFile, unlink, rename, mkdir } from \"node:fs/promises\";\nimport { dirname, join, relative } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport { glob } from \"glob\";\nimport matter from \"gray-matter\";\nimport type { UpdateNoteOptions } from \"./types.js\";\n\nexport class NoteCrud {\n private notesPath: string;\n\n constructor(notesPath: string) {\n this.notesPath = notesPath;\n }\n\n async create(\n relativePath: string,\n content: string,\n frontmatter?: Record<string, unknown>\n ): Promise<string> {\n const absPath = join(this.notesPath, relativePath);\n if (existsSync(absPath)) {\n throw new Error(`Note already exists: ${relativePath}`);\n }\n\n await mkdir(dirname(absPath), { recursive: true });\n\n let fileContent: string;\n if (frontmatter && Object.keys(frontmatter).length > 0) {\n fileContent = matter.stringify(content, frontmatter);\n } else {\n fileContent = content;\n }\n\n await writeFile(absPath, fileContent, \"utf-8\");\n return relativePath;\n }\n\n async read(relativePath: string): Promise<string> {\n const absPath = join(this.notesPath, relativePath);\n return readFile(absPath, \"utf-8\");\n }\n\n async readMultiple(paths: string[]): Promise<Map<string, string>> {\n const results = new Map<string, string>();\n await Promise.all(\n paths.map(async (p) => {\n try {\n const content = await this.read(p);\n results.set(p, content);\n } catch {\n results.set(p, `[Error: could not read ${p}]`);\n }\n })\n );\n return results;\n }\n\n async update(\n relativePath: string,\n content: string,\n options: UpdateNoteOptions\n ): Promise<void> {\n const absPath = join(this.notesPath, relativePath);\n if (!existsSync(absPath)) {\n throw new Error(`Note does not exist: ${relativePath}`);\n }\n\n const existing = await readFile(absPath, \"utf-8\");\n\n let updated: string;\n\n switch (options.mode) {\n case \"overwrite\":\n updated = content;\n break;\n\n case \"append\":\n updated = existing + \"\\n\" + content;\n break;\n\n case \"prepend\": {\n const { data, content: body } = matter(existing);\n const newBody = content + \"\\n\" + body;\n updated = Object.keys(data).length > 0 ? matter.stringify(newBody, data) : newBody;\n break;\n }\n\n case \"patch-by-heading\": {\n if (!options.heading) throw new Error(\"patch-by-heading requires a heading\");\n updated = this.patchByHeading(existing, options.heading, content);\n break;\n }\n }\n\n await writeFile(absPath, updated, \"utf-8\");\n }\n\n async delete(relativePath: string): Promise<void> {\n const absPath = join(this.notesPath, relativePath);\n if (!existsSync(absPath)) {\n throw new Error(`Note does not exist: ${relativePath}`);\n }\n await unlink(absPath);\n }\n\n async move(fromPath: string, toPath: string): Promise<void> {\n const absFrom = join(this.notesPath, fromPath);\n const absTo = join(this.notesPath, toPath);\n\n if (!existsSync(absFrom)) {\n throw new Error(`Note does not exist: ${fromPath}`);\n }\n if (existsSync(absTo)) {\n throw new Error(`Destination already exists: ${toPath}`);\n }\n\n await mkdir(dirname(absTo), { recursive: true });\n await rename(absFrom, absTo);\n\n // Update wikilinks in other files that reference the old path\n await this.updateWikilinksAfterMove(fromPath, toPath);\n }\n\n private async updateWikilinksAfterMove(\n oldPath: string,\n newPath: string\n ): Promise<void> {\n const oldName = oldPath.replace(/\\.md$/, \"\").split(\"/\").pop()!;\n const newName = newPath.replace(/\\.md$/, \"\").split(\"/\").pop()!;\n\n if (oldName === newName) return;\n\n const files = await glob(\"**/*.md\", { cwd: this.notesPath });\n\n for (const file of files) {\n const absPath = join(this.notesPath, file);\n const content = await readFile(absPath, \"utf-8\");\n\n const pattern = new RegExp(`\\\\[\\\\[${escapeRegex(oldName)}(\\\\|[^\\\\]]*)?\\\\]\\\\]`, \"g\");\n if (!pattern.test(content)) continue;\n\n const updated = content.replace(pattern, `[[${newName}$1]]`);\n await writeFile(absPath, updated, \"utf-8\");\n }\n }\n\n private patchByHeading(\n content: string,\n heading: string,\n newContent: string\n ): string {\n const lines = content.split(\"\\n\");\n const headingPattern = new RegExp(`^#{1,6}\\\\s+${escapeRegex(heading)}\\\\s*$`, \"i\");\n\n let headingIndex = -1;\n let headingLevel = 0;\n\n for (let i = 0; i < lines.length; i++) {\n if (headingPattern.test(lines[i])) {\n headingIndex = i;\n const match = lines[i].match(/^(#{1,6})\\s+/);\n headingLevel = match ? match[1].length : 1;\n break;\n }\n }\n\n if (headingIndex === -1) {\n throw new Error(`Heading not found: ${heading}`);\n }\n\n // Find the end of this section (next heading of same or higher level)\n let endIndex = lines.length;\n for (let i = headingIndex + 1; i < lines.length; i++) {\n const match = lines[i].match(/^(#{1,6})\\s+/);\n if (match && match[1].length <= headingLevel) {\n endIndex = i;\n break;\n }\n }\n\n const before = lines.slice(0, headingIndex + 1);\n const after = lines.slice(endIndex);\n\n return [...before, \"\", newContent, \"\", ...after].join(\"\\n\");\n }\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport matter from \"gray-matter\";\n\nexport class FrontmatterManager {\n private notesPath: string;\n\n constructor(notesPath: string) {\n this.notesPath = notesPath;\n }\n\n async get(relativePath: string): Promise<Record<string, unknown>> {\n const absPath = join(this.notesPath, relativePath);\n const raw = await readFile(absPath, \"utf-8\");\n const { data } = matter(raw);\n return data;\n }\n\n async update(\n relativePath: string,\n fields: Record<string, unknown>\n ): Promise<void> {\n const absPath = join(this.notesPath, relativePath);\n const raw = await readFile(absPath, \"utf-8\");\n const { data, content } = matter(raw);\n\n for (const [key, value] of Object.entries(fields)) {\n if (value === null || value === undefined) {\n delete data[key];\n } else {\n data[key] = value;\n }\n }\n\n const updated = matter.stringify(content, data);\n await writeFile(absPath, updated, \"utf-8\");\n }\n}\n\nexport class TagManager {\n private notesPath: string;\n\n constructor(notesPath: string) {\n this.notesPath = notesPath;\n }\n\n async list(relativePath: string): Promise<string[]> {\n const absPath = join(this.notesPath, relativePath);\n const raw = await readFile(absPath, \"utf-8\");\n const { data, content } = matter(raw);\n\n const fmTags = Array.isArray(data.tags) ? (data.tags as string[]) : [];\n const inlineTags = [...content.matchAll(/(?:^|\\s)#([a-zA-Z][\\w-/]*)/g)].map(\n (m) => m[1]\n );\n\n return [...new Set([...fmTags, ...inlineTags])];\n }\n\n async add(relativePath: string, tags: string[]): Promise<void> {\n const absPath = join(this.notesPath, relativePath);\n const raw = await readFile(absPath, \"utf-8\");\n const { data, content } = matter(raw);\n\n const existing = Array.isArray(data.tags) ? (data.tags as string[]) : [];\n const merged = [...new Set([...existing, ...tags])];\n data.tags = merged;\n\n const updated = matter.stringify(content, data);\n await writeFile(absPath, updated, \"utf-8\");\n }\n\n async remove(relativePath: string, tags: string[]): Promise<void> {\n const absPath = join(this.notesPath, relativePath);\n const raw = await readFile(absPath, \"utf-8\");\n const { data, content } = matter(raw);\n\n // Remove from frontmatter\n if (Array.isArray(data.tags)) {\n data.tags = (data.tags as string[]).filter((t) => !tags.includes(t));\n }\n\n // Remove inline tags\n let updatedContent = content;\n for (const tag of tags) {\n const pattern = new RegExp(`(^|\\\\s)#${escapeRegex(tag)}(?=\\\\s|$)`, \"g\");\n updatedContent = updatedContent.replace(pattern, \"$1\");\n }\n\n const updated = matter.stringify(updatedContent, data);\n await writeFile(absPath, updated, \"utf-8\");\n }\n\n async renameVaultWide(oldTag: string, newTag: string): Promise<number> {\n const files = await glob(\"**/*.md\", { cwd: this.notesPath });\n let count = 0;\n\n for (const file of files) {\n const absPath = join(this.notesPath, file);\n const raw = await readFile(absPath, \"utf-8\");\n const { data, content } = matter(raw);\n let changed = false;\n\n // Rename in frontmatter\n if (Array.isArray(data.tags)) {\n const idx = (data.tags as string[]).indexOf(oldTag);\n if (idx >= 0) {\n (data.tags as string[])[idx] = newTag;\n changed = true;\n }\n }\n\n // Rename inline tags\n const pattern = new RegExp(`(^|\\\\s)#${escapeRegex(oldTag)}(?=\\\\s|$)`, \"g\");\n const updatedContent = content.replace(pattern, `$1#${newTag}`);\n if (updatedContent !== content) changed = true;\n\n if (changed) {\n const updated = matter.stringify(updatedContent, data);\n await writeFile(absPath, updated, \"utf-8\");\n count++;\n }\n }\n\n return count;\n }\n}\n\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { watch, type FSWatcher } from \"chokidar\";\nimport { EventEmitter } from \"node:events\";\n\nexport interface WatcherEvents {\n changed: (paths: string[]) => void;\n error: (error: Error) => void;\n}\n\nexport class Watcher extends EventEmitter {\n private notesPath: string;\n private fsWatcher: FSWatcher | null = null;\n private debounceMs: number;\n private pendingChanges = new Set<string>();\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private readyPromise: Promise<void> | null = null;\n private usePolling: boolean;\n\n constructor(notesPath: string, debounceMs: number = 500, usePolling: boolean = false) {\n super();\n this.notesPath = notesPath;\n this.debounceMs = debounceMs;\n this.usePolling = usePolling;\n }\n\n start(): void {\n if (this.fsWatcher) return;\n\n this.fsWatcher = watch(\"**/*.md\", {\n cwd: this.notesPath,\n ignoreInitial: true,\n ignored: [\n \"**/node_modules/**\",\n \"**/.semantic-pages-index/**\",\n \"**/.git/**\",\n ],\n ...(this.usePolling ? { usePolling: true, interval: 100 } : {}),\n });\n\n this.readyPromise = new Promise<void>((resolve) => {\n this.fsWatcher!.on(\"ready\", resolve);\n });\n\n this.fsWatcher.on(\"add\", (path) => this.enqueue(path));\n this.fsWatcher.on(\"change\", (path) => this.enqueue(path));\n this.fsWatcher.on(\"unlink\", (path) => this.enqueue(path));\n this.fsWatcher.on(\"error\", (err) => this.emit(\"error\", err));\n }\n\n async ready(): Promise<void> {\n if (this.readyPromise) await this.readyPromise;\n }\n\n stop(): void {\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n this.fsWatcher?.close();\n this.fsWatcher = null;\n this.pendingChanges.clear();\n }\n\n private enqueue(path: string): void {\n this.pendingChanges.add(path);\n\n if (this.debounceTimer) clearTimeout(this.debounceTimer);\n this.debounceTimer = setTimeout(() => {\n const paths = [...this.pendingChanges];\n this.pendingChanges.clear();\n this.emit(\"changed\", paths);\n }, this.debounceMs);\n }\n}\n"],"mappings":"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","names":["readFile","writeFile","join","existsSync","readFile","writeFile","join","existsSync","readFile","writeFile","mkdir","join","existsSync","readFile","writeFile","join","glob","matter","escapeRegex"]}
@@ -0,0 +1,114 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ // src/core/indexer.ts
8
+ import { unified } from "unified";
9
+ import remarkParse from "remark-parse";
10
+ import remarkWikiLink from "remark-wiki-link";
11
+ import matter from "gray-matter";
12
+ import { glob } from "glob";
13
+ import { readFile } from "fs/promises";
14
+ import { basename, join } from "path";
15
+ var CHUNK_TARGET_CHARS = 2e3;
16
+ var Indexer = class {
17
+ notesPath;
18
+ processor;
19
+ constructor(notesPath) {
20
+ this.notesPath = notesPath;
21
+ this.processor = unified().use(remarkParse).use(remarkWikiLink);
22
+ }
23
+ async indexAll() {
24
+ const files = await glob("**/*.md", { cwd: this.notesPath });
25
+ const docs = await Promise.all(
26
+ files.map((file) => this.indexFile(join(this.notesPath, file), file))
27
+ );
28
+ return docs;
29
+ }
30
+ async indexFile(absolutePath, relativePath) {
31
+ const raw = await readFile(absolutePath, "utf-8");
32
+ const { data: frontmatter, content } = matter(raw);
33
+ const tree = this.processor.parse(content);
34
+ const wikilinks = this.extractWikilinks(tree);
35
+ const tags = this.extractTags(content, frontmatter);
36
+ const headers = this.extractHeaders(tree);
37
+ const plainText = this.stripMarkdown(content);
38
+ const chunks = this.chunkText(plainText);
39
+ const title = frontmatter.title || headers[0] || basename(relativePath, ".md");
40
+ return {
41
+ path: relativePath,
42
+ title,
43
+ content: plainText,
44
+ frontmatter,
45
+ wikilinks,
46
+ tags,
47
+ headers,
48
+ chunks
49
+ };
50
+ }
51
+ extractWikilinks(tree) {
52
+ const links = [];
53
+ const walk = (node) => {
54
+ if (node.type === "wikiLink") {
55
+ links.push(node.value || node.data?.alias || "");
56
+ }
57
+ if (node.children) {
58
+ for (const child of node.children) walk(child);
59
+ }
60
+ };
61
+ walk(tree);
62
+ return [...new Set(links.filter(Boolean))];
63
+ }
64
+ extractTags(content, frontmatter) {
65
+ const inlineTags = [...content.matchAll(/(?:^|\s)#([a-zA-Z][\w-/]*)/g)].map(
66
+ (m) => m[1]
67
+ );
68
+ const fmTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
69
+ return [.../* @__PURE__ */ new Set([...fmTags, ...inlineTags])];
70
+ }
71
+ extractHeaders(tree) {
72
+ const headers = [];
73
+ const walk = (node) => {
74
+ if (node.type === "heading") {
75
+ const text = this.nodeToText(node);
76
+ if (text) headers.push(text);
77
+ }
78
+ if (node.children) {
79
+ for (const child of node.children) walk(child);
80
+ }
81
+ };
82
+ walk(tree);
83
+ return headers;
84
+ }
85
+ nodeToText(node) {
86
+ if (node.type === "text") return node.value;
87
+ if (node.children) return node.children.map((c) => this.nodeToText(c)).join("");
88
+ return "";
89
+ }
90
+ stripMarkdown(content) {
91
+ return content.replace(/```[\s\S]*?```/g, "").replace(/`[^`]+`/g, "").replace(/!\[.*?\]\(.*?\)/g, "").replace(/\[([^\]]+)\]\(.*?\)/g, "$1").replace(/#{1,6}\s+/g, "").replace(/[*_~]{1,3}/g, "").replace(/>\s+/g, "").replace(/\|.*\|/g, "").replace(/-{3,}/g, "").replace(/\n{3,}/g, "\n\n").trim();
92
+ }
93
+ chunkText(text) {
94
+ if (text.length <= CHUNK_TARGET_CHARS) return [text];
95
+ const sentences = text.match(/[^.!?\n]+[.!?\n]+|[^.!?\n]+$/g) || [text];
96
+ const chunks = [];
97
+ let current = "";
98
+ for (const sentence of sentences) {
99
+ if (current.length + sentence.length > CHUNK_TARGET_CHARS && current) {
100
+ chunks.push(current.trim());
101
+ current = "";
102
+ }
103
+ current += sentence;
104
+ }
105
+ if (current.trim()) chunks.push(current.trim());
106
+ return chunks;
107
+ }
108
+ };
109
+
110
+ export {
111
+ __export,
112
+ Indexer
113
+ };
114
+ //# sourceMappingURL=chunk-TDC45FQJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/indexer.ts"],"sourcesContent":["import { unified } from \"unified\";\nimport remarkParse from \"remark-parse\";\nimport remarkWikiLink from \"remark-wiki-link\";\nimport matter from \"gray-matter\";\nimport { glob } from \"glob\";\nimport { readFile } from \"node:fs/promises\";\nimport { basename, join, relative } from \"node:path\";\nimport type { IndexedDocument } from \"./types.js\";\n\nconst CHUNK_TARGET_CHARS = 2000; // ~512 tokens\n\nexport class Indexer {\n private notesPath: string;\n private processor: any;\n\n constructor(notesPath: string) {\n this.notesPath = notesPath;\n this.processor = unified().use(remarkParse).use(remarkWikiLink);\n }\n\n async indexAll(): Promise<IndexedDocument[]> {\n const files = await glob(\"**/*.md\", { cwd: this.notesPath });\n const docs = await Promise.all(\n files.map((file) => this.indexFile(join(this.notesPath, file), file))\n );\n return docs;\n }\n\n async indexFile(\n absolutePath: string,\n relativePath: string\n ): Promise<IndexedDocument> {\n const raw = await readFile(absolutePath, \"utf-8\");\n const { data: frontmatter, content } = matter(raw);\n const tree = this.processor.parse(content);\n\n const wikilinks = this.extractWikilinks(tree);\n const tags = this.extractTags(content, frontmatter);\n const headers = this.extractHeaders(tree);\n const plainText = this.stripMarkdown(content);\n const chunks = this.chunkText(plainText);\n\n const title =\n (frontmatter.title as string) ||\n headers[0] ||\n basename(relativePath, \".md\");\n\n return {\n path: relativePath,\n title,\n content: plainText,\n frontmatter,\n wikilinks,\n tags,\n headers,\n chunks,\n };\n }\n\n private extractWikilinks(tree: any): string[] {\n const links: string[] = [];\n const walk = (node: any) => {\n if (node.type === \"wikiLink\") {\n links.push(node.value || node.data?.alias || \"\");\n }\n if (node.children) {\n for (const child of node.children) walk(child);\n }\n };\n walk(tree);\n return [...new Set(links.filter(Boolean))];\n }\n\n private extractTags(content: string, frontmatter: Record<string, unknown>): string[] {\n const inlineTags = [...content.matchAll(/(?:^|\\s)#([a-zA-Z][\\w-/]*)/g)].map(\n (m) => m[1]\n );\n\n const fmTags = Array.isArray(frontmatter.tags)\n ? (frontmatter.tags as string[])\n : [];\n\n return [...new Set([...fmTags, ...inlineTags])];\n }\n\n private extractHeaders(tree: any): string[] {\n const headers: string[] = [];\n const walk = (node: any) => {\n if (node.type === \"heading\") {\n const text = this.nodeToText(node);\n if (text) headers.push(text);\n }\n if (node.children) {\n for (const child of node.children) walk(child);\n }\n };\n walk(tree);\n return headers;\n }\n\n private nodeToText(node: any): string {\n if (node.type === \"text\") return node.value;\n if (node.children) return node.children.map((c: any) => this.nodeToText(c)).join(\"\");\n return \"\";\n }\n\n private stripMarkdown(content: string): string {\n return content\n .replace(/```[\\s\\S]*?```/g, \"\")\n .replace(/`[^`]+`/g, \"\")\n .replace(/!\\[.*?\\]\\(.*?\\)/g, \"\")\n .replace(/\\[([^\\]]+)\\]\\(.*?\\)/g, \"$1\")\n .replace(/#{1,6}\\s+/g, \"\")\n .replace(/[*_~]{1,3}/g, \"\")\n .replace(/>\\s+/g, \"\")\n .replace(/\\|.*\\|/g, \"\")\n .replace(/-{3,}/g, \"\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n }\n\n chunkText(text: string): string[] {\n if (text.length <= CHUNK_TARGET_CHARS) return [text];\n\n const sentences = text.match(/[^.!?\\n]+[.!?\\n]+|[^.!?\\n]+$/g) || [text];\n const chunks: string[] = [];\n let current = \"\";\n\n for (const sentence of sentences) {\n if (current.length + sentence.length > CHUNK_TARGET_CHARS && current) {\n chunks.push(current.trim());\n current = \"\";\n }\n current += sentence;\n }\n if (current.trim()) chunks.push(current.trim());\n\n return chunks;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/index.ts
4
+ import { program } from "commander";
5
+ import { resolve } from "path";
6
+ import { existsSync } from "fs";
7
+ program.name("semantic-pages").description("Semantic search + knowledge graph MCP server for markdown files").version("0.1.0").requiredOption("--notes <path>", "Path to markdown notes directory").option("--reindex", "Force full reindex and exit").option("--stats", "Show vault statistics and exit").option("--model <name>", "Embedding model to use", "nomic-ai/nomic-embed-text-v1.5").option("--no-watch", "Disable file watcher");
8
+ program.parse();
9
+ var opts = program.opts();
10
+ var notesPath = resolve(opts.notes);
11
+ if (!existsSync(notesPath)) {
12
+ console.error(`Error: notes directory not found: ${notesPath}`);
13
+ process.exit(1);
14
+ }
15
+ async function main() {
16
+ if (opts.stats) {
17
+ const { Indexer } = await import("../indexer-HSCSXWIO.js");
18
+ const indexer = new Indexer(notesPath);
19
+ const docs = await indexer.indexAll();
20
+ console.log(`Notes: ${docs.length}`);
21
+ console.log(`Chunks: ${docs.reduce((n, d) => n + d.chunks.length, 0)}`);
22
+ console.log(`Wikilinks: ${docs.reduce((n, d) => n + d.wikilinks.length, 0)}`);
23
+ console.log(`Tags: ${new Set(docs.flatMap((d) => d.tags)).size} unique`);
24
+ process.exit(0);
25
+ }
26
+ if (opts.reindex) {
27
+ const { createServer } = await import("../mcp/server.js");
28
+ await createServer(notesPath, { watch: false });
29
+ console.log("Reindex complete.");
30
+ process.exit(0);
31
+ }
32
+ const { startServer } = await import("../mcp/server.js");
33
+ await startServer(notesPath, { watch: opts.watch });
34
+ }
35
+ main().catch((err) => {
36
+ console.error(err);
37
+ process.exit(1);
38
+ });
39
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { program } from \"commander\";\nimport { resolve } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\nprogram\n .name(\"semantic-pages\")\n .description(\"Semantic search + knowledge graph MCP server for markdown files\")\n .version(\"0.1.0\")\n .requiredOption(\"--notes <path>\", \"Path to markdown notes directory\")\n .option(\"--reindex\", \"Force full reindex and exit\")\n .option(\"--stats\", \"Show vault statistics and exit\")\n .option(\"--model <name>\", \"Embedding model to use\", \"nomic-ai/nomic-embed-text-v1.5\")\n .option(\"--no-watch\", \"Disable file watcher\");\n\nprogram.parse();\n\nconst opts = program.opts();\nconst notesPath = resolve(opts.notes);\n\nif (!existsSync(notesPath)) {\n console.error(`Error: notes directory not found: ${notesPath}`);\n process.exit(1);\n}\n\nasync function main() {\n if (opts.stats) {\n const { Indexer } = await import(\"../core/indexer.js\");\n const indexer = new Indexer(notesPath);\n const docs = await indexer.indexAll();\n console.log(`Notes: ${docs.length}`);\n console.log(`Chunks: ${docs.reduce((n, d) => n + d.chunks.length, 0)}`);\n console.log(`Wikilinks: ${docs.reduce((n, d) => n + d.wikilinks.length, 0)}`);\n console.log(`Tags: ${new Set(docs.flatMap((d) => d.tags)).size} unique`);\n process.exit(0);\n }\n\n if (opts.reindex) {\n const { createServer } = await import(\"../mcp/server.js\");\n await createServer(notesPath, { watch: false });\n console.log(\"Reindex complete.\");\n process.exit(0);\n }\n\n // Default: start MCP server on stdio\n const { startServer } = await import(\"../mcp/server.js\");\n await startServer(notesPath, { watch: opts.watch });\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n"],"mappings":";;;AAEA,SAAS,eAAe;AACxB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAE3B,QACG,KAAK,gBAAgB,EACrB,YAAY,iEAAiE,EAC7E,QAAQ,OAAO,EACf,eAAe,kBAAkB,kCAAkC,EACnE,OAAO,aAAa,6BAA6B,EACjD,OAAO,WAAW,gCAAgC,EAClD,OAAO,kBAAkB,0BAA0B,gCAAgC,EACnF,OAAO,cAAc,sBAAsB;AAE9C,QAAQ,MAAM;AAEd,IAAM,OAAO,QAAQ,KAAK;AAC1B,IAAM,YAAY,QAAQ,KAAK,KAAK;AAEpC,IAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,UAAQ,MAAM,qCAAqC,SAAS,EAAE;AAC9D,UAAQ,KAAK,CAAC;AAChB;AAEA,eAAe,OAAO;AACpB,MAAI,KAAK,OAAO;AACd,UAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,wBAAoB;AACrD,UAAM,UAAU,IAAI,QAAQ,SAAS;AACrC,UAAM,OAAO,MAAM,QAAQ,SAAS;AACpC,YAAQ,IAAI,UAAU,KAAK,MAAM,EAAE;AACnC,YAAQ,IAAI,WAAW,KAAK,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,OAAO,QAAQ,CAAC,CAAC,EAAE;AACtE,YAAQ,IAAI,cAAc,KAAK,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,UAAU,QAAQ,CAAC,CAAC,EAAE;AAC5E,YAAQ,IAAI,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,IAAI,SAAS;AACvE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,MAAI,KAAK,SAAS;AAChB,UAAM,EAAE,aAAa,IAAI,MAAM,OAAO,kBAAkB;AACxD,UAAM,aAAa,WAAW,EAAE,OAAO,MAAM,CAAC;AAC9C,YAAQ,IAAI,mBAAmB;AAC/B,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,kBAAkB;AACvD,QAAM,YAAY,WAAW,EAAE,OAAO,KAAK,MAAM,CAAC;AACpD;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,GAAG;AACjB,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
@@ -0,0 +1,177 @@
1
+ import Graph from 'graphology';
2
+ import { EventEmitter } from 'node:events';
3
+
4
+ interface IndexedDocument {
5
+ path: string;
6
+ title: string;
7
+ content: string;
8
+ frontmatter: Record<string, unknown>;
9
+ wikilinks: string[];
10
+ tags: string[];
11
+ headers: string[];
12
+ chunks: string[];
13
+ }
14
+ interface SearchResult {
15
+ path: string;
16
+ title: string;
17
+ score: number;
18
+ snippet: string;
19
+ matchedChunk?: string;
20
+ }
21
+ interface GraphNode {
22
+ path: string;
23
+ title: string;
24
+ tags: string[];
25
+ linkCount: number;
26
+ backlinkCount: number;
27
+ }
28
+ interface GraphEdge {
29
+ source: string;
30
+ target: string;
31
+ type: "wikilink" | "tag" | "backlink";
32
+ weight: number;
33
+ }
34
+ interface GraphStats {
35
+ totalNodes: number;
36
+ totalEdges: number;
37
+ orphanCount: number;
38
+ mostConnected: Array<{
39
+ path: string;
40
+ connections: number;
41
+ }>;
42
+ density: number;
43
+ }
44
+ interface VaultStats {
45
+ totalNotes: number;
46
+ totalChunks: number;
47
+ totalEmbeddings: number;
48
+ embeddingDimensions: number;
49
+ embeddingModel: string;
50
+ graphNodes: number;
51
+ graphEdges: number;
52
+ indexSize: number;
53
+ lastIndexed: string | null;
54
+ }
55
+ interface UpdateNoteOptions {
56
+ mode: "overwrite" | "append" | "prepend" | "patch-by-heading";
57
+ heading?: string;
58
+ }
59
+ interface SearchTextOptions {
60
+ pattern: string;
61
+ regex?: boolean;
62
+ caseSensitive?: boolean;
63
+ pathGlob?: string;
64
+ tagFilter?: string[];
65
+ limit?: number;
66
+ }
67
+
68
+ declare class Indexer {
69
+ private notesPath;
70
+ private processor;
71
+ constructor(notesPath: string);
72
+ indexAll(): Promise<IndexedDocument[]>;
73
+ indexFile(absolutePath: string, relativePath: string): Promise<IndexedDocument>;
74
+ private extractWikilinks;
75
+ private extractTags;
76
+ private extractHeaders;
77
+ private nodeToText;
78
+ private stripMarkdown;
79
+ chunkText(text: string): string[];
80
+ }
81
+
82
+ declare class Embedder {
83
+ private model;
84
+ private extractor;
85
+ private dimensions;
86
+ constructor(model?: string);
87
+ init(): Promise<void>;
88
+ embed(text: string): Promise<Float32Array>;
89
+ embedBatch(texts: string[]): Promise<Float32Array[]>;
90
+ getDimensions(): number;
91
+ getModel(): string;
92
+ saveEmbeddings(embeddings: Map<string, Float32Array>, indexPath: string): Promise<void>;
93
+ loadEmbeddings(indexPath: string): Promise<Map<string, Float32Array>>;
94
+ }
95
+
96
+ declare class GraphBuilder {
97
+ private graph;
98
+ constructor();
99
+ buildFromDocuments(documents: IndexedDocument[]): void;
100
+ backlinks(notePath: string): GraphNode[];
101
+ forwardlinks(notePath: string): GraphNode[];
102
+ findPath(from: string, to: string): string[] | null;
103
+ searchGraph(concept: string, maxDepth?: number): GraphNode[];
104
+ statistics(): GraphStats;
105
+ private nodeToGraphNode;
106
+ save(indexPath: string): Promise<void>;
107
+ load(indexPath: string): Promise<boolean>;
108
+ getGraph(): Graph;
109
+ }
110
+
111
+ interface ChunkMeta {
112
+ docPath: string;
113
+ chunkIndex: number;
114
+ text: string;
115
+ }
116
+ declare class VectorIndex {
117
+ private index;
118
+ private dimensions;
119
+ private chunkMeta;
120
+ constructor(dimensions: number);
121
+ build(embeddings: Float32Array[], meta: ChunkMeta[]): void;
122
+ search(queryEmbedding: Float32Array, k?: number): SearchResult[];
123
+ save(indexPath: string): Promise<void>;
124
+ load(indexPath: string): Promise<boolean>;
125
+ getChunkMeta(): ChunkMeta[];
126
+ }
127
+
128
+ declare class TextSearch {
129
+ private documents;
130
+ setDocuments(documents: IndexedDocument[]): void;
131
+ search(options: SearchTextOptions): SearchResult[];
132
+ }
133
+
134
+ declare class NoteCrud {
135
+ private notesPath;
136
+ constructor(notesPath: string);
137
+ create(relativePath: string, content: string, frontmatter?: Record<string, unknown>): Promise<string>;
138
+ read(relativePath: string): Promise<string>;
139
+ readMultiple(paths: string[]): Promise<Map<string, string>>;
140
+ update(relativePath: string, content: string, options: UpdateNoteOptions): Promise<void>;
141
+ delete(relativePath: string): Promise<void>;
142
+ move(fromPath: string, toPath: string): Promise<void>;
143
+ private updateWikilinksAfterMove;
144
+ private patchByHeading;
145
+ }
146
+
147
+ declare class FrontmatterManager {
148
+ private notesPath;
149
+ constructor(notesPath: string);
150
+ get(relativePath: string): Promise<Record<string, unknown>>;
151
+ update(relativePath: string, fields: Record<string, unknown>): Promise<void>;
152
+ }
153
+ declare class TagManager {
154
+ private notesPath;
155
+ constructor(notesPath: string);
156
+ list(relativePath: string): Promise<string[]>;
157
+ add(relativePath: string, tags: string[]): Promise<void>;
158
+ remove(relativePath: string, tags: string[]): Promise<void>;
159
+ renameVaultWide(oldTag: string, newTag: string): Promise<number>;
160
+ }
161
+
162
+ declare class Watcher extends EventEmitter {
163
+ private notesPath;
164
+ private fsWatcher;
165
+ private debounceMs;
166
+ private pendingChanges;
167
+ private debounceTimer;
168
+ private readyPromise;
169
+ private usePolling;
170
+ constructor(notesPath: string, debounceMs?: number, usePolling?: boolean);
171
+ start(): void;
172
+ ready(): Promise<void>;
173
+ stop(): void;
174
+ private enqueue;
175
+ }
176
+
177
+ export { Embedder, FrontmatterManager, GraphBuilder, type GraphEdge, type GraphNode, type IndexedDocument, Indexer, NoteCrud, type SearchResult, TagManager, TextSearch, type VaultStats, VectorIndex, Watcher };
@@ -0,0 +1,25 @@
1
+ import {
2
+ Embedder,
3
+ FrontmatterManager,
4
+ GraphBuilder,
5
+ NoteCrud,
6
+ TagManager,
7
+ TextSearch,
8
+ VectorIndex,
9
+ Watcher
10
+ } from "../chunk-KF45H64M.js";
11
+ import {
12
+ Indexer
13
+ } from "../chunk-TDC45FQJ.js";
14
+ export {
15
+ Embedder,
16
+ FrontmatterManager,
17
+ GraphBuilder,
18
+ Indexer,
19
+ NoteCrud,
20
+ TagManager,
21
+ TextSearch,
22
+ VectorIndex,
23
+ Watcher
24
+ };
25
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ Indexer
3
+ } from "./chunk-TDC45FQJ.js";
4
+ export {
5
+ Indexer
6
+ };
7
+ //# sourceMappingURL=indexer-HSCSXWIO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,10 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+
3
+ declare function createServer(notesPath: string, options?: {
4
+ watch?: boolean;
5
+ }): Promise<McpServer>;
6
+ declare function startServer(notesPath: string, options?: {
7
+ watch?: boolean;
8
+ }): Promise<void>;
9
+
10
+ export { createServer, startServer };