@theglitchking/semantic-memory 1.1.1 → 1.2.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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +45 -0
- package/ROADMAP.md +218 -0
- package/dist/{chunk-XBAN7GRT.js → chunk-CYWPDFNI.js} +2 -2
- package/dist/{chunk-XBAN7GRT.js.map → chunk-CYWPDFNI.js.map} +1 -1
- package/dist/{chunk-CCSX3BHF.js → chunk-EVAJGOCB.js} +25 -1
- package/dist/chunk-EVAJGOCB.js.map +1 -0
- package/dist/cli/index.js +127 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/core/index.js +2 -2
- package/dist/{healthcheck-DCFGPI7M.js → healthcheck-7YOPNDXN.js} +2 -2
- package/dist/mcp/server.js +1 -1
- package/hooks/vault-context.js +76 -25
- package/package.json +2 -1
- package/dist/chunk-CCSX3BHF.js.map +0 -1
- /package/dist/{healthcheck-DCFGPI7M.js.map → healthcheck-7YOPNDXN.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/crud.ts","../src/core/frontmatter.ts","../src/core/watcher.ts","../src/core/ingest.ts","../src/core/index-regen.ts","../src/core/patch.ts","../src/core/synthesize.ts","../src/core/agents-contract.ts","../src/core/session.ts"],"sourcesContent":["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 // Merge auto-stamps with caller-supplied frontmatter (caller wins on conflicts)\n const stamps = computeStamps(content);\n const merged = { ...stamps, ...(frontmatter ?? {}) };\n const fileContent = matter.stringify(content, merged);\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 // Re-parse the final content and inject updated stamps\n // (preserves all existing frontmatter, only overwrites the three auto fields)\n const { data: existingFm, content: body } = matter(updated);\n const stamps = computeStamps(body);\n const newFm = { ...existingFm, ...stamps };\n await writeFile(absPath, matter.stringify(body, newFm), \"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\n/**\n * Compute the three auto-stamp fields from note body text.\n * These are always factual/computable — no editorial judgment required.\n * - last_updated: today's date (YYYY-MM-DD)\n * - word_count: whitespace-separated token count\n * - estimated_read_time: assumes ~200 wpm average reading speed\n */\nfunction computeStamps(body: string): Record<string, unknown> {\n const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD\n const wordCount = body.trim() ? body.trim().split(/\\s+/).length : 0;\n const minutes = Math.max(1, Math.ceil(wordCount / 200));\n return {\n last_updated: today,\n word_count: wordCount,\n estimated_read_time: `${minutes} min read`,\n };\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 followSymlinks: true,\n ignored: [\n \"**/node_modules/**\",\n \"**/.semantic-sidekick-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","import { basename } from \"node:path\";\nimport type { ChangeSet, PatchCreate } from \"./patch.js\";\n\nexport interface IngestSource {\n /** Identifier of the source — URL, filesystem path, DOI, ISBN, etc. */\n source_uri: string;\n /** Human-readable title for the source-note's frontmatter */\n source_title: string;\n /** One of the schema types; typically \"source\" */\n source_type?: string;\n /** Path under the vault to write the source-note. Defaults to `sources/<slug>.md`. */\n source_path?: string;\n /** Free-form body for the source-note (summary, abstract, your notes on it) */\n source_summary?: string;\n /** Tags for the source-note */\n source_tags?: string[];\n}\n\nexport interface IngestUnit {\n /** Path under the vault where this extracted note should live */\n path: string;\n /** Note title — if absent, derived from basename */\n title?: string;\n /** Note body — markdown */\n content: string;\n /** Schema type. Default: \"note\". */\n type?: string;\n /** Additional wikilink-style derived_from targets beyond the source-note */\n extra_derived_from?: string[];\n /** Additional free-form frontmatter fields */\n extra_frontmatter?: Record<string, unknown>;\n /** Per-unit confidence override */\n confidence?: string;\n}\n\nexport interface IngestInput {\n source: IngestSource;\n units: IngestUnit[];\n /** If true, the existing source-note is updated if present; else an error is raised */\n allow_existing_source?: boolean;\n}\n\nexport interface IngestPreview {\n changeset: ChangeSet;\n sourcePath: string;\n unitPaths: string[];\n}\n\n/**\n * Build a ChangeSet for ingesting a source into the vault.\n *\n * Shape: one create for the source-note (type=source) + one create per unit,\n * each stamped with `derived_from: [<source-path>]` so lint.missing_provenance\n * is satisfied automatically and the graph reflects the provenance edge.\n *\n * Intentionally does NOT touch the filesystem — callers route the result\n * through applyPatch for atomicity, validation, and rollback.\n */\nexport function buildIngestChangeSet(input: IngestInput): IngestPreview {\n const today = new Date().toISOString().slice(0, 10);\n const sourcePath = input.source.source_path ?? defaultSourcePath(input.source);\n\n const sourceNote: PatchCreate = {\n path: sourcePath,\n content: buildSourceBody(input.source),\n frontmatter: {\n title: input.source.source_title,\n type: input.source.source_type ?? \"source\",\n status: \"active\",\n source: input.source.source_uri,\n last_verified: today,\n confidence: \"high\",\n ...(input.source.source_tags && input.source.source_tags.length > 0 && { tags: input.source.source_tags }),\n },\n };\n\n const unitCreates: PatchCreate[] = input.units.map((u) => {\n const title = u.title ?? titleFromPath(u.path);\n const derivedFrom = [sourcePath, ...(u.extra_derived_from ?? [])];\n return {\n path: u.path.endsWith(\".md\") ? u.path : u.path + \".md\",\n content: u.content.startsWith(\"#\") ? u.content : `# ${title}\\n\\n${u.content.trim()}\\n`,\n frontmatter: {\n title,\n type: u.type ?? \"note\",\n status: \"active\",\n derived_from: derivedFrom,\n last_verified: today,\n confidence: u.confidence ?? \"medium\",\n ...(u.extra_frontmatter ?? {}),\n },\n };\n });\n\n return {\n changeset: { creates: [sourceNote, ...unitCreates] },\n sourcePath,\n unitPaths: unitCreates.map((c) => c.path),\n };\n}\n\nfunction buildSourceBody(source: IngestSource): string {\n const lines = [`# ${source.source_title}`, \"\", `**Source:** ${source.source_uri}`, \"\"];\n if (source.source_summary) {\n lines.push(source.source_summary.trim(), \"\");\n } else {\n lines.push(\"_Summary not provided — fill in when referencing this source._\", \"\");\n }\n return lines.join(\"\\n\");\n}\n\nfunction defaultSourcePath(source: IngestSource): string {\n const slug = slugify(source.source_title);\n return `sources/${slug}.md`;\n}\n\nfunction titleFromPath(p: string): string {\n return basename(p).replace(/\\.md$/, \"\").replace(/[-_]/g, \" \");\n}\n\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 80);\n}\n","import { readdir, readFile, writeFile, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname, basename, sep } from \"node:path\";\nimport matter from \"gray-matter\";\n\nexport const INDEX_FILE = \"INDEX.md\";\nexport const INDEX_OVERFLOW_THRESHOLD = 100; // entries before we warn about splitting\n\nexport interface IndexEntry {\n path: string; // relative to the directory\n title: string;\n summary?: string;\n}\n\nexport interface RegenResult {\n directory: string;\n indexPath: string;\n entryCount: number;\n overflowed: boolean;\n wrote: boolean;\n}\n\n/**\n * Regenerate INDEX.md for a single vault directory.\n * Lists every *.md file in the directory (non-recursive, excluding the index itself)\n * with its title (frontmatter or first heading) and optional summary (from frontmatter\n * `purpose` or first non-heading paragraph).\n */\nexport async function regenDirectoryIndex(vaultPath: string, dirRel: string): Promise<RegenResult> {\n const absDir = join(vaultPath, dirRel);\n const indexPath = join(absDir, INDEX_FILE);\n const relIndex = join(dirRel, INDEX_FILE);\n\n if (!existsSync(absDir)) {\n return { directory: dirRel, indexPath: relIndex, entryCount: 0, overflowed: false, wrote: false };\n }\n\n const files = (await readdir(absDir)).filter(\n (f) => f.endsWith(\".md\") && f !== INDEX_FILE && f !== \"README.md\"\n );\n\n const entries: IndexEntry[] = [];\n for (const f of files) {\n const full = join(absDir, f);\n try {\n const st = await stat(full);\n if (!st.isFile()) continue;\n const raw = await readFile(full, \"utf-8\");\n const { data, content } = matter(raw);\n const title = typeof data.title === \"string\" ? data.title : firstHeading(content) ?? basenameNoExt(f);\n const summary = typeof data.purpose === \"string\" ? data.purpose : firstParagraph(content);\n entries.push({ path: f, title, summary });\n } catch {\n /* skip unreadable */\n }\n }\n\n entries.sort((a, b) => a.title.localeCompare(b.title));\n const body = renderIndex(dirRel, entries);\n const overflowed = entries.length > INDEX_OVERFLOW_THRESHOLD;\n\n let wrote = false;\n if (existsSync(indexPath)) {\n const current = await readFile(indexPath, \"utf-8\");\n if (current !== body) {\n await writeFile(indexPath, body, \"utf-8\");\n wrote = true;\n }\n } else {\n await writeFile(indexPath, body, \"utf-8\");\n wrote = true;\n }\n\n return { directory: dirRel, indexPath: relIndex, entryCount: entries.length, overflowed, wrote };\n}\n\n/**\n * Regenerate indexes for every directory affected by a set of paths.\n * Walks up the parent chain for each input so a deep change at\n * `a/b/c/note.md` regenerates `a/b/c/INDEX.md`. Does NOT recurse further —\n * `a/b/INDEX.md` stays as-is unless a path under `a/b/` (not `a/b/c/`)\n * also changed.\n */\nexport async function regenIndexesForPaths(\n vaultPath: string,\n paths: string[]\n): Promise<RegenResult[]> {\n const dirs = new Set<string>();\n for (const p of paths) {\n const d = dirname(p);\n if (d === \".\" || d === \"/\") continue;\n dirs.add(d);\n }\n const results: RegenResult[] = [];\n for (const d of dirs) {\n results.push(await regenDirectoryIndex(vaultPath, d));\n }\n return results;\n}\n\nfunction renderIndex(dirRel: string, entries: IndexEntry[]): string {\n const header = `# ${toTitle(dirRel)} Index\\n\\n> Auto-generated by semantic-sidekick. Changes are overwritten on apply_patch. Add prose to per-note files, not here.\\n\\n`;\n if (entries.length === 0) return header + \"_No notes in this directory yet._\\n\";\n const overflowNote =\n entries.length > INDEX_OVERFLOW_THRESHOLD\n ? `> ⚠️ ${entries.length} entries — consider splitting this directory into subfolders (threshold: ${INDEX_OVERFLOW_THRESHOLD}).\\n\\n`\n : \"\";\n const lines = entries.map((e) => {\n const linkText = e.title;\n const target = e.path.replace(/\\.md$/, \"\");\n const summary = e.summary ? ` — ${truncate(e.summary, 140)}` : \"\";\n return `- [${linkText}](./${e.path}) — \\`[[${target}]]\\`${summary}`;\n });\n return header + overflowNote + lines.join(\"\\n\") + \"\\n\";\n}\n\nfunction toTitle(dirRel: string): string {\n return dirRel\n .split(sep)\n .pop()!\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction basenameNoExt(p: string): string {\n return basename(p).replace(/\\.md$/, \"\");\n}\n\nfunction firstHeading(content: string): string | null {\n const m = content.match(/^\\s*#{1,6}\\s+(.+)$/m);\n return m ? m[1].trim() : null;\n}\n\nfunction firstParagraph(content: string): string | undefined {\n // Skip leading headings, then take the first line of non-heading text\n const lines = content.split(\"\\n\");\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (trimmed.startsWith(\"#\")) continue;\n if (trimmed.startsWith(\">\")) continue;\n if (trimmed.startsWith(\"```\")) continue;\n return trimmed.replace(/\\s+/g, \" \");\n }\n return undefined;\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length <= n ? s : s.slice(0, n - 1).trimEnd() + \"…\";\n}\n","import { readFile, writeFile, unlink, rename, mkdir } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type { UpdateNoteOptions } from \"./types.js\";\nimport { NoteCrud } from \"./crud.js\";\nimport { loadSchema, validateNote, type LintFinding, type VaultSchema } from \"./schema.js\";\nimport { regenIndexesForPaths, type RegenResult } from \"./index-regen.js\";\n\nexport interface PatchCreate {\n path: string;\n content: string;\n frontmatter?: Record<string, unknown>;\n}\n\nexport interface PatchUpdate {\n path: string;\n content: string;\n mode: UpdateNoteOptions[\"mode\"];\n heading?: string;\n}\n\nexport interface PatchDelete {\n path: string;\n}\n\nexport interface PatchMove {\n from: string;\n to: string;\n}\n\nexport interface ChangeSet {\n creates?: PatchCreate[];\n updates?: PatchUpdate[];\n deletes?: PatchDelete[];\n moves?: PatchMove[];\n}\n\nexport interface ApplyPatchOptions {\n dryRun?: boolean;\n validate?: boolean;\n allowLintWarnings?: boolean;\n /** Regenerate INDEX.md in directories touched by this patch. Default: true. */\n regenIndexes?: boolean;\n}\n\nexport interface AppliedOp {\n op: \"create\" | \"update\" | \"delete\" | \"move\";\n path: string;\n}\n\nexport interface ApplyPatchResult {\n ok: boolean;\n applied: AppliedOp[];\n rolledBack: AppliedOp[];\n lint: LintFinding[];\n errors: string[];\n /** Index files regenerated as a side-effect. Empty if regenIndexes=false or dry-run. */\n indexes_regenerated?: RegenResult[];\n}\n\ninterface Snapshot {\n op: \"create\" | \"update\" | \"delete\" | \"move\";\n path: string;\n absPath: string;\n prevContent?: string; // for update/delete/move(from)\n prevAbsPath?: string; // for move (prev location)\n}\n\n/**\n * Atomic multi-note patch with rollback. Applies creates → updates → moves → deletes\n * in that order. If any op fails, all successful ops before it are reverted in reverse.\n *\n * In dry-run mode, nothing is written; the result describes what would have happened,\n * including lint findings on the proposed state.\n */\nexport async function applyPatch(\n notesPath: string,\n changeset: ChangeSet,\n options: ApplyPatchOptions = {}\n): Promise<ApplyPatchResult> {\n const validate = options.validate !== false;\n const dryRun = options.dryRun === true;\n const allowWarn = options.allowLintWarnings !== false;\n\n const errors: string[] = [];\n const lint: LintFinding[] = [];\n const applied: AppliedOp[] = [];\n const snapshots: Snapshot[] = [];\n\n const crud = new NoteCrud(notesPath);\n\n let schema: VaultSchema | null = null;\n if (validate) {\n try {\n schema = await loadSchema(notesPath);\n } catch (e) {\n errors.push(`failed to load schema: ${e instanceof Error ? e.message : String(e)}`);\n return { ok: false, applied, rolledBack: [], lint, errors };\n }\n }\n\n // Pre-check: collisions on creates, missing sources on updates/deletes/moves.\n for (const c of changeset.creates ?? []) {\n if (existsSync(join(notesPath, c.path))) {\n errors.push(`create: already exists: ${c.path}`);\n }\n }\n for (const u of changeset.updates ?? []) {\n if (!existsSync(join(notesPath, u.path))) {\n errors.push(`update: does not exist: ${u.path}`);\n }\n }\n for (const d of changeset.deletes ?? []) {\n if (!existsSync(join(notesPath, d.path))) {\n errors.push(`delete: does not exist: ${d.path}`);\n }\n }\n for (const m of changeset.moves ?? []) {\n if (!existsSync(join(notesPath, m.from))) {\n errors.push(`move: source does not exist: ${m.from}`);\n }\n if (existsSync(join(notesPath, m.to))) {\n errors.push(`move: destination exists: ${m.to}`);\n }\n }\n\n if (errors.length > 0) {\n return { ok: false, applied, rolledBack: [], lint, errors };\n }\n\n // Lint pre-flight on proposed state. Simulate each create/update and validate.\n if (validate && schema) {\n for (const c of changeset.creates ?? []) {\n const proposed = matter.stringify(c.content, c.frontmatter ?? {});\n lint.push(...validateNote(c.path, proposed, schema));\n }\n for (const u of changeset.updates ?? []) {\n const abs = join(notesPath, u.path);\n let proposed: string;\n try {\n const existing = await readFile(abs, \"utf-8\");\n proposed = applyUpdateInMemory(existing, u);\n } catch (e) {\n errors.push(`update precheck: ${u.path}: ${e instanceof Error ? e.message : String(e)}`);\n continue;\n }\n lint.push(...validateNote(u.path, proposed, schema));\n }\n\n const schemaErrors = lint.filter((f) => f.rule === \"schema_violations\" && f.severity === \"error\");\n if (schemaErrors.length > 0) {\n errors.push(`${schemaErrors.length} schema violation(s) blocked the patch`);\n }\n if (!allowWarn) {\n const warns = lint.filter((f) => f.severity === \"warn\");\n if (warns.length > 0) errors.push(`${warns.length} lint warning(s) blocked the patch (allowLintWarnings=false)`);\n }\n }\n\n if (errors.length > 0 || dryRun) {\n return { ok: errors.length === 0, applied, rolledBack: [], lint, errors };\n }\n\n // Execute — capture snapshots as we go so rollback can reverse.\n try {\n for (const c of changeset.creates ?? []) {\n const abs = join(notesPath, c.path);\n await crud.create(c.path, c.content, c.frontmatter);\n snapshots.push({ op: \"create\", path: c.path, absPath: abs });\n applied.push({ op: \"create\", path: c.path });\n }\n for (const u of changeset.updates ?? []) {\n const abs = join(notesPath, u.path);\n const prev = await readFile(abs, \"utf-8\");\n await crud.update(u.path, u.content, { mode: u.mode, heading: u.heading } as UpdateNoteOptions);\n snapshots.push({ op: \"update\", path: u.path, absPath: abs, prevContent: prev });\n applied.push({ op: \"update\", path: u.path });\n }\n for (const m of changeset.moves ?? []) {\n await crud.move(m.from, m.to);\n snapshots.push({\n op: \"move\",\n path: m.to,\n absPath: join(notesPath, m.to),\n prevAbsPath: join(notesPath, m.from),\n });\n applied.push({ op: \"move\", path: `${m.from} -> ${m.to}` });\n }\n for (const d of changeset.deletes ?? []) {\n const abs = join(notesPath, d.path);\n const prev = await readFile(abs, \"utf-8\");\n await crud.delete(d.path);\n snapshots.push({ op: \"delete\", path: d.path, absPath: abs, prevContent: prev });\n applied.push({ op: \"delete\", path: d.path });\n }\n } catch (e) {\n errors.push(`op failed: ${e instanceof Error ? e.message : String(e)} — rolling back`);\n const rolledBack = await rollback(snapshots);\n return { ok: false, applied, rolledBack, lint, errors };\n }\n\n let indexes_regenerated: RegenResult[] = [];\n if (options.regenIndexes !== false) {\n const touched = [\n ...(changeset.creates ?? []).map((c) => c.path),\n ...(changeset.deletes ?? []).map((d) => d.path),\n ...(changeset.moves ?? []).flatMap((m) => [m.from, m.to]),\n ];\n try {\n indexes_regenerated = await regenIndexesForPaths(notesPath, touched);\n } catch (e) {\n // Non-fatal: patch succeeded; index regen is a best-effort side effect.\n errors.push(`index regen failed (patch still applied): ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n return { ok: true, applied, rolledBack: [], lint, errors, indexes_regenerated };\n}\n\nasync function rollback(snapshots: Snapshot[]): Promise<AppliedOp[]> {\n const reverted: AppliedOp[] = [];\n for (const s of snapshots.slice().reverse()) {\n try {\n switch (s.op) {\n case \"create\":\n if (existsSync(s.absPath)) await unlink(s.absPath);\n reverted.push({ op: \"create\", path: s.path });\n break;\n case \"update\":\n if (s.prevContent !== undefined) {\n await writeFile(s.absPath, s.prevContent, \"utf-8\");\n }\n reverted.push({ op: \"update\", path: s.path });\n break;\n case \"delete\":\n if (s.prevContent !== undefined) {\n await mkdir(dirname(s.absPath), { recursive: true });\n await writeFile(s.absPath, s.prevContent, \"utf-8\");\n }\n reverted.push({ op: \"delete\", path: s.path });\n break;\n case \"move\":\n if (s.prevAbsPath && existsSync(s.absPath)) {\n await mkdir(dirname(s.prevAbsPath), { recursive: true });\n await rename(s.absPath, s.prevAbsPath);\n }\n reverted.push({ op: \"move\", path: s.path });\n break;\n }\n } catch {\n // swallow — best-effort rollback\n }\n }\n return reverted;\n}\n\nfunction applyUpdateInMemory(existing: string, u: PatchUpdate): string {\n switch (u.mode) {\n case \"overwrite\":\n return u.content;\n case \"append\":\n return existing + \"\\n\" + u.content;\n case \"prepend\": {\n const { data, content: body } = matter(existing);\n const newBody = u.content + \"\\n\" + body;\n return Object.keys(data).length > 0 ? matter.stringify(newBody, data) : newBody;\n }\n case \"patch-by-heading\": {\n if (!u.heading) throw new Error(\"patch-by-heading requires heading\");\n return patchByHeadingInMemory(existing, u.heading, u.content);\n }\n }\n}\n\nfunction patchByHeadingInMemory(content: string, heading: string, newContent: string): string {\n const lines = content.split(\"\\n\");\n const re = new RegExp(`^#{1,6}\\\\s+${heading.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\s*$`, \"i\");\n let idx = -1;\n let level = 0;\n for (let i = 0; i < lines.length; i++) {\n if (re.test(lines[i])) {\n idx = i;\n const m = lines[i].match(/^(#{1,6})\\s+/);\n level = m ? m[1].length : 1;\n break;\n }\n }\n if (idx === -1) throw new Error(`Heading not found: ${heading}`);\n let end = lines.length;\n for (let i = idx + 1; i < lines.length; i++) {\n const m = lines[i].match(/^(#{1,6})\\s+/);\n if (m && m[1].length <= level) {\n end = i;\n break;\n }\n }\n return [...lines.slice(0, idx + 1), \"\", newContent, \"\", ...lines.slice(end)].join(\"\\n\");\n}\n","import { basename } from \"node:path\";\nimport type { ChangeSet, PatchCreate, PatchUpdate } from \"./patch.js\";\n\nexport interface SynthesizeNoteInput {\n topic: string;\n answer: string;\n suggested_path: string;\n type?: string;\n sources?: string[];\n derived_from?: string[];\n related_notes?: string[];\n status?: string;\n confidence?: string;\n decision_maker?: string;\n decided_on?: string;\n severity?: string;\n extra_frontmatter?: Record<string, unknown>;\n /**\n * When true, the resulting note is filed under `proposals/<date>-<slug>.md` with\n * `status: proposal` frontmatter, regardless of `suggested_path`. The original path\n * is preserved as `proposed_target` in frontmatter so `synthesize_promote` knows\n * where to move it on acceptance.\n */\n proposal?: boolean;\n /**\n * When provided alongside `proposal: true`, used as the proposal subdirectory name.\n * Defaults to \"proposals\". Useful for callers that want to segregate by source\n * (e.g. \"proposals/research\", \"proposals/outage\").\n */\n proposal_subdir?: string;\n}\n\nexport interface SynthesizePreview {\n changeset: ChangeSet;\n path: string;\n title: string;\n /** True when the preview is a proposal — caller (or `synthesize_promote`) must move it later. */\n is_proposal?: boolean;\n /** When `is_proposal` is true, the canonical path that `synthesize_promote` should move it to. */\n proposed_target?: string;\n}\n\n/**\n * Convert a query answer + provenance into a ChangeSet that can be fed to applyPatch.\n * The shaped note includes provenance frontmatter and auto-inserts [[wikilinks]] for\n * any related_notes that appear as bare titles in the body.\n *\n * Does NOT write to disk — always returns a ChangeSet. Callers pass it to applyPatch.\n *\n * Proposal mode: when `proposal: true`, the path is rewritten to\n * `<proposal_subdir>/<YYYY-MM-DD>-<slug>.md`, the `status` is forced to `proposal`,\n * and `proposed_target` records the original suggested path. `synthesize_promote`\n * later moves the file to that target and clears the proposal frontmatter.\n */\nexport function buildSynthesizeChangeSet(input: SynthesizeNoteInput): SynthesizePreview {\n const today = new Date().toISOString().slice(0, 10);\n const title = input.topic.trim();\n const canonicalPath = input.suggested_path.endsWith(\".md\") ? input.suggested_path : input.suggested_path + \".md\";\n\n const isProposal = input.proposal === true;\n const subdir = (input.proposal_subdir ?? \"proposals\").replace(/\\/+$/, \"\");\n const slug = slugForTopic(title);\n const path = isProposal ? `${subdir}/${today}-${slug}.md` : canonicalPath;\n\n const frontmatter: Record<string, unknown> = {\n title,\n type: input.type ?? \"note\",\n status: isProposal ? \"proposal\" : (input.status ?? \"active\"),\n last_verified: today,\n confidence: input.confidence ?? \"medium\",\n ...(input.sources && input.sources.length > 0 && { sources: input.sources }),\n ...(input.derived_from && input.derived_from.length > 0 && { derived_from: input.derived_from }),\n ...(input.decision_maker && { decision_maker: input.decision_maker }),\n ...(input.decided_on && { decided_on: input.decided_on }),\n ...(input.severity && { severity: input.severity }),\n ...(isProposal && { proposed_target: canonicalPath }),\n ...(input.extra_frontmatter ?? {}),\n };\n\n const body = buildBody(title, input.answer, input.related_notes ?? []);\n\n const create: PatchCreate = { path, content: body, frontmatter };\n const changeset: ChangeSet = { creates: [create] };\n\n return {\n changeset,\n path,\n title,\n ...(isProposal && { is_proposal: true, proposed_target: canonicalPath }),\n };\n}\n\nfunction slugForTopic(topic: string): string {\n return topic\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"untitled\";\n}\n\nfunction buildBody(title: string, answer: string, relatedNotes: string[]): string {\n let body = `# ${title}\\n\\n${answer.trim()}\\n`;\n\n if (relatedNotes.length > 0) {\n body = autoWikilink(body, relatedNotes);\n const links = relatedNotes.map((r) => `- [[${wikilinkName(r)}]]`).join(\"\\n\");\n body += `\\n## Related\\n\\n${links}\\n`;\n }\n\n return body;\n}\n\n/**\n * For each related note, replace the first bare occurrence of its title in the body\n * with a [[wikilink]]. Conservative — only replaces whole-word matches outside code blocks.\n */\nfunction autoWikilink(body: string, relatedNotes: string[]): string {\n // Skip content inside fenced code blocks\n const parts = body.split(/(```[\\s\\S]*?```)/g);\n return parts\n .map((part, i) => {\n if (i % 2 === 1) return part; // code block\n let out = part;\n for (const r of relatedNotes) {\n const name = wikilinkName(r);\n if (/[\\[\\]]/.test(out) && out.includes(`[[${name}]]`)) continue; // already linked\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`\\\\b${escaped}\\\\b`, \"i\");\n out = out.replace(re, `[[${name}]]`);\n }\n return out;\n })\n .join(\"\");\n}\n\nfunction wikilinkName(pathOrName: string): string {\n // \"decisions/auth-migration.md\" → \"auth-migration\"\n // \"Auth Migration\" → \"Auth Migration\"\n const base = basename(pathOrName).replace(/\\.md$/, \"\");\n return base;\n}\n\nexport interface PromoteInput {\n /**\n * Path to the proposal note (relative to vault root). Must have `status: proposal` frontmatter\n * AND `proposed_target` frontmatter, both written by `buildSynthesizeChangeSet` in proposal mode.\n */\n proposal_path: string;\n /**\n * Optional override for the destination path. When omitted, the proposal's own\n * `proposed_target` frontmatter is used.\n */\n target_path?: string;\n}\n\nexport interface PromotePreview {\n changeset: ChangeSet;\n from: string;\n to: string;\n}\n\n/**\n * Promote a reviewed proposal to its canonical destination.\n *\n * Reads the proposal's existing content + frontmatter, strips `status: proposal` and\n * `proposed_target` keys, restores the original status (default \"active\"), and returns\n * a ChangeSet that:\n * - creates the canonical path with the cleaned frontmatter + body\n * - deletes the proposal path\n *\n * The caller (the MCP tool) reads the proposal file, runs this builder, and applies the\n * resulting changeset via applyPatch — preserving atomicity and rollback semantics.\n */\nexport function buildPromoteChangeSet(input: {\n proposal_path: string;\n target_path?: string;\n body: string;\n frontmatter: Record<string, unknown>;\n}): PromotePreview {\n const fm = { ...input.frontmatter };\n const target = input.target_path ?? (typeof fm.proposed_target === \"string\" ? fm.proposed_target : undefined);\n if (!target) {\n throw new Error(\n `Cannot promote ${input.proposal_path}: no target_path provided and no 'proposed_target' frontmatter on the proposal.`\n );\n }\n // Restore canonical status (default \"active\") and strip proposal markers.\n if (fm.status === \"proposal\") fm.status = \"active\";\n delete fm.proposed_target;\n\n const create: PatchCreate = {\n path: target,\n content: input.body,\n frontmatter: fm,\n };\n const changeset: ChangeSet = {\n creates: [create],\n deletes: [{ path: input.proposal_path }],\n };\n\n return { changeset, from: input.proposal_path, to: target };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst BEGIN_MARKER = \"<!-- semantic-memory:begin contract -->\";\nconst END_MARKER = \"<!-- semantic-memory:end contract -->\";\nconst LOCAL_NOTES_HEADING = \"## Local Notes\";\n\nexport interface ToolSummary {\n name: string;\n description: string;\n deprecated?: boolean;\n}\n\nexport interface ModeSummary {\n name: string;\n description: string;\n isDefault?: boolean;\n}\n\nexport interface RegenerateContractOptions {\n projectRoot: string;\n pluginVersion: string;\n tools: ToolSummary[];\n modes?: ModeSummary[];\n /** When true, overwrite hand-edited managed blocks. Default false → fails loudly on detected hand-edits. */\n force?: boolean;\n /** Override \"now\" — used by tests for deterministic timestamps. */\n nowIso?: string;\n}\n\nexport interface RegenerateContractResult {\n path: string;\n written: boolean;\n reason?: string;\n hand_edit_detected?: boolean;\n preserved_local_notes_chars?: number;\n}\n\n/**\n * Generate or refresh AGENTS.md at the project root.\n *\n * Behavior:\n * - If AGENTS.md is absent, write the full contract + an empty Local Notes section.\n * - If AGENTS.md exists and contains the managed block markers:\n * * Regenerate the content INSIDE the markers from the current tool/mode inventory.\n * * Preserve everything OUTSIDE the markers (Local Notes tail, custom sections, frontmatter the\n * user added).\n * * If `force` is false and the inside-block content differs from a freshly-generated block AND\n * the difference is not just a regenerable timestamp/version, fail with hand_edit_detected.\n * - If AGENTS.md exists without managed-block markers, refuse and report — the user has a custom\n * AGENTS.md and we should not silently take it over.\n */\nexport async function regenerateAgentsContract(opts: RegenerateContractOptions): Promise<RegenerateContractResult> {\n const path = join(opts.projectRoot, \"AGENTS.md\");\n const generatedBlock = buildManagedBlock(opts);\n const generatedFull = buildFullDocument(opts, generatedBlock);\n\n if (!existsSync(path)) {\n await writeFile(path, generatedFull, \"utf-8\");\n return { path, written: true };\n }\n\n const existing = await readFile(path, \"utf-8\");\n const beginIdx = existing.indexOf(BEGIN_MARKER);\n const endIdx = existing.indexOf(END_MARKER);\n\n if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {\n return {\n path,\n written: false,\n reason:\n \"AGENTS.md exists but does not contain the semantic-memory managed-block markers. Move your existing content into a Local Notes section outside the managed block, or delete AGENTS.md and re-run to bootstrap from scratch.\",\n };\n }\n\n const existingBlock = existing.slice(beginIdx, endIdx + END_MARKER.length);\n const localTail = extractLocalTail(existing, endIdx + END_MARKER.length);\n\n if (!opts.force && hasHandEdits(existingBlock, generatedBlock)) {\n return {\n path,\n written: false,\n hand_edit_detected: true,\n reason:\n \"Managed-block content has been hand-edited. Move your changes to the Local Notes section (outside the managed block) and re-run, or pass force=true to overwrite.\",\n };\n }\n\n // Refresh the entire pre-block prefix (frontmatter + title) so timestamp/version\n // stamps reflect the current regeneration. Keep the managed block fresh and the\n // Local Notes tail untouched.\n const newPrefix = buildPrefix(opts);\n const newDoc = newPrefix + generatedBlock + localTail;\n await writeFile(path, newDoc, \"utf-8\");\n return {\n path,\n written: true,\n preserved_local_notes_chars: localTail.length,\n };\n}\n\nfunction buildPrefix(opts: RegenerateContractOptions): string {\n return [\n \"---\",\n `generated_by: semantic-memory`,\n `version: ${opts.pluginVersion}`,\n `last_generated: ${opts.nowIso ?? new Date().toISOString()}`,\n \"---\",\n \"\",\n \"# Project Agent Contract\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction buildFullDocument(opts: RegenerateContractOptions, managedBlock: string): string {\n const localNotes = `\\n\\n${LOCAL_NOTES_HEADING}\\n\\n_User-authored content lives here. Preserved across \\`regenerate_contract\\` runs._\\n`;\n return buildPrefix(opts) + managedBlock + localNotes;\n}\n\nfunction buildManagedBlock(opts: RegenerateContractOptions): string {\n const lines: string[] = [BEGIN_MARKER, \"\"];\n\n lines.push(\"## Active Modes\", \"\");\n const modes = opts.modes ?? defaultModes();\n for (const m of modes) {\n const tag = m.isDefault ? \" (default)\" : \"\";\n lines.push(`- **${m.name}**${tag} — ${m.description}`);\n }\n lines.push(\"\");\n\n lines.push(\"## Required Workflow\", \"\");\n lines.push(\"1. For multi-step work that needs verification, open a session: `session_start({task})`.\");\n lines.push(\"2. Use mode-aware retrieval: `search_hybrid`, `read_note`, `list_notes`, `backlinks`/`forwardlinks`.\");\n lines.push(\"3. Make durable updates via `apply_patch` or `synthesize_note` — never bypass the patch layer for multi-note edits.\");\n lines.push(\"4. Run verification commands inside a session: `session_run({cmd})`.\");\n lines.push(\"5. Close with `session_finish({summary})` — refused without verification unless explicitly waived.\");\n lines.push(\"6. Lint regularly: `lint_vault({checks: ['schema','provenance','stale','broken_links']})`.\");\n lines.push(\"\");\n\n lines.push(\"## Tool Surface\", \"\");\n const live = opts.tools.filter((t) => !t.deprecated);\n const deprecated = opts.tools.filter((t) => t.deprecated);\n for (const t of live) {\n lines.push(`- \\`${t.name}\\` — ${stripDeprecationPrefix(t.description)}`);\n }\n if (deprecated.length > 0) {\n lines.push(\"\");\n lines.push(\"### Deprecated (removed in v2.0.0)\", \"\");\n for (const t of deprecated) {\n lines.push(`- \\`${t.name}\\` — ${stripDeprecationPrefix(t.description)}`);\n }\n }\n lines.push(\"\");\n\n lines.push(\"## Memory Policy\", \"\");\n lines.push(\"- Markdown is canonical. The vault under `.claude/.vault/` (or wherever `--notes` points) is the source of truth.\");\n lines.push(\"- Indexes (vector / graph / FTS) are derived state — rebuildable via `reindex` without losing knowledge.\");\n lines.push(\"- Provenance frontmatter is required for `note`/`decision`/`gotcha` types — `sources` (external) or `derived_from` (internal).\");\n lines.push(\"- Mode router lives at `.claude/.sidekick-mode`; explicit `/mode` is ground truth.\");\n lines.push(\"\");\n\n lines.push(END_MARKER);\n return lines.join(\"\\n\");\n}\n\nfunction defaultModes(): ModeSummary[] {\n return [\n { name: \"vault-first\", description: \"Project-scoped prose questions trigger vault consultation with cite-or-deflect.\", isDefault: true },\n { name: \"research\", description: \"Every source introduced is filed via `ingest_source`; mandatory `synthesize_note` on exit.\" },\n { name: \"outage-silence\", description: \"No proactive vault search. Terse responses. Postmortem `synthesize_note` on exit.\" },\n ];\n}\n\nfunction stripDeprecationPrefix(description: string): string {\n return description.replace(/^\\[DEPRECATED[^\\]]*\\]\\s*/, \"(deprecated) \");\n}\n\nfunction extractLocalTail(existing: string, afterEndIdx: number): string {\n const tail = existing.slice(afterEndIdx);\n // Trim a single leading blank-line-then-newline if present so we don't double-space across regenerations.\n return tail;\n}\n\n/**\n * Detect hand-edits by comparing semantic content of the managed block, ignoring lines that are\n * regenerable (currently no per-block timestamps live inside the block — they're in frontmatter).\n * Returns true if the existing block diverges from what we'd generate now.\n */\nfunction hasHandEdits(existingBlock: string, generatedBlock: string): boolean {\n return existingBlock.trim() !== generatedBlock.trim();\n}\n\n/**\n * Inspect AGENTS.md without modifying it. Returns whether the file exists, whether it has managed\n * markers, and how many characters of Local Notes tail are preserved.\n */\nexport async function inspectAgentsContract(projectRoot: string): Promise<{\n path: string;\n exists: boolean;\n has_managed_block: boolean;\n local_notes_chars: number;\n}> {\n const path = join(projectRoot, \"AGENTS.md\");\n if (!existsSync(path)) {\n return { path, exists: false, has_managed_block: false, local_notes_chars: 0 };\n }\n const raw = await readFile(path, \"utf-8\");\n const beginIdx = raw.indexOf(BEGIN_MARKER);\n const endIdx = raw.indexOf(END_MARKER);\n if (beginIdx === -1 || endIdx === -1) {\n return { path, exists: true, has_managed_block: false, local_notes_chars: 0 };\n }\n const localTail = raw.slice(endIdx + END_MARKER.length).trim();\n return { path, exists: true, has_managed_block: true, local_notes_chars: localTail.length };\n}\n","import { mkdir, readFile, writeFile, rm } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nexport interface VerificationRecord {\n cmd: string;\n exit: number | null;\n signal: string | null;\n duration_ms: number;\n tail: string;\n at: string;\n}\n\nexport interface SessionState {\n id: string;\n task: string;\n started_at: string;\n last_activity_at: string;\n verifications: VerificationRecord[];\n notes_touched: string[];\n closed_at?: string;\n closed_summary?: string;\n closed_verified_waived?: boolean;\n closed_waiver_reason?: string;\n}\n\nexport type SessionStatus =\n | { state: \"no_session\" }\n | (SessionState & { state: \"active\" | \"stale\" });\n\nexport interface SessionStartOptions {\n /** When true and a session for the same task is already open, return the existing id rather than failing. Defaults to true. */\n reuseSameTask?: boolean;\n}\n\nexport interface SessionFinishInput {\n summary: string;\n /**\n * When false, the caller is explicitly waiving verification (e.g. for doc-only edits).\n * Requires a non-empty `reason`. When true (default) and no verifications have been\n * recorded, the call is refused.\n */\n verified?: boolean;\n reason?: string;\n}\n\nconst STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\nconst TAIL_BYTES = 4096;\n\n/**\n * SessionManager owns a single session.json file at `<sessionDir>/session.json`.\n *\n * State is durable across MCP server restarts (the file persists). At most one session\n * is active per project at a time — concurrent sessions are explicitly out of scope\n * for v1.1; brain has the same constraint.\n *\n * Hard gates:\n * - session_finish refuses without verifications unless verified=false + reason\n * - session_run refuses without an open session\n * - session_start refuses if another session is open with a different task\n *\n * Stale detection: a session whose last_activity_at is >24h old is reported as\n * state:\"stale\" by status(). Stale sessions can still be finished or extended; the\n * label is informational so callers can decide whether to abandon (rm + start new) or\n * resume.\n */\nexport class SessionManager {\n constructor(private sessionDir: string) {}\n\n get sessionPath(): string {\n return join(this.sessionDir, \"session.json\");\n }\n\n async start(task: string, opts: SessionStartOptions = {}): Promise<{ ok: true; id: string; reused?: boolean } | { ok: false; error: string }> {\n if (!task || task.trim().length === 0) {\n return { ok: false, error: \"session_start requires a non-empty task.\" };\n }\n await mkdir(this.sessionDir, { recursive: true });\n const existing = await this.read();\n if (existing && !existing.closed_at) {\n const reuseSameTask = opts.reuseSameTask !== false;\n if (reuseSameTask && existing.task === task) {\n return { ok: true, id: existing.id, reused: true };\n }\n return {\n ok: false,\n error: `Session already open: id=${existing.id}, task=${JSON.stringify(existing.task)}. Close it with session_finish first.`,\n };\n }\n const now = new Date().toISOString();\n const id = `ses_${now.replace(/[:.]/g, \"-\")}`;\n const state: SessionState = {\n id,\n task: task.trim(),\n started_at: now,\n last_activity_at: now,\n verifications: [],\n notes_touched: [],\n };\n await writeFile(this.sessionPath, JSON.stringify(state, null, 2), \"utf-8\");\n return { ok: true, id };\n }\n\n async status(): Promise<SessionStatus> {\n const state = await this.read();\n if (!state || state.closed_at) return { state: \"no_session\" };\n const age = Date.now() - new Date(state.last_activity_at).getTime();\n const label: \"active\" | \"stale\" = age > STALE_THRESHOLD_MS ? \"stale\" : \"active\";\n return { ...state, state: label };\n }\n\n async run(cmd: string, timeoutMs?: number): Promise<{ ok: true; record: VerificationRecord } | { ok: false; error: string }> {\n const state = await this.read();\n if (!state || state.closed_at) {\n return { ok: false, error: \"No active session. Run session_start first.\" };\n }\n if (!cmd || cmd.trim().length === 0) {\n return { ok: false, error: \"session_run requires a non-empty command.\" };\n }\n const start = Date.now();\n const record = await this.spawnAndCapture(cmd, timeoutMs ?? DEFAULT_TIMEOUT_MS);\n record.duration_ms = Date.now() - start;\n state.verifications.push(record);\n state.last_activity_at = new Date().toISOString();\n await writeFile(this.sessionPath, JSON.stringify(state, null, 2), \"utf-8\");\n return { ok: true, record };\n }\n\n async finish(input: SessionFinishInput): Promise<{ ok: true; closed: SessionState } | { ok: false; error: string }> {\n const state = await this.read();\n if (!state || state.closed_at) {\n return { ok: false, error: \"No active session to finish.\" };\n }\n const explicitWaiver = input.verified === false;\n if (!explicitWaiver && state.verifications.length === 0) {\n return {\n ok: false,\n error:\n \"session_finish refused: no verification commands recorded. Run session_run with a real verification command (tests, lint, etc.) first, OR pass verified=false with a reason to waive verification (e.g. for doc-only edits).\",\n };\n }\n if (explicitWaiver && (!input.reason || input.reason.trim().length === 0)) {\n return {\n ok: false,\n error: \"session_finish refused: verified=false requires a non-empty `reason` explaining why verification is not applicable to this task.\",\n };\n }\n if (!input.summary || input.summary.trim().length === 0) {\n return { ok: false, error: \"session_finish requires a non-empty `summary`.\" };\n }\n const closed: SessionState = {\n ...state,\n closed_at: new Date().toISOString(),\n closed_summary: input.summary.trim(),\n ...(explicitWaiver && {\n closed_verified_waived: true,\n closed_waiver_reason: input.reason!.trim(),\n }),\n };\n // Sessions are ephemeral runtime state — finished sessions are removed from disk.\n // Durable artifacts (synthesize_note proposals, log entries) capture what mattered.\n await rm(this.sessionPath, { force: true });\n return { ok: true, closed };\n }\n\n /**\n * Append paths to notes_touched on the active session, deduplicating. No-op when no\n * session is active. Used by patch tools to record what the session edited.\n */\n async recordNotesTouched(paths: string[]): Promise<void> {\n if (paths.length === 0) return;\n const state = await this.read();\n if (!state || state.closed_at) return;\n const set = new Set(state.notes_touched);\n for (const p of paths) set.add(p);\n state.notes_touched = Array.from(set);\n state.last_activity_at = new Date().toISOString();\n await writeFile(this.sessionPath, JSON.stringify(state, null, 2), \"utf-8\");\n }\n\n /**\n * Read the active session WITHOUT computing the stale label. Returns null when no\n * session is active or when the file is unparseable.\n */\n async readActive(): Promise<SessionState | null> {\n const state = await this.read();\n if (!state || state.closed_at) return null;\n return state;\n }\n\n private async read(): Promise<SessionState | null> {\n if (!existsSync(this.sessionPath)) return null;\n try {\n const raw = await readFile(this.sessionPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (typeof parsed?.id !== \"string\") return null;\n return parsed as SessionState;\n } catch {\n return null;\n }\n }\n\n private spawnAndCapture(cmd: string, timeoutMs: number): Promise<VerificationRecord> {\n return new Promise((resolve) => {\n const proc = spawn(cmd, { shell: true });\n let buf = \"\";\n let killedByTimeout = false;\n const timer = setTimeout(() => {\n killedByTimeout = true;\n proc.kill(\"SIGTERM\");\n setTimeout(() => {\n try { proc.kill(\"SIGKILL\"); } catch { /* already gone */ }\n }, 1000).unref();\n }, timeoutMs);\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n if (buf.length > TAIL_BYTES * 4) {\n buf = buf.slice(buf.length - TAIL_BYTES * 2);\n }\n };\n proc.stdout?.on(\"data\", onData);\n proc.stderr?.on(\"data\", onData);\n\n proc.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n const tail = buf.slice(-TAIL_BYTES);\n resolve({\n cmd,\n exit: killedByTimeout ? null : code,\n signal: killedByTimeout ? \"SIGTERM (timeout)\" : (signal ?? null),\n duration_ms: 0,\n tail,\n at: new Date().toISOString(),\n });\n });\n\n proc.on(\"error\", (err) => {\n clearTimeout(timer);\n const tail = buf + `\\n[spawn error: ${err.message}]`;\n resolve({\n cmd,\n exit: null,\n signal: null,\n duration_ms: 0,\n tail: tail.slice(-TAIL_BYTES),\n at: new Date().toISOString(),\n });\n });\n });\n }\n}\n\n/**\n * Heuristic: derive the project root from the vault path. If the vault path ends in\n * `.claude/.vault`, the project root is two levels up. Otherwise the vault path is\n * treated as the project root.\n */\nexport function deriveProjectRoot(notesPath: string): string {\n const norm = notesPath.replace(/[\\\\/]+$/, \"\");\n if (norm.endsWith(\"/.claude/.vault\") || norm.endsWith(\"\\\\.claude\\\\.vault\")) {\n return norm.slice(0, -\".claude/.vault\".length).replace(/[\\\\/]+$/, \"\");\n }\n return norm;\n}\n\nexport function deriveSessionDir(notesPath: string): string {\n return join(deriveProjectRoot(notesPath), \".claude\", \".semantic-memory\");\n}\n"],"mappings":"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","names":["body","readFile","writeFile","join","glob","matter","escapeRegex","readFile","writeFile","existsSync","join","dirname","basename","matter","readFile","writeFile","unlink","rename","mkdir","existsSync","dirname","join","matter","existsSync","join","matter","readFile","unlink","writeFile","mkdir","dirname","rename","basename","readFile","writeFile","existsSync","join","mkdir","readFile","writeFile","existsSync","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/crud.ts","../src/core/frontmatter.ts","../src/core/watcher.ts","../src/core/ingest.ts","../src/core/index-regen.ts","../src/core/patch.ts","../src/core/synthesize.ts","../src/core/agents-contract.ts","../src/core/session.ts"],"sourcesContent":["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 // Merge auto-stamps with caller-supplied frontmatter (caller wins on conflicts)\n const stamps = computeStamps(content);\n const merged = { ...stamps, ...(frontmatter ?? {}) };\n const fileContent = matter.stringify(content, merged);\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 // Re-parse the final content and inject updated stamps\n // (preserves all existing frontmatter, only overwrites the three auto fields)\n const { data: existingFm, content: body } = matter(updated);\n const stamps = computeStamps(body);\n const newFm = { ...existingFm, ...stamps };\n await writeFile(absPath, matter.stringify(body, newFm), \"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\n/**\n * Compute the three auto-stamp fields from note body text.\n * These are always factual/computable — no editorial judgment required.\n * - last_updated: today's date (YYYY-MM-DD)\n * - word_count: whitespace-separated token count\n * - estimated_read_time: assumes ~200 wpm average reading speed\n */\nfunction computeStamps(body: string): Record<string, unknown> {\n const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD\n const wordCount = body.trim() ? body.trim().split(/\\s+/).length : 0;\n const minutes = Math.max(1, Math.ceil(wordCount / 200));\n return {\n last_updated: today,\n word_count: wordCount,\n estimated_read_time: `${minutes} min read`,\n };\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 followSymlinks: true,\n ignored: [\n \"**/node_modules/**\",\n \"**/.semantic-sidekick-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","import { basename } from \"node:path\";\nimport type { ChangeSet, PatchCreate } from \"./patch.js\";\n\nexport interface IngestSource {\n /** Identifier of the source — URL, filesystem path, DOI, ISBN, etc. */\n source_uri: string;\n /** Human-readable title for the source-note's frontmatter */\n source_title: string;\n /** One of the schema types; typically \"source\" */\n source_type?: string;\n /** Path under the vault to write the source-note. Defaults to `sources/<slug>.md`. */\n source_path?: string;\n /** Free-form body for the source-note (summary, abstract, your notes on it) */\n source_summary?: string;\n /** Tags for the source-note */\n source_tags?: string[];\n}\n\nexport interface IngestUnit {\n /** Path under the vault where this extracted note should live */\n path: string;\n /** Note title — if absent, derived from basename */\n title?: string;\n /** Note body — markdown */\n content: string;\n /** Schema type. Default: \"note\". */\n type?: string;\n /** Additional wikilink-style derived_from targets beyond the source-note */\n extra_derived_from?: string[];\n /** Additional free-form frontmatter fields */\n extra_frontmatter?: Record<string, unknown>;\n /** Per-unit confidence override */\n confidence?: string;\n}\n\nexport interface IngestInput {\n source: IngestSource;\n units: IngestUnit[];\n /** If true, the existing source-note is updated if present; else an error is raised */\n allow_existing_source?: boolean;\n}\n\nexport interface IngestPreview {\n changeset: ChangeSet;\n sourcePath: string;\n unitPaths: string[];\n}\n\n/**\n * Build a ChangeSet for ingesting a source into the vault.\n *\n * Shape: one create for the source-note (type=source) + one create per unit,\n * each stamped with `derived_from: [<source-path>]` so lint.missing_provenance\n * is satisfied automatically and the graph reflects the provenance edge.\n *\n * Intentionally does NOT touch the filesystem — callers route the result\n * through applyPatch for atomicity, validation, and rollback.\n */\nexport function buildIngestChangeSet(input: IngestInput): IngestPreview {\n const today = new Date().toISOString().slice(0, 10);\n const sourcePath = input.source.source_path ?? defaultSourcePath(input.source);\n\n const sourceNote: PatchCreate = {\n path: sourcePath,\n content: buildSourceBody(input.source),\n frontmatter: {\n title: input.source.source_title,\n type: input.source.source_type ?? \"source\",\n status: \"active\",\n source: input.source.source_uri,\n last_verified: today,\n confidence: \"high\",\n ...(input.source.source_tags && input.source.source_tags.length > 0 && { tags: input.source.source_tags }),\n },\n };\n\n const unitCreates: PatchCreate[] = input.units.map((u) => {\n const title = u.title ?? titleFromPath(u.path);\n const derivedFrom = [sourcePath, ...(u.extra_derived_from ?? [])];\n return {\n path: u.path.endsWith(\".md\") ? u.path : u.path + \".md\",\n content: u.content.startsWith(\"#\") ? u.content : `# ${title}\\n\\n${u.content.trim()}\\n`,\n frontmatter: {\n title,\n type: u.type ?? \"note\",\n status: \"active\",\n derived_from: derivedFrom,\n last_verified: today,\n confidence: u.confidence ?? \"medium\",\n ...(u.extra_frontmatter ?? {}),\n },\n };\n });\n\n return {\n changeset: { creates: [sourceNote, ...unitCreates] },\n sourcePath,\n unitPaths: unitCreates.map((c) => c.path),\n };\n}\n\nfunction buildSourceBody(source: IngestSource): string {\n const lines = [`# ${source.source_title}`, \"\", `**Source:** ${source.source_uri}`, \"\"];\n if (source.source_summary) {\n lines.push(source.source_summary.trim(), \"\");\n } else {\n lines.push(\"_Summary not provided — fill in when referencing this source._\", \"\");\n }\n return lines.join(\"\\n\");\n}\n\nfunction defaultSourcePath(source: IngestSource): string {\n const slug = slugify(source.source_title);\n return `sources/${slug}.md`;\n}\n\nfunction titleFromPath(p: string): string {\n return basename(p).replace(/\\.md$/, \"\").replace(/[-_]/g, \" \");\n}\n\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^\\w\\s-]/g, \"\")\n .replace(/\\s+/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\")\n .slice(0, 80);\n}\n","import { readdir, readFile, writeFile, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname, basename, sep } from \"node:path\";\nimport matter from \"gray-matter\";\n\nexport const INDEX_FILE = \"INDEX.md\";\nexport const INDEX_OVERFLOW_THRESHOLD = 100; // entries before we warn about splitting\n\nexport interface IndexEntry {\n path: string; // relative to the directory\n title: string;\n summary?: string;\n}\n\nexport interface RegenResult {\n directory: string;\n indexPath: string;\n entryCount: number;\n overflowed: boolean;\n wrote: boolean;\n}\n\n/**\n * Regenerate INDEX.md for a single vault directory.\n * Lists every *.md file in the directory (non-recursive, excluding the index itself)\n * with its title (frontmatter or first heading) and optional summary (from frontmatter\n * `purpose` or first non-heading paragraph).\n */\nexport async function regenDirectoryIndex(vaultPath: string, dirRel: string): Promise<RegenResult> {\n const absDir = join(vaultPath, dirRel);\n const indexPath = join(absDir, INDEX_FILE);\n const relIndex = join(dirRel, INDEX_FILE);\n\n if (!existsSync(absDir)) {\n return { directory: dirRel, indexPath: relIndex, entryCount: 0, overflowed: false, wrote: false };\n }\n\n const files = (await readdir(absDir)).filter(\n (f) => f.endsWith(\".md\") && f !== INDEX_FILE && f !== \"README.md\"\n );\n\n const entries: IndexEntry[] = [];\n for (const f of files) {\n const full = join(absDir, f);\n try {\n const st = await stat(full);\n if (!st.isFile()) continue;\n const raw = await readFile(full, \"utf-8\");\n const { data, content } = matter(raw);\n const title = typeof data.title === \"string\" ? data.title : firstHeading(content) ?? basenameNoExt(f);\n const summary = typeof data.purpose === \"string\" ? data.purpose : firstParagraph(content);\n entries.push({ path: f, title, summary });\n } catch {\n /* skip unreadable */\n }\n }\n\n entries.sort((a, b) => a.title.localeCompare(b.title));\n const body = renderIndex(dirRel, entries);\n const overflowed = entries.length > INDEX_OVERFLOW_THRESHOLD;\n\n let wrote = false;\n if (existsSync(indexPath)) {\n const current = await readFile(indexPath, \"utf-8\");\n if (current !== body) {\n await writeFile(indexPath, body, \"utf-8\");\n wrote = true;\n }\n } else {\n await writeFile(indexPath, body, \"utf-8\");\n wrote = true;\n }\n\n return { directory: dirRel, indexPath: relIndex, entryCount: entries.length, overflowed, wrote };\n}\n\n/**\n * Regenerate indexes for every directory affected by a set of paths.\n * Walks up the parent chain for each input so a deep change at\n * `a/b/c/note.md` regenerates `a/b/c/INDEX.md`. Does NOT recurse further —\n * `a/b/INDEX.md` stays as-is unless a path under `a/b/` (not `a/b/c/`)\n * also changed.\n */\nexport async function regenIndexesForPaths(\n vaultPath: string,\n paths: string[]\n): Promise<RegenResult[]> {\n const dirs = new Set<string>();\n for (const p of paths) {\n const d = dirname(p);\n if (d === \".\" || d === \"/\") continue;\n dirs.add(d);\n }\n const results: RegenResult[] = [];\n for (const d of dirs) {\n results.push(await regenDirectoryIndex(vaultPath, d));\n }\n return results;\n}\n\nfunction renderIndex(dirRel: string, entries: IndexEntry[]): string {\n const header = `# ${toTitle(dirRel)} Index\\n\\n> Auto-generated by semantic-sidekick. Changes are overwritten on apply_patch. Add prose to per-note files, not here.\\n\\n`;\n if (entries.length === 0) return header + \"_No notes in this directory yet._\\n\";\n const overflowNote =\n entries.length > INDEX_OVERFLOW_THRESHOLD\n ? `> ⚠️ ${entries.length} entries — consider splitting this directory into subfolders (threshold: ${INDEX_OVERFLOW_THRESHOLD}).\\n\\n`\n : \"\";\n const lines = entries.map((e) => {\n const linkText = e.title;\n const target = e.path.replace(/\\.md$/, \"\");\n const summary = e.summary ? ` — ${truncate(e.summary, 140)}` : \"\";\n return `- [${linkText}](./${e.path}) — \\`[[${target}]]\\`${summary}`;\n });\n return header + overflowNote + lines.join(\"\\n\") + \"\\n\";\n}\n\nfunction toTitle(dirRel: string): string {\n return dirRel\n .split(sep)\n .pop()!\n .replace(/[-_]/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction basenameNoExt(p: string): string {\n return basename(p).replace(/\\.md$/, \"\");\n}\n\nfunction firstHeading(content: string): string | null {\n const m = content.match(/^\\s*#{1,6}\\s+(.+)$/m);\n return m ? m[1].trim() : null;\n}\n\nfunction firstParagraph(content: string): string | undefined {\n // Skip leading headings, then take the first line of non-heading text\n const lines = content.split(\"\\n\");\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n if (trimmed.startsWith(\"#\")) continue;\n if (trimmed.startsWith(\">\")) continue;\n if (trimmed.startsWith(\"```\")) continue;\n return trimmed.replace(/\\s+/g, \" \");\n }\n return undefined;\n}\n\nfunction truncate(s: string, n: number): string {\n return s.length <= n ? s : s.slice(0, n - 1).trimEnd() + \"…\";\n}\n","import { readFile, writeFile, unlink, rename, mkdir } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport matter from \"gray-matter\";\nimport type { UpdateNoteOptions } from \"./types.js\";\nimport { NoteCrud } from \"./crud.js\";\nimport { loadSchema, validateNote, type LintFinding, type VaultSchema } from \"./schema.js\";\nimport { regenIndexesForPaths, type RegenResult } from \"./index-regen.js\";\n\nexport interface PatchCreate {\n path: string;\n content: string;\n frontmatter?: Record<string, unknown>;\n}\n\nexport interface PatchUpdate {\n path: string;\n content: string;\n mode: UpdateNoteOptions[\"mode\"];\n heading?: string;\n}\n\nexport interface PatchDelete {\n path: string;\n}\n\nexport interface PatchMove {\n from: string;\n to: string;\n}\n\nexport interface ChangeSet {\n creates?: PatchCreate[];\n updates?: PatchUpdate[];\n deletes?: PatchDelete[];\n moves?: PatchMove[];\n}\n\nexport interface ApplyPatchOptions {\n dryRun?: boolean;\n validate?: boolean;\n allowLintWarnings?: boolean;\n /** Regenerate INDEX.md in directories touched by this patch. Default: true. */\n regenIndexes?: boolean;\n}\n\nexport interface AppliedOp {\n op: \"create\" | \"update\" | \"delete\" | \"move\";\n path: string;\n}\n\nexport interface ApplyPatchResult {\n ok: boolean;\n applied: AppliedOp[];\n rolledBack: AppliedOp[];\n lint: LintFinding[];\n errors: string[];\n /** Index files regenerated as a side-effect. Empty if regenIndexes=false or dry-run. */\n indexes_regenerated?: RegenResult[];\n}\n\ninterface Snapshot {\n op: \"create\" | \"update\" | \"delete\" | \"move\";\n path: string;\n absPath: string;\n prevContent?: string; // for update/delete/move(from)\n prevAbsPath?: string; // for move (prev location)\n}\n\n/**\n * Atomic multi-note patch with rollback. Applies creates → updates → moves → deletes\n * in that order. If any op fails, all successful ops before it are reverted in reverse.\n *\n * In dry-run mode, nothing is written; the result describes what would have happened,\n * including lint findings on the proposed state.\n */\nexport async function applyPatch(\n notesPath: string,\n changeset: ChangeSet,\n options: ApplyPatchOptions = {}\n): Promise<ApplyPatchResult> {\n const validate = options.validate !== false;\n const dryRun = options.dryRun === true;\n const allowWarn = options.allowLintWarnings !== false;\n\n const errors: string[] = [];\n const lint: LintFinding[] = [];\n const applied: AppliedOp[] = [];\n const snapshots: Snapshot[] = [];\n\n const crud = new NoteCrud(notesPath);\n\n let schema: VaultSchema | null = null;\n if (validate) {\n try {\n schema = await loadSchema(notesPath);\n } catch (e) {\n errors.push(`failed to load schema: ${e instanceof Error ? e.message : String(e)}`);\n return { ok: false, applied, rolledBack: [], lint, errors };\n }\n }\n\n // Pre-check: collisions on creates, missing sources on updates/deletes/moves.\n for (const c of changeset.creates ?? []) {\n if (existsSync(join(notesPath, c.path))) {\n errors.push(`create: already exists: ${c.path}`);\n }\n }\n for (const u of changeset.updates ?? []) {\n if (!existsSync(join(notesPath, u.path))) {\n errors.push(`update: does not exist: ${u.path}`);\n }\n }\n for (const d of changeset.deletes ?? []) {\n if (!existsSync(join(notesPath, d.path))) {\n errors.push(`delete: does not exist: ${d.path}`);\n }\n }\n for (const m of changeset.moves ?? []) {\n if (!existsSync(join(notesPath, m.from))) {\n errors.push(`move: source does not exist: ${m.from}`);\n }\n if (existsSync(join(notesPath, m.to))) {\n errors.push(`move: destination exists: ${m.to}`);\n }\n }\n\n if (errors.length > 0) {\n return { ok: false, applied, rolledBack: [], lint, errors };\n }\n\n // Lint pre-flight on proposed state. Simulate each create/update and validate.\n if (validate && schema) {\n for (const c of changeset.creates ?? []) {\n const proposed = matter.stringify(c.content, c.frontmatter ?? {});\n lint.push(...validateNote(c.path, proposed, schema));\n }\n for (const u of changeset.updates ?? []) {\n const abs = join(notesPath, u.path);\n let proposed: string;\n try {\n const existing = await readFile(abs, \"utf-8\");\n proposed = applyUpdateInMemory(existing, u);\n } catch (e) {\n errors.push(`update precheck: ${u.path}: ${e instanceof Error ? e.message : String(e)}`);\n continue;\n }\n lint.push(...validateNote(u.path, proposed, schema));\n }\n\n const schemaErrors = lint.filter((f) => f.rule === \"schema_violations\" && f.severity === \"error\");\n if (schemaErrors.length > 0) {\n errors.push(`${schemaErrors.length} schema violation(s) blocked the patch`);\n }\n if (!allowWarn) {\n const warns = lint.filter((f) => f.severity === \"warn\");\n if (warns.length > 0) errors.push(`${warns.length} lint warning(s) blocked the patch (allowLintWarnings=false)`);\n }\n }\n\n if (errors.length > 0 || dryRun) {\n return { ok: errors.length === 0, applied, rolledBack: [], lint, errors };\n }\n\n // Execute — capture snapshots as we go so rollback can reverse.\n try {\n for (const c of changeset.creates ?? []) {\n const abs = join(notesPath, c.path);\n await crud.create(c.path, c.content, c.frontmatter);\n snapshots.push({ op: \"create\", path: c.path, absPath: abs });\n applied.push({ op: \"create\", path: c.path });\n }\n for (const u of changeset.updates ?? []) {\n const abs = join(notesPath, u.path);\n const prev = await readFile(abs, \"utf-8\");\n await crud.update(u.path, u.content, { mode: u.mode, heading: u.heading } as UpdateNoteOptions);\n snapshots.push({ op: \"update\", path: u.path, absPath: abs, prevContent: prev });\n applied.push({ op: \"update\", path: u.path });\n }\n for (const m of changeset.moves ?? []) {\n await crud.move(m.from, m.to);\n snapshots.push({\n op: \"move\",\n path: m.to,\n absPath: join(notesPath, m.to),\n prevAbsPath: join(notesPath, m.from),\n });\n applied.push({ op: \"move\", path: `${m.from} -> ${m.to}` });\n }\n for (const d of changeset.deletes ?? []) {\n const abs = join(notesPath, d.path);\n const prev = await readFile(abs, \"utf-8\");\n await crud.delete(d.path);\n snapshots.push({ op: \"delete\", path: d.path, absPath: abs, prevContent: prev });\n applied.push({ op: \"delete\", path: d.path });\n }\n } catch (e) {\n errors.push(`op failed: ${e instanceof Error ? e.message : String(e)} — rolling back`);\n const rolledBack = await rollback(snapshots);\n return { ok: false, applied, rolledBack, lint, errors };\n }\n\n let indexes_regenerated: RegenResult[] = [];\n if (options.regenIndexes !== false) {\n const touched = [\n ...(changeset.creates ?? []).map((c) => c.path),\n ...(changeset.deletes ?? []).map((d) => d.path),\n ...(changeset.moves ?? []).flatMap((m) => [m.from, m.to]),\n ];\n try {\n indexes_regenerated = await regenIndexesForPaths(notesPath, touched);\n } catch (e) {\n // Non-fatal: patch succeeded; index regen is a best-effort side effect.\n errors.push(`index regen failed (patch still applied): ${e instanceof Error ? e.message : String(e)}`);\n }\n }\n\n return { ok: true, applied, rolledBack: [], lint, errors, indexes_regenerated };\n}\n\nasync function rollback(snapshots: Snapshot[]): Promise<AppliedOp[]> {\n const reverted: AppliedOp[] = [];\n for (const s of snapshots.slice().reverse()) {\n try {\n switch (s.op) {\n case \"create\":\n if (existsSync(s.absPath)) await unlink(s.absPath);\n reverted.push({ op: \"create\", path: s.path });\n break;\n case \"update\":\n if (s.prevContent !== undefined) {\n await writeFile(s.absPath, s.prevContent, \"utf-8\");\n }\n reverted.push({ op: \"update\", path: s.path });\n break;\n case \"delete\":\n if (s.prevContent !== undefined) {\n await mkdir(dirname(s.absPath), { recursive: true });\n await writeFile(s.absPath, s.prevContent, \"utf-8\");\n }\n reverted.push({ op: \"delete\", path: s.path });\n break;\n case \"move\":\n if (s.prevAbsPath && existsSync(s.absPath)) {\n await mkdir(dirname(s.prevAbsPath), { recursive: true });\n await rename(s.absPath, s.prevAbsPath);\n }\n reverted.push({ op: \"move\", path: s.path });\n break;\n }\n } catch {\n // swallow — best-effort rollback\n }\n }\n return reverted;\n}\n\nfunction applyUpdateInMemory(existing: string, u: PatchUpdate): string {\n switch (u.mode) {\n case \"overwrite\":\n return u.content;\n case \"append\":\n return existing + \"\\n\" + u.content;\n case \"prepend\": {\n const { data, content: body } = matter(existing);\n const newBody = u.content + \"\\n\" + body;\n return Object.keys(data).length > 0 ? matter.stringify(newBody, data) : newBody;\n }\n case \"patch-by-heading\": {\n if (!u.heading) throw new Error(\"patch-by-heading requires heading\");\n return patchByHeadingInMemory(existing, u.heading, u.content);\n }\n }\n}\n\nfunction patchByHeadingInMemory(content: string, heading: string, newContent: string): string {\n const lines = content.split(\"\\n\");\n const re = new RegExp(`^#{1,6}\\\\s+${heading.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}\\\\s*$`, \"i\");\n let idx = -1;\n let level = 0;\n for (let i = 0; i < lines.length; i++) {\n if (re.test(lines[i])) {\n idx = i;\n const m = lines[i].match(/^(#{1,6})\\s+/);\n level = m ? m[1].length : 1;\n break;\n }\n }\n if (idx === -1) throw new Error(`Heading not found: ${heading}`);\n let end = lines.length;\n for (let i = idx + 1; i < lines.length; i++) {\n const m = lines[i].match(/^(#{1,6})\\s+/);\n if (m && m[1].length <= level) {\n end = i;\n break;\n }\n }\n return [...lines.slice(0, idx + 1), \"\", newContent, \"\", ...lines.slice(end)].join(\"\\n\");\n}\n","import { basename } from \"node:path\";\nimport type { ChangeSet, PatchCreate, PatchUpdate } from \"./patch.js\";\n\nexport interface SynthesizeNoteInput {\n topic: string;\n answer: string;\n suggested_path: string;\n type?: string;\n sources?: string[];\n derived_from?: string[];\n related_notes?: string[];\n status?: string;\n confidence?: string;\n decision_maker?: string;\n decided_on?: string;\n severity?: string;\n extra_frontmatter?: Record<string, unknown>;\n /**\n * When true, the resulting note is filed under `proposals/<date>-<slug>.md` with\n * `status: proposal` frontmatter, regardless of `suggested_path`. The original path\n * is preserved as `proposed_target` in frontmatter so `synthesize_promote` knows\n * where to move it on acceptance.\n */\n proposal?: boolean;\n /**\n * When provided alongside `proposal: true`, used as the proposal subdirectory name.\n * Defaults to \"proposals\". Useful for callers that want to segregate by source\n * (e.g. \"proposals/research\", \"proposals/outage\").\n */\n proposal_subdir?: string;\n}\n\nexport interface SynthesizePreview {\n changeset: ChangeSet;\n path: string;\n title: string;\n /** True when the preview is a proposal — caller (or `synthesize_promote`) must move it later. */\n is_proposal?: boolean;\n /** When `is_proposal` is true, the canonical path that `synthesize_promote` should move it to. */\n proposed_target?: string;\n}\n\n/**\n * Convert a query answer + provenance into a ChangeSet that can be fed to applyPatch.\n * The shaped note includes provenance frontmatter and auto-inserts [[wikilinks]] for\n * any related_notes that appear as bare titles in the body.\n *\n * Does NOT write to disk — always returns a ChangeSet. Callers pass it to applyPatch.\n *\n * Proposal mode: when `proposal: true`, the path is rewritten to\n * `<proposal_subdir>/<YYYY-MM-DD>-<slug>.md`, the `status` is forced to `proposal`,\n * and `proposed_target` records the original suggested path. `synthesize_promote`\n * later moves the file to that target and clears the proposal frontmatter.\n */\nexport function buildSynthesizeChangeSet(input: SynthesizeNoteInput): SynthesizePreview {\n const today = new Date().toISOString().slice(0, 10);\n const title = input.topic.trim();\n const canonicalPath = input.suggested_path.endsWith(\".md\") ? input.suggested_path : input.suggested_path + \".md\";\n\n const isProposal = input.proposal === true;\n const subdir = (input.proposal_subdir ?? \"proposals\").replace(/\\/+$/, \"\");\n const slug = slugForTopic(title);\n const path = isProposal ? `${subdir}/${today}-${slug}.md` : canonicalPath;\n\n const frontmatter: Record<string, unknown> = {\n title,\n type: input.type ?? \"note\",\n status: isProposal ? \"proposal\" : (input.status ?? \"active\"),\n last_verified: today,\n confidence: input.confidence ?? \"medium\",\n ...(input.sources && input.sources.length > 0 && { sources: input.sources }),\n ...(input.derived_from && input.derived_from.length > 0 && { derived_from: input.derived_from }),\n ...(input.decision_maker && { decision_maker: input.decision_maker }),\n ...(input.decided_on && { decided_on: input.decided_on }),\n ...(input.severity && { severity: input.severity }),\n ...(isProposal && { proposed_target: canonicalPath }),\n ...(input.extra_frontmatter ?? {}),\n };\n\n const body = buildBody(title, input.answer, input.related_notes ?? []);\n\n const create: PatchCreate = { path, content: body, frontmatter };\n const changeset: ChangeSet = { creates: [create] };\n\n return {\n changeset,\n path,\n title,\n ...(isProposal && { is_proposal: true, proposed_target: canonicalPath }),\n };\n}\n\nfunction slugForTopic(topic: string): string {\n return topic\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 60) || \"untitled\";\n}\n\nfunction buildBody(title: string, answer: string, relatedNotes: string[]): string {\n let body = `# ${title}\\n\\n${answer.trim()}\\n`;\n\n if (relatedNotes.length > 0) {\n body = autoWikilink(body, relatedNotes);\n const links = relatedNotes.map((r) => `- [[${wikilinkName(r)}]]`).join(\"\\n\");\n body += `\\n## Related\\n\\n${links}\\n`;\n }\n\n return body;\n}\n\n/**\n * For each related note, replace the first bare occurrence of its title in the body\n * with a [[wikilink]]. Conservative — only replaces whole-word matches outside code blocks.\n */\nfunction autoWikilink(body: string, relatedNotes: string[]): string {\n // Skip content inside fenced code blocks\n const parts = body.split(/(```[\\s\\S]*?```)/g);\n return parts\n .map((part, i) => {\n if (i % 2 === 1) return part; // code block\n let out = part;\n for (const r of relatedNotes) {\n const name = wikilinkName(r);\n if (/[\\[\\]]/.test(out) && out.includes(`[[${name}]]`)) continue; // already linked\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`\\\\b${escaped}\\\\b`, \"i\");\n out = out.replace(re, `[[${name}]]`);\n }\n return out;\n })\n .join(\"\");\n}\n\nfunction wikilinkName(pathOrName: string): string {\n // \"decisions/auth-migration.md\" → \"auth-migration\"\n // \"Auth Migration\" → \"Auth Migration\"\n const base = basename(pathOrName).replace(/\\.md$/, \"\");\n return base;\n}\n\nexport interface PromoteInput {\n /**\n * Path to the proposal note (relative to vault root). Must have `status: proposal` frontmatter\n * AND `proposed_target` frontmatter, both written by `buildSynthesizeChangeSet` in proposal mode.\n */\n proposal_path: string;\n /**\n * Optional override for the destination path. When omitted, the proposal's own\n * `proposed_target` frontmatter is used.\n */\n target_path?: string;\n}\n\nexport interface PromotePreview {\n changeset: ChangeSet;\n from: string;\n to: string;\n}\n\n/**\n * Promote a reviewed proposal to its canonical destination.\n *\n * Reads the proposal's existing content + frontmatter, strips `status: proposal` and\n * `proposed_target` keys, restores the original status (default \"active\"), and returns\n * a ChangeSet that:\n * - creates the canonical path with the cleaned frontmatter + body\n * - deletes the proposal path\n *\n * The caller (the MCP tool) reads the proposal file, runs this builder, and applies the\n * resulting changeset via applyPatch — preserving atomicity and rollback semantics.\n */\nexport function buildPromoteChangeSet(input: {\n proposal_path: string;\n target_path?: string;\n body: string;\n frontmatter: Record<string, unknown>;\n}): PromotePreview {\n const fm = { ...input.frontmatter };\n const target = input.target_path ?? (typeof fm.proposed_target === \"string\" ? fm.proposed_target : undefined);\n if (!target) {\n throw new Error(\n `Cannot promote ${input.proposal_path}: no target_path provided and no 'proposed_target' frontmatter on the proposal.`\n );\n }\n // Restore canonical status (default \"active\") and strip proposal markers.\n if (fm.status === \"proposal\") fm.status = \"active\";\n delete fm.proposed_target;\n\n const create: PatchCreate = {\n path: target,\n content: input.body,\n frontmatter: fm,\n };\n const changeset: ChangeSet = {\n creates: [create],\n deletes: [{ path: input.proposal_path }],\n };\n\n return { changeset, from: input.proposal_path, to: target };\n}\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\nconst BEGIN_MARKER = \"<!-- semantic-memory:begin contract -->\";\nconst END_MARKER = \"<!-- semantic-memory:end contract -->\";\nconst LOCAL_NOTES_HEADING = \"## Local Notes\";\n\nexport interface ToolSummary {\n name: string;\n description: string;\n deprecated?: boolean;\n}\n\nexport interface ModeSummary {\n name: string;\n description: string;\n isDefault?: boolean;\n}\n\nexport interface RegenerateContractOptions {\n projectRoot: string;\n pluginVersion: string;\n tools: ToolSummary[];\n modes?: ModeSummary[];\n /** When true, overwrite hand-edited managed blocks. Default false → fails loudly on detected hand-edits. */\n force?: boolean;\n /** Override \"now\" — used by tests for deterministic timestamps. */\n nowIso?: string;\n}\n\nexport interface RegenerateContractResult {\n path: string;\n written: boolean;\n reason?: string;\n hand_edit_detected?: boolean;\n preserved_local_notes_chars?: number;\n}\n\n/**\n * Generate or refresh AGENTS.md at the project root.\n *\n * Behavior:\n * - If AGENTS.md is absent, write the full contract + an empty Local Notes section.\n * - If AGENTS.md exists and contains the managed block markers:\n * * Regenerate the content INSIDE the markers from the current tool/mode inventory.\n * * Preserve everything OUTSIDE the markers (Local Notes tail, custom sections, frontmatter the\n * user added).\n * * If `force` is false and the inside-block content differs from a freshly-generated block AND\n * the difference is not just a regenerable timestamp/version, fail with hand_edit_detected.\n * - If AGENTS.md exists without managed-block markers, refuse and report — the user has a custom\n * AGENTS.md and we should not silently take it over.\n */\nexport async function regenerateAgentsContract(opts: RegenerateContractOptions): Promise<RegenerateContractResult> {\n const path = join(opts.projectRoot, \"AGENTS.md\");\n const generatedBlock = buildManagedBlock(opts);\n const generatedFull = buildFullDocument(opts, generatedBlock);\n\n if (!existsSync(path)) {\n await writeFile(path, generatedFull, \"utf-8\");\n return { path, written: true };\n }\n\n const existing = await readFile(path, \"utf-8\");\n const beginIdx = existing.indexOf(BEGIN_MARKER);\n const endIdx = existing.indexOf(END_MARKER);\n\n if (beginIdx === -1 || endIdx === -1 || endIdx < beginIdx) {\n return {\n path,\n written: false,\n reason:\n \"AGENTS.md exists but does not contain the semantic-memory managed-block markers. Move your existing content into a Local Notes section outside the managed block, or delete AGENTS.md and re-run to bootstrap from scratch.\",\n };\n }\n\n const existingBlock = existing.slice(beginIdx, endIdx + END_MARKER.length);\n const localTail = extractLocalTail(existing, endIdx + END_MARKER.length);\n\n if (!opts.force && hasHandEdits(existingBlock, generatedBlock)) {\n return {\n path,\n written: false,\n hand_edit_detected: true,\n reason:\n \"Managed-block content has been hand-edited. Move your changes to the Local Notes section (outside the managed block) and re-run, or pass force=true to overwrite.\",\n };\n }\n\n // Refresh the entire pre-block prefix (frontmatter + title) so timestamp/version\n // stamps reflect the current regeneration. Keep the managed block fresh and the\n // Local Notes tail untouched.\n const newPrefix = buildPrefix(opts);\n const newDoc = newPrefix + generatedBlock + localTail;\n await writeFile(path, newDoc, \"utf-8\");\n return {\n path,\n written: true,\n preserved_local_notes_chars: localTail.length,\n };\n}\n\nfunction buildPrefix(opts: RegenerateContractOptions): string {\n return [\n \"---\",\n `generated_by: semantic-memory`,\n `version: ${opts.pluginVersion}`,\n `last_generated: ${opts.nowIso ?? new Date().toISOString()}`,\n \"---\",\n \"\",\n \"# Project Agent Contract\",\n \"\",\n ].join(\"\\n\");\n}\n\nfunction buildFullDocument(opts: RegenerateContractOptions, managedBlock: string): string {\n const localNotes = `\\n\\n${LOCAL_NOTES_HEADING}\\n\\n_User-authored content lives here. Preserved across \\`regenerate_contract\\` runs._\\n`;\n return buildPrefix(opts) + managedBlock + localNotes;\n}\n\nfunction buildManagedBlock(opts: RegenerateContractOptions): string {\n const lines: string[] = [BEGIN_MARKER, \"\"];\n\n lines.push(\"## Active Modes\", \"\");\n const modes = opts.modes ?? defaultModes();\n for (const m of modes) {\n const tag = m.isDefault ? \" (default)\" : \"\";\n lines.push(`- **${m.name}**${tag} — ${m.description}`);\n }\n lines.push(\"\");\n\n lines.push(\"## Required Workflow\", \"\");\n lines.push(\"1. For multi-step work that needs verification, open a session: `session_start({task})`.\");\n lines.push(\"2. Use mode-aware retrieval: `search_hybrid`, `read_note`, `list_notes`, `backlinks`/`forwardlinks`.\");\n lines.push(\"3. Make durable updates via `apply_patch` or `synthesize_note` — never bypass the patch layer for multi-note edits.\");\n lines.push(\"4. Run verification commands inside a session: `session_run({cmd})`.\");\n lines.push(\"5. Close with `session_finish({summary})` — refused without verification unless explicitly waived.\");\n lines.push(\"6. Lint regularly: `lint_vault({checks: ['schema','provenance','stale','broken_links']})`.\");\n lines.push(\"\");\n\n lines.push(\"## Tool Surface\", \"\");\n const live = opts.tools.filter((t) => !t.deprecated);\n const deprecated = opts.tools.filter((t) => t.deprecated);\n for (const t of live) {\n lines.push(`- \\`${t.name}\\` — ${stripDeprecationPrefix(t.description)}`);\n }\n if (deprecated.length > 0) {\n lines.push(\"\");\n lines.push(\"### Deprecated (removed in v2.0.0)\", \"\");\n for (const t of deprecated) {\n lines.push(`- \\`${t.name}\\` — ${stripDeprecationPrefix(t.description)}`);\n }\n }\n lines.push(\"\");\n\n lines.push(\"## Memory Policy\", \"\");\n lines.push(\"- Markdown is canonical. The vault under `.claude/.vault/` (or wherever `--notes` points) is the source of truth.\");\n lines.push(\"- Indexes (vector / graph / FTS) are derived state — rebuildable via `reindex` without losing knowledge.\");\n lines.push(\"- Provenance frontmatter is required for `note`/`decision`/`gotcha` types — `sources` (external) or `derived_from` (internal).\");\n lines.push(\"- Mode router lives at `.claude/.semantic-memory/mode` (legacy `.claude/.sidekick-mode` still readable through v1.x); explicit `/mode` is ground truth.\");\n lines.push(\"\");\n\n lines.push(END_MARKER);\n return lines.join(\"\\n\");\n}\n\nfunction defaultModes(): ModeSummary[] {\n return [\n { name: \"vault-first\", description: \"Project-scoped prose questions trigger vault consultation with cite-or-deflect.\", isDefault: true },\n { name: \"research\", description: \"Every source introduced is filed via `ingest_source`; mandatory `synthesize_note` on exit.\" },\n { name: \"outage-silence\", description: \"No proactive vault search. Terse responses. Postmortem `synthesize_note` on exit.\" },\n ];\n}\n\nfunction stripDeprecationPrefix(description: string): string {\n return description.replace(/^\\[DEPRECATED[^\\]]*\\]\\s*/, \"(deprecated) \");\n}\n\nfunction extractLocalTail(existing: string, afterEndIdx: number): string {\n const tail = existing.slice(afterEndIdx);\n // Trim a single leading blank-line-then-newline if present so we don't double-space across regenerations.\n return tail;\n}\n\n/**\n * Detect hand-edits by comparing semantic content of the managed block, ignoring lines that are\n * regenerable (currently no per-block timestamps live inside the block — they're in frontmatter).\n * Returns true if the existing block diverges from what we'd generate now.\n */\nfunction hasHandEdits(existingBlock: string, generatedBlock: string): boolean {\n return existingBlock.trim() !== generatedBlock.trim();\n}\n\n/**\n * Inspect AGENTS.md without modifying it. Returns whether the file exists, whether it has managed\n * markers, and how many characters of Local Notes tail are preserved.\n */\nexport async function inspectAgentsContract(projectRoot: string): Promise<{\n path: string;\n exists: boolean;\n has_managed_block: boolean;\n local_notes_chars: number;\n}> {\n const path = join(projectRoot, \"AGENTS.md\");\n if (!existsSync(path)) {\n return { path, exists: false, has_managed_block: false, local_notes_chars: 0 };\n }\n const raw = await readFile(path, \"utf-8\");\n const beginIdx = raw.indexOf(BEGIN_MARKER);\n const endIdx = raw.indexOf(END_MARKER);\n if (beginIdx === -1 || endIdx === -1) {\n return { path, exists: true, has_managed_block: false, local_notes_chars: 0 };\n }\n const localTail = raw.slice(endIdx + END_MARKER.length).trim();\n return { path, exists: true, has_managed_block: true, local_notes_chars: localTail.length };\n}\n","import { mkdir, readFile, writeFile, rm } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nexport interface VerificationRecord {\n cmd: string;\n exit: number | null;\n signal: string | null;\n duration_ms: number;\n tail: string;\n at: string;\n}\n\nexport interface SessionState {\n id: string;\n task: string;\n started_at: string;\n last_activity_at: string;\n verifications: VerificationRecord[];\n notes_touched: string[];\n closed_at?: string;\n closed_summary?: string;\n closed_verified_waived?: boolean;\n closed_waiver_reason?: string;\n}\n\nexport type SessionStatus =\n | { state: \"no_session\" }\n | (SessionState & { state: \"active\" | \"stale\" });\n\nexport interface SessionStartOptions {\n /** When true and a session for the same task is already open, return the existing id rather than failing. Defaults to true. */\n reuseSameTask?: boolean;\n}\n\nexport interface SessionFinishInput {\n summary: string;\n /**\n * When false, the caller is explicitly waiving verification (e.g. for doc-only edits).\n * Requires a non-empty `reason`. When true (default) and no verifications have been\n * recorded, the call is refused.\n */\n verified?: boolean;\n reason?: string;\n}\n\nconst STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000;\nconst DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;\nconst TAIL_BYTES = 4096;\n\n/**\n * SessionManager owns a single session.json file at `<sessionDir>/session.json`.\n *\n * State is durable across MCP server restarts (the file persists). At most one session\n * is active per project at a time — concurrent sessions are explicitly out of scope\n * for v1.1; brain has the same constraint.\n *\n * Hard gates:\n * - session_finish refuses without verifications unless verified=false + reason\n * - session_run refuses without an open session\n * - session_start refuses if another session is open with a different task\n *\n * Stale detection: a session whose last_activity_at is >24h old is reported as\n * state:\"stale\" by status(). Stale sessions can still be finished or extended; the\n * label is informational so callers can decide whether to abandon (rm + start new) or\n * resume.\n */\nexport class SessionManager {\n constructor(private sessionDir: string) {}\n\n get sessionPath(): string {\n return join(this.sessionDir, \"session.json\");\n }\n\n async start(task: string, opts: SessionStartOptions = {}): Promise<{ ok: true; id: string; reused?: boolean } | { ok: false; error: string }> {\n if (!task || task.trim().length === 0) {\n return { ok: false, error: \"session_start requires a non-empty task.\" };\n }\n await mkdir(this.sessionDir, { recursive: true });\n const existing = await this.read();\n if (existing && !existing.closed_at) {\n const reuseSameTask = opts.reuseSameTask !== false;\n if (reuseSameTask && existing.task === task) {\n return { ok: true, id: existing.id, reused: true };\n }\n return {\n ok: false,\n error: `Session already open: id=${existing.id}, task=${JSON.stringify(existing.task)}. Close it with session_finish first.`,\n };\n }\n const now = new Date().toISOString();\n const id = `ses_${now.replace(/[:.]/g, \"-\")}`;\n const state: SessionState = {\n id,\n task: task.trim(),\n started_at: now,\n last_activity_at: now,\n verifications: [],\n notes_touched: [],\n };\n await writeFile(this.sessionPath, JSON.stringify(state, null, 2), \"utf-8\");\n return { ok: true, id };\n }\n\n async status(): Promise<SessionStatus> {\n const state = await this.read();\n if (!state || state.closed_at) return { state: \"no_session\" };\n const age = Date.now() - new Date(state.last_activity_at).getTime();\n const label: \"active\" | \"stale\" = age > STALE_THRESHOLD_MS ? \"stale\" : \"active\";\n return { ...state, state: label };\n }\n\n async run(cmd: string, timeoutMs?: number): Promise<{ ok: true; record: VerificationRecord } | { ok: false; error: string }> {\n const state = await this.read();\n if (!state || state.closed_at) {\n return { ok: false, error: \"No active session. Run session_start first.\" };\n }\n if (!cmd || cmd.trim().length === 0) {\n return { ok: false, error: \"session_run requires a non-empty command.\" };\n }\n const start = Date.now();\n const record = await this.spawnAndCapture(cmd, timeoutMs ?? DEFAULT_TIMEOUT_MS);\n record.duration_ms = Date.now() - start;\n state.verifications.push(record);\n state.last_activity_at = new Date().toISOString();\n await writeFile(this.sessionPath, JSON.stringify(state, null, 2), \"utf-8\");\n return { ok: true, record };\n }\n\n async finish(input: SessionFinishInput): Promise<{ ok: true; closed: SessionState } | { ok: false; error: string }> {\n const state = await this.read();\n if (!state || state.closed_at) {\n return { ok: false, error: \"No active session to finish.\" };\n }\n const explicitWaiver = input.verified === false;\n if (!explicitWaiver && state.verifications.length === 0) {\n return {\n ok: false,\n error:\n \"session_finish refused: no verification commands recorded. Run session_run with a real verification command (tests, lint, etc.) first, OR pass verified=false with a reason to waive verification (e.g. for doc-only edits).\",\n };\n }\n if (explicitWaiver && (!input.reason || input.reason.trim().length === 0)) {\n return {\n ok: false,\n error: \"session_finish refused: verified=false requires a non-empty `reason` explaining why verification is not applicable to this task.\",\n };\n }\n if (!input.summary || input.summary.trim().length === 0) {\n return { ok: false, error: \"session_finish requires a non-empty `summary`.\" };\n }\n const closed: SessionState = {\n ...state,\n closed_at: new Date().toISOString(),\n closed_summary: input.summary.trim(),\n ...(explicitWaiver && {\n closed_verified_waived: true,\n closed_waiver_reason: input.reason!.trim(),\n }),\n };\n // Sessions are ephemeral runtime state — finished sessions are removed from disk.\n // Durable artifacts (synthesize_note proposals, log entries) capture what mattered.\n await rm(this.sessionPath, { force: true });\n return { ok: true, closed };\n }\n\n /**\n * Append paths to notes_touched on the active session, deduplicating. No-op when no\n * session is active. Used by patch tools to record what the session edited.\n */\n async recordNotesTouched(paths: string[]): Promise<void> {\n if (paths.length === 0) return;\n const state = await this.read();\n if (!state || state.closed_at) return;\n const set = new Set(state.notes_touched);\n for (const p of paths) set.add(p);\n state.notes_touched = Array.from(set);\n state.last_activity_at = new Date().toISOString();\n await writeFile(this.sessionPath, JSON.stringify(state, null, 2), \"utf-8\");\n }\n\n /**\n * Read the active session WITHOUT computing the stale label. Returns null when no\n * session is active or when the file is unparseable.\n */\n async readActive(): Promise<SessionState | null> {\n const state = await this.read();\n if (!state || state.closed_at) return null;\n return state;\n }\n\n private async read(): Promise<SessionState | null> {\n if (!existsSync(this.sessionPath)) return null;\n try {\n const raw = await readFile(this.sessionPath, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (typeof parsed?.id !== \"string\") return null;\n return parsed as SessionState;\n } catch {\n return null;\n }\n }\n\n private spawnAndCapture(cmd: string, timeoutMs: number): Promise<VerificationRecord> {\n return new Promise((resolve) => {\n const proc = spawn(cmd, { shell: true });\n let buf = \"\";\n let killedByTimeout = false;\n const timer = setTimeout(() => {\n killedByTimeout = true;\n proc.kill(\"SIGTERM\");\n setTimeout(() => {\n try { proc.kill(\"SIGKILL\"); } catch { /* already gone */ }\n }, 1000).unref();\n }, timeoutMs);\n\n const onData = (chunk: Buffer) => {\n buf += chunk.toString(\"utf-8\");\n if (buf.length > TAIL_BYTES * 4) {\n buf = buf.slice(buf.length - TAIL_BYTES * 2);\n }\n };\n proc.stdout?.on(\"data\", onData);\n proc.stderr?.on(\"data\", onData);\n\n proc.on(\"close\", (code, signal) => {\n clearTimeout(timer);\n const tail = buf.slice(-TAIL_BYTES);\n resolve({\n cmd,\n exit: killedByTimeout ? null : code,\n signal: killedByTimeout ? \"SIGTERM (timeout)\" : (signal ?? null),\n duration_ms: 0,\n tail,\n at: new Date().toISOString(),\n });\n });\n\n proc.on(\"error\", (err) => {\n clearTimeout(timer);\n const tail = buf + `\\n[spawn error: ${err.message}]`;\n resolve({\n cmd,\n exit: null,\n signal: null,\n duration_ms: 0,\n tail: tail.slice(-TAIL_BYTES),\n at: new Date().toISOString(),\n });\n });\n });\n }\n}\n\n/**\n * Heuristic: derive the project root from the vault path. If the vault path ends in\n * `.claude/.vault`, the project root is two levels up. Otherwise the vault path is\n * treated as the project root.\n */\nexport function deriveProjectRoot(notesPath: string): string {\n const norm = notesPath.replace(/[\\\\/]+$/, \"\");\n if (norm.endsWith(\"/.claude/.vault\") || norm.endsWith(\"\\\\.claude\\\\.vault\")) {\n return norm.slice(0, -\".claude/.vault\".length).replace(/[\\\\/]+$/, \"\");\n }\n return norm;\n}\n\nexport function deriveSessionDir(notesPath: string): string {\n return join(deriveProjectRoot(notesPath), \".claude\", \".semantic-memory\");\n}\n"],"mappings":"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","names":["body","readFile","writeFile","join","glob","matter","escapeRegex","readFile","writeFile","existsSync","join","dirname","basename","matter","readFile","writeFile","unlink","rename","mkdir","existsSync","dirname","join","matter","existsSync","join","matter","readFile","unlink","writeFile","mkdir","dirname","rename","basename","readFile","writeFile","existsSync","join","mkdir","readFile","writeFile","existsSync","join"]}
|
|
@@ -55,6 +55,7 @@ async function runFastTier(opts) {
|
|
|
55
55
|
checkIndexFreshness(opts.projectRoot, opts.vaultPath),
|
|
56
56
|
checkSessionStaleness(opts.projectRoot),
|
|
57
57
|
checkAgentsContract(opts.projectRoot),
|
|
58
|
+
checkLegacyStateFiles(opts.projectRoot),
|
|
58
59
|
...await skillManifestChecks(opts.projectRoot)
|
|
59
60
|
];
|
|
60
61
|
const results = await Promise.all(checks.map((p) => p.catch((err) => failOpenWarn(err))));
|
|
@@ -192,6 +193,29 @@ async function checkSessionStaleness(projectRoot) {
|
|
|
192
193
|
return null;
|
|
193
194
|
}
|
|
194
195
|
}
|
|
196
|
+
async function checkLegacyStateFiles(projectRoot) {
|
|
197
|
+
const legacy = [
|
|
198
|
+
".sidekick-mode",
|
|
199
|
+
".sidekick-fingerprints.json",
|
|
200
|
+
".sidekick-capture-pending.json"
|
|
201
|
+
];
|
|
202
|
+
const present = [];
|
|
203
|
+
for (const name of legacy) {
|
|
204
|
+
if (existsSync(join(projectRoot, ".claude", name))) {
|
|
205
|
+
present.push(name);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (present.length === 0) {
|
|
209
|
+
return { check: "legacy_state_files", severity: "ok", summary: "no legacy state files" };
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
check: "legacy_state_files",
|
|
213
|
+
severity: "warn",
|
|
214
|
+
summary: `${present.length} legacy state file${present.length === 1 ? "" : "s"} at .claude/.sidekick-*`,
|
|
215
|
+
detail: `Files: ${present.join(", ")}. Run \`bin/semantic-memory migrate-state\` to move them under .claude/.semantic-memory/. These files are still being read for backwards-compat through v1.x; the legacy fallback is removed in v2.0.`,
|
|
216
|
+
fixable_via: "none"
|
|
217
|
+
};
|
|
218
|
+
}
|
|
195
219
|
async function checkAgentsContract(projectRoot) {
|
|
196
220
|
const path = join(projectRoot, "AGENTS.md");
|
|
197
221
|
if (!existsSync(path)) return null;
|
|
@@ -270,4 +294,4 @@ export {
|
|
|
270
294
|
filterToDrift,
|
|
271
295
|
formatDriftBanner
|
|
272
296
|
};
|
|
273
|
-
//# sourceMappingURL=chunk-
|
|
297
|
+
//# sourceMappingURL=chunk-EVAJGOCB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/healthcheck.ts"],"sourcesContent":["import { readFile, writeFile, mkdir, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport { homedir } from \"node:os\";\nimport { lintVault } from \"./lint.js\";\nimport { deriveProjectRoot } from \"./session.js\";\n\nexport type Tier = \"fast\" | \"slow\" | \"all\";\nexport type Severity = \"ok\" | \"warn\" | \"error\";\n\nexport interface DriftFinding {\n check: string;\n severity: Severity;\n summary: string;\n detail?: string;\n fixable_via?: \"skill-link\" | \"mcp-reconcile\" | \"regenerate-contract\" | \"reindex\" | \"none\";\n}\n\nexport interface HealthcheckResult {\n tier_run: Tier;\n ran_at: string;\n duration_ms: number;\n findings: DriftFinding[];\n counts: { ok: number; warn: number; error: number };\n cached?: boolean;\n}\n\nexport interface HealthcheckOptions {\n projectRoot: string;\n /** Vault path. When omitted, derived from projectRoot's .claude/.vault. */\n vaultPath?: string;\n tier?: Tier;\n /** Cache file path. Default: <projectRoot>/.claude/.semantic-memory/healthcheck-cache.json */\n cachePath?: string;\n /** Force re-run even if cache is <5 minutes old. */\n force?: boolean;\n /** Override \"now\" — used by tests. */\n nowMs?: number;\n}\n\nconst CACHE_TTL_MS = 5 * 60 * 1000;\n\n/**\n * Run the drift-detection healthcheck.\n *\n * - Fast tier: cheap file-system probes (~ms each). Plugin version, skill manifests,\n * .mcp.json registration, hook registration, index freshness, session staleness,\n * AGENTS.md managed-block sha. All independent, run in parallel. Total budget: <100ms\n * on a representative install.\n * - Slow tier: full vault lint. Adds tens of seconds on large vaults — invoked by\n * explicit /healthcheck, never by the SessionStart auto path.\n * - Cache: when force=false and the cache file is <5min old, return the cached result.\n *\n * Fail-open: any individual check that throws produces a `severity: \"warn\"` finding\n * with the error message; never propagates.\n */\nexport async function runHealthcheck(opts: HealthcheckOptions): Promise<HealthcheckResult> {\n const start = (opts.nowMs ?? Date.now());\n const tier: Tier = opts.tier ?? \"fast\";\n const cachePath = opts.cachePath ?? join(opts.projectRoot, \".claude\", \".semantic-memory\", \"healthcheck-cache.json\");\n\n if (!opts.force && existsSync(cachePath)) {\n try {\n const raw = await readFile(cachePath, \"utf-8\");\n const cached = JSON.parse(raw) as HealthcheckResult & { ran_at_ms: number };\n if (cached.tier_run === tier && start - cached.ran_at_ms < CACHE_TTL_MS) {\n return { ...cached, cached: true };\n }\n } catch {\n // ignore corrupt cache; will overwrite below\n }\n }\n\n const findings = tier === \"fast\" ? await runFastTier(opts) : tier === \"slow\" ? await runSlowTier(opts) : [...(await runFastTier(opts)), ...(await runSlowTier(opts))];\n\n const counts = countBySeverity(findings);\n const ranAt = new Date(start).toISOString();\n const result: HealthcheckResult = {\n tier_run: tier,\n ran_at: ranAt,\n duration_ms: Date.now() - start,\n findings,\n counts,\n };\n\n // Persist cache (best-effort; failure is non-fatal).\n try {\n await mkdir(join(opts.projectRoot, \".claude\", \".semantic-memory\"), { recursive: true });\n await writeFile(cachePath, JSON.stringify({ ...result, ran_at_ms: start }, null, 2), \"utf-8\");\n } catch {\n // never let cache write block the result\n }\n\n return result;\n}\n\n/**\n * Surface filter — return findings with severity >= warn. Used by the SessionStart hook\n * to decide whether to emit anything at all.\n */\nexport function filterToDrift(result: HealthcheckResult): DriftFinding[] {\n return result.findings.filter((f) => f.severity !== \"ok\");\n}\n\nfunction countBySeverity(findings: DriftFinding[]): { ok: number; warn: number; error: number } {\n const counts = { ok: 0, warn: 0, error: 0 };\n for (const f of findings) counts[f.severity]++;\n return counts;\n}\n\nasync function runFastTier(opts: HealthcheckOptions): Promise<DriftFinding[]> {\n const checks: Promise<DriftFinding | null>[] = [\n checkPluginVersion(opts.projectRoot),\n checkMcpJsonEntry(opts.projectRoot),\n checkHookRegistration(opts.projectRoot),\n checkIndexFreshness(opts.projectRoot, opts.vaultPath),\n checkSessionStaleness(opts.projectRoot),\n checkAgentsContract(opts.projectRoot),\n checkLegacyStateFiles(opts.projectRoot),\n ...(await skillManifestChecks(opts.projectRoot)),\n ];\n const results = await Promise.all(checks.map((p) => p.catch((err) => failOpenWarn(err))));\n return results.filter((r): r is DriftFinding => r !== null);\n}\n\nasync function runSlowTier(opts: HealthcheckOptions): Promise<DriftFinding[]> {\n const findings: DriftFinding[] = [];\n if (opts.vaultPath && existsSync(opts.vaultPath)) {\n try {\n const report = await lintVault(opts.vaultPath);\n const total = report.findings.length;\n if (total === 0) {\n findings.push({ check: \"lint_vault\", severity: \"ok\", summary: `vault lint clean (${report.counts.filesLinted} files)` });\n } else {\n findings.push({\n check: \"lint_vault\",\n severity: report.counts.errors > 0 ? \"error\" : \"warn\",\n summary: `vault lint: ${total} findings (errors=${report.counts.errors}, warnings=${report.counts.warnings})`,\n detail: `Run \\`lint_vault\\` for the full report. Many lint findings need human review (stale notes, broken wikilinks).`,\n });\n }\n } catch (err: any) {\n findings.push({ check: \"lint_vault\", severity: \"warn\", summary: `lint_vault threw: ${err?.message ?? err}` });\n }\n }\n return findings;\n}\n\nfunction failOpenWarn(err: unknown): DriftFinding {\n return {\n check: \"internal\",\n severity: \"warn\",\n summary: `healthcheck internal error: ${err instanceof Error ? err.message : String(err)}`,\n };\n}\n\n// --- individual checks ---\n\nasync function checkPluginVersion(projectRoot: string): Promise<DriftFinding | null> {\n const pkgPaths = [\n join(projectRoot, \"node_modules\", \"@theglitchking\", \"semantic-memory\", \"package.json\"),\n join(projectRoot, \"node_modules\", \"@theglitchking\", \"semantic-sidekick\", \"package.json\"),\n ];\n for (const p of pkgPaths) {\n if (!existsSync(p)) continue;\n try {\n const installed = JSON.parse(await readFile(p, \"utf-8\"));\n if (typeof installed?.version === \"string\") {\n return { check: \"plugin_version\", severity: \"ok\", summary: `installed=${installed.version}` };\n }\n } catch {\n // continue to next candidate\n }\n }\n return null; // no installation present — not a drift event for non-npm dev environments\n}\n\nasync function checkMcpJsonEntry(projectRoot: string): Promise<DriftFinding | null> {\n const mcpPath = join(projectRoot, \".mcp.json\");\n if (!existsSync(mcpPath)) return null; // no .mcp.json — fine for dev\n const data = JSON.parse(await readFile(mcpPath, \"utf-8\"));\n const servers = data?.mcpServers ?? {};\n const hasEntry = Object.keys(servers).some((k) => k === \"semantic-vault\" || k === \"semantic-sidekick\" || k === \"semantic-memory\");\n if (!hasEntry) {\n return {\n check: \"mcp_json_entry\",\n severity: \"warn\",\n summary: \".mcp.json present but no semantic-memory/sidekick/vault server entry\",\n fixable_via: \"mcp-reconcile\",\n };\n }\n return { check: \"mcp_json_entry\", severity: \"ok\", summary: \".mcp.json server entry present\" };\n}\n\nasync function checkHookRegistration(projectRoot: string): Promise<DriftFinding | null> {\n const settings = join(projectRoot, \".claude\", \"settings.json\");\n if (!existsSync(settings)) return null;\n try {\n const data = JSON.parse(await readFile(settings, \"utf-8\"));\n const hooks = data?.hooks ?? {};\n const required = [\"SessionStart\", \"UserPromptSubmit\", \"Stop\"];\n const missing: string[] = [];\n for (const event of required) {\n const arr = hooks[event];\n if (!Array.isArray(arr) || arr.length === 0) missing.push(event);\n }\n if (missing.length > 0) {\n return {\n check: \"hook_registration\",\n severity: \"warn\",\n summary: `missing hook registrations: ${missing.join(\", \")}`,\n detail: `Run \\`/relink\\` or reinstall the plugin to refresh hook registrations.`,\n };\n }\n return { check: \"hook_registration\", severity: \"ok\", summary: \"all 3 hook events registered\" };\n } catch (err: any) {\n return failOpenWarn(err);\n }\n}\n\nasync function checkIndexFreshness(projectRoot: string, vaultPath?: string): Promise<DriftFinding | null> {\n const vault = vaultPath ?? defaultVaultPath(projectRoot);\n if (!vault || !existsSync(vault)) return null;\n const indexDir = join(vault, \".semantic-sidekick-index\");\n const meta = join(indexDir, \"meta.json\");\n if (!existsSync(meta)) {\n return {\n check: \"index_freshness\",\n severity: \"warn\",\n summary: \"no index built yet\",\n detail: \"Run `reindex` to build the vector index.\",\n fixable_via: \"reindex\",\n };\n }\n try {\n const data = JSON.parse(await readFile(meta, \"utf-8\"));\n if (typeof data.indexedAt === \"string\") {\n return { check: \"index_freshness\", severity: \"ok\", summary: `last reindex ${data.indexedAt}` };\n }\n } catch {\n // ignore\n }\n return null;\n}\n\nasync function checkSessionStaleness(projectRoot: string): Promise<DriftFinding | null> {\n const sessionPath = join(projectRoot, \".claude\", \".semantic-memory\", \"session.json\");\n if (!existsSync(sessionPath)) return null;\n try {\n const state = JSON.parse(await readFile(sessionPath, \"utf-8\"));\n if (state?.closed_at) return null;\n const last = state?.last_activity_at;\n if (typeof last !== \"string\") return null;\n const ageMs = Date.now() - new Date(last).getTime();\n if (ageMs > 24 * 60 * 60 * 1000) {\n return {\n check: \"session_staleness\",\n severity: \"warn\",\n summary: `session ${state.id} stale (>${Math.round(ageMs / (60 * 60 * 1000))}h since activity)`,\n detail: `Either resume the session (run session_run / session_finish) or remove .claude/.semantic-memory/session.json to abandon it.`,\n };\n }\n return { check: \"session_staleness\", severity: \"ok\", summary: `session ${state.id} active` };\n } catch {\n return null;\n }\n}\n\nasync function checkLegacyStateFiles(projectRoot: string): Promise<DriftFinding | null> {\n // Detect v1.1-and-earlier state files at .claude/.sidekick-*. v1.2 moved these to\n // .claude/.semantic-memory/. The hook still reads them via fallback for backwards-\n // compat, but they should be migrated explicitly so the legacy paths stop existing.\n const legacy = [\n \".sidekick-mode\",\n \".sidekick-fingerprints.json\",\n \".sidekick-capture-pending.json\",\n ];\n const present: string[] = [];\n for (const name of legacy) {\n if (existsSync(join(projectRoot, \".claude\", name))) {\n present.push(name);\n }\n }\n if (present.length === 0) {\n return { check: \"legacy_state_files\", severity: \"ok\", summary: \"no legacy state files\" };\n }\n return {\n check: \"legacy_state_files\",\n severity: \"warn\",\n summary: `${present.length} legacy state file${present.length === 1 ? \"\" : \"s\"} at .claude/.sidekick-*`,\n detail:\n `Files: ${present.join(\", \")}. Run \\`bin/semantic-memory migrate-state\\` to move them under .claude/.semantic-memory/. ` +\n `These files are still being read for backwards-compat through v1.x; the legacy fallback is removed in v2.0.`,\n fixable_via: \"none\",\n };\n}\n\nasync function checkAgentsContract(projectRoot: string): Promise<DriftFinding | null> {\n const path = join(projectRoot, \"AGENTS.md\");\n if (!existsSync(path)) return null; // optional artifact\n try {\n const raw = await readFile(path, \"utf-8\");\n const begin = raw.indexOf(\"<!-- semantic-memory:begin contract -->\");\n const end = raw.indexOf(\"<!-- semantic-memory:end contract -->\");\n if (begin === -1 || end === -1) {\n return {\n check: \"agents_contract\",\n severity: \"warn\",\n summary: \"AGENTS.md exists but lacks managed-block markers\",\n detail: \"Either move custom content to a Local Notes section, or delete AGENTS.md and run `regenerate_contract` to bootstrap.\",\n fixable_via: \"regenerate-contract\",\n };\n }\n return { check: \"agents_contract\", severity: \"ok\", summary: \"AGENTS.md managed block intact\" };\n } catch {\n return null;\n }\n}\n\nasync function skillManifestChecks(projectRoot: string): Promise<Promise<DriftFinding | null>[]> {\n const home = homedir();\n const targets = [\n { agent: \"claude\", path: join(home, \".claude\", \"skills\") },\n { agent: \"codex\", path: join(home, \".codex\", \"skills\") },\n { agent: \"copilot\", path: join(home, \".copilot\", \"skills\") },\n { agent: \"pi\", path: join(home, \".pi\", \"agent\", \"skills\") },\n { agent: \"claude-local\", path: join(projectRoot, \".claude\", \"skills\") },\n ];\n const out: Promise<DriftFinding | null>[] = [];\n for (const t of targets) {\n out.push(checkOneSkillManifest(t.agent, t.path));\n }\n return out;\n}\n\nasync function checkOneSkillManifest(agentLabel: string, skillsRoot: string): Promise<DriftFinding | null> {\n const manifestPath = join(skillsRoot, \".semantic-memory-skill-manifest.json\");\n if (!existsSync(manifestPath)) return null; // not installed for this agent — fine\n try {\n const raw = await readFile(manifestPath, \"utf-8\");\n const manifest = JSON.parse(raw);\n if (typeof manifest?.installed_at !== \"string\") return null;\n const age = Date.now() - new Date(manifest.installed_at).getTime();\n // Manifest itself is the source of truth; deeper sha-recheck happens via slow tier\n return {\n check: `skill_manifest:${agentLabel}`,\n severity: \"ok\",\n summary: `${agentLabel} skills installed v${manifest.version} (${Math.round(age / (24 * 60 * 60 * 1000))}d ago)`,\n };\n } catch {\n return null;\n }\n}\n\nfunction defaultVaultPath(projectRoot: string): string {\n return join(projectRoot, \".claude\", \".vault\");\n}\n\n/**\n * Format a drift summary for the SessionStart hook. Returns the text to inject as\n * additionalContext, or an empty string when there's nothing to surface.\n */\nexport function formatDriftBanner(result: HealthcheckResult): string {\n const drift = filterToDrift(result);\n if (drift.length === 0) return \"\";\n const lines = [\n `<vault-drift count=\"${drift.length}\">`,\n `⚠️ semantic-memory: ${drift.length} drift issue${drift.length === 1 ? \"\" : \"s\"} detected`,\n ];\n for (const f of drift.slice(0, 5)) {\n const sev = f.severity === \"error\" ? \"✗\" : \"⚠\";\n lines.push(` ${sev} ${f.check}: ${f.summary}`);\n }\n if (drift.length > 5) lines.push(` …and ${drift.length - 5} more`);\n lines.push(`Run /healthcheck for details, or /healthcheck --fix to auto-fix safe items.`);\n lines.push(`</vault-drift>`);\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|