@theglitchking/semantic-memory 1.5.0 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,13 +6,13 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Single-plugin marketplace for semantic-memory — the unified memory-layer plugin. v1.1.0 absorbs brain's strengths: AGENTS.md contract artifact, hard-gated verification sessions, multi-agent skill bundler (codex/copilot/pi), and SessionStart drift detection. v1.3 adds confidence decay — age-aware retrieval ranking with verify_note to reset the clock. All backwards-compatible with v1.0.x via deprecation shims.",
9
- "version": "1.5.0"
9
+ "version": "1.5.2"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "semantic-memory",
14
14
  "description": "Unified memory-layer plugin for Claude Code. One MCP server, 44 tools across multi-corpus search/read/write/graph/lint/sessions/contract. v1.1.0 adds: AGENTS.md contract artifact, hard-gated verification sessions (session_start/run/finish/status), proposal-mode synthesize_note + synthesize_promote, multi-agent skill bundler (codex/copilot/pi via the new `skills` CLI), and SessionStart drift detection. v1.2 completes the hygiene line: `/healthcheck --fix` auto-remediation, `lint_vault({checks:['code_symbols']})` code-path drift, and a fresh-install CI smoke gate. v1.3 adds confidence decay: age-aware search ranking (0.5^(age/half-life), type-aware, floored, composes with load_priority), a `decay` block surfaced on decayed results, `verify_note` to reset the clock, `evergreen` opt-out, and `decay-config`/`decay-trace` CLI introspection. v1.3.1 adds local-only selection-logging telemetry (which note each search result actually got used), the `decay_candidates` lint, and a `selection-stats` CLI. v1.4.0 is the resident-expert bridge: a learned human→artifact lexicon (`manage_lexicon`) with propose-and-confirm alias capture, verbatim symptom capture + symptom-keyed indexing (terse symptom queries match the note), Tier-1 lexicon query expansion in the hook, archive path-class down-weight, injection hygiene (score-gate + dedup + top-3 + compact instructions), section-targeted `read_note`, and opt-in conditional tool registration. v1.5.0 is the expert-character layer: entity dossiers (`manage_dossier`) — per-component living notes (purpose / failure modes / knobs / incident log / current state) that accrete in place, with two-hop retrieval (utterance→entity→dossier injected first); usage-feedback ranking that boosts repeatedly-cited notes (capped, composes with decay/path-class) plus a `decoys` lint for retrieved-but-never-cited notes (surfaced, never auto-down-ranked); session paging (Stop drafts a durable session digest as a proposal, SessionStart pages in a curated digest instead of a broad sweep); and a speaker profile (`manage_profile`) that models how the human communicates — severity calibration, chronic omissions, verbosity, shorthand — injected at SessionStart. Backwards-compatible with v1.0.x — every previous tool name remains callable through deprecation shims (removed in v2.0.0).",
15
- "version": "1.5.0",
15
+ "version": "1.5.2",
16
16
  "author": {
17
17
  "name": "TheGlitchKing"
18
18
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "semantic-memory",
3
3
  "description": "Unified memory-layer plugin for Claude Code. One MCP server, multiple typed corpora (vault / code / plans / docs / research / project-map), unified cross-corpus knowledge graph, conditional tool registration, in-process drift detection, and human↔code translation. Rebrand of `semantic-sidekick` at v1.0.0 — see CHANGELOG for migration.",
4
- "version": "1.5.0",
4
+ "version": "1.5.2",
5
5
  "author": {
6
6
  "name": "TheGlitchKing",
7
7
  "email": "theglitchking@users.noreply.github.com"
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  All notable changes to semantic-memory (formerly semantic-sidekick) will be documented here.
4
4
 
5
+ ## [1.5.2] - 2026-10-24 — Docs index freshness (issue #29)
6
+
7
+ Patch release. In the standard two-server setup (`semantic-vault` → `.claude/.vault`, `semantic-sidekick` → `.documentation --read-only`) the docs index went stale after it was first built and the hooks never searched the docs. Fixes for every cause found, plus the dependency audit.
8
+
9
+ ### Fixed
10
+
11
+ - **Stale cached index is rebuilt at startup.** A cache whose `meta.totalChunks` no longer matches the notes on disk loaded as `"stale"` and was served forever; startup only reindexed when the cache failed to load. It now rebuilds in the background and serves the stale index meanwhile.
12
+ - **File watcher works under chokidar 5.** `watch("**/*.md")` is a literal path since chokidar 4 dropped globs, so no event ever fired and edits during a session were never reindexed. The watcher now watches the notes folder and filters (`node_modules`, `.semantic-sidekick-index`, `.git`, non-markdown).
13
+ - **One bad doc no longer fails the whole index.** `indexAll()` rejected if any file failed, so a single doc with invalid YAML frontmatter (e.g. an unquoted colon in `purpose:`) left the entire corpus unindexed. Invalid frontmatter now indexes the body with empty frontmatter; any other per-file error skips that file. Both are reported on stderr.
14
+ - **Hooks search every `semantic-*` notes path.** The prompt hook and the SessionStart search used only the first `.mcp.json` entry, normally an (often empty) vault. They now search every entry's `--notes` path in parallel and merge hits by score; with more than one corpus each hit is labelled with its server. Writes (log events, dossiers, session digests, speaker profile) stay on one path: the first entry without `--read-only`. `SIDEKICK_VAULT_PATH` still means a single path. Hook latency on a two-server project: ~670 ms → ~800 ms.
15
+ - **Capture prompts stay in their own session.** `capture-pending.json` is per project, so a cue typed in one session showed up in another concurrent session's Stop prompt, and any SessionStart or Stop wiped every session's items. Items now carry `session_id`; each session asks about and clears only its own. Items from before 1.5.2 go to whichever session reads them first; other sessions' items older than a week are pruned.
16
+ - **Watcher tests can fail again.** They treated their own timeout as "filesystem events not available" and passed while the watcher was dead.
17
+
18
+ ### Changed
19
+
20
+ - **Dependency audit:** 30 findings (1 critical, 19 high; 22 in production) → 3 (1 in production). `overrides` re-pinned to current patches (several 1.5.1 pins had become the vulnerable versions); in-range updates including `@modelcontextprotocol/sdk` 1.32.2, `@huggingface/transformers` 4.3.1, `onnxruntime-node` 1.30.0, `vitest` 4.1.12. Still open, breaking-only fixes: `zod` (every 3.x is affected; the fix is zod 4, which changes the MCP tool schemas) and dev-only `esbuild` (low).
21
+
22
+ ### Tests
23
+
24
+ - Full suite **440 green** (was 418); build and lint clean; golden retrieval eval unchanged (recall@1 86%, MRR 0.929). Each fix has a test that fails on 1.5.1.
25
+
26
+ ## [1.5.1] - 2026-07-12 — Dependency audit hardening (supply chain)
27
+
28
+ Patch release: clears the transitive `npm audit` findings surfaced after upgrading from the 1.2.x line, with **no runtime or API change**. Every advisory was unreachable in practice — this is a local **stdio** MCP server plus hook scripts processing the user's **own** files: the `hono`/`express-rate-limit`/`ip-address`/`qs`/`fast-uri` cluster is the MCP SDK's HTTP-server transport, which this plugin never instantiates (stdio only); `protobufjs`/`@xmldom/xmldom` are the local embedding runtime; `js-yaml`/`brace-expansion` parse your own notes/globs.
29
+
30
+ ### Changed
31
+
32
+ - **`overrides` block pins patched, SAME-MAJOR transitive versions** — `protobufjs 7.6.5`, `@protobufjs/utf8 1.1.2`, `@xmldom/xmldom 0.9.10`, `brace-expansion 5.0.7`, `qs 6.15.3`, `ip-address 10.2.0`, `express-rate-limit 8.5.2`, `hono 4.12.29`, `@hono/node-server 1.19.14`. No breaking bumps. `js-yaml` (3.15.0) and `fast-uri` (3.1.3) resolve to their patched in-range versions on a fresh install — no override needed.
33
+ - **Result: `npm audit --omit=dev` (the production tree consumers install) = 0 vulnerabilities.** The one remaining full-audit item (`esbuild`, low) is a dev-only tsup/vitest tool whose advisory is a *Windows dev server* file-read — it never ships in the package (`files` excludes it) and never runs in a consumer.
34
+
35
+ ### Notes for consumers
36
+
37
+ npm ignores a **dependency's** `overrides` in downstream installs — only the *root* project's overrides apply. So a project that gates CI on `npm audit` after installing this plugin should either run `npm audit fix` (all fixes are in-range, non-breaking, no `--force`) or mirror the `overrides` block above. `js-yaml` and `fast-uri` are deliberately **not** force-bumped to their next majors: doing so breaks `gray-matter` frontmatter parsing and `ajv`/MCP tool-input validation (empirically — forcing the majors failed 76 tests), and the DoS vectors require attacker-controlled input this plugin never accepts.
38
+
39
+ ### Tests
40
+
41
+ - Full suite **418 green** with the pinned overrides; `npm run build` clean; production audit 0.
42
+
5
43
  ## [1.5.0] - 2026-07-11 — Expert-character layer (v1.5.0: Phases 8–11)
6
44
 
7
45
  Where v1.4.0 taught the plugin *how this human talks about the system*, v1.5.0 gives it the other two traits of a resident expert: it organizes memory by **component** (not by file) and **learns from its own usage**. Tool surface 42 → 44 (`manage_dossier`, `manage_profile`; read-only unchanged at 21). Additive throughout; `usage_boost.enabled: false` reproduces byte-identical pre-v1.5 ranking.
@@ -124,8 +124,8 @@ var Embedder = class {
124
124
  max_length: 512,
125
125
  return_tensor: false
126
126
  });
127
- const inputIdsRaw = Array.from(encoded.input_ids.data ?? encoded.input_ids);
128
- const attentionMaskRaw = Array.from(encoded.attention_mask.data ?? encoded.attention_mask);
127
+ const inputIdsRaw = Array.from(encoded.input_ids);
128
+ const attentionMaskRaw = Array.from(encoded.attention_mask);
129
129
  const seqLen = inputIdsRaw.length;
130
130
  const inputIds = new this.ort.Tensor(
131
131
  "int64",
@@ -222,8 +222,8 @@ var Embedder = class {
222
222
  )
223
223
  );
224
224
  const tokenized = encodings.map((enc) => ({
225
- ids: Array.from(enc.input_ids.data ?? enc.input_ids),
226
- mask: Array.from(enc.attention_mask.data ?? enc.attention_mask)
225
+ ids: Array.from(enc.input_ids),
226
+ mask: Array.from(enc.attention_mask)
227
227
  }));
228
228
  const seqLen = Math.max(...tokenized.map((t) => t.ids.length));
229
229
  const flatIds = new BigInt64Array(n * seqLen);
@@ -648,4 +648,4 @@ export {
648
648
  VectorIndex,
649
649
  TextSearch
650
650
  };
651
- //# sourceMappingURL=chunk-25EIQWXL.js.map
651
+ //# sourceMappingURL=chunk-44BQDRXD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/embedder.ts","../src/core/graph.ts","../src/core/vector.ts","../src/core/search-text.ts"],"sourcesContent":["import { AutoTokenizer } from \"@huggingface/transformers\";\nimport { readFile, writeFile, mkdir, rename, unlink } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { existsSync, createWriteStream } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { Readable } from \"node:stream\";\nimport { pipeline as streamPipeline } from \"node:stream/promises\";\nimport { Worker } from \"node:worker_threads\";\nimport { fileURLToPath } from \"node:url\";\n\n// MiniLM-L6-v2 is the default: ~3 min to index 2,853 chunks on CPU vs ~16 min for nomic.\n// nomic-embed-text-v1.5 gives higher quality embeddings but is much slower on CPU.\nconst DEFAULT_MODEL = \"sentence-transformers/all-MiniLM-L6-v2\";\nconst CACHE_DIR = join(homedir(), \".semantic-sidekick\", \"models\");\n// Default to 1 worker (serial). Worker threads only help on memory-rich machines\n// (each worker loads its own ONNX session). On typical dev machines\n// with <4 GB free RAM, parallel workers cause swap thrashing and are slower.\n// Enable with --workers N when you have sufficient RAM.\nconst DEFAULT_WORKERS = 1;\n// batch=16 is optimal for MiniLM on CPU (short sequences, low padding waste).\n// For larger models (nomic 768d), batch=1 is faster due to padding overhead.\nconst DEFAULT_BATCH_SIZE = 16;\n// Quantized ONNX: faster on CPU but not all models have a quantized variant.\n// MiniLM fp32 is already fast enough; nomic benefits from quantized.\n// Falls back to fp32 automatically if quantized file is not available.\nconst DEFAULT_QUANTIZED = false;\n\n// Full-precision ONNX model subpaths\nconst ONNX_MODEL_PATHS: Record<string, string> = {\n \"nomic-ai/nomic-embed-text-v1.5\": \"onnx/model.onnx\",\n \"sentence-transformers/all-MiniLM-L6-v2\": \"onnx/model.onnx\",\n};\n\n// Quantized (int8) ONNX model subpaths — faster on CPU, ~same quality\nconst ONNX_QUANTIZED_MODEL_PATHS: Record<string, string> = {\n \"nomic-ai/nomic-embed-text-v1.5\": \"onnx/model_quantized.onnx\",\n \"sentence-transformers/all-MiniLM-L6-v2\": \"onnx/model_quantized.onnx\",\n};\n\ninterface OrtSession {\n run(feeds: Record<string, unknown>): Promise<Record<string, { data: Float32Array; dims: number[] }>>;\n inputNames: string[];\n outputNames: string[];\n}\n\ninterface OrtModule {\n InferenceSession: {\n create(path: string, options?: Record<string, unknown>): Promise<OrtSession>;\n };\n Tensor: new (type: string, data: ArrayLike<number | bigint>, dims: number[]) => unknown;\n}\n\ntype RuntimeLabel = \"native\" | \"wasm\";\n\nasync function resolveOnnxRuntime(): Promise<{ ort: OrtModule; label: RuntimeLabel }> {\n try {\n const ort = await import(\"onnxruntime-node\");\n return { ort: ort as unknown as OrtModule, label: \"native\" };\n } catch {\n const ort = await import(\"onnxruntime-web\");\n return { ort: ort as unknown as OrtModule, label: \"wasm\" };\n }\n}\n\n// Download to a process-unique temp file, then atomically rename onto the\n// final path. Two concurrent downloaders won't corrupt each other's partial\n// writes — the worst case is one wasted download (rename is last-writer-wins\n// but each rename installs a complete, valid file). Without this, multiple\n// processes calling Embedder.init() against the same cache (e.g. parallel\n// vitest workers, or multiple semantic-sidekick servers on a fresh install) race\n// on a single shared writeStream and produce a corrupt ONNX file that fails\n// Protobuf parsing on load.\nasync function downloadFile(url: string, destPath: string): Promise<void> {\n const response = await fetch(url);\n if (!response.ok) throw new Error(`Download failed (${response.status}): ${url}`);\n if (!response.body) throw new Error(`No response body: ${url}`);\n const tempPath = `${destPath}.${process.pid}.${Date.now()}.tmp`;\n try {\n const fileStream = createWriteStream(tempPath);\n await streamPipeline(Readable.fromWeb(response.body as never), fileStream);\n await rename(tempPath, destPath);\n } catch (err) {\n try { await unlink(tempPath); } catch { /* temp may not exist */ }\n throw err;\n }\n}\n\nexport class Embedder {\n private model: string;\n private session: OrtSession | null = null;\n private tokenizer: Awaited<ReturnType<typeof AutoTokenizer.from_pretrained>> | null = null;\n private ort: OrtModule | null = null;\n private dimensions = 0;\n private runtimeLabel: RuntimeLabel = \"wasm\";\n private initialized = false;\n private numWorkers: number;\n private batchSize: number;\n private quantized: boolean;\n private modelPath = \"\";\n\n constructor(\n model: string = DEFAULT_MODEL,\n numWorkers: number = DEFAULT_WORKERS,\n batchSize: number = DEFAULT_BATCH_SIZE,\n quantized: boolean = DEFAULT_QUANTIZED\n ) {\n this.model = model;\n this.numWorkers = numWorkers;\n this.batchSize = batchSize;\n this.quantized = quantized;\n }\n\n async init(): Promise<void> {\n if (this.initialized) return;\n\n const modelDir = join(CACHE_DIR, this.model.replace(/\\//g, \"--\"));\n await mkdir(modelDir, { recursive: true });\n\n // Resolve ONNX runtime (native C++ or WASM fallback)\n const { ort, label } = await resolveOnnxRuntime();\n this.ort = ort;\n this.runtimeLabel = label;\n\n // Download ONNX model if not cached. Tries quantized first if requested,\n // falls back to fp32 if the quantized file is not available for this model.\n let useQuantized = this.quantized;\n const modelFileName = useQuantized ? \"model_quantized.onnx\" : \"model.onnx\";\n this.modelPath = join(modelDir, modelFileName);\n const modelPath = this.modelPath;\n if (!existsSync(modelPath)) {\n const pathMap = useQuantized ? ONNX_QUANTIZED_MODEL_PATHS : ONNX_MODEL_PATHS;\n const onnxSubpath = pathMap[this.model] ?? (useQuantized ? \"onnx/model_quantized.onnx\" : \"onnx/model.onnx\");\n const url = `https://huggingface.co/${this.model}/resolve/main/${onnxSubpath}`;\n process.stderr.write(`Downloading ONNX model: ${this.model} (${useQuantized ? \"quantized\" : \"fp32\"})...\\n`);\n try {\n await downloadFile(url, modelPath);\n } catch (err: any) {\n if (useQuantized && err?.message?.includes(\"404\")) {\n // Quantized not available for this model — fall back to fp32\n process.stderr.write(`Quantized model not available, falling back to fp32\\n`);\n useQuantized = false;\n this.modelPath = join(modelDir, \"model.onnx\");\n const fp32Subpath = ONNX_MODEL_PATHS[this.model] ?? \"onnx/model.onnx\";\n const fp32Url = `https://huggingface.co/${this.model}/resolve/main/${fp32Subpath}`;\n process.stderr.write(`Downloading ONNX model: ${this.model} (fp32)...\\n`);\n await downloadFile(fp32Url, this.modelPath);\n } else {\n throw err;\n }\n }\n process.stderr.write(`Model downloaded to ${modelDir}\\n`);\n }\n // Load tokenizer (uses HF transformers tokenizer infrastructure)\n this.tokenizer = await AutoTokenizer.from_pretrained(this.model, {\n cache_dir: CACHE_DIR,\n });\n\n // Create ONNX inference session (use this.modelPath — may have been updated by fallback)\n this.session = await ort.InferenceSession.create(this.modelPath, {\n executionProviders: [label === \"native\" ? \"cpu\" : \"wasm\"],\n });\n\n // Determine dimensions from a test embedding\n const test = await this.embed(\"test\");\n this.dimensions = test.length;\n this.initialized = true;\n\n const modelShort = this.model.split(\"/\").pop() ?? this.model;\n process.stderr.write(`Embedder ready (${modelShort}, ${label}, ${this.dimensions}d, batch=${this.batchSize})\\n`);\n }\n\n async embed(text: string): Promise<Float32Array> {\n if (!this.session || !this.tokenizer || !this.ort)\n throw new Error(\"Embedder not initialized. Call init() first.\");\n\n // Tokenize\n const encoded = await this.tokenizer(text, {\n padding: true,\n truncation: true,\n max_length: 512,\n return_tensor: false,\n });\n\n const inputIdsRaw: number[] = Array.from(encoded.input_ids);\n const attentionMaskRaw: number[] = Array.from(encoded.attention_mask);\n const seqLen = inputIdsRaw.length;\n\n // Build ONNX input tensors (most models expect int64)\n const inputIds = new this.ort.Tensor(\n \"int64\",\n BigInt64Array.from(inputIdsRaw.map(BigInt)),\n [1, seqLen]\n );\n const attentionMask = new this.ort.Tensor(\n \"int64\",\n BigInt64Array.from(attentionMaskRaw.map(BigInt)),\n [1, seqLen]\n );\n\n const feeds: Record<string, unknown> = { input_ids: inputIds, attention_mask: attentionMask };\n\n // Some models need token_type_ids\n if (this.session.inputNames.includes(\"token_type_ids\")) {\n feeds.token_type_ids = new this.ort.Tensor(\n \"int64\",\n new BigInt64Array(seqLen),\n [1, seqLen]\n );\n }\n\n // Run inference\n const output = await this.session.run(feeds);\n const outputTensor = output[this.session.outputNames[0]];\n const hiddenSize = outputTensor.dims[outputTensor.dims.length - 1];\n\n // Mean pooling with attention mask + L2 normalization\n return this.meanPoolAndNormalize(outputTensor.data, attentionMaskRaw, seqLen, hiddenSize);\n }\n\n private meanPoolAndNormalize(\n embeddings: Float32Array,\n attentionMask: number[],\n seqLen: number,\n hiddenSize: number\n ): Float32Array {\n const result = new Float32Array(hiddenSize);\n let maskSum = 0;\n\n for (let t = 0; t < seqLen; t++) {\n const mask = attentionMask[t];\n maskSum += mask;\n const offset = t * hiddenSize;\n for (let d = 0; d < hiddenSize; d++) {\n result[d] += embeddings[offset + d] * mask;\n }\n }\n\n if (maskSum > 0) {\n for (let d = 0; d < hiddenSize; d++) result[d] /= maskSum;\n }\n\n // L2 normalize\n let norm = 0;\n for (let d = 0; d < hiddenSize; d++) norm += result[d] * result[d];\n norm = Math.sqrt(norm);\n if (norm > 0) {\n for (let d = 0; d < hiddenSize; d++) result[d] /= norm;\n }\n\n return result;\n }\n\n // Mean pool + L2 normalize the output of a batched ONNX forward pass.\n // outputData: flat Float32Array of shape [batchSize, seqLen, hiddenSize]\n // maskData: flat number[] of shape [batchSize, seqLen]\n private meanPoolAndNormalizeMany(\n outputData: Float32Array,\n maskData: number[],\n batchSize: number,\n seqLen: number,\n hiddenSize: number\n ): Float32Array[] {\n const results: Float32Array[] = [];\n for (let b = 0; b < batchSize; b++) {\n const result = new Float32Array(hiddenSize);\n let maskSum = 0;\n for (let t = 0; t < seqLen; t++) {\n const mask = maskData[b * seqLen + t];\n maskSum += mask;\n const offset = (b * seqLen + t) * hiddenSize;\n for (let d = 0; d < hiddenSize; d++) {\n result[d] += outputData[offset + d] * mask;\n }\n }\n if (maskSum > 0) {\n for (let d = 0; d < hiddenSize; d++) result[d] /= maskSum;\n }\n // L2 normalize\n let norm = 0;\n for (let d = 0; d < hiddenSize; d++) norm += result[d] * result[d];\n norm = Math.sqrt(norm);\n if (norm > 0) {\n for (let d = 0; d < hiddenSize; d++) result[d] /= norm;\n }\n results.push(result);\n }\n return results;\n }\n\n // Embed a sub-batch of texts (length <= batchSize) in a single ONNX forward pass.\n //\n // Tokenizes each text individually (well-defined output format for all HF versions),\n // then manually pads to the longest sequence in the batch and builds a [n, seqLen]\n // tensor for one ONNX call. The speedup is from batching the ONNX inference —\n // individual tokenization is negligible (<1ms each).\n private async embedSubBatch(texts: string[]): Promise<Float32Array[]> {\n if (!this.session || !this.tokenizer || !this.ort)\n throw new Error(\"Embedder not initialized. Call init() first.\");\n\n const n = texts.length;\n\n // Tokenize each text individually — avoids ambiguous batch tokenizer output format\n const encodings = await Promise.all(\n texts.map((text) =>\n this.tokenizer!(text, {\n padding: false,\n truncation: true,\n max_length: 512,\n return_tensor: false,\n })\n )\n );\n\n // Extract flat token arrays and find max sequence length for this batch\n const tokenized = encodings.map((enc) => ({\n ids: Array.from(enc.input_ids) as number[],\n mask: Array.from(enc.attention_mask) as number[],\n }));\n const seqLen = Math.max(...tokenized.map((t) => t.ids.length));\n\n // Build flat padded tensors [n * seqLen] — pad with 0 (PAD token, zero attention)\n const flatIds = new BigInt64Array(n * seqLen);\n const flatMask = new BigInt64Array(n * seqLen);\n const flatMaskNums = new Array<number>(n * seqLen).fill(0);\n\n for (let i = 0; i < n; i++) {\n const { ids, mask } = tokenized[i];\n for (let j = 0; j < ids.length; j++) {\n flatIds[i * seqLen + j] = BigInt(ids[j]);\n flatMask[i * seqLen + j] = BigInt(mask[j]);\n flatMaskNums[i * seqLen + j] = mask[j];\n }\n // Positions beyond ids.length remain 0 (padding)\n }\n\n // Build batched ONNX tensors [n, seqLen]\n const inputIds = new this.ort.Tensor(\"int64\", flatIds, [n, seqLen]);\n const attentionMask = new this.ort.Tensor(\"int64\", flatMask, [n, seqLen]);\n\n const feeds: Record<string, unknown> = { input_ids: inputIds, attention_mask: attentionMask };\n\n if (this.session.inputNames.includes(\"token_type_ids\")) {\n feeds.token_type_ids = new this.ort.Tensor(\n \"int64\",\n new BigInt64Array(n * seqLen),\n [n, seqLen]\n );\n }\n\n // Single forward pass → output shape [n, seqLen, hiddenSize]\n const output = await this.session.run(feeds);\n const outputTensor = output[this.session.outputNames[0]];\n const hiddenSize = outputTensor.dims[outputTensor.dims.length - 1];\n\n return this.meanPoolAndNormalizeMany(\n outputTensor.data,\n flatMaskNums,\n n,\n seqLen,\n hiddenSize\n );\n }\n\n async embedBatch(\n texts: string[],\n onProgress?: (embedded: number, total: number, subBatch?: Float32Array[]) => Promise<void> | void\n ): Promise<Float32Array[]> {\n // Workers path (disabled by default; kept for --workers N users)\n if (this.numWorkers > 1 && texts.length >= this.numWorkers * 2) {\n return this.embedBatchParallel(texts, onProgress);\n }\n\n // True batched inference: slice into sub-batches and run one ONNX call each\n const results: Float32Array[] = [];\n for (let i = 0; i < texts.length; i += this.batchSize) {\n const subBatch = texts.slice(i, i + this.batchSize);\n const embeddings = await this.embedSubBatch(subBatch);\n results.push(...embeddings);\n await onProgress?.(Math.min(i + subBatch.length, texts.length), texts.length, embeddings);\n }\n return results;\n }\n\n private async embedBatchParallel(\n texts: string[],\n onProgress?: (embedded: number, total: number, subBatch?: Float32Array[]) => Promise<void> | void\n ): Promise<Float32Array[]> {\n // Resolve worker script path. With tsup splitting, embedder code may land\n // in a top-level chunk (dist/chunk-*.js) rather than dist/core/index.js,\n // so we try two locations: adjacent to this chunk, and dist/core/ relative\n // to the package root (two levels up from a dist/ chunk file).\n const thisDir = dirname(fileURLToPath(import.meta.url));\n let workerPath = join(thisDir, \"embed-worker.js\");\n if (!existsSync(workerPath)) {\n // Chunk file at dist/ level — worker is in dist/core/\n workerPath = join(thisDir, \"core\", \"embed-worker.js\");\n }\n\n if (!existsSync(workerPath)) {\n // Fallback to serial batched if worker script not found\n process.stderr.write(\"Worker script not found, falling back to batched embedding\\n\");\n return this.embedBatch(texts, onProgress);\n }\n\n // Split texts into chunks for each worker\n const chunkSize = Math.ceil(texts.length / this.numWorkers);\n const chunks: { texts: string[]; startIndex: number }[] = [];\n for (let i = 0; i < texts.length; i += chunkSize) {\n chunks.push({ texts: texts.slice(i, i + chunkSize), startIndex: i });\n }\n\n const allResults = new Array<Float32Array>(texts.length);\n let totalDone = 0;\n\n const workerPromises = chunks.map((chunk) => {\n return new Promise<void>((resolve, reject) => {\n const worker = new Worker(workerPath, {\n workerData: {\n modelPath: this.modelPath,\n modelName: this.model,\n cacheDir: CACHE_DIR,\n runtimeLabel: this.runtimeLabel,\n batchSize: this.batchSize,\n },\n });\n\n worker.on(\"message\", (msg: any) => {\n if (msg.type === \"ready\") {\n worker.postMessage({ type: \"embed\", texts: chunk.texts, startIndex: chunk.startIndex });\n } else if (msg.type === \"progress\") {\n totalDone++;\n onProgress?.(totalDone, texts.length);\n } else if (msg.type === \"result\") {\n for (let i = 0; i < msg.embeddings.length; i++) {\n allResults[chunk.startIndex + i] = new Float32Array(msg.embeddings[i]);\n }\n worker.terminate();\n resolve();\n } else if (msg.type === \"error\") {\n worker.terminate();\n reject(new Error(msg.error));\n }\n });\n\n worker.on(\"error\", (err) => {\n worker.terminate();\n reject(err);\n });\n });\n });\n\n await Promise.all(workerPromises);\n return allResults;\n }\n\n getDimensions(): number {\n return this.dimensions;\n }\n\n getModel(): string {\n return this.model;\n }\n\n getRuntime(): RuntimeLabel {\n return this.runtimeLabel;\n }\n\n async saveEmbeddings(\n embeddings: Map<string, Float32Array>,\n indexPath: string\n ): Promise<void> {\n const entries: Array<{ key: string; data: number[] }> = [];\n for (const [key, vec] of embeddings) {\n entries.push({ key, data: Array.from(vec) });\n }\n await writeFile(join(indexPath, \"embeddings.json\"), JSON.stringify(entries));\n }\n\n async loadEmbeddings(\n indexPath: string\n ): Promise<Map<string, Float32Array>> {\n const filePath = join(indexPath, \"embeddings.json\");\n if (!existsSync(filePath)) return new Map();\n\n const raw = await readFile(filePath, \"utf-8\");\n const entries: Array<{ key: string; data: number[] }> = JSON.parse(raw);\n const map = new Map<string, Float32Array>();\n for (const entry of entries) {\n map.set(entry.key, new Float32Array(entry.data));\n }\n return map;\n }\n}\n","import Graph from \"graphology\";\nimport { bfsFromNode } from \"graphology-traversal\";\nimport { bidirectional } from \"graphology-shortest-path\";\nimport type { IndexedDocument, GraphNode, GraphEdge, GraphStats } from \"./types.js\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n/**\n * Tags shared by more than this many docs are too generic to be meaningful\n * graph edges (e.g. \"api\", \"testing\"). Only specific/rare tags create edges.\n * Docs without frontmatter are unaffected — their inline #tags still participate\n * but only if those tags are specific enough to pass this threshold.\n */\nconst TAG_EDGE_MAX_DOCS = 15;\n\nexport class GraphBuilder {\n private graph: Graph;\n\n constructor() {\n this.graph = new Graph({ type: \"directed\", multi: false });\n }\n\n buildFromDocuments(documents: IndexedDocument[]): void {\n this.graph.clear();\n\n // Add nodes — store optional metadata fields when present\n for (const doc of documents) {\n this.graph.addNode(doc.path, {\n title: doc.title,\n tags: doc.tags,\n ...(doc.status !== undefined && { status: doc.status }),\n ...(doc.loadPriority !== undefined && { loadPriority: doc.loadPriority }),\n });\n }\n\n const pathLookup = new Map<string, string>();\n for (const doc of documents) {\n const nameNoExt = doc.path.replace(/\\.md$/, \"\");\n const basename = nameNoExt.split(\"/\").pop()!;\n pathLookup.set(basename.toLowerCase(), doc.path);\n pathLookup.set(nameNoExt.toLowerCase(), doc.path);\n }\n\n // Add wikilink edges (weight 1.0)\n for (const doc of documents) {\n for (const link of doc.wikilinks) {\n const target = pathLookup.get(link.toLowerCase());\n if (target && target !== doc.path && !this.graph.hasEdge(doc.path, target)) {\n this.graph.addEdge(doc.path, target, { type: \"wikilink\", weight: 1.0 });\n }\n }\n }\n\n // Add related_docs edges (weight 1.0) — only present when frontmatter has related_docs\n // Falls back gracefully: docs without this field are simply skipped\n for (const doc of documents) {\n if (!doc.relatedDocs?.length) continue;\n for (const ref of doc.relatedDocs) {\n // Try full normalized path first, then basename — same strategy as wikilinks\n const normalized = ref.replace(/\\.md$/, \"\").toLowerCase();\n const base = normalized.split(\"/\").pop()!;\n const target = pathLookup.get(normalized) ?? pathLookup.get(base);\n if (target && target !== doc.path && !this.graph.hasEdge(doc.path, target)) {\n this.graph.addEdge(doc.path, target, { type: \"related\", weight: 1.0 });\n }\n }\n }\n\n // Add tag-based edges (weight 0.5) — capped at TAG_EDGE_MAX_DOCS per tag.\n // Generic tags shared by many docs (e.g. \"api\", \"testing\") create noise;\n // only specific tags (e.g. \"qdrant\", \"celery\") create meaningful connections.\n const tagToNotes = new Map<string, string[]>();\n for (const doc of documents) {\n for (const tag of doc.tags) {\n const notes = tagToNotes.get(tag) ?? [];\n notes.push(doc.path);\n tagToNotes.set(tag, notes);\n }\n }\n\n for (const [, notes] of tagToNotes) {\n if (notes.length >= TAG_EDGE_MAX_DOCS) continue; // too generic — skip\n for (let i = 0; i < notes.length; i++) {\n for (let j = i + 1; j < notes.length; j++) {\n if (!this.graph.hasEdge(notes[i], notes[j])) {\n this.graph.addEdge(notes[i], notes[j], { type: \"tag\", weight: 0.5 });\n }\n if (!this.graph.hasEdge(notes[j], notes[i])) {\n this.graph.addEdge(notes[j], notes[i], { type: \"tag\", weight: 0.5 });\n }\n }\n }\n }\n }\n\n backlinks(notePath: string): GraphNode[] {\n if (!this.graph.hasNode(notePath)) return [];\n return this.graph.inNeighbors(notePath).map((n) => this.nodeToGraphNode(n));\n }\n\n forwardlinks(notePath: string): GraphNode[] {\n if (!this.graph.hasNode(notePath)) return [];\n return this.graph.outNeighbors(notePath).map((n) => this.nodeToGraphNode(n));\n }\n\n findPath(from: string, to: string): string[] | null {\n if (!this.graph.hasNode(from) || !this.graph.hasNode(to)) return null;\n const path = bidirectional(this.graph, from, to);\n return path;\n }\n\n searchGraph(concept: string, maxDepth = 2, limit = 20): GraphNode[] {\n const lc = concept.toLowerCase();\n\n const directMatches = new Set<string>(\n this.graph.nodes().filter((n) => {\n const attrs = this.graph.getNodeAttributes(n);\n const title = typeof attrs.title === \"string\" ? attrs.title : String(attrs.title ?? \"\");\n return (\n n.toLowerCase().includes(lc) ||\n title.toLowerCase().includes(lc) ||\n (attrs.tags as string[] | undefined)?.some((t) => t.toLowerCase().includes(lc))\n );\n })\n );\n\n const visited = new Set<string>();\n for (const start of directMatches) {\n bfsFromNode(this.graph, start, (_node, _attr, depth) => {\n if (visited.size >= limit * 3) return true; // gather 3x limit before ranking\n visited.add(_node);\n return depth >= maxDepth;\n });\n }\n\n // Sort: direct matches first, then by loadPriority desc, then by degree desc\n // deprecated/archived nodes sort to the end\n const ranked = [...visited].sort((a, b) => {\n const aAttrs = this.graph.getNodeAttributes(a);\n const bAttrs = this.graph.getNodeAttributes(b);\n const aDeprecated = aAttrs.status === \"deprecated\" || aAttrs.status === \"archived\" ? 1 : 0;\n const bDeprecated = bAttrs.status === \"deprecated\" || bAttrs.status === \"archived\" ? 1 : 0;\n if (aDeprecated !== bDeprecated) return aDeprecated - bDeprecated;\n const aDirect = directMatches.has(a) ? 0 : 1;\n const bDirect = directMatches.has(b) ? 0 : 1;\n if (aDirect !== bDirect) return aDirect - bDirect;\n const aPriority = (aAttrs.loadPriority as number | undefined) ?? 5;\n const bPriority = (bAttrs.loadPriority as number | undefined) ?? 5;\n if (aPriority !== bPriority) return bPriority - aPriority;\n return this.graph.degree(b) - this.graph.degree(a);\n });\n\n return ranked.slice(0, limit).map((n) => this.nodeToGraphNode(n));\n }\n\n statistics(): GraphStats {\n const nodes = this.graph.order;\n const edges = this.graph.size;\n const orphans = this.graph.nodes().filter(\n (n) => this.graph.degree(n) === 0\n );\n\n const connections = this.graph.nodes().map((n) => ({\n path: n,\n connections: this.graph.degree(n),\n }));\n connections.sort((a, b) => b.connections - a.connections);\n\n const maxPossibleEdges = nodes * (nodes - 1);\n const density = maxPossibleEdges > 0 ? edges / maxPossibleEdges : 0;\n\n return {\n totalNodes: nodes,\n totalEdges: edges,\n orphanCount: orphans.length,\n mostConnected: connections.slice(0, 10),\n density,\n };\n }\n\n private nodeToGraphNode(nodePath: string): GraphNode {\n const attrs = this.graph.getNodeAttributes(nodePath);\n return {\n path: nodePath,\n title: typeof attrs.title === \"string\" ? attrs.title : nodePath,\n tags: (attrs.tags as string[]) || [],\n linkCount: this.graph.outDegree(nodePath),\n backlinkCount: this.graph.inDegree(nodePath),\n ...(attrs.loadPriority !== undefined && { loadPriority: attrs.loadPriority as number }),\n ...(attrs.status !== undefined && { status: attrs.status as string }),\n };\n }\n\n async save(indexPath: string): Promise<void> {\n const data = this.graph.export();\n await writeFile(join(indexPath, \"graph.json\"), JSON.stringify(data));\n }\n\n async load(indexPath: string): Promise<boolean> {\n const filePath = join(indexPath, \"graph.json\");\n if (!existsSync(filePath)) return false;\n\n const raw = await readFile(filePath, \"utf-8\");\n const data = JSON.parse(raw);\n this.graph.import(data);\n return true;\n }\n\n getGraph(): Graph {\n return this.graph;\n }\n}\n","import hnswlib from \"hnswlib-node\";\nconst { HierarchicalNSW } = hnswlib;\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport type { SearchResult } from \"./types.js\";\n\ninterface ChunkMeta {\n docPath: string;\n chunkIndex: number;\n text: string;\n}\n\nexport class VectorIndex {\n private index: InstanceType<typeof HierarchicalNSW> | null = null;\n private dimensions: number;\n private chunkMeta: ChunkMeta[] = [];\n\n constructor(dimensions: number) {\n this.dimensions = dimensions;\n }\n\n build(\n embeddings: Float32Array[],\n meta: ChunkMeta[]\n ): void {\n if (embeddings.length === 0) {\n this.index = null;\n this.chunkMeta = [];\n return;\n }\n\n this.index = new HierarchicalNSW(\"cosine\", this.dimensions);\n this.index.initIndex(embeddings.length);\n\n for (let i = 0; i < embeddings.length; i++) {\n this.index.addPoint(Array.from(embeddings[i]), i);\n }\n\n this.chunkMeta = meta;\n }\n\n search(queryEmbedding: Float32Array, k: number = 10): SearchResult[] {\n if (!this.index || this.chunkMeta.length === 0) return [];\n\n const numResults = Math.min(k, this.chunkMeta.length);\n const result = this.index.searchKnn(Array.from(queryEmbedding), numResults);\n\n const seen = new Set<string>();\n const results: SearchResult[] = [];\n\n for (let i = 0; i < result.neighbors.length; i++) {\n const idx = result.neighbors[i];\n const meta = this.chunkMeta[idx];\n if (!meta || seen.has(meta.docPath)) continue;\n seen.add(meta.docPath);\n\n results.push({\n path: meta.docPath,\n title: meta.docPath,\n score: 1 - result.distances[i],\n snippet: meta.text.slice(0, 200),\n matchedChunk: meta.text,\n });\n }\n\n return results;\n }\n\n async save(indexPath: string): Promise<void> {\n if (!this.index) return;\n\n this.index.writeIndexSync(join(indexPath, \"hnsw.bin\"));\n await writeFile(\n join(indexPath, \"hnsw-meta.json\"),\n JSON.stringify(this.chunkMeta)\n );\n }\n\n async load(indexPath: string): Promise<boolean> {\n const hnswPath = join(indexPath, \"hnsw.bin\");\n const metaPath = join(indexPath, \"hnsw-meta.json\");\n\n if (!existsSync(hnswPath) || !existsSync(metaPath)) return false;\n\n const raw = await readFile(metaPath, \"utf-8\");\n this.chunkMeta = JSON.parse(raw);\n\n this.index = new HierarchicalNSW(\"cosine\", this.dimensions);\n this.index.initIndex(this.chunkMeta.length);\n this.index.readIndexSync(hnswPath);\n\n return true;\n }\n\n getChunkMeta(): ChunkMeta[] {\n return this.chunkMeta;\n }\n}\n","import type { IndexedDocument, SearchResult, SearchTextOptions } from \"./types.js\";\nimport { minimatch } from \"minimatch\";\n\nexport class TextSearch {\n private documents: IndexedDocument[] = [];\n\n setDocuments(documents: IndexedDocument[]): void {\n this.documents = documents;\n }\n\n search(options: SearchTextOptions): SearchResult[] {\n const { pattern, regex, caseSensitive, pathGlob, tagFilter, limit = 20 } = options;\n\n let matcher: (text: string) => { matched: boolean; index: number };\n\n if (regex) {\n const flags = caseSensitive ? \"g\" : \"gi\";\n const re = new RegExp(pattern, flags);\n matcher = (text) => {\n re.lastIndex = 0;\n const m = re.exec(text);\n return { matched: !!m, index: m?.index ?? -1 };\n };\n } else {\n const needle = caseSensitive ? pattern : pattern.toLowerCase();\n matcher = (text) => {\n const haystack = caseSensitive ? text : text.toLowerCase();\n const idx = haystack.indexOf(needle);\n return { matched: idx >= 0, index: idx };\n };\n }\n\n const results: SearchResult[] = [];\n\n for (const doc of this.documents) {\n // Path filter\n if (pathGlob && !minimatch(doc.path, pathGlob)) continue;\n\n // Tag filter\n if (tagFilter?.length) {\n const hasTag = tagFilter.some((t) => doc.tags.includes(t));\n if (!hasTag) continue;\n }\n\n const { matched, index } = matcher(doc.content);\n if (!matched) continue;\n\n const snippetStart = Math.max(0, index - 80);\n const snippetEnd = Math.min(doc.content.length, index + 120);\n const snippet = doc.content.slice(snippetStart, snippetEnd).trim();\n\n results.push({\n path: doc.path,\n title: doc.title,\n score: 1.0,\n snippet: (snippetStart > 0 ? \"...\" : \"\") + snippet + (snippetEnd < doc.content.length ? \"...\" : \"\"),\n });\n\n if (results.length >= limit) break;\n }\n\n return results;\n }\n}\n"],"mappings":";AAAA,SAAS,qBAAqB;AAC9B,SAAS,UAAU,WAAW,OAAO,QAAQ,cAAc;AAC3D,SAAS,MAAM,eAAe;AAC9B,SAAS,YAAY,yBAAyB;AAC9C,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,YAAY,sBAAsB;AAC3C,SAAS,cAAc;AACvB,SAAS,qBAAqB;AAI9B,IAAM,gBAAgB;AACtB,IAAM,YAAY,KAAK,QAAQ,GAAG,sBAAsB,QAAQ;AAKhE,IAAM,kBAAkB;AAGxB,IAAM,qBAAqB;AAI3B,IAAM,oBAAoB;AAG1B,IAAM,mBAA2C;AAAA,EAC/C,kCAAkC;AAAA,EAClC,0CAA0C;AAC5C;AAGA,IAAM,6BAAqD;AAAA,EACzD,kCAAkC;AAAA,EAClC,0CAA0C;AAC5C;AAiBA,eAAe,qBAAuE;AACpF,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,kBAAkB;AAC3C,WAAO,EAAE,KAAkC,OAAO,SAAS;AAAA,EAC7D,QAAQ;AACN,UAAM,MAAM,MAAM,OAAO,iBAAiB;AAC1C,WAAO,EAAE,KAAkC,OAAO,OAAO;AAAA,EAC3D;AACF;AAUA,eAAe,aAAa,KAAa,UAAiC;AACxE,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,CAAC,SAAS,GAAI,OAAM,IAAI,MAAM,oBAAoB,SAAS,MAAM,MAAM,GAAG,EAAE;AAChF,MAAI,CAAC,SAAS,KAAM,OAAM,IAAI,MAAM,qBAAqB,GAAG,EAAE;AAC9D,QAAM,WAAW,GAAG,QAAQ,IAAI,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC;AACzD,MAAI;AACF,UAAM,aAAa,kBAAkB,QAAQ;AAC7C,UAAM,eAAe,SAAS,QAAQ,SAAS,IAAa,GAAG,UAAU;AACzE,UAAM,OAAO,UAAU,QAAQ;AAAA,EACjC,SAAS,KAAK;AACZ,QAAI;AAAE,YAAM,OAAO,QAAQ;AAAA,IAAG,QAAQ;AAAA,IAA2B;AACjE,UAAM;AAAA,EACR;AACF;AAEO,IAAM,WAAN,MAAe;AAAA,EACZ;AAAA,EACA,UAA6B;AAAA,EAC7B,YAA8E;AAAA,EAC9E,MAAwB;AAAA,EACxB,aAAa;AAAA,EACb,eAA6B;AAAA,EAC7B,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EAEpB,YACE,QAAgB,eAChB,aAAqB,iBACrB,YAAoB,oBACpB,YAAqB,mBACrB;AACA,SAAK,QAAQ;AACb,SAAK,aAAa;AAClB,SAAK,YAAY;AACjB,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,OAAsB;AAC1B,QAAI,KAAK,YAAa;AAEtB,UAAM,WAAW,KAAK,WAAW,KAAK,MAAM,QAAQ,OAAO,IAAI,CAAC;AAChE,UAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAGzC,UAAM,EAAE,KAAK,MAAM,IAAI,MAAM,mBAAmB;AAChD,SAAK,MAAM;AACX,SAAK,eAAe;AAIpB,QAAI,eAAe,KAAK;AACxB,UAAM,gBAAgB,eAAe,yBAAyB;AAC9D,SAAK,YAAY,KAAK,UAAU,aAAa;AAC7C,UAAM,YAAY,KAAK;AACvB,QAAI,CAAC,WAAW,SAAS,GAAG;AAC1B,YAAM,UAAU,eAAe,6BAA6B;AAC5D,YAAM,cAAc,QAAQ,KAAK,KAAK,MAAM,eAAe,8BAA8B;AACzF,YAAM,MAAM,0BAA0B,KAAK,KAAK,iBAAiB,WAAW;AAC5E,cAAQ,OAAO,MAAM,2BAA2B,KAAK,KAAK,KAAK,eAAe,cAAc,MAAM;AAAA,CAAQ;AAC1G,UAAI;AACF,cAAM,aAAa,KAAK,SAAS;AAAA,MACnC,SAAS,KAAU;AACjB,YAAI,gBAAgB,KAAK,SAAS,SAAS,KAAK,GAAG;AAEjD,kBAAQ,OAAO,MAAM;AAAA,CAAuD;AAC5E,yBAAe;AACf,eAAK,YAAY,KAAK,UAAU,YAAY;AAC5C,gBAAM,cAAc,iBAAiB,KAAK,KAAK,KAAK;AACpD,gBAAM,UAAU,0BAA0B,KAAK,KAAK,iBAAiB,WAAW;AAChF,kBAAQ,OAAO,MAAM,2BAA2B,KAAK,KAAK;AAAA,CAAc;AACxE,gBAAM,aAAa,SAAS,KAAK,SAAS;AAAA,QAC5C,OAAO;AACL,gBAAM;AAAA,QACR;AAAA,MACF;AACA,cAAQ,OAAO,MAAM,uBAAuB,QAAQ;AAAA,CAAI;AAAA,IAC1D;AAEA,SAAK,YAAY,MAAM,cAAc,gBAAgB,KAAK,OAAO;AAAA,MAC/D,WAAW;AAAA,IACb,CAAC;AAGD,SAAK,UAAU,MAAM,IAAI,iBAAiB,OAAO,KAAK,WAAW;AAAA,MAC/D,oBAAoB,CAAC,UAAU,WAAW,QAAQ,MAAM;AAAA,IAC1D,CAAC;AAGD,UAAM,OAAO,MAAM,KAAK,MAAM,MAAM;AACpC,SAAK,aAAa,KAAK;AACvB,SAAK,cAAc;AAEnB,UAAM,aAAa,KAAK,MAAM,MAAM,GAAG,EAAE,IAAI,KAAK,KAAK;AACvD,YAAQ,OAAO,MAAM,mBAAmB,UAAU,KAAK,KAAK,KAAK,KAAK,UAAU,YAAY,KAAK,SAAS;AAAA,CAAK;AAAA,EACjH;AAAA,EAEA,MAAM,MAAM,MAAqC;AAC/C,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,aAAa,CAAC,KAAK;AAC5C,YAAM,IAAI,MAAM,8CAA8C;AAGhE,UAAM,UAAU,MAAM,KAAK,UAAU,MAAM;AAAA,MACzC,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,eAAe;AAAA,IACjB,CAAC;AAED,UAAM,cAAwB,MAAM,KAAK,QAAQ,SAAS;AAC1D,UAAM,mBAA6B,MAAM,KAAK,QAAQ,cAAc;AACpE,UAAM,SAAS,YAAY;AAG3B,UAAM,WAAW,IAAI,KAAK,IAAI;AAAA,MAC5B;AAAA,MACA,cAAc,KAAK,YAAY,IAAI,MAAM,CAAC;AAAA,MAC1C,CAAC,GAAG,MAAM;AAAA,IACZ;AACA,UAAM,gBAAgB,IAAI,KAAK,IAAI;AAAA,MACjC;AAAA,MACA,cAAc,KAAK,iBAAiB,IAAI,MAAM,CAAC;AAAA,MAC/C,CAAC,GAAG,MAAM;AAAA,IACZ;AAEA,UAAM,QAAiC,EAAE,WAAW,UAAU,gBAAgB,cAAc;AAG5F,QAAI,KAAK,QAAQ,WAAW,SAAS,gBAAgB,GAAG;AACtD,YAAM,iBAAiB,IAAI,KAAK,IAAI;AAAA,QAClC;AAAA,QACA,IAAI,cAAc,MAAM;AAAA,QACxB,CAAC,GAAG,MAAM;AAAA,MACZ;AAAA,IACF;AAGA,UAAM,SAAS,MAAM,KAAK,QAAQ,IAAI,KAAK;AAC3C,UAAM,eAAe,OAAO,KAAK,QAAQ,YAAY,CAAC,CAAC;AACvD,UAAM,aAAa,aAAa,KAAK,aAAa,KAAK,SAAS,CAAC;AAGjE,WAAO,KAAK,qBAAqB,aAAa,MAAM,kBAAkB,QAAQ,UAAU;AAAA,EAC1F;AAAA,EAEQ,qBACN,YACA,eACA,QACA,YACc;AACd,UAAM,SAAS,IAAI,aAAa,UAAU;AAC1C,QAAI,UAAU;AAEd,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,YAAM,OAAO,cAAc,CAAC;AAC5B,iBAAW;AACX,YAAM,SAAS,IAAI;AACnB,eAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,eAAO,CAAC,KAAK,WAAW,SAAS,CAAC,IAAI;AAAA,MACxC;AAAA,IACF;AAEA,QAAI,UAAU,GAAG;AACf,eAAS,IAAI,GAAG,IAAI,YAAY,IAAK,QAAO,CAAC,KAAK;AAAA,IACpD;AAGA,QAAI,OAAO;AACX,aAAS,IAAI,GAAG,IAAI,YAAY,IAAK,SAAQ,OAAO,CAAC,IAAI,OAAO,CAAC;AACjE,WAAO,KAAK,KAAK,IAAI;AACrB,QAAI,OAAO,GAAG;AACZ,eAAS,IAAI,GAAG,IAAI,YAAY,IAAK,QAAO,CAAC,KAAK;AAAA,IACpD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,yBACN,YACA,UACA,WACA,QACA,YACgB;AAChB,UAAM,UAA0B,CAAC;AACjC,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,YAAM,SAAS,IAAI,aAAa,UAAU;AAC1C,UAAI,UAAU;AACd,eAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAC/B,cAAM,OAAO,SAAS,IAAI,SAAS,CAAC;AACpC,mBAAW;AACX,cAAM,UAAU,IAAI,SAAS,KAAK;AAClC,iBAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,iBAAO,CAAC,KAAK,WAAW,SAAS,CAAC,IAAI;AAAA,QACxC;AAAA,MACF;AACA,UAAI,UAAU,GAAG;AACf,iBAAS,IAAI,GAAG,IAAI,YAAY,IAAK,QAAO,CAAC,KAAK;AAAA,MACpD;AAEA,UAAI,OAAO;AACX,eAAS,IAAI,GAAG,IAAI,YAAY,IAAK,SAAQ,OAAO,CAAC,IAAI,OAAO,CAAC;AACjE,aAAO,KAAK,KAAK,IAAI;AACrB,UAAI,OAAO,GAAG;AACZ,iBAAS,IAAI,GAAG,IAAI,YAAY,IAAK,QAAO,CAAC,KAAK;AAAA,MACpD;AACA,cAAQ,KAAK,MAAM;AAAA,IACrB;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,cAAc,OAA0C;AACpE,QAAI,CAAC,KAAK,WAAW,CAAC,KAAK,aAAa,CAAC,KAAK;AAC5C,YAAM,IAAI,MAAM,8CAA8C;AAEhE,UAAM,IAAI,MAAM;AAGhB,UAAM,YAAY,MAAM,QAAQ;AAAA,MAC9B,MAAM;AAAA,QAAI,CAAC,SACT,KAAK,UAAW,MAAM;AAAA,UACpB,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,eAAe;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF;AAGA,UAAM,YAAY,UAAU,IAAI,CAAC,SAAS;AAAA,MACxC,KAAK,MAAM,KAAK,IAAI,SAAS;AAAA,MAC7B,MAAM,MAAM,KAAK,IAAI,cAAc;AAAA,IACrC,EAAE;AACF,UAAM,SAAS,KAAK,IAAI,GAAG,UAAU,IAAI,CAAC,MAAM,EAAE,IAAI,MAAM,CAAC;AAG7D,UAAM,UAAU,IAAI,cAAc,IAAI,MAAM;AAC5C,UAAM,WAAW,IAAI,cAAc,IAAI,MAAM;AAC7C,UAAM,eAAe,IAAI,MAAc,IAAI,MAAM,EAAE,KAAK,CAAC;AAEzD,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,EAAE,KAAK,KAAK,IAAI,UAAU,CAAC;AACjC,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,gBAAQ,IAAI,SAAS,CAAC,IAAI,OAAO,IAAI,CAAC,CAAC;AACvC,iBAAS,IAAI,SAAS,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC;AACzC,qBAAa,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;AAAA,MACvC;AAAA,IAEF;AAGA,UAAM,WAAW,IAAI,KAAK,IAAI,OAAO,SAAS,SAAS,CAAC,GAAG,MAAM,CAAC;AAClE,UAAM,gBAAgB,IAAI,KAAK,IAAI,OAAO,SAAS,UAAU,CAAC,GAAG,MAAM,CAAC;AAExE,UAAM,QAAiC,EAAE,WAAW,UAAU,gBAAgB,cAAc;AAE5F,QAAI,KAAK,QAAQ,WAAW,SAAS,gBAAgB,GAAG;AACtD,YAAM,iBAAiB,IAAI,KAAK,IAAI;AAAA,QAClC;AAAA,QACA,IAAI,cAAc,IAAI,MAAM;AAAA,QAC5B,CAAC,GAAG,MAAM;AAAA,MACZ;AAAA,IACF;AAGA,UAAM,SAAS,MAAM,KAAK,QAAQ,IAAI,KAAK;AAC3C,UAAM,eAAe,OAAO,KAAK,QAAQ,YAAY,CAAC,CAAC;AACvD,UAAM,aAAa,aAAa,KAAK,aAAa,KAAK,SAAS,CAAC;AAEjE,WAAO,KAAK;AAAA,MACV,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAM,WACJ,OACA,YACyB;AAEzB,QAAI,KAAK,aAAa,KAAK,MAAM,UAAU,KAAK,aAAa,GAAG;AAC9D,aAAO,KAAK,mBAAmB,OAAO,UAAU;AAAA,IAClD;AAGA,UAAM,UAA0B,CAAC;AACjC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,KAAK,WAAW;AACrD,YAAM,WAAW,MAAM,MAAM,GAAG,IAAI,KAAK,SAAS;AAClD,YAAM,aAAa,MAAM,KAAK,cAAc,QAAQ;AACpD,cAAQ,KAAK,GAAG,UAAU;AAC1B,YAAM,aAAa,KAAK,IAAI,IAAI,SAAS,QAAQ,MAAM,MAAM,GAAG,MAAM,QAAQ,UAAU;AAAA,IAC1F;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,mBACZ,OACA,YACyB;AAKzB,UAAM,UAAU,QAAQ,cAAc,YAAY,GAAG,CAAC;AACtD,QAAI,aAAa,KAAK,SAAS,iBAAiB;AAChD,QAAI,CAAC,WAAW,UAAU,GAAG;AAE3B,mBAAa,KAAK,SAAS,QAAQ,iBAAiB;AAAA,IACtD;AAEA,QAAI,CAAC,WAAW,UAAU,GAAG;AAE3B,cAAQ,OAAO,MAAM,8DAA8D;AACnF,aAAO,KAAK,WAAW,OAAO,UAAU;AAAA,IAC1C;AAGA,UAAM,YAAY,KAAK,KAAK,MAAM,SAAS,KAAK,UAAU;AAC1D,UAAM,SAAoD,CAAC;AAC3D,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,aAAO,KAAK,EAAE,OAAO,MAAM,MAAM,GAAG,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;AAAA,IACrE;AAEA,UAAM,aAAa,IAAI,MAAoB,MAAM,MAAM;AACvD,QAAI,YAAY;AAEhB,UAAM,iBAAiB,OAAO,IAAI,CAAC,UAAU;AAC3C,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,SAAS,IAAI,OAAO,YAAY;AAAA,UACpC,YAAY;AAAA,YACV,WAAW,KAAK;AAAA,YAChB,WAAW,KAAK;AAAA,YAChB,UAAU;AAAA,YACV,cAAc,KAAK;AAAA,YACnB,WAAW,KAAK;AAAA,UAClB;AAAA,QACF,CAAC;AAED,eAAO,GAAG,WAAW,CAAC,QAAa;AACjC,cAAI,IAAI,SAAS,SAAS;AACxB,mBAAO,YAAY,EAAE,MAAM,SAAS,OAAO,MAAM,OAAO,YAAY,MAAM,WAAW,CAAC;AAAA,UACxF,WAAW,IAAI,SAAS,YAAY;AAClC;AACA,yBAAa,WAAW,MAAM,MAAM;AAAA,UACtC,WAAW,IAAI,SAAS,UAAU;AAChC,qBAAS,IAAI,GAAG,IAAI,IAAI,WAAW,QAAQ,KAAK;AAC9C,yBAAW,MAAM,aAAa,CAAC,IAAI,IAAI,aAAa,IAAI,WAAW,CAAC,CAAC;AAAA,YACvE;AACA,mBAAO,UAAU;AACjB,oBAAQ;AAAA,UACV,WAAW,IAAI,SAAS,SAAS;AAC/B,mBAAO,UAAU;AACjB,mBAAO,IAAI,MAAM,IAAI,KAAK,CAAC;AAAA,UAC7B;AAAA,QACF,CAAC;AAED,eAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,iBAAO,UAAU;AACjB,iBAAO,GAAG;AAAA,QACZ,CAAC;AAAA,MACH,CAAC;AAAA,IACH,CAAC;AAED,UAAM,QAAQ,IAAI,cAAc;AAChC,WAAO;AAAA,EACT;AAAA,EAEA,gBAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAmB;AACjB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,aAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,eACJ,YACA,WACe;AACf,UAAM,UAAkD,CAAC;AACzD,eAAW,CAAC,KAAK,GAAG,KAAK,YAAY;AACnC,cAAQ,KAAK,EAAE,KAAK,MAAM,MAAM,KAAK,GAAG,EAAE,CAAC;AAAA,IAC7C;AACA,UAAM,UAAU,KAAK,WAAW,iBAAiB,GAAG,KAAK,UAAU,OAAO,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAM,eACJ,WACoC;AACpC,UAAM,WAAW,KAAK,WAAW,iBAAiB;AAClD,QAAI,CAAC,WAAW,QAAQ,EAAG,QAAO,oBAAI,IAAI;AAE1C,UAAM,MAAM,MAAM,SAAS,UAAU,OAAO;AAC5C,UAAM,UAAkD,KAAK,MAAM,GAAG;AACtE,UAAM,MAAM,oBAAI,IAA0B;AAC1C,eAAW,SAAS,SAAS;AAC3B,UAAI,IAAI,MAAM,KAAK,IAAI,aAAa,MAAM,IAAI,CAAC;AAAA,IACjD;AACA,WAAO;AAAA,EACT;AACF;;;AC5eA,OAAO,WAAW;AAClB,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAE9B,SAAS,YAAAA,WAAU,aAAAC,kBAAiB;AACpC,SAAS,QAAAC,aAAY;AACrB,SAAS,cAAAC,mBAAkB;AAQ3B,IAAM,oBAAoB;AAEnB,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EAER,cAAc;AACZ,SAAK,QAAQ,IAAI,MAAM,EAAE,MAAM,YAAY,OAAO,MAAM,CAAC;AAAA,EAC3D;AAAA,EAEA,mBAAmB,WAAoC;AACrD,SAAK,MAAM,MAAM;AAGjB,eAAW,OAAO,WAAW;AAC3B,WAAK,MAAM,QAAQ,IAAI,MAAM;AAAA,QAC3B,OAAO,IAAI;AAAA,QACX,MAAM,IAAI;AAAA,QACV,GAAI,IAAI,WAAW,UAAa,EAAE,QAAQ,IAAI,OAAO;AAAA,QACrD,GAAI,IAAI,iBAAiB,UAAa,EAAE,cAAc,IAAI,aAAa;AAAA,MACzE,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,oBAAI,IAAoB;AAC3C,eAAW,OAAO,WAAW;AAC3B,YAAM,YAAY,IAAI,KAAK,QAAQ,SAAS,EAAE;AAC9C,YAAM,WAAW,UAAU,MAAM,GAAG,EAAE,IAAI;AAC1C,iBAAW,IAAI,SAAS,YAAY,GAAG,IAAI,IAAI;AAC/C,iBAAW,IAAI,UAAU,YAAY,GAAG,IAAI,IAAI;AAAA,IAClD;AAGA,eAAW,OAAO,WAAW;AAC3B,iBAAW,QAAQ,IAAI,WAAW;AAChC,cAAM,SAAS,WAAW,IAAI,KAAK,YAAY,CAAC;AAChD,YAAI,UAAU,WAAW,IAAI,QAAQ,CAAC,KAAK,MAAM,QAAQ,IAAI,MAAM,MAAM,GAAG;AAC1E,eAAK,MAAM,QAAQ,IAAI,MAAM,QAAQ,EAAE,MAAM,YAAY,QAAQ,EAAI,CAAC;AAAA,QACxE;AAAA,MACF;AAAA,IACF;AAIA,eAAW,OAAO,WAAW;AAC3B,UAAI,CAAC,IAAI,aAAa,OAAQ;AAC9B,iBAAW,OAAO,IAAI,aAAa;AAEjC,cAAM,aAAa,IAAI,QAAQ,SAAS,EAAE,EAAE,YAAY;AACxD,cAAM,OAAO,WAAW,MAAM,GAAG,EAAE,IAAI;AACvC,cAAM,SAAS,WAAW,IAAI,UAAU,KAAK,WAAW,IAAI,IAAI;AAChE,YAAI,UAAU,WAAW,IAAI,QAAQ,CAAC,KAAK,MAAM,QAAQ,IAAI,MAAM,MAAM,GAAG;AAC1E,eAAK,MAAM,QAAQ,IAAI,MAAM,QAAQ,EAAE,MAAM,WAAW,QAAQ,EAAI,CAAC;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAKA,UAAM,aAAa,oBAAI,IAAsB;AAC7C,eAAW,OAAO,WAAW;AAC3B,iBAAW,OAAO,IAAI,MAAM;AAC1B,cAAM,QAAQ,WAAW,IAAI,GAAG,KAAK,CAAC;AACtC,cAAM,KAAK,IAAI,IAAI;AACnB,mBAAW,IAAI,KAAK,KAAK;AAAA,MAC3B;AAAA,IACF;AAEA,eAAW,CAAC,EAAE,KAAK,KAAK,YAAY;AAClC,UAAI,MAAM,UAAU,kBAAmB;AACvC,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,iBAAS,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzC,cAAI,CAAC,KAAK,MAAM,QAAQ,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG;AAC3C,iBAAK,MAAM,QAAQ,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,MAAM,OAAO,QAAQ,IAAI,CAAC;AAAA,UACrE;AACA,cAAI,CAAC,KAAK,MAAM,QAAQ,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG;AAC3C,iBAAK,MAAM,QAAQ,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,EAAE,MAAM,OAAO,QAAQ,IAAI,CAAC;AAAA,UACrE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,UAA+B;AACvC,QAAI,CAAC,KAAK,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AAC3C,WAAO,KAAK,MAAM,YAAY,QAAQ,EAAE,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC;AAAA,EAC5E;AAAA,EAEA,aAAa,UAA+B;AAC1C,QAAI,CAAC,KAAK,MAAM,QAAQ,QAAQ,EAAG,QAAO,CAAC;AAC3C,WAAO,KAAK,MAAM,aAAa,QAAQ,EAAE,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC;AAAA,EAC7E;AAAA,EAEA,SAAS,MAAc,IAA6B;AAClD,QAAI,CAAC,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,KAAK,MAAM,QAAQ,EAAE,EAAG,QAAO;AACjE,UAAM,OAAO,cAAc,KAAK,OAAO,MAAM,EAAE;AAC/C,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,SAAiB,WAAW,GAAG,QAAQ,IAAiB;AAClE,UAAM,KAAK,QAAQ,YAAY;AAE/B,UAAM,gBAAgB,IAAI;AAAA,MACxB,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM;AAC/B,cAAM,QAAQ,KAAK,MAAM,kBAAkB,CAAC;AAC5C,cAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,OAAO,MAAM,SAAS,EAAE;AACtF,eACE,EAAE,YAAY,EAAE,SAAS,EAAE,KAC3B,MAAM,YAAY,EAAE,SAAS,EAAE,KAC9B,MAAM,MAA+B,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;AAAA,MAElF,CAAC;AAAA,IACH;AAEA,UAAM,UAAU,oBAAI,IAAY;AAChC,eAAW,SAAS,eAAe;AACjC,kBAAY,KAAK,OAAO,OAAO,CAAC,OAAO,OAAO,UAAU;AACtD,YAAI,QAAQ,QAAQ,QAAQ,EAAG,QAAO;AACtC,gBAAQ,IAAI,KAAK;AACjB,eAAO,SAAS;AAAA,MAClB,CAAC;AAAA,IACH;AAIA,UAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM;AACzC,YAAM,SAAS,KAAK,MAAM,kBAAkB,CAAC;AAC7C,YAAM,SAAS,KAAK,MAAM,kBAAkB,CAAC;AAC7C,YAAM,cAAc,OAAO,WAAW,gBAAgB,OAAO,WAAW,aAAa,IAAI;AACzF,YAAM,cAAc,OAAO,WAAW,gBAAgB,OAAO,WAAW,aAAa,IAAI;AACzF,UAAI,gBAAgB,YAAa,QAAO,cAAc;AACtD,YAAM,UAAU,cAAc,IAAI,CAAC,IAAI,IAAI;AAC3C,YAAM,UAAU,cAAc,IAAI,CAAC,IAAI,IAAI;AAC3C,UAAI,YAAY,QAAS,QAAO,UAAU;AAC1C,YAAM,YAAa,OAAO,gBAAuC;AACjE,YAAM,YAAa,OAAO,gBAAuC;AACjE,UAAI,cAAc,UAAW,QAAO,YAAY;AAChD,aAAO,KAAK,MAAM,OAAO,CAAC,IAAI,KAAK,MAAM,OAAO,CAAC;AAAA,IACnD,CAAC;AAED,WAAO,OAAO,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,CAAC;AAAA,EAClE;AAAA,EAEA,aAAyB;AACvB,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,QAAQ,KAAK,MAAM;AACzB,UAAM,UAAU,KAAK,MAAM,MAAM,EAAE;AAAA,MACjC,CAAC,MAAM,KAAK,MAAM,OAAO,CAAC,MAAM;AAAA,IAClC;AAEA,UAAM,cAAc,KAAK,MAAM,MAAM,EAAE,IAAI,CAAC,OAAO;AAAA,MACjD,MAAM;AAAA,MACN,aAAa,KAAK,MAAM,OAAO,CAAC;AAAA,IAClC,EAAE;AACF,gBAAY,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW;AAExD,UAAM,mBAAmB,SAAS,QAAQ;AAC1C,UAAM,UAAU,mBAAmB,IAAI,QAAQ,mBAAmB;AAElE,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,aAAa,QAAQ;AAAA,MACrB,eAAe,YAAY,MAAM,GAAG,EAAE;AAAA,MACtC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,gBAAgB,UAA6B;AACnD,UAAM,QAAQ,KAAK,MAAM,kBAAkB,QAAQ;AACnD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ;AAAA,MACvD,MAAO,MAAM,QAAqB,CAAC;AAAA,MACnC,WAAW,KAAK,MAAM,UAAU,QAAQ;AAAA,MACxC,eAAe,KAAK,MAAM,SAAS,QAAQ;AAAA,MAC3C,GAAI,MAAM,iBAAiB,UAAa,EAAE,cAAc,MAAM,aAAuB;AAAA,MACrF,GAAI,MAAM,WAAW,UAAa,EAAE,QAAQ,MAAM,OAAiB;AAAA,IACrE;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,WAAkC;AAC3C,UAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,UAAMF,WAAUC,MAAK,WAAW,YAAY,GAAG,KAAK,UAAU,IAAI,CAAC;AAAA,EACrE;AAAA,EAEA,MAAM,KAAK,WAAqC;AAC9C,UAAM,WAAWA,MAAK,WAAW,YAAY;AAC7C,QAAI,CAACC,YAAW,QAAQ,EAAG,QAAO;AAElC,UAAM,MAAM,MAAMH,UAAS,UAAU,OAAO;AAC5C,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,SAAK,MAAM,OAAO,IAAI;AACtB,WAAO;AAAA,EACT;AAAA,EAEA,WAAkB;AAChB,WAAO,KAAK;AAAA,EACd;AACF;;;ACpNA,OAAO,aAAa;AAEpB,SAAS,YAAAI,WAAU,aAAAC,kBAAiB;AACpC,SAAS,QAAAC,aAAY;AACrB,SAAS,cAAAC,mBAAkB;AAH3B,IAAM,EAAE,gBAAgB,IAAI;AAYrB,IAAM,cAAN,MAAkB;AAAA,EACf,QAAqD;AAAA,EACrD;AAAA,EACA,YAAyB,CAAC;AAAA,EAElC,YAAY,YAAoB;AAC9B,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MACE,YACA,MACM;AACN,QAAI,WAAW,WAAW,GAAG;AAC3B,WAAK,QAAQ;AACb,WAAK,YAAY,CAAC;AAClB;AAAA,IACF;AAEA,SAAK,QAAQ,IAAI,gBAAgB,UAAU,KAAK,UAAU;AAC1D,SAAK,MAAM,UAAU,WAAW,MAAM;AAEtC,aAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,WAAK,MAAM,SAAS,MAAM,KAAK,WAAW,CAAC,CAAC,GAAG,CAAC;AAAA,IAClD;AAEA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,OAAO,gBAA8B,IAAY,IAAoB;AACnE,QAAI,CAAC,KAAK,SAAS,KAAK,UAAU,WAAW,EAAG,QAAO,CAAC;AAExD,UAAM,aAAa,KAAK,IAAI,GAAG,KAAK,UAAU,MAAM;AACpD,UAAM,SAAS,KAAK,MAAM,UAAU,MAAM,KAAK,cAAc,GAAG,UAAU;AAE1E,UAAM,OAAO,oBAAI,IAAY;AAC7B,UAAM,UAA0B,CAAC;AAEjC,aAAS,IAAI,GAAG,IAAI,OAAO,UAAU,QAAQ,KAAK;AAChD,YAAM,MAAM,OAAO,UAAU,CAAC;AAC9B,YAAM,OAAO,KAAK,UAAU,GAAG;AAC/B,UAAI,CAAC,QAAQ,KAAK,IAAI,KAAK,OAAO,EAAG;AACrC,WAAK,IAAI,KAAK,OAAO;AAErB,cAAQ,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,OAAO,KAAK;AAAA,QACZ,OAAO,IAAI,OAAO,UAAU,CAAC;AAAA,QAC7B,SAAS,KAAK,KAAK,MAAM,GAAG,GAAG;AAAA,QAC/B,cAAc,KAAK;AAAA,MACrB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,WAAkC;AAC3C,QAAI,CAAC,KAAK,MAAO;AAEjB,SAAK,MAAM,eAAeD,MAAK,WAAW,UAAU,CAAC;AACrD,UAAMD;AAAA,MACJC,MAAK,WAAW,gBAAgB;AAAA,MAChC,KAAK,UAAU,KAAK,SAAS;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,MAAM,KAAK,WAAqC;AAC9C,UAAM,WAAWA,MAAK,WAAW,UAAU;AAC3C,UAAM,WAAWA,MAAK,WAAW,gBAAgB;AAEjD,QAAI,CAACC,YAAW,QAAQ,KAAK,CAACA,YAAW,QAAQ,EAAG,QAAO;AAE3D,UAAM,MAAM,MAAMH,UAAS,UAAU,OAAO;AAC5C,SAAK,YAAY,KAAK,MAAM,GAAG;AAE/B,SAAK,QAAQ,IAAI,gBAAgB,UAAU,KAAK,UAAU;AAC1D,SAAK,MAAM,UAAU,KAAK,UAAU,MAAM;AAC1C,SAAK,MAAM,cAAc,QAAQ;AAEjC,WAAO;AAAA,EACT;AAAA,EAEA,eAA4B;AAC1B,WAAO,KAAK;AAAA,EACd;AACF;;;ACjGA,SAAS,iBAAiB;AAEnB,IAAM,aAAN,MAAiB;AAAA,EACd,YAA+B,CAAC;AAAA,EAExC,aAAa,WAAoC;AAC/C,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,OAAO,SAA4C;AACjD,UAAM,EAAE,SAAS,OAAO,eAAe,UAAU,WAAW,QAAQ,GAAG,IAAI;AAE3E,QAAI;AAEJ,QAAI,OAAO;AACT,YAAM,QAAQ,gBAAgB,MAAM;AACpC,YAAM,KAAK,IAAI,OAAO,SAAS,KAAK;AACpC,gBAAU,CAAC,SAAS;AAClB,WAAG,YAAY;AACf,cAAM,IAAI,GAAG,KAAK,IAAI;AACtB,eAAO,EAAE,SAAS,CAAC,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG;AAAA,MAC/C;AAAA,IACF,OAAO;AACL,YAAM,SAAS,gBAAgB,UAAU,QAAQ,YAAY;AAC7D,gBAAU,CAAC,SAAS;AAClB,cAAM,WAAW,gBAAgB,OAAO,KAAK,YAAY;AACzD,cAAM,MAAM,SAAS,QAAQ,MAAM;AACnC,eAAO,EAAE,SAAS,OAAO,GAAG,OAAO,IAAI;AAAA,MACzC;AAAA,IACF;AAEA,UAAM,UAA0B,CAAC;AAEjC,eAAW,OAAO,KAAK,WAAW;AAEhC,UAAI,YAAY,CAAC,UAAU,IAAI,MAAM,QAAQ,EAAG;AAGhD,UAAI,WAAW,QAAQ;AACrB,cAAM,SAAS,UAAU,KAAK,CAAC,MAAM,IAAI,KAAK,SAAS,CAAC,CAAC;AACzD,YAAI,CAAC,OAAQ;AAAA,MACf;AAEA,YAAM,EAAE,SAAS,MAAM,IAAI,QAAQ,IAAI,OAAO;AAC9C,UAAI,CAAC,QAAS;AAEd,YAAM,eAAe,KAAK,IAAI,GAAG,QAAQ,EAAE;AAC3C,YAAM,aAAa,KAAK,IAAI,IAAI,QAAQ,QAAQ,QAAQ,GAAG;AAC3D,YAAM,UAAU,IAAI,QAAQ,MAAM,cAAc,UAAU,EAAE,KAAK;AAEjE,cAAQ,KAAK;AAAA,QACX,MAAM,IAAI;AAAA,QACV,OAAO,IAAI;AAAA,QACX,OAAO;AAAA,QACP,UAAU,eAAe,IAAI,QAAQ,MAAM,WAAW,aAAa,IAAI,QAAQ,SAAS,QAAQ;AAAA,MAClG,CAAC;AAED,UAAI,QAAQ,UAAU,MAAO;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AACF;","names":["readFile","writeFile","join","existsSync","readFile","writeFile","join","existsSync"]}
@@ -9,6 +9,15 @@ import { existsSync } from "fs";
9
9
  import { basename, join } from "path";
10
10
  var DOCS_CACHE_FILE = "docs.cache.json";
11
11
  var CHUNK_TARGET_CHARS = 2e3;
12
+ function parseFrontmatter(raw, file) {
13
+ try {
14
+ return matter(raw);
15
+ } catch (err) {
16
+ process.stderr.write(`Invalid frontmatter in ${file}, indexing body only: ${err?.message?.split("\n")[0] ?? err}
17
+ `);
18
+ return { data: {}, content: raw.replace(/^---\r?\n[\s\S]*?\r?\n---[^\S\r\n]*(\r?\n|$)/, "") };
19
+ }
20
+ }
12
21
  var Indexer = class {
13
22
  notesPath;
14
23
  processor;
@@ -19,9 +28,15 @@ var Indexer = class {
19
28
  async indexAll() {
20
29
  const files = await glob("**/*.md", { cwd: this.notesPath, follow: true });
21
30
  const docs = await Promise.all(
22
- files.map((file) => this.indexFile(join(this.notesPath, file), file))
31
+ files.map(
32
+ (file) => this.indexFile(join(this.notesPath, file), file).catch((err) => {
33
+ process.stderr.write(`Skipping ${file}: ${err?.message ?? err}
34
+ `);
35
+ return null;
36
+ })
37
+ )
23
38
  );
24
- return docs;
39
+ return docs.filter((d) => d !== null);
25
40
  }
26
41
  /**
27
42
  * Load parsed-doc cache from disk. Returns null if missing or unreadable.
@@ -51,7 +66,7 @@ var Indexer = class {
51
66
  readFile(absolutePath, "utf-8"),
52
67
  stat(absolutePath)
53
68
  ]);
54
- const { data: frontmatter, content } = matter(raw);
69
+ const { data: frontmatter, content } = parseFrontmatter(raw, relativePath);
55
70
  const tree = this.processor.parse(content);
56
71
  const wikilinks = this.extractWikilinks(tree);
57
72
  const tags = this.extractTags(content, frontmatter);
@@ -168,4 +183,4 @@ var Indexer = class {
168
183
  export {
169
184
  Indexer
170
185
  };
171
- //# sourceMappingURL=chunk-V45NJIHI.js.map
186
+ //# sourceMappingURL=chunk-IGHCVAAL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/indexer.ts"],"sourcesContent":["import { unified } from \"unified\";\nimport remarkParse from \"remark-parse\";\nimport remarkWikiLink from \"remark-wiki-link\";\nimport matter from \"gray-matter\";\nimport { glob } from \"glob\";\nimport { readFile, writeFile, stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { basename, join, relative } from \"node:path\";\nimport type { IndexedDocument } from \"./types.js\";\n\nconst DOCS_CACHE_FILE = \"docs.cache.json\";\n\nconst CHUNK_TARGET_CHARS = 2000; // ~512 tokens\n\n// Invalid YAML (e.g. an unquoted colon in a value) keeps the body searchable with empty frontmatter\nfunction parseFrontmatter(raw: string, file: string): { data: Record<string, unknown>; content: string } {\n try {\n return matter(raw);\n } catch (err: any) {\n process.stderr.write(`Invalid frontmatter in ${file}, indexing body only: ${err?.message?.split(\"\\n\")[0] ?? err}\\n`);\n return { data: {}, content: raw.replace(/^---\\r?\\n[\\s\\S]*?\\r?\\n---[^\\S\\r\\n]*(\\r?\\n|$)/, \"\") };\n }\n}\n\nexport class Indexer {\n private notesPath: string;\n private processor: any;\n\n constructor(notesPath: string) {\n this.notesPath = notesPath;\n this.processor = unified().use(remarkParse).use(remarkWikiLink);\n }\n\n async indexAll(): Promise<IndexedDocument[]> {\n // follow: true descends into symlinked directories. Without this, users\n // who symlink an external folder into their vault (a common pattern for\n // sharing notes across projects) get silently empty indexing for the\n // symlinked content. glob does cycle detection via visited inodes so\n // circular symlinks are handled safely.\n const files = await glob(\"**/*.md\", { cwd: this.notesPath, follow: true });\n // One unreadable file must not take the whole corpus down with it\n const docs = await Promise.all(\n files.map((file) =>\n this.indexFile(join(this.notesPath, file), file).catch((err) => {\n process.stderr.write(`Skipping ${file}: ${err?.message ?? err}\\n`);\n return null;\n })\n )\n );\n return docs.filter((d): d is IndexedDocument => d !== null);\n }\n\n /**\n * Load parsed-doc cache from disk. Returns null if missing or unreadable.\n * The cache is a verbatim JSON dump of a prior indexAll() result, written by\n * saveDocsCache. Consumers (search CLI / hook path) prefer this over re-parsing\n * 500+ markdown files; the tradeoff is that cache may be stale if the vault\n * changed since the last reindex — same staleness window as the vector cache.\n */\n async loadDocsCache(indexPath: string): Promise<IndexedDocument[] | null> {\n const p = join(indexPath, DOCS_CACHE_FILE);\n if (!existsSync(p)) return null;\n try {\n const raw = await readFile(p, \"utf-8\");\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) return null;\n return parsed as IndexedDocument[];\n } catch {\n return null;\n }\n }\n\n async saveDocsCache(indexPath: string, docs: IndexedDocument[]): Promise<void> {\n const p = join(indexPath, DOCS_CACHE_FILE);\n await writeFile(p, JSON.stringify(docs), \"utf-8\");\n }\n\n async indexFile(\n absolutePath: string,\n relativePath: string\n ): Promise<IndexedDocument> {\n const [raw, fileStat] = await Promise.all([\n readFile(absolutePath, \"utf-8\"),\n stat(absolutePath),\n ]);\n const { data: frontmatter, content } = parseFrontmatter(raw, relativePath);\n const tree = this.processor.parse(content);\n\n const wikilinks = this.extractWikilinks(tree);\n const tags = this.extractTags(content, frontmatter);\n const headers = this.extractHeaders(tree);\n const plainText = this.stripMarkdown(content);\n const chunks = this.chunkText(plainText);\n\n // Symptom-keyed indexing (v1.4 Phase 5). Each verbatim `symptoms:` phrase is\n // added as its own chunk, so a terse symptom query (\"it hangs after reindex\")\n // matches the note directly — the fix for asymmetric retrieval (query-shaped\n // keys stored alongside document-shaped content). Synthetic child chunks point\n // at the same note path; no index-format change.\n const symptoms = Array.isArray(frontmatter.symptoms)\n ? (frontmatter.symptoms as unknown[]).filter((s): s is string => typeof s === \"string\" && s.trim().length > 0).map((s) => s.trim())\n : [];\n const allChunks = symptoms.length > 0 ? [...chunks, ...symptoms] : chunks;\n\n const title =\n (frontmatter.title as string) ||\n headers[0] ||\n basename(relativePath, \".md\");\n\n // Resolve modification time: prefer frontmatter date fields over fs.stat\n // Supports hit-em-with-the-docs (last_updated) and common alternatives\n const mtime = this.resolveMtime(frontmatter, fileStat.mtime);\n\n // Optional hit-em-with-the-docs fields (only populated when present)\n const loadPriority =\n typeof frontmatter.load_priority === \"number\"\n ? Math.min(10, Math.max(1, frontmatter.load_priority))\n : undefined;\n const status =\n typeof frontmatter.status === \"string\" ? frontmatter.status : undefined;\n const tier =\n typeof frontmatter.tier === \"string\" ? frontmatter.tier : undefined;\n const domains = Array.isArray(frontmatter.domains)\n ? (frontmatter.domains as string[])\n : undefined;\n const purpose =\n typeof frontmatter.purpose === \"string\" ? frontmatter.purpose : undefined;\n const relatedDocs = Array.isArray(frontmatter.related_docs)\n ? (frontmatter.related_docs as string[]).filter((v) => typeof v === \"string\")\n : undefined;\n\n return {\n path: relativePath,\n title,\n content: plainText,\n frontmatter,\n wikilinks,\n tags,\n headers,\n chunks: allChunks,\n mtime,\n ...(loadPriority !== undefined && { loadPriority }),\n ...(status !== undefined && { status }),\n ...(tier !== undefined && { tier }),\n ...(domains !== undefined && { domains }),\n ...(purpose !== undefined && { purpose }),\n ...(relatedDocs !== undefined && { relatedDocs }),\n };\n }\n\n /**\n * Resolve the best available modification date for a document.\n * Priority: last_updated → updated → date → lastmod → fs.stat mtime\n * Accepts YYYY-MM-DD strings or full ISO timestamps.\n */\n private resolveMtime(\n frontmatter: Record<string, unknown>,\n statMtime: Date\n ): string {\n const candidates = [\n frontmatter.last_updated,\n frontmatter.updated,\n frontmatter.date,\n frontmatter.lastmod,\n ];\n for (const val of candidates) {\n if (!val) continue;\n const str = val instanceof Date ? val.toISOString() : String(val);\n const parsed = new Date(str);\n if (!isNaN(parsed.getTime())) return parsed.toISOString();\n }\n return statMtime.toISOString();\n }\n\n private extractWikilinks(tree: any): string[] {\n const links: string[] = [];\n const walk = (node: any) => {\n if (node.type === \"wikiLink\") {\n links.push(node.value || node.data?.alias || \"\");\n }\n if (node.children) {\n for (const child of node.children) walk(child);\n }\n };\n walk(tree);\n return [...new Set(links.filter(Boolean))];\n }\n\n private extractTags(content: string, frontmatter: Record<string, unknown>): string[] {\n const inlineTags = [...content.matchAll(/(?:^|\\s)#([a-zA-Z][\\w-/]*)/g)].map(\n (m) => m[1]\n );\n\n const fmTags = Array.isArray(frontmatter.tags)\n ? (frontmatter.tags as string[])\n : [];\n\n return [...new Set([...fmTags, ...inlineTags])];\n }\n\n private extractHeaders(tree: any): string[] {\n const headers: string[] = [];\n const walk = (node: any) => {\n if (node.type === \"heading\") {\n const text = this.nodeToText(node);\n if (text) headers.push(text);\n }\n if (node.children) {\n for (const child of node.children) walk(child);\n }\n };\n walk(tree);\n return headers;\n }\n\n private nodeToText(node: any): string {\n if (node.type === \"text\") return node.value;\n if (node.children) return node.children.map((c: any) => this.nodeToText(c)).join(\"\");\n return \"\";\n }\n\n private stripMarkdown(content: string): string {\n return content\n .replace(/```[\\s\\S]*?```/g, \"\")\n .replace(/`[^`]+`/g, \"\")\n .replace(/!\\[.*?\\]\\(.*?\\)/g, \"\")\n .replace(/\\[([^\\]]+)\\]\\(.*?\\)/g, \"$1\")\n .replace(/#{1,6}\\s+/g, \"\")\n .replace(/[*_~]{1,3}/g, \"\")\n .replace(/>\\s+/g, \"\")\n .replace(/\\|.*\\|/g, \"\")\n .replace(/-{3,}/g, \"\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .trim();\n }\n\n chunkText(text: string): string[] {\n if (text.length <= CHUNK_TARGET_CHARS) return [text];\n\n const sentences = text.match(/[^.!?\\n]+[.!?\\n]+|[^.!?\\n]+$/g) || [text];\n const chunks: string[] = [];\n let current = \"\";\n\n for (const sentence of sentences) {\n if (current.length + sentence.length > CHUNK_TARGET_CHARS && current) {\n chunks.push(current.trim());\n current = \"\";\n }\n current += sentence;\n }\n if (current.trim()) chunks.push(current.trim());\n\n return chunks;\n }\n}\n"],"mappings":";AAAA,SAAS,eAAe;AACxB,OAAO,iBAAiB;AACxB,OAAO,oBAAoB;AAC3B,OAAO,YAAY;AACnB,SAAS,YAAY;AACrB,SAAS,UAAU,WAAW,YAAY;AAC1C,SAAS,kBAAkB;AAC3B,SAAS,UAAU,YAAsB;AAGzC,IAAM,kBAAkB;AAExB,IAAM,qBAAqB;AAG3B,SAAS,iBAAiB,KAAa,MAAkE;AACvG,MAAI;AACF,WAAO,OAAO,GAAG;AAAA,EACnB,SAAS,KAAU;AACjB,YAAQ,OAAO,MAAM,0BAA0B,IAAI,yBAAyB,KAAK,SAAS,MAAM,IAAI,EAAE,CAAC,KAAK,GAAG;AAAA,CAAI;AACnH,WAAO,EAAE,MAAM,CAAC,GAAG,SAAS,IAAI,QAAQ,gDAAgD,EAAE,EAAE;AAAA,EAC9F;AACF;AAEO,IAAM,UAAN,MAAc;AAAA,EACX;AAAA,EACA;AAAA,EAER,YAAY,WAAmB;AAC7B,SAAK,YAAY;AACjB,SAAK,YAAY,QAAQ,EAAE,IAAI,WAAW,EAAE,IAAI,cAAc;AAAA,EAChE;AAAA,EAEA,MAAM,WAAuC;AAM3C,UAAM,QAAQ,MAAM,KAAK,WAAW,EAAE,KAAK,KAAK,WAAW,QAAQ,KAAK,CAAC;AAEzE,UAAM,OAAO,MAAM,QAAQ;AAAA,MACzB,MAAM;AAAA,QAAI,CAAC,SACT,KAAK,UAAU,KAAK,KAAK,WAAW,IAAI,GAAG,IAAI,EAAE,MAAM,CAAC,QAAQ;AAC9D,kBAAQ,OAAO,MAAM,YAAY,IAAI,KAAK,KAAK,WAAW,GAAG;AAAA,CAAI;AACjE,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO,KAAK,OAAO,CAAC,MAA4B,MAAM,IAAI;AAAA,EAC5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cAAc,WAAsD;AACxE,UAAM,IAAI,KAAK,WAAW,eAAe;AACzC,QAAI,CAAC,WAAW,CAAC,EAAG,QAAO;AAC3B,QAAI;AACF,YAAM,MAAM,MAAM,SAAS,GAAG,OAAO;AACrC,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,WAAmB,MAAwC;AAC7E,UAAM,IAAI,KAAK,WAAW,eAAe;AACzC,UAAM,UAAU,GAAG,KAAK,UAAU,IAAI,GAAG,OAAO;AAAA,EAClD;AAAA,EAEA,MAAM,UACJ,cACA,cAC0B;AAC1B,UAAM,CAAC,KAAK,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,MACxC,SAAS,cAAc,OAAO;AAAA,MAC9B,KAAK,YAAY;AAAA,IACnB,CAAC;AACD,UAAM,EAAE,MAAM,aAAa,QAAQ,IAAI,iBAAiB,KAAK,YAAY;AACzE,UAAM,OAAO,KAAK,UAAU,MAAM,OAAO;AAEzC,UAAM,YAAY,KAAK,iBAAiB,IAAI;AAC5C,UAAM,OAAO,KAAK,YAAY,SAAS,WAAW;AAClD,UAAM,UAAU,KAAK,eAAe,IAAI;AACxC,UAAM,YAAY,KAAK,cAAc,OAAO;AAC5C,UAAM,SAAS,KAAK,UAAU,SAAS;AAOvC,UAAM,WAAW,MAAM,QAAQ,YAAY,QAAQ,IAC9C,YAAY,SAAuB,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAChI,CAAC;AACL,UAAM,YAAY,SAAS,SAAS,IAAI,CAAC,GAAG,QAAQ,GAAG,QAAQ,IAAI;AAEnE,UAAM,QACH,YAAY,SACb,QAAQ,CAAC,KACT,SAAS,cAAc,KAAK;AAI9B,UAAM,QAAQ,KAAK,aAAa,aAAa,SAAS,KAAK;AAG3D,UAAM,eACJ,OAAO,YAAY,kBAAkB,WACjC,KAAK,IAAI,IAAI,KAAK,IAAI,GAAG,YAAY,aAAa,CAAC,IACnD;AACN,UAAM,SACJ,OAAO,YAAY,WAAW,WAAW,YAAY,SAAS;AAChE,UAAM,OACJ,OAAO,YAAY,SAAS,WAAW,YAAY,OAAO;AAC5D,UAAM,UAAU,MAAM,QAAQ,YAAY,OAAO,IAC5C,YAAY,UACb;AACJ,UAAM,UACJ,OAAO,YAAY,YAAY,WAAW,YAAY,UAAU;AAClE,UAAM,cAAc,MAAM,QAAQ,YAAY,YAAY,IACrD,YAAY,aAA0B,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,IAC1E;AAEJ,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,GAAI,iBAAiB,UAAa,EAAE,aAAa;AAAA,MACjD,GAAI,WAAW,UAAa,EAAE,OAAO;AAAA,MACrC,GAAI,SAAS,UAAa,EAAE,KAAK;AAAA,MACjC,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,MACvC,GAAI,YAAY,UAAa,EAAE,QAAQ;AAAA,MACvC,GAAI,gBAAgB,UAAa,EAAE,YAAY;AAAA,IACjD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,aACN,aACA,WACQ;AACR,UAAM,aAAa;AAAA,MACjB,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,YAAY;AAAA,IACd;AACA,eAAW,OAAO,YAAY;AAC5B,UAAI,CAAC,IAAK;AACV,YAAM,MAAM,eAAe,OAAO,IAAI,YAAY,IAAI,OAAO,GAAG;AAChE,YAAM,SAAS,IAAI,KAAK,GAAG;AAC3B,UAAI,CAAC,MAAM,OAAO,QAAQ,CAAC,EAAG,QAAO,OAAO,YAAY;AAAA,IAC1D;AACA,WAAO,UAAU,YAAY;AAAA,EAC/B;AAAA,EAEQ,iBAAiB,MAAqB;AAC5C,UAAM,QAAkB,CAAC;AACzB,UAAM,OAAO,CAAC,SAAc;AAC1B,UAAI,KAAK,SAAS,YAAY;AAC5B,cAAM,KAAK,KAAK,SAAS,KAAK,MAAM,SAAS,EAAE;AAAA,MACjD;AACA,UAAI,KAAK,UAAU;AACjB,mBAAW,SAAS,KAAK,SAAU,MAAK,KAAK;AAAA,MAC/C;AAAA,IACF;AACA,SAAK,IAAI;AACT,WAAO,CAAC,GAAG,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC,CAAC;AAAA,EAC3C;AAAA,EAEQ,YAAY,SAAiB,aAAgD;AACnF,UAAM,aAAa,CAAC,GAAG,QAAQ,SAAS,6BAA6B,CAAC,EAAE;AAAA,MACtE,CAAC,MAAM,EAAE,CAAC;AAAA,IACZ;AAEA,UAAM,SAAS,MAAM,QAAQ,YAAY,IAAI,IACxC,YAAY,OACb,CAAC;AAEL,WAAO,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC;AAAA,EAChD;AAAA,EAEQ,eAAe,MAAqB;AAC1C,UAAM,UAAoB,CAAC;AAC3B,UAAM,OAAO,CAAC,SAAc;AAC1B,UAAI,KAAK,SAAS,WAAW;AAC3B,cAAM,OAAO,KAAK,WAAW,IAAI;AACjC,YAAI,KAAM,SAAQ,KAAK,IAAI;AAAA,MAC7B;AACA,UAAI,KAAK,UAAU;AACjB,mBAAW,SAAS,KAAK,SAAU,MAAK,KAAK;AAAA,MAC/C;AAAA,IACF;AACA,SAAK,IAAI;AACT,WAAO;AAAA,EACT;AAAA,EAEQ,WAAW,MAAmB;AACpC,QAAI,KAAK,SAAS,OAAQ,QAAO,KAAK;AACtC,QAAI,KAAK,SAAU,QAAO,KAAK,SAAS,IAAI,CAAC,MAAW,KAAK,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE;AACnF,WAAO;AAAA,EACT;AAAA,EAEQ,cAAc,SAAyB;AAC7C,WAAO,QACJ,QAAQ,mBAAmB,EAAE,EAC7B,QAAQ,YAAY,EAAE,EACtB,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,wBAAwB,IAAI,EACpC,QAAQ,cAAc,EAAE,EACxB,QAAQ,eAAe,EAAE,EACzB,QAAQ,SAAS,EAAE,EACnB,QAAQ,WAAW,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,QAAQ,WAAW,MAAM,EACzB,KAAK;AAAA,EACV;AAAA,EAEA,UAAU,MAAwB;AAChC,QAAI,KAAK,UAAU,mBAAoB,QAAO,CAAC,IAAI;AAEnD,UAAM,YAAY,KAAK,MAAM,+BAA+B,KAAK,CAAC,IAAI;AACtE,UAAM,SAAmB,CAAC;AAC1B,QAAI,UAAU;AAEd,eAAW,YAAY,WAAW;AAChC,UAAI,QAAQ,SAAS,SAAS,SAAS,sBAAsB,SAAS;AACpE,eAAO,KAAK,QAAQ,KAAK,CAAC;AAC1B,kBAAU;AAAA,MACZ;AACA,iBAAW;AAAA,IACb;AACA,QAAI,QAAQ,KAAK,EAAG,QAAO,KAAK,QAAQ,KAAK,CAAC;AAE9C,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -257,6 +257,8 @@ function escapeRegex2(str) {
257
257
  // src/core/watcher.ts
258
258
  import { watch } from "chokidar";
259
259
  import { EventEmitter } from "events";
260
+ var IGNORED_DIRS = /(^|[\\/])(node_modules|\.semantic-sidekick-index|\.git)([\\/]|$)/;
261
+ var isMarkdown = (path) => path.endsWith(".md");
260
262
  var Watcher = class extends EventEmitter {
261
263
  notesPath;
262
264
  fsWatcher = null;
@@ -273,15 +275,11 @@ var Watcher = class extends EventEmitter {
273
275
  }
274
276
  start() {
275
277
  if (this.fsWatcher) return;
276
- this.fsWatcher = watch("**/*.md", {
278
+ this.fsWatcher = watch(".", {
277
279
  cwd: this.notesPath,
278
280
  ignoreInitial: true,
279
281
  followSymlinks: true,
280
- ignored: [
281
- "**/node_modules/**",
282
- "**/.semantic-sidekick-index/**",
283
- "**/.git/**"
284
- ],
282
+ ignored: (path, stats) => IGNORED_DIRS.test(path) || stats?.isFile() === true && !isMarkdown(path),
285
283
  ...this.usePolling ? { usePolling: true, interval: 100 } : {}
286
284
  });
287
285
  this.readyPromise = new Promise((resolve) => {
@@ -302,6 +300,7 @@ var Watcher = class extends EventEmitter {
302
300
  this.pendingChanges.clear();
303
301
  }
304
302
  enqueue(path) {
303
+ if (!isMarkdown(path)) return;
305
304
  this.pendingChanges.add(path);
306
305
  if (this.debounceTimer) clearTimeout(this.debounceTimer);
307
306
  this.debounceTimer = setTimeout(() => {
@@ -934,4 +933,4 @@ export {
934
933
  regenerateAgentsContract,
935
934
  inspectAgentsContract
936
935
  };
937
- //# sourceMappingURL=chunk-CSXZ7QUU.js.map
936
+ //# sourceMappingURL=chunk-JWPR2B2X.js.map
@@ -0,0 +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"],"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\nconst IGNORED_DIRS = /(^|[\\\\/])(node_modules|\\.semantic-sidekick-index|\\.git)([\\\\/]|$)/;\nconst isMarkdown = (path: string) => path.endsWith(\".md\");\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 // chokidar ≥4 has no glob support: watch the folder and filter (a \"**/*.md\" target never fires)\n this.fsWatcher = watch(\".\", {\n cwd: this.notesPath,\n ignoreInitial: true,\n followSymlinks: true,\n ignored: (path, stats) => IGNORED_DIRS.test(path) || (stats?.isFile() === true && !isMarkdown(path)),\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 // `ignored` gets no stats for some events (e.g. unlink), so non-markdown can reach here\n if (!isMarkdown(path)) return;\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 /** Verbatim symptom phrases (the user's own words) — symptom-keyed for retrieval. */\n symptoms?: 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 ...(input.symptoms && input.symptoms.length > 0 && { symptoms: input.symptoms }),\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"],"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"]}
package/dist/cli/index.js CHANGED
@@ -621,7 +621,7 @@ program.name("semantic-memory").description(
621
621
  "Unified memory-layer MCP server for Claude Code (rebrand of semantic-sidekick at v1.0.0).\nThe `semantic-sidekick` command is preserved as an alias.\n\n Start MCP server: semantic-memory --notes ./vault\n Show vault stats: semantic-memory --notes ./vault --stats\n Force reindex: semantic-memory --notes ./vault --reindex\n List MCP tools: semantic-memory tools\n Tool details: semantic-memory tools search_semantic"
622
622
  ).version(version);
623
623
  program.command("search <query>").description("Run a vault search and print top hits as JSON (used by activation hooks)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--limit <n>", "Max results", (v) => parseInt(v, 10), 8).option("--text-only", "Skip embedder \u2014 keyword-only search (fast path, no model init)").option("--json", "Emit raw JSON (default)", true).action(async (query, opts) => {
624
- const { runSearch } = await import("../search-AHKEFI2B.js");
624
+ const { runSearch } = await import("../search-ZS6OW74K.js");
625
625
  try {
626
626
  const hits = await runSearch({
627
627
  notes: opts.notes,
@@ -708,7 +708,7 @@ program.command("serve", { isDefault: true }).description("Start the MCP server
708
708
  process.exit(1);
709
709
  }
710
710
  if (opts.stats) {
711
- const { Indexer } = await import("../indexer-YW4O3VVM.js");
711
+ const { Indexer } = await import("../indexer-RAKJ7JR3.js");
712
712
  const indexer = new Indexer(notesPath);
713
713
  const docs = await indexer.indexAll();
714
714
  console.log(`Notes: ${docs.length}`);
@@ -18,7 +18,7 @@ import {
18
18
  regenDirectoryIndex,
19
19
  regenIndexesForPaths,
20
20
  regenerateAgentsContract
21
- } from "../chunk-CSXZ7QUU.js";
21
+ } from "../chunk-JWPR2B2X.js";
22
22
  import {
23
23
  formatLintReport,
24
24
  lintVault
@@ -36,10 +36,10 @@ import {
36
36
  GraphBuilder,
37
37
  TextSearch,
38
38
  VectorIndex
39
- } from "../chunk-25EIQWXL.js";
39
+ } from "../chunk-44BQDRXD.js";
40
40
  import {
41
41
  Indexer
42
- } from "../chunk-V45NJIHI.js";
42
+ } from "../chunk-IGHCVAAL.js";
43
43
  import {
44
44
  logEvent,
45
45
  logQuery
@@ -0,0 +1,8 @@
1
+ import {
2
+ Indexer
3
+ } from "./chunk-IGHCVAAL.js";
4
+ import "./chunk-PZ5AY32C.js";
5
+ export {
6
+ Indexer
7
+ };
8
+ //# sourceMappingURL=indexer-RAKJ7JR3.js.map