@remnic/core 1.1.23 → 1.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-cli.js +2 -2
- package/dist/access-http.js +5 -5
- package/dist/access-mcp.js +4 -4
- package/dist/access-schema.js +2 -2
- package/dist/access-service.js +2 -2
- package/dist/{chunk-RA73CTVY.js → chunk-2DM72JF3.js} +4 -4
- package/dist/{chunk-FHXVW3L4.js → chunk-6CB4E7ZV.js} +3 -3
- package/dist/{chunk-JKXFF3NT.js → chunk-AMVN77EU.js} +5 -1
- package/dist/chunk-AMVN77EU.js.map +1 -0
- package/dist/{chunk-HWF42K6J.js → chunk-JUYT2J3K.js} +11 -15
- package/dist/chunk-JUYT2J3K.js.map +1 -0
- package/dist/{chunk-MM5EBZVW.js → chunk-LCTP7YRU.js} +2 -2
- package/dist/{chunk-7E7SZRPP.js → chunk-NW7JW5GA.js} +2 -2
- package/dist/cli.js +6 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/offline-sync.d.ts +3 -1
- package/dist/offline-sync.js +5 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/transfer/types.d.ts +12 -12
- package/package.json +1 -1
- package/src/access-http.test.ts +66 -3
- package/src/access-http.ts +16 -11
- package/src/index.ts +2 -0
- package/src/offline-sync.ts +2 -0
- package/dist/chunk-HWF42K6J.js.map +0 -1
- package/dist/chunk-JKXFF3NT.js.map +0 -1
- /package/dist/{chunk-RA73CTVY.js.map → chunk-2DM72JF3.js.map} +0 -0
- /package/dist/{chunk-FHXVW3L4.js.map → chunk-6CB4E7ZV.js.map} +0 -0
- /package/dist/{chunk-MM5EBZVW.js.map → chunk-LCTP7YRU.js.map} +0 -0
- /package/dist/{chunk-7E7SZRPP.js.map → chunk-NW7JW5GA.js.map} +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/coding/review-context.ts","../src/binary-lifecycle/types.ts","../src/binary-lifecycle/backend.ts","../src/binary-lifecycle/scanner.ts","../src/binary-lifecycle/manifest.ts","../src/binary-lifecycle/pipeline.ts","../src/projection/index.ts","../src/utils/category-dir.ts","../src/onboarding/index.ts","../src/curation/index.ts","../src/dedup/index.ts","../src/review/index.ts","../src/sync/index.ts","../src/spaces/index.ts","../src/memory-extension/shared-instructions.ts","../src/memory-extension/codex-publisher.ts","../src/memory-extension/claude-code-publisher.ts","../src/memory-extension/hermes-publisher.ts","../src/memory-extension/index.ts","../src/importers/base.ts","../src/transfer/capsule-fork.ts"],"sourcesContent":["/**\n * Diff-aware review-context packer (issue #569 PR 4).\n *\n * When an agent is asked \"review this PR\" / \"what changed in this diff\" /\n * \"look at this diff\", the prompt that reaches recall is short and generic\n * — the real signal is the diff itself. This module:\n *\n * 1. Detects review-intent prompts via `isReviewPrompt`.\n * 2. Extracts the touched file list from a unified diff via\n * `parseTouchedFiles`.\n * 3. Re-ranks a set of candidate memories so that memories whose\n * `entityRefs` mention a touched path float to the top. The boost is\n * additive and bounded so it doesn't obliterate the original ranking —\n * it's a bias, not a filter.\n *\n * Pure — no orchestrator, no storage. Callers inject the candidate memories\n * they already have from their normal recall pipeline. This keeps the\n * module easy to test and integrates cleanly with the existing tiered-recall\n * code in `orchestrator.ts` (the tier itself can be wired later; the pure\n * surface is what PRs 5/6/7 will call).\n */\n\n// ──────────────────────────────────────────────────────────────────────────\n// Public types\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * A memory candidate as fed into review-context ranking. The shape is a\n * deliberate subset of the core `MemorySummary` / recall result — only the\n * fields we actually need — so this module stays decoupled from the rest of\n * the codebase and can be reused by CLI tools, bench fixtures, etc.\n */\nexport interface ReviewCandidate {\n /** Opaque identifier. Echoed unchanged in the output. */\n id: string;\n /**\n * Pre-review relevance score from the upstream recall pipeline. Higher is\n * better. `0` is treated as \"no prior signal\" and gets the full review\n * boost when a path match is found.\n */\n score: number;\n /**\n * References the memory mentions (file paths, entity names, etc.). Used\n * to decide whether any touched file appears in the memory's scope.\n *\n * Accepts `undefined`/missing so callers can pass sparse records from\n * legacy storage without pre-filling.\n */\n entityRefs?: string[];\n}\n\nexport interface ReviewContext {\n /**\n * Normalized file paths touched by the diff. Each entry is forward-slashed\n * and relative to the repo root when possible.\n */\n touchedFiles: string[];\n /**\n * Candidates re-sorted so memories whose `entityRefs` mention a touched\n * path are boosted. Shape matches the input `ReviewCandidate[]` — the\n * boost is recorded on each entry as `boost` for observability.\n */\n rankedRecall: Array<ReviewCandidate & { boost: number }>;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Review-prompt heuristic\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Keyword list from the #569 design doc, plus obvious paraphrases. All\n * matching is case-insensitive and whole-word (so `reviewer` doesn't trigger\n * on `review` alone).\n */\nconst REVIEW_KEYWORD_PATTERNS: RegExp[] = [\n /\\breview\\b/i,\n /\\bdiff\\b/i,\n /\\bwhat changed\\b/i,\n /\\blook at this pr\\b/i,\n /\\bwhat('?s|\\s+is)\\s+in\\s+this\\s+(pr|patch|diff|change)\\b/i,\n /\\bcode review\\b/i,\n];\n\n/**\n * `true` when the prompt looks like a review / diff-explanation request.\n *\n * Empty / non-string input → `false` (the caller shouldn't branch on an\n * invalid prompt).\n */\nexport function isReviewPrompt(prompt: string | null | undefined): boolean {\n if (typeof prompt !== \"string\") return false;\n const trimmed = prompt.trim();\n if (!trimmed) return false;\n return REVIEW_KEYWORD_PATTERNS.some((re) => re.test(trimmed));\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Unified-diff parser — extract touched files\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a unified diff and return the set of files touched. Accepts both the\n * `diff --git` form (`diff --git a/foo b/bar`) and the `--- / +++` form\n * (`--- a/foo\\n+++ b/bar`). Returns deduplicated, repo-root-relative paths\n * (with the conventional `a/` / `b/` prefixes stripped).\n *\n * Path entries of `/dev/null` (used in adds/deletes) are excluded.\n */\nexport function parseTouchedFiles(diff: string | null | undefined): string[] {\n if (typeof diff !== \"string\" || !diff.trim()) return [];\n const touched = new Set<string>();\n const lines = diff.split(/\\r?\\n/);\n\n for (const line of lines) {\n // `diff --git a/foo/bar.ts b/foo/bar.ts`\n //\n // Paths may be quoted by git when they contain spaces or non-ASCII\n // characters, e.g. `diff --git \"a/src/my file.ts\" \"b/src/my file.ts\"`.\n // The two paths are always separated by whitespace at the top level, but\n // whitespace INSIDE quoted paths must NOT split the tokens. Parse the\n // two paths with an explicit tokenizer instead of a `\\S+`-based regex\n // (which would shatter `\"a/my file.ts\"` into `\"a/my` / `file.ts\"`).\n const gitPrefix = line.match(/^diff --git\\s+/);\n if (gitPrefix) {\n const rest = line.slice(gitPrefix[0].length);\n const paths = splitDiffGitPaths(rest);\n if (paths) {\n for (const raw of paths) {\n const stripped = stripDiffPathPrefix(raw);\n if (stripped && stripped !== \"/dev/null\") touched.add(stripped);\n }\n }\n continue;\n }\n // `--- a/foo/bar.ts` or `+++ b/foo/bar.ts` — or quoted:\n // `--- \"a/foo bar.ts\"` / `+++ \"b/foo bar.ts\"` when git emits C-quoted\n // paths for whitespace or non-ASCII filenames.\n //\n // Matched with an anchored prefix and an explicit tokenizer for the\n // tail so quoted paths containing whitespace are not split on the\n // first internal space. The previous `\\S+` form silently dropped\n // everything after the first whitespace in a quoted path.\n const headerPrefix = line.match(/^(?:---|\\+\\+\\+)[ \\t]+/);\n if (headerPrefix) {\n const tail = line.slice(headerPrefix[0].length).replace(/[ \\t]+$/, \"\");\n const raw = extractSingleDiffPathToken(tail);\n if (raw) {\n const stripped = stripDiffPathPrefix(raw);\n if (stripped && stripped !== \"/dev/null\") touched.add(stripped);\n }\n }\n }\n\n return Array.from(touched).sort();\n}\n\n/**\n * Extract the single path token from the tail of a `---` / `+++` header\n * line. Returns `null` when the tail is empty or malformed (e.g. an\n * unterminated quoted path). The whole tail is consumed — trailing\n * timestamps from non-git diff frontends (`--- a/foo\t2023-01-01`) fall\n * into a leading-token extraction like the quoted-form case.\n */\nfunction extractSingleDiffPathToken(tail: string): string | null {\n if (tail.length === 0) return null;\n if (tail[0] === '\"') {\n let j = 1;\n while (j < tail.length) {\n if (tail[j] === \"\\\\\" && j + 1 < tail.length) {\n j += 2;\n continue;\n }\n if (tail[j] === '\"') break;\n j += 1;\n }\n if (j >= tail.length) return null; // unterminated quoted path\n return tail.slice(0, j + 1);\n }\n // Unquoted: consume up to the first tab or whitespace-run, so standard\n // `--- a/foo\t<timestamp>` lines surface just `a/foo`. For ordinary\n // git-style output the tail has no whitespace at all.\n let j = 0;\n while (j < tail.length && tail[j] !== \" \" && tail[j] !== \"\\t\") j += 1;\n return tail.slice(0, j);\n}\n\n/**\n * Split the `a-path b-path` portion of a `diff --git` line into exactly two\n * path tokens, respecting git's quoting convention. Returns `null` when the\n * input cannot be parsed into exactly two tokens.\n */\nfunction splitDiffGitPaths(rest: string): [string, string] | null {\n const tokens: string[] = [];\n let i = 0;\n while (i < rest.length && tokens.length < 2) {\n // Skip leading whitespace between tokens.\n while (i < rest.length && (rest[i] === \" \" || rest[i] === \"\\t\")) i += 1;\n if (i >= rest.length) break;\n if (rest[i] === '\"') {\n // Quoted path: consume up to the matching unescaped `\"`. Git escapes\n // inner quotes as `\\\"`, so we respect backslash escaping.\n let j = i + 1;\n while (j < rest.length) {\n if (rest[j] === \"\\\\\" && j + 1 < rest.length) {\n j += 2;\n continue;\n }\n if (rest[j] === '\"') break;\n j += 1;\n }\n if (j >= rest.length) return null; // unterminated quoted path\n tokens.push(rest.slice(i, j + 1));\n i = j + 1;\n } else {\n // Unquoted path: run of non-whitespace.\n let j = i;\n while (j < rest.length && rest[j] !== \" \" && rest[j] !== \"\\t\") j += 1;\n tokens.push(rest.slice(i, j));\n i = j;\n }\n }\n if (tokens.length !== 2) return null;\n return [tokens[0]!, tokens[1]!];\n}\n\nfunction stripDiffPathPrefix(raw: string): string {\n // Git conventionally prefixes paths with `a/` or `b/` in diffs, and\n // quotes the whole path (including the prefix) when it contains spaces or\n // non-ASCII bytes. Quote-stripping must therefore happen BEFORE the\n // prefix check — otherwise `\"a/path with spaces.ts\"` still starts with\n // `\"a` and the prefix is never recognized.\n let s = raw;\n if (s.length >= 2 && s[0] === '\"' && s[s.length - 1] === '\"') {\n s = s.slice(1, -1);\n // Git octal-escapes non-ASCII and escapes inner backslashes/quotes via\n // the C-quoted path form. We handle the common cases (`\\\"`, `\\\\`) so\n // the rest of the pipeline gets the raw filename. Octal escapes for\n // non-ASCII are left as-is here — they are extremely rare and the\n // touched-file set is only used for lexical matches against\n // entityRefs, where matching either form produces the same hit.\n s = s.replace(/\\\\([\"\\\\])/g, \"$1\");\n }\n // Normalize Windows-style backslashes. Must happen AFTER quote stripping\n // so that the `\\\"` and `\\\\` escape pairs above aren't corrupted.\n s = s.replace(/\\\\/g, \"/\");\n if (s.startsWith(\"a/\") || s.startsWith(\"b/\")) s = s.slice(2);\n return s;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Ranking\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Additive boost per matching touched-file. Tuned so that a single exact\n * match is enough to float a `score=0` candidate above a `score=0.4`\n * unmatched peer, but not so large it buries multi-signal results. `0.5`\n * per match, capped at `1.0` so three matches don't eclipse strong recall.\n */\nconst BOOST_PER_MATCH = 0.5;\nconst MAX_BOOST = 1.0;\n\n/**\n * Count how many touched files appear in a memory's entityRefs. Matches are\n * literal substring matches on either direction — either the ref contains\n * the path, or the path contains the ref — so both\n * - `\"src/foo.ts\"` refs matching a touched `\"src/foo.ts\"`, and\n * - `\"foo.ts\"` refs matching a touched `\"src/foo.ts\"`\n * succeed.\n */\nfunction countPathHits(entityRefs: string[] | undefined, touchedFiles: string[]): number {\n if (!entityRefs || entityRefs.length === 0) return 0;\n if (touchedFiles.length === 0) return 0;\n let hits = 0;\n for (const ref of entityRefs) {\n if (typeof ref !== \"string\" || !ref) continue;\n const lowered = ref.toLowerCase();\n for (const file of touchedFiles) {\n const flower = file.toLowerCase();\n if (lowered === flower) {\n hits += 1;\n break;\n }\n if (lowered.includes(flower) || flower.includes(lowered)) {\n hits += 1;\n break;\n }\n }\n }\n return hits;\n}\n\n/**\n * Build a review-context ranking for a set of candidate memories.\n *\n * Contract:\n * - `touchedFiles` is the parsed diff file list.\n * - `candidates` is passed through unchanged when no boost applies.\n * - When a boost applies, the result is sorted by `(score + boost)` desc,\n * with a stable secondary sort on the original `id` for determinism\n * (CLAUDE.md #19 — comparators must return 0 for equal items).\n */\nexport function rankReviewCandidates(\n candidates: ReviewCandidate[],\n touchedFiles: string[],\n): Array<ReviewCandidate & { boost: number }> {\n const annotated: Array<ReviewCandidate & { boost: number }> = candidates.map((c) => {\n const hits = countPathHits(c.entityRefs, touchedFiles);\n const boost = Math.min(MAX_BOOST, hits * BOOST_PER_MATCH);\n return { ...c, boost };\n });\n\n annotated.sort((a, b) => {\n const adjA = a.score + a.boost;\n const adjB = b.score + b.boost;\n if (adjA !== adjB) return adjB - adjA;\n // Stable secondary sort for deterministic ordering on ties.\n if (a.id < b.id) return -1;\n if (a.id > b.id) return 1;\n return 0;\n });\n\n return annotated;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Packer entry point\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface PackReviewContextInput {\n /** Unified diff, as produced by `git diff`. */\n diff: string | null | undefined;\n /** Candidate memories from the upstream recall pipeline. */\n candidates: ReviewCandidate[];\n}\n\n/**\n * Top-level entry point used by the orchestrator (and CLI / bench) when a\n * review-intent prompt is detected.\n *\n * Parses the diff, re-ranks the candidates, and returns both artefacts so\n * the caller can surface `touchedFiles` as context and `rankedRecall` as\n * the recall result.\n */\nexport function packReviewContext(input: PackReviewContextInput): ReviewContext {\n const touchedFiles = parseTouchedFiles(input.diff);\n const rankedRecall = rankReviewCandidates(input.candidates, touchedFiles);\n return { touchedFiles, rankedRecall };\n}\n","/**\n * Binary file lifecycle management types.\n *\n * Defines the configuration, manifest, and record structures for the\n * three-stage binary lifecycle pipeline: mirror, redirect, clean.\n */\n\nexport interface BinaryLifecycleConfig {\n /** Master toggle. Default: false. */\n enabled: boolean;\n /** Days after mirror before local copy is eligible for cleanup. Default: 7. */\n gracePeriodDays: number;\n /** Files larger than this are skipped during scan. Default: 50 MB. */\n maxBinarySizeBytes: number;\n /** Glob patterns for binary file types to manage. */\n scanPatterns: string[];\n /** Backend configuration for binary storage. */\n backend: BinaryStorageBackendConfig;\n}\n\nexport interface BinaryStorageBackendConfig {\n /** Backend type. \"filesystem\" copies to a local directory. \"none\" is a no-op (dry-run/testing). */\n type: \"filesystem\" | \"s3\" | \"none\";\n /** Destination directory for the filesystem backend. */\n basePath?: string;\n /** S3 bucket name (future). */\n s3Bucket?: string;\n /** S3 region (future). */\n s3Region?: string;\n /** S3 key prefix (future). */\n s3Prefix?: string;\n}\n\nexport type BinaryAssetStatus =\n | \"pending\"\n | \"mirrored\"\n | \"redirected\"\n | \"cleaned\"\n | \"error\";\n\nexport interface BinaryAssetRecord {\n /** Relative path from memoryDir to the original file. */\n originalPath: string;\n /** Path (or URL) in the backend storage. */\n mirroredPath: string;\n /** SHA-256 hex digest of file content. */\n contentHash: string;\n /** File size in bytes. */\n sizeBytes: number;\n /** MIME type (e.g. \"image/png\"). */\n mimeType: string;\n /** ISO 8601 timestamp when the file was mirrored. */\n mirroredAt: string;\n /** ISO 8601 timestamp when markdown references were rewritten. */\n redirectedAt?: string;\n /** ISO 8601 timestamp when the local copy was deleted. */\n cleanedAt?: string;\n /** Current lifecycle status. */\n status: BinaryAssetStatus;\n}\n\nexport interface BinaryLifecycleManifest {\n version: 1;\n assets: BinaryAssetRecord[];\n lastScanAt?: string;\n}\n\nexport interface PipelineResult {\n scanned: number;\n mirrored: number;\n redirected: number;\n cleaned: number;\n errors: string[];\n dryRun: boolean;\n}\n\nexport const DEFAULT_SCAN_PATTERNS = [\n \"*.png\",\n \"*.jpg\",\n \"*.jpeg\",\n \"*.gif\",\n \"*.pdf\",\n \"*.mp3\",\n \"*.mp4\",\n \"*.wav\",\n];\n\nexport const DEFAULT_MAX_BINARY_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB\nexport const DEFAULT_GRACE_PERIOD_DAYS = 7;\n","/**\n * Binary storage backend interface and implementations.\n *\n * Backends handle the actual persistence of binary files to an external\n * location. The pipeline calls upload/exists/delete through this interface\n * so swapping storage providers requires no pipeline changes.\n */\n\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { BinaryStorageBackendConfig } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\nexport interface BinaryStorageBackend {\n /** Discriminator for the backend type. */\n readonly type: string;\n /**\n * Upload a local file to the backend.\n * @returns The backend path or URL where the file was stored.\n */\n upload(localPath: string, remotePath: string): Promise<string>;\n /** Check whether a remote path already exists in the backend. */\n exists(remotePath: string): Promise<boolean>;\n /** Delete a file from the backend. */\n delete(remotePath: string): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Filesystem backend\n// ---------------------------------------------------------------------------\n\nexport class FilesystemBackend implements BinaryStorageBackend {\n readonly type = \"filesystem\";\n private readonly basePath: string;\n\n constructor(basePath: string) {\n if (!basePath || basePath.trim().length === 0) {\n throw new Error(\"FilesystemBackend requires a non-empty basePath\");\n }\n this.basePath = basePath;\n }\n\n async upload(localPath: string, remotePath: string): Promise<string> {\n const dest = path.join(this.basePath, remotePath);\n const destDir = path.dirname(dest);\n await fsp.mkdir(destDir, { recursive: true });\n await fsp.copyFile(localPath, dest);\n return dest;\n }\n\n async exists(remotePath: string): Promise<boolean> {\n const dest = path.join(this.basePath, remotePath);\n try {\n await fsp.access(dest, fs.constants.F_OK);\n return true;\n } catch {\n return false;\n }\n }\n\n async delete(remotePath: string): Promise<void> {\n const dest = path.join(this.basePath, remotePath);\n try {\n await fsp.unlink(dest);\n } catch (err: unknown) {\n // Ignore ENOENT (already deleted); rethrow everything else.\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// None backend (no-op, for dry-run / testing)\n// ---------------------------------------------------------------------------\n\nexport class NoneBackend implements BinaryStorageBackend {\n readonly type = \"none\";\n\n async upload(_localPath: string, remotePath: string): Promise<string> {\n return remotePath;\n }\n\n async exists(_remotePath: string): Promise<boolean> {\n return false;\n }\n\n async delete(_remotePath: string): Promise<void> {\n // intentional no-op\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createBackend(cfg: BinaryStorageBackendConfig): BinaryStorageBackend {\n switch (cfg.type) {\n case \"filesystem\": {\n if (!cfg.basePath) {\n throw new Error(\n \"BinaryStorageBackendConfig.basePath is required when type is \\\"filesystem\\\"\",\n );\n }\n return new FilesystemBackend(cfg.basePath);\n }\n case \"s3\":\n throw new Error(\"S3 binary storage backend is not yet implemented\");\n case \"none\":\n return new NoneBackend();\n default:\n throw new Error(`Unknown binary storage backend type: ${String((cfg as { type: string }).type)}`);\n }\n}\n","/**\n * Binary file scanner.\n *\n * Recursively walks the memory directory, matches files against configured\n * glob patterns, skips files already tracked in the manifest, and respects\n * the max-size limit.\n */\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { BinaryLifecycleConfig, BinaryLifecycleManifest } from \"./types.js\";\n\n/**\n * Test whether a filename matches any of the provided glob patterns.\n * Supports simple `*.ext` patterns (the default scan patterns).\n * For more complex globs a proper library should be used; this covers\n * the 95% case without adding a dependency.\n */\nexport function matchesPatterns(filename: string, patterns: string[]): boolean {\n const lower = filename.toLowerCase();\n for (const pattern of patterns) {\n // Simple *.ext matching\n if (pattern.startsWith(\"*.\")) {\n const ext = pattern.slice(1).toLowerCase(); // e.g. \".png\"\n if (lower.endsWith(ext)) return true;\n } else if (lower === pattern.toLowerCase()) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Scan memoryDir recursively for binary files matching the configured patterns.\n * Returns relative paths (relative to memoryDir) for files not yet tracked.\n */\nexport async function scanForBinaries(\n memoryDir: string,\n config: BinaryLifecycleConfig,\n manifest: BinaryLifecycleManifest,\n): Promise<string[]> {\n const tracked = new Set(manifest.assets.map((a) => a.originalPath));\n const results: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return; // skip unreadable directories\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n // Normalize to POSIX separators so redirect matching works on Windows\n // (markdown links always use forward slashes).\n const relativePath = path.relative(memoryDir, fullPath).split(path.sep).join(\"/\");\n\n if (entry.isDirectory()) {\n // Skip the manifest directory itself and hidden dirs starting with .\n if (entry.name === \".binary-lifecycle\") continue;\n await walk(fullPath);\n continue;\n }\n\n if (!entry.isFile()) continue;\n\n // Check pattern match\n if (!matchesPatterns(entry.name, config.scanPatterns)) continue;\n\n // Skip already tracked\n if (tracked.has(relativePath)) continue;\n\n // Check file size\n try {\n const stat = await fsp.stat(fullPath);\n if (stat.size > config.maxBinarySizeBytes) continue;\n if (stat.size === 0) continue; // skip empty files\n } catch {\n continue; // stat failure, skip\n }\n\n results.push(relativePath);\n }\n }\n\n await walk(memoryDir);\n return results;\n}\n","/**\n * Binary lifecycle manifest — read/write operations.\n *\n * The manifest lives at `${memoryDir}/.binary-lifecycle/manifest.json`.\n * Writes use the atomic temp-then-rename pattern (CLAUDE.md #54).\n */\n\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport type { BinaryLifecycleManifest } from \"./types.js\";\n\nconst MANIFEST_DIR = \".binary-lifecycle\";\nconst MANIFEST_FILE = \"manifest.json\";\n\nexport function manifestDir(memoryDir: string): string {\n return path.join(memoryDir, MANIFEST_DIR);\n}\n\nexport function manifestPath(memoryDir: string): string {\n return path.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);\n}\n\n/**\n * Read the manifest from disk. Returns a fresh empty manifest if the file\n * does not exist or contains invalid JSON (CLAUDE.md #18).\n */\nexport async function readManifest(memoryDir: string): Promise<BinaryLifecycleManifest> {\n const filePath = manifestPath(memoryDir);\n try {\n const raw = await fsp.readFile(filePath, \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n // CLAUDE.md #18: validate the parsed result is a non-null object.\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return emptyManifest();\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.version !== 1 || !Array.isArray(obj.assets)) {\n return emptyManifest();\n }\n return parsed as BinaryLifecycleManifest;\n } catch {\n return emptyManifest();\n }\n}\n\n/**\n * Write the manifest atomically: write to a temp file, then rename.\n * CLAUDE.md #54: never delete before write. Write temp first, rename atomically.\n */\nexport async function writeManifest(\n memoryDir: string,\n manifest: BinaryLifecycleManifest,\n): Promise<void> {\n const dir = manifestDir(memoryDir);\n await fsp.mkdir(dir, { recursive: true });\n const dest = manifestPath(memoryDir);\n const tmpSuffix = crypto.randomBytes(8).toString(\"hex\");\n const tmpPath = `${dest}.${tmpSuffix}.tmp`;\n // Sort keys for deterministic output (CLAUDE.md #38).\n const content = JSON.stringify(manifest, null, 2) + \"\\n\";\n await fsp.writeFile(tmpPath, content, \"utf-8\");\n try {\n await fsp.rename(tmpPath, dest);\n } catch (renameErr) {\n // Clean up temp on rename failure (cross-device edge case).\n try {\n await fsp.unlink(tmpPath);\n } catch {\n // ignore cleanup failure\n }\n throw renameErr;\n }\n}\n\nexport function emptyManifest(): BinaryLifecycleManifest {\n return { version: 1, assets: [] };\n}\n","/**\n * Binary lifecycle pipeline — mirror, redirect, clean.\n *\n * Three-stage pipeline:\n * 1. Mirror: upload binary to backend, record in manifest\n * 2. Redirect: scan markdown for inline refs, replace with redirect path\n * 3. Clean: after grace period, delete local copy\n */\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport type {\n BinaryAssetRecord,\n BinaryLifecycleConfig,\n PipelineResult,\n} from \"./types.js\";\nimport type { BinaryStorageBackend } from \"./backend.js\";\nimport { readManifest, writeManifest } from \"./manifest.js\";\nimport { scanForBinaries } from \"./scanner.js\";\n\n/** Minimal logger interface so we don't depend on the full logger module. */\ninterface PipelineLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n}\n\ninterface PipelineOptions {\n dryRun?: boolean;\n /** Force-clean all files past grace period, ignoring redirect status. */\n forceClean?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function hashFile(filePath: string): Promise<string> {\n const content = await fsp.readFile(filePath);\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nfunction guessMimeType(ext: string): string {\n const map: Record<string, string> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".pdf\": \"application/pdf\",\n \".mp3\": \"audio/mpeg\",\n \".mp4\": \"video/mp4\",\n \".wav\": \"audio/wav\",\n };\n return map[ext.toLowerCase()] ?? \"application/octet-stream\";\n}\n\n/**\n * Escape special regex characters in a string.\n * CLAUDE.md #46: always escapeRegex on user-derived parts.\n */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline stages\n// ---------------------------------------------------------------------------\n\nasync function stageMirror(\n memoryDir: string,\n newPaths: string[],\n backend: BinaryStorageBackend,\n assets: BinaryAssetRecord[],\n log: PipelineLogger,\n dryRun: boolean,\n): Promise<{ mirrored: number; errors: string[] }> {\n let mirrored = 0;\n const errors: string[] = [];\n\n for (const relPath of newPaths) {\n const fullPath = path.join(memoryDir, relPath);\n try {\n const stat = await fsp.stat(fullPath);\n const contentHash = await hashFile(fullPath);\n const ext = path.extname(relPath);\n const mimeType = guessMimeType(ext);\n const remotePath = relPath;\n\n let backendLocation = remotePath;\n if (!dryRun) {\n backendLocation = await backend.upload(fullPath, remotePath);\n }\n\n const record: BinaryAssetRecord = {\n originalPath: relPath,\n mirroredPath: backendLocation,\n contentHash,\n sizeBytes: stat.size,\n mimeType,\n mirroredAt: new Date().toISOString(),\n status: \"mirrored\",\n };\n\n assets.push(record);\n mirrored++;\n log.info(`[binary-lifecycle] mirrored: ${relPath} (${stat.size} bytes)${dryRun ? \" [dry-run]\" : \"\"}`);\n } catch (err) {\n const msg = `mirror failed for ${relPath}: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`[binary-lifecycle] ${msg}`);\n errors.push(msg);\n }\n }\n\n return { mirrored, errors };\n}\n\nasync function stageRedirect(\n memoryDir: string,\n assets: BinaryAssetRecord[],\n log: PipelineLogger,\n dryRun: boolean,\n): Promise<{ redirected: number; errors: string[] }> {\n let redirected = 0;\n const errors: string[] = [];\n\n // Only redirect assets that are mirrored but not yet redirected.\n const candidates = assets.filter((a) => a.status === \"mirrored\");\n if (candidates.length === 0) return { redirected, errors };\n\n // Find all markdown files in memoryDir (recursive).\n const mdFiles = await findMarkdownFiles(memoryDir);\n\n for (const asset of candidates) {\n let matchCount = 0;\n let writeFailCount = 0;\n for (const mdPath of mdFiles) {\n try {\n const content = await fsp.readFile(mdPath, \"utf-8\");\n\n // Build the match path relative to this markdown file's directory.\n // Markdown links like `` are file-relative, but\n // asset.originalPath is memory-root relative (e.g. `sub/image.png`).\n // Resolve the asset path relative to the markdown file's directory\n // so both forms match correctly.\n const mdDir = path.dirname(mdPath);\n const assetAbsolute = path.join(memoryDir, asset.originalPath);\n const relativeToMd = path.relative(mdDir, assetAbsolute);\n // Normalise to forward slashes for regex matching (markdown uses /).\n const relativeForward = relativeToMd.split(path.sep).join(\"/\");\n const escaped = escapeRegex(relativeForward);\n\n // Build a regex that matches markdown image/link references to the file.\n // Handles:  ,  , [text](./path)\n const pattern = new RegExp(\n `(!?\\\\[[^\\\\]]*\\\\]\\\\()(\\\\.\\\\/)?(${escaped})(\\\\))`,\n \"g\",\n );\n\n if (!pattern.test(content)) continue;\n matchCount++;\n\n if (!dryRun) {\n // Reset lastIndex after test().\n pattern.lastIndex = 0;\n const updated = content.replace(pattern, (_match, open, _dotSlash, _file, close) => {\n return `${open as string}${asset.mirroredPath}${close as string}`;\n });\n await fsp.writeFile(mdPath, updated, \"utf-8\");\n }\n } catch (err) {\n // Track write failures separately so we don't transition status\n // when some markdown rewrites failed (P1: block redirect on failure).\n writeFailCount++;\n const msg = `redirect scan failed for ${mdPath}: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`[binary-lifecycle] ${msg}`);\n errors.push(msg);\n }\n }\n\n // Only transition to \"redirected\" when at least one reference was found\n // AND all matched files were rewritten successfully.\n if (matchCount > 0 && writeFailCount === 0) {\n asset.status = \"redirected\";\n asset.redirectedAt = new Date().toISOString();\n redirected++;\n log.info(`[binary-lifecycle] redirected: ${asset.originalPath}${dryRun ? \" [dry-run]\" : \"\"}`);\n } else if (matchCount > 0 && writeFailCount > 0) {\n // Some rewrites failed — set error status so the asset is not cleaned\n // prematurely. It can be retried on the next pipeline run.\n asset.status = \"error\";\n log.warn(\n `[binary-lifecycle] redirect partial failure for ${asset.originalPath}: ` +\n `${matchCount} match(es), ${writeFailCount} write failure(s) — status set to error`,\n );\n }\n }\n\n return { redirected, errors };\n}\n\nasync function stageClean(\n memoryDir: string,\n assets: BinaryAssetRecord[],\n gracePeriodDays: number,\n log: PipelineLogger,\n dryRun: boolean,\n forceClean: boolean,\n): Promise<{ cleaned: number; errors: string[] }> {\n let cleaned = 0;\n const errors: string[] = [];\n const now = Date.now();\n const graceMs = gracePeriodDays * 24 * 60 * 60 * 1000;\n\n // Clean only assets that have been redirected (markdown refs already rewritten).\n // Mirrored-only assets must NOT be cleaned — their markdown refs still point\n // to the local file, so deletion would break links.\n const candidates = assets.filter(\n (a) => a.status === \"redirected\",\n );\n\n for (const asset of candidates) {\n const mirroredMs = new Date(asset.mirroredAt).getTime();\n const ageMs = now - mirroredMs;\n\n if (!forceClean && ageMs < graceMs) {\n // Not yet past grace period.\n continue;\n }\n\n const fullPath = path.join(memoryDir, asset.originalPath);\n try {\n if (!dryRun) {\n await fsp.unlink(fullPath);\n }\n asset.status = \"cleaned\";\n asset.cleanedAt = new Date().toISOString();\n cleaned++;\n log.info(`[binary-lifecycle] cleaned: ${asset.originalPath}${dryRun ? \" [dry-run]\" : \"\"}`);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n // Already gone — mark as cleaned.\n asset.status = \"cleaned\";\n asset.cleanedAt = new Date().toISOString();\n cleaned++;\n } else {\n const msg = `clean failed for ${asset.originalPath}: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`[binary-lifecycle] ${msg}`);\n errors.push(msg);\n }\n }\n }\n\n return { cleaned, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Markdown file discovery\n// ---------------------------------------------------------------------------\n\nasync function findMarkdownFiles(dir: string): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(current: string): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fsp.readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = path.join(current, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === \".binary-lifecycle\") continue;\n await walk(full);\n } else if (entry.isFile() && entry.name.endsWith(\".md\")) {\n results.push(full);\n }\n }\n }\n\n await walk(dir);\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Main pipeline entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Run the binary lifecycle pipeline: scan, mirror, redirect, clean.\n */\nexport async function runBinaryLifecyclePipeline(\n memoryDir: string,\n config: BinaryLifecycleConfig,\n backend: BinaryStorageBackend,\n log: PipelineLogger,\n opts?: PipelineOptions,\n): Promise<PipelineResult> {\n const dryRun = opts?.dryRun ?? false;\n const forceClean = opts?.forceClean ?? false;\n\n const manifest = await readManifest(memoryDir);\n\n // Stage 0: Scan\n const newPaths = await scanForBinaries(memoryDir, config, manifest);\n const scanned = newPaths.length;\n\n // Stage 1: Mirror\n const mirrorResult = await stageMirror(\n memoryDir,\n newPaths,\n backend,\n manifest.assets,\n log,\n dryRun,\n );\n\n // Stage 2: Redirect\n const redirectResult = await stageRedirect(memoryDir, manifest.assets, log, dryRun);\n\n // Stage 3: Clean\n const cleanResult = await stageClean(\n memoryDir,\n manifest.assets,\n config.gracePeriodDays,\n log,\n dryRun,\n forceClean,\n );\n\n // Persist manifest (unless dry-run).\n manifest.lastScanAt = new Date().toISOString();\n if (!dryRun) {\n await writeManifest(memoryDir, manifest);\n }\n\n const allErrors = [\n ...mirrorResult.errors,\n ...redirectResult.errors,\n ...cleanResult.errors,\n ];\n\n return {\n scanned,\n mirrored: mirrorResult.mirrored,\n redirected: redirectResult.redirected,\n cleaned: cleanResult.cleaned,\n errors: allErrors,\n dryRun,\n };\n}\n","/**\n * @remnic/core — Workspace Tree Projection\n *\n * Generates a human-readable `.engram/context-tree/` from canonical memory.\n * Each node is a `.md` file with rich metadata, * (provenance, trust, confidence, source anchors).\n * Manual edits are preserved in fenced blocks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getCategoryDir, ALL_CATEGORY_KEYS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface TreeNode {\n /** Relative path from context-tree root, e.g. \"entities/claude.md\" */\n path: string;\n /** Category from canonical memory */\n category: string;\n /** Human-readable title */\n title: string;\n /** File content (rendered markdown) */\n content: string;\n /** Source memory IDs that contributed to this node */\n sourceAnchors: string[];\n /** Confidence (0-1) */\n confidence: number;\n /** Trust zone classification */\n confidenceTier: string;\n /** When this node was generated */\n generatedAt: string;\n /** Provenance chain */\n provenance: ProvenanceEntry[];\n}\n\nexport interface ProvenanceEntry {\n memoryId: string;\n source: string;\n extracted: string;\n}\n\nexport interface GenerateOptions {\n /** Memory root directory (e.g. ~/.openclaw/workspace/memory/local) */\n memoryDir: string;\n /** Output directory (e.g. .engram/context-tree) */\n outputDir: string;\n /** Categories to include (default: all) */\n categories?: string[];\n /** Whether to include entity graph */\n includeEntities?: boolean;\n /** Whether to include orphaned questions */\n includeQuestions?: boolean;\n /** Max nodes per category (default: unlimited) */\n maxPerCategory?: number;\n /** Whether to watch for changes and regenerate incrementally */\n watch?: boolean;\n}\n\nexport interface GenerateResult {\n nodesGenerated: number;\n nodesSkipped: number;\n categories: Record<string, number>;\n durationMs: number;\n outputDir: string;\n}\n\n// ── Generation ──────────────────────────────────────────────────────────────\n\n/**\n * Generate a context tree from canonical memory.\n *\n * Reads memory `.md` files from the source directory, * and projects them into a clean, * human-readable tree structure at `outputDir`.\n */\nexport async function generateContextTree(options: GenerateOptions): Promise<GenerateResult> {\n const startTime = Date.now();\n const {\n memoryDir,\n outputDir,\n categories: filterCategories,\n includeEntities = true,\n includeQuestions = true,\n maxPerCategory = Infinity,\n } = options;\n\n let nodesGenerated = 0;\n let nodesSkipped = 0;\n const categoryCounts: Record<string, number> = {};\n\n // Ensure output directory exists\n fs.mkdirSync(outputDir, { recursive: true });\n\n // Process each category (exclude 'question' — handled by separate includeQuestions pass)\n const allCategories = filterCategories ?? ALL_CATEGORY_KEYS.filter((c) => c !== \"question\");\n\n for (const category of allCategories) {\n const categoryDir = getCategoryDir(memoryDir, category);\n if (!fs.existsSync(categoryDir)) continue;\n\n categoryCounts[category] = 0;\n const files = walkR(categoryDir);\n let count = 0;\n\n for (const filePath of files) {\n if (count >= maxPerCategory) {\n nodesSkipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf8\");\n const fm = parseFrontmatter(content);\n if (!fm) {\n nodesSkipped++;\n continue;\n }\n\n const node = projectNode(filePath, category, fm, content);\n if (!node) {\n nodesSkipped++;\n continue;\n }\n\n // Write node to output\n const outputPath = path.join(outputDir, node.path);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, node.content);\n\n nodesGenerated++;\n categoryCounts[category] = (categoryCounts[category] ?? 0) + 1;\n count++;\n }\n }\n\n // Process entities\n if (includeEntities) {\n const entitiesDir = path.join(memoryDir, \"entities\");\n if (fs.existsSync(entitiesDir)) {\n categoryCounts[\"entity\"] = 0;\n const entityFiles = walkR(entitiesDir);\n let count = 0;\n\n for (const filePath of entityFiles) {\n if (count >= maxPerCategory) {\n nodesSkipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf8\");\n const fileName = path.basename(filePath, \".md\");\n const node = projectEntityNode(fileName, content);\n\n const outputPath = path.join(outputDir, \"entities\", `${fileName}.md`);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, node.content);\n\n nodesGenerated++;\n categoryCounts[\"entity\"] = (categoryCounts[\"entity\"] ?? 0) + 1;\n count++;\n }\n }\n }\n\n // Process questions\n if (includeQuestions) {\n const questionsDir = path.join(memoryDir, \"questions\");\n if (fs.existsSync(questionsDir)) {\n categoryCounts[\"question\"] = 0;\n const qFiles = walkR(questionsDir);\n let count = 0;\n\n for (const filePath of qFiles) {\n if (count >= maxPerCategory) {\n nodesSkipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf8\");\n const fm = parseFrontmatter(content);\n if (!fm) {\n nodesSkipped++;\n continue;\n }\n\n const node = projectNode(filePath, \"question\", fm, content);\n if (!node) {\n nodesSkipped++;\n continue;\n }\n\n const outputPath = path.join(outputDir, node.path);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, node.content);\n\n nodesGenerated++;\n categoryCounts[\"question\"] = (categoryCounts[\"question\"] ?? 0) + 1;\n count++;\n }\n }\n }\n\n // Write index\n const index = generateIndex(categoryCounts, outputDir);\n fs.writeFileSync(path.join(outputDir, \"INDEX.md\"), index);\n\n return {\n nodesGenerated,\n nodesSkipped,\n categories: categoryCounts,\n durationMs: Date.now() - startTime,\n outputDir,\n };\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\n\nfunction walkR(dir: string): string[] {\n const results: string[] = [];\n function walk(directory: string): void {\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n const fullPath = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.name.endsWith(\".md\")) {\n results.push(fullPath);\n }\n }\n }\n walk(dir);\n return results;\n}\n\ninterface Frontmatter {\n id: string;\n category: string;\n created: string;\n updated: string;\n confidence: number;\n confidenceTier: string;\n tags: string[];\n source: string;\n entityRef?: string;\n lifecycleState?: string;\n}\n\nfunction parseFrontmatter(content: string): Frontmatter | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fmText = match[1];\n const fm: Record<string, unknown> = {};\n for (const line of fmText.split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n if (key === \"tags\") {\n try {\n fm[key] = JSON.parse(value) as string[];\n } catch {\n fm[key] = [] as string[];\n }\n } else if (key === \"confidence\") {\n const parsed = parseFloat(value);\n fm[key] = Number.isFinite(parsed) ? parsed : 0;\n } else {\n fm[key] = value;\n }\n }\n return fm as unknown as Frontmatter;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction projectNode(\n filePath: string,\n category: string,\n fm: Frontmatter,\n rawContent: string,\n): TreeNode | null {\n const body = extractBody(rawContent);\n const fileName = path.basename(filePath, \".md\");\n const dateDir = path.basename(path.dirname(filePath));\n\n // Build relative path: category/date/file or just category/file\n let relPath: string;\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(dateDir)) {\n relPath = path.join(category, dateDir, `${fileName}.md`);\n } else {\n relPath = path.join(category, `${fileName}.md`);\n }\n\n const generatedAt = new Date().toISOString();\n\n const md = `# ${fm.id}\n\n> **Category:** ${fm.category}\n> **Created:** ${fm.created}\n> **Updated:** ${fm.updated ?? fm.created}\n> **Confidence:** ${fm.confidence} (${fm.confidenceTier}${fm.lifecycleState ? `, ${fm.lifecycleState}` : \"\"})\n${fm.tags?.length ? `\\n> **Tags:** ${fm.tags.join(\", \")}` : \"\"}\n${fm.entityRef ? `\\n> **Entity:** ${fm.entityRef}` : \"\"}\n> **Source:** ${fm.source ?? \"unknown\"}\n> **Projected:** ${generatedAt}\n\n---\n\n${body}\n`;\n\n return {\n path: relPath,\n category,\n title: fm.id,\n content: md,\n sourceAnchors: [fm.id],\n confidence: fm.confidence ?? 0,\n confidenceTier: fm.confidenceTier ?? \"unknown\",\n generatedAt,\n provenance: [{\n memoryId: fm.id,\n source: fm.source ?? \"unknown\",\n extracted: fm.created,\n }],\n };\n}\n\nfunction projectEntityNode(fileName: string, content: string): TreeNode {\n const generatedAt = new Date().toISOString();\n\n const md = `> **Projected:** ${generatedAt}\n> **Source:** canonical\n\n---\n\n${content}\n`;\n\n return {\n path: path.join(\"entities\", `${fileName}.md`),\n category: \"entity\",\n title: fileName,\n content: md,\n sourceAnchors: [fileName],\n confidence: 1,\n confidenceTier: \"explicit\",\n generatedAt,\n provenance: [{\n memoryId: fileName,\n source: \"canonical\",\n extracted: generatedAt,\n }],\n };\n}\n\nfunction generateIndex(\n categoryCounts: Record<string, number>,\n outputDir: string,\n): string {\n const lines = [\n \"# Context Tree Index\",\n \"\",\n `Generated: ${new Date().toISOString()}`,\n \"\",\n \"## Summary\",\n \"\",\n `| Category | Count |`,\n `|----------|-------|`,\n ];\n\n let total = 0;\n for (const [cat, count] of Object.entries(categoryCounts).sort()) {\n lines.push(`| ${cat} | ${count} |`);\n total += count;\n }\n\n lines.push(\"\");\n lines.push(`**Total:** ${total} nodes`);\n lines.push(\"\");\n lines.push(\"## Structure\");\n lines.push(\"\");\n lines.push(\"```\");\n lines.push(\"context-tree/\");\n lines.push(\"├── entities/ # Entity knowledge graph\");\n lines.push(\"├── fact/ # Factual memories (date-partitioned)\");\n lines.push(\"├── correction/ # Correction memories\");\n lines.push(\"├── decision/ # Decisions\");\n lines.push(\"├── moment/ # Notable moments\");\n lines.push(\"├── preference/ # Preferences\");\n lines.push(\"├── principle/ # Principles\");\n lines.push(\"├── question/ # Open questions\");\n lines.push(\"└── INDEX.md # This file\");\n lines.push(\"```\");\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","/**\n * @remnic/core — Category Directory Map\n *\n * Shared mapping of memory category names to directory names.\n * Single source of truth — import from here instead of copy-pasting.\n */\n\nimport path from \"node:path\";\n\nexport const CATEGORY_DIR_MAP: Record<string, string> = {\n correction: \"corrections\",\n question: \"questions\",\n preference: \"preferences\",\n decision: \"decisions\",\n moment: \"moments\",\n commitment: \"commitments\",\n principle: \"principles\",\n rule: \"rules\",\n skill: \"skills\",\n relationship: \"relationships\",\n procedure: \"procedures\",\n reasoning_trace: \"reasoning-traces\",\n};\n\n/** All directory names derived from CATEGORY_DIR_MAP, plus \"facts\" (the default). */\nexport const ALL_CATEGORY_DIRS: string[] = [\n \"facts\",\n ...Object.values(CATEGORY_DIR_MAP),\n];\n\n/** All category keys (singular form) — used when iterating categories and calling getCategoryDir. */\nexport const ALL_CATEGORY_KEYS: string[] = [\n \"fact\",\n ...Object.keys(CATEGORY_DIR_MAP),\n];\n\n/**\n * Resolve a category name to its directory path under memoryDir.\n * Falls back to `facts/` for unknown categories.\n */\nexport function getCategoryDir(memoryDir: string, category: string): string {\n const dir = CATEGORY_DIR_MAP[category];\n return dir ? path.join(memoryDir, dir) : path.join(memoryDir, \"facts\");\n}\n","/**\n * @remnic/core — Onboarding\n *\n * Detects project language, shape, and documentation to produce\n * an onboarding plan for memory ingestion.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface OnboardOptions {\n /** Directory to scan (defaults to cwd) */\n directory: string;\n /** Max depth to walk (default: 6) */\n maxDepth?: number;\n /** Directories to skip */\n excludeDirs?: string[];\n}\n\nexport interface LanguageInfo {\n /** Language name (e.g. \"TypeScript\", \"Python\") */\n language: string;\n /** Confidence in detection (0-1) */\n confidence: number;\n /** Evidence (e.g. [\"package.json\", \"tsconfig.json\", \"*.ts files\"]) */\n evidence: string[];\n}\n\nexport interface DocFile {\n /** Absolute path */\n path: string;\n /** Relative path from project root */\n relativePath: string;\n /** Estimated type */\n kind: \"readme\" | \"changelog\" | \"contributing\" | \"license\" | \"config\" | \"docs\" | \"other\";\n /** File size in bytes */\n size: number;\n}\n\nexport type ProjectShape = \"app\" | \"library\" | \"monorepo\" | \"workspace\" | \"script\" | \"unknown\";\n\nexport interface OnboardResult {\n /** Project root */\n directory: string;\n /** Detected languages (sorted by confidence) */\n languages: LanguageInfo[];\n /** Detected project shape */\n shape: ProjectShape;\n /** Shape evidence */\n shapeEvidence: string[];\n /** Discovered documentation files */\n docs: DocFile[];\n /** Total files scanned */\n totalFiles: number;\n /** Duration in ms */\n durationMs: number;\n /** Suggested ingestion plan */\n plan: IngestionPlan;\n}\n\nexport interface IngestionPlan {\n /** Priority files to ingest first */\n priorityFiles: DocFile[];\n /** Estimated total files to ingest */\n estimatedFiles: number;\n /** Recommended categories */\n categories: string[];\n /** Suggested memory namespace */\n suggestedNamespace: string;\n}\n\n// ── Language detection rules ─────────────────────────────────────────────────\n\ninterface LanguageRule {\n language: string;\n extensions: string[];\n manifests: string[];\n configFiles: string[];\n}\n\nconst LANGUAGE_RULES: LanguageRule[] = [\n {\n language: \"TypeScript\",\n extensions: [\".ts\", \".tsx\"],\n manifests: [\"package.json\"],\n configFiles: [\"tsconfig.json\", \"tsup.config.ts\"],\n },\n {\n language: \"JavaScript\",\n extensions: [\".js\", \".jsx\", \".mjs\", \".cjs\"],\n manifests: [\"package.json\"],\n configFiles: [\".eslintrc\", \".prettierrc\"],\n },\n {\n language: \"Python\",\n extensions: [\".py\", \".pyi\"],\n manifests: [\"pyproject.toml\", \"setup.py\", \"setup.cfg\", \"requirements.txt\"],\n configFiles: [\"mypy.ini\", \".flake8\", \"tox.ini\"],\n },\n {\n language: \"Go\",\n extensions: [\".go\"],\n manifests: [\"go.mod\", \"go.sum\"],\n configFiles: [],\n },\n {\n language: \"Rust\",\n extensions: [\".rs\"],\n manifests: [\"Cargo.toml\"],\n configFiles: [],\n },\n {\n language: \"Ruby\",\n extensions: [\".rb\"],\n manifests: [\"Gemfile\", \"*.gemspec\"],\n configFiles: [\".rubocop.yml\"],\n },\n {\n language: \"PHP\",\n extensions: [\".php\"],\n manifests: [\"composer.json\"],\n configFiles: [\"phpcs.xml\"],\n },\n {\n language: \"Java\",\n extensions: [\".java\", \".kt\"],\n manifests: [\"pom.xml\", \"build.gradle\", \"build.gradle.kts\"],\n configFiles: [],\n },\n {\n language: \"Swift\",\n extensions: [\".swift\"],\n manifests: [\"Package.swift\", \"Podfile\"],\n configFiles: [],\n },\n {\n language: \"C#\",\n extensions: [\".cs\"],\n manifests: [\"*.csproj\", \"*.sln\"],\n configFiles: [],\n },\n {\n language: \"Shell\",\n extensions: [\".sh\", \".bash\", \".zsh\"],\n manifests: [],\n configFiles: [],\n },\n {\n language: \"Dart\",\n extensions: [\".dart\"],\n manifests: [\"pubspec.yaml\"],\n configFiles: [],\n },\n {\n language: \"Elixir\",\n extensions: [\".ex\", \".exs\"],\n manifests: [\"mix.exs\"],\n configFiles: [],\n },\n];\n\nconst DEFAULT_EXCLUDE = new Set([\n \"node_modules\",\n \".git\",\n \"vendor\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n \"dist\",\n \"build\",\n \".next\",\n \".nuxt\",\n \"target\",\n \"coverage\",\n \".engram\",\n]);\n\n// ── Main function ────────────────────────────────────────────────────────────\n\nexport function onboard(options: OnboardOptions): OnboardResult {\n const startTime = Date.now();\n const {\n directory,\n maxDepth = 6,\n excludeDirs = [],\n } = options;\n\n const exclude = new Set([...DEFAULT_EXCLUDE, ...excludeDirs]);\n\n // Collect all files\n const files = walkDir(directory, exclude, maxDepth);\n\n // Detect languages\n const languages = detectLanguages(files, directory);\n\n // Detect shape\n const { shape, evidence: shapeEvidence } = detectShape(files, directory);\n\n // Discover docs\n const docs = discoverDocs(files, directory);\n\n // Build plan\n const plan = buildPlan(languages, shape, docs, directory);\n\n return {\n directory,\n languages,\n shape,\n shapeEvidence,\n docs,\n totalFiles: files.length,\n durationMs: Date.now() - startTime,\n plan,\n };\n}\n\n// ── Walk directory ───────────────────────────────────────────────────────────\n\nfunction walkDir(\n root: string,\n exclude: Set<string>,\n maxDepth: number,\n): string[] {\n const results: string[] = [];\n\n function walk(dir: string, depth: number): void {\n if (depth > maxDepth) return;\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (exclude.has(entry.name)) continue;\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath, depth + 1);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n }\n\n walk(root, 0);\n return results;\n}\n\n// ── Language detection ───────────────────────────────────────────────────────\n\nfunction detectLanguages(files: string[], root: string): LanguageInfo[] {\n const results: LanguageInfo[] = [];\n\n // Count extensions\n const extCounts = new Map<string, number>();\n for (const f of files) {\n const ext = path.extname(f).toLowerCase();\n if (ext) extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);\n }\n\n // Check manifests at root level\n const rootFiles = new Set(\n files\n .filter((f) => path.dirname(f) === root)\n .map((f) => path.basename(f)),\n );\n\n for (const rule of LANGUAGE_RULES) {\n const evidence: string[] = [];\n let score = 0;\n\n // Extension matches\n let extMatch = 0;\n for (const ext of rule.extensions) {\n const count = extCounts.get(ext) ?? 0;\n if (count > 0) {\n extMatch += count;\n evidence.push(`${ext} files (${count})`);\n }\n }\n score += Math.min(extMatch * 0.05, 0.5);\n\n // Manifest matches\n for (const manifest of rule.manifests) {\n if (manifest.includes(\"*\")) {\n // Glob pattern — e.g. \"*.gemspec\" matches files ending with \".gemspec\"\n const suffix = manifest.replaceAll(/\\*/g, \"\");\n if ([...rootFiles].some((f) => f.endsWith(suffix))) {\n score += 0.2;\n evidence.push(manifest);\n }\n } else if (rootFiles.has(manifest)) {\n score += 0.2;\n evidence.push(manifest);\n }\n }\n\n // Config matches\n for (const cfg of rule.configFiles) {\n if (rootFiles.has(cfg)) {\n score += 0.1;\n evidence.push(cfg);\n }\n }\n\n if (score > 0) {\n results.push({\n language: rule.language,\n confidence: Math.min(score, 1),\n evidence,\n });\n }\n }\n\n return results.sort((a, b) => b.confidence - a.confidence);\n}\n\n// ── Shape detection ──────────────────────────────────────────────────────────\n\nfunction detectShape(\n files: string[],\n root: string,\n): { shape: ProjectShape; evidence: string[] } {\n const rootFiles = new Set(\n files\n .filter((f) => path.dirname(f) === root)\n .map((f) => path.basename(f)),\n );\n\n const rootDirs = new Set<string>();\n try {\n for (const entry of fs.readdirSync(root, { withFileTypes: true })) {\n if (entry.isDirectory()) rootDirs.add(entry.name);\n }\n } catch {\n // ignore\n }\n\n const evidence: string[] = [];\n\n // Monorepo: workspace packages/ or libs/ dirs + root package.json with workspaces\n if (rootFiles.has(\"package.json\")) {\n const pkg = readJsonSafe(path.join(root, \"package.json\"));\n if (pkg?.workspaces) {\n evidence.push(\"package.json has workspaces\");\n return { shape: \"monorepo\", evidence };\n }\n }\n\n if (rootDirs.has(\"packages\") || rootDirs.has(\"libs\")) {\n evidence.push(\"has packages/ or libs/ directory\");\n return { shape: \"monorepo\", evidence };\n }\n\n // Workspace: pnpm-workspace.yaml, Cargo workspace, go.work\n if (rootFiles.has(\"pnpm-workspace.yaml\") || rootFiles.has(\"go.work\")) {\n evidence.push(\"workspace manifest found\");\n return { shape: \"workspace\", evidence };\n }\n\n const cargoToml = readTomlWorkspace(path.join(root, \"Cargo.toml\"));\n if (cargoToml) {\n evidence.push(\"Cargo.toml has workspace\");\n return { shape: \"workspace\", evidence };\n }\n\n // Library: has lib/ or src/index.*, exports in package.json\n if (rootFiles.has(\"package.json\")) {\n const pkg = readJsonSafe(path.join(root, \"package.json\"));\n if (pkg?.exports || pkg?.main) {\n // If it also has \"bin\", it's more of an app\n if (pkg?.bin) {\n evidence.push(\"package.json has bin\");\n return { shape: \"app\", evidence };\n }\n evidence.push(\"package.json has exports/main\");\n return { shape: \"library\", evidence };\n }\n }\n\n // Check for app indicators\n if (\n rootFiles.has(\"Dockerfile\") ||\n rootFiles.has(\"docker-compose.yml\") ||\n rootFiles.has(\"docker-compose.yaml\") ||\n rootDirs.has(\"app\") ||\n rootDirs.has(\"src\") && rootDirs.has(\"public\")\n ) {\n evidence.push(\"app-like structure detected\");\n return { shape: \"app\", evidence };\n }\n\n // Script: few files, no package manifest\n if (files.length <= 5 && !rootFiles.has(\"package.json\") && !rootFiles.has(\"pyproject.toml\")) {\n evidence.push(\"few files, no manifest\");\n return { shape: \"script\", evidence };\n }\n\n return { shape: \"unknown\", evidence: [\"no strong shape signal\"] };\n}\n\n// ── Doc discovery ────────────────────────────────────────────────────────────\n\nfunction discoverDocs(files: string[], root: string): DocFile[] {\n const docs: DocFile[] = [];\n const docPatterns: Array<{ pattern: RegExp; kind: DocFile[\"kind\"] }> = [\n { pattern: /^readme(\\.\\w+)?$/i, kind: \"readme\" },\n { pattern: /^changelog(\\.\\w+)?$/i, kind: \"changelog\" },\n { pattern: /^changes(\\.\\w+)?$/i, kind: \"changelog\" },\n { pattern: /^contributing(\\.\\w+)?$/i, kind: \"contributing\" },\n { pattern: /^code[_-]of[_-]conduct(\\.\\w+)?$/i, kind: \"contributing\" },\n { pattern: /^license(\\.\\w+)?$/i, kind: \"license\" },\n { pattern: /^copying(\\.\\w+)?$/i, kind: \"license\" },\n { pattern: /^\\.env\\.example$/i, kind: \"config\" },\n { pattern: /^\\.editorconfig$/i, kind: \"config\" },\n ];\n\n for (const filePath of files) {\n const basename = path.basename(filePath).toLowerCase();\n const relPath = path.relative(root, filePath);\n let kind: DocFile[\"kind\"] | undefined;\n\n // Check against patterns\n for (const { pattern, kind: k } of docPatterns) {\n if (pattern.test(basename)) {\n kind = k;\n break;\n }\n }\n\n // Check docs/ directories\n if (!kind && isUnderDocsDir(relPath)) {\n kind = \"docs\";\n }\n\n // Check markdown files at root or in docs/\n if (!kind && (basename.endsWith(\".md\") || basename.endsWith(\".mdx\"))) {\n if (path.dirname(relPath) === \".\" || isUnderDocsDir(relPath)) {\n kind = \"docs\";\n }\n }\n\n if (kind) {\n let size = 0;\n try {\n size = fs.statSync(filePath).size;\n } catch {\n // ignore\n }\n docs.push({\n path: filePath,\n relativePath: relPath,\n kind,\n size,\n });\n }\n }\n\n return docs;\n}\n\nfunction isUnderDocsDir(relPath: string): boolean {\n const parts = relPath.split(path.sep);\n return parts[0] === \"docs\" || parts[0] === \"doc\" || parts[0] === \"documentation\";\n}\n\n// ── Plan generation ──────────────────────────────────────────────────────────\n\nfunction buildPlan(\n languages: LanguageInfo[],\n shape: ProjectShape,\n docs: DocFile[],\n _root: string,\n): IngestionPlan {\n // Priority: README, CONTRIBUTING, then other docs\n const priorityOrder: Record<DocFile[\"kind\"], number> = {\n readme: 0,\n contributing: 1,\n changelog: 2,\n license: 3,\n docs: 4,\n config: 5,\n other: 6,\n };\n\n const priorityFiles = [...docs]\n .filter((d) => d.size > 0)\n .sort((a, b) => priorityOrder[a.kind] - priorityOrder[b.kind]);\n\n // Recommended categories based on project shape\n const categories: string[] = [\"fact\", \"preference\", \"decision\", \"principle\"];\n if (shape === \"monorepo\" || shape === \"workspace\") {\n categories.push(\"entity\");\n }\n\n // Namespace suggestion from primary language\n const suggestedNamespace = languages.length > 0\n ? languages[0].language.toLowerCase()\n : \"project\";\n\n return {\n priorityFiles,\n estimatedFiles: docs.filter((d) => d.size > 0).length,\n categories,\n suggestedNamespace,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction readJsonSafe(filePath: string): Record<string, unknown> | null {\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction readTomlWorkspace(filePath: string): boolean {\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n return content.includes(\"[workspace]\");\n } catch {\n return false;\n }\n}\n","/**\n * @remnic/core — Curation\n *\n * Deliberate ingestion of files into memory with provenance tracking.\n * Supports statement-level extraction, dedup, and contradiction checks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { getCategoryDir, ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface CurateOptions {\n /** File or directory path to curate */\n targetPath: string;\n /** Memory root directory for writing */\n memoryDir: string;\n /** Source label (e.g. \"manual\", \"docs\", \"onboarding\") */\n source?: string;\n /** Category override (default: auto-detect) */\n category?: string;\n /** Confidence to assign (default: 0.9 for curated items) */\n confidence?: number;\n /** Entity reference to attach */\n entityRef?: string;\n /** Tags to add */\n tags?: string[];\n /** Whether to perform dedup check against existing memories */\n checkDuplicates?: boolean;\n /** Whether to detect contradictions */\n checkContradictions?: boolean;\n /** Whether to write files (default: true). False = dry run */\n write?: boolean;\n}\n\nexport interface CuratedStatement {\n /** Unique ID for this statement */\n id: string;\n /** The extracted statement text */\n content: string;\n /** Category */\n category: string;\n /** Confidence */\n confidence: number;\n /** Provenance info */\n provenance: StatementProvenance;\n /** Hash of content for dedup */\n contentHash: string;\n /** Tags */\n tags: string[];\n /** Entity reference */\n entityRef?: string;\n}\n\nexport interface StatementProvenance {\n /** Source file path */\n sourcePath: string;\n /** Relative path from project root */\n relativePath: string;\n /** Source label */\n source: string;\n /** Line number if extractable (0 = unknown) */\n lineNumber: number;\n /** Timestamp of ingestion */\n ingestedAt: string;\n /** Hash of the source file for diff tracking */\n sourceFileHash: string;\n}\n\nexport interface CurateResult {\n /** Statements extracted */\n statements: CuratedStatement[];\n /** Files processed */\n filesProcessed: number;\n /** Files skipped (empty, binary, etc.) */\n filesSkipped: number;\n /** Duplicate statements found (if checkDuplicates) */\n duplicates: DuplicateResult[];\n /** Contradictions found (if checkContradictions) */\n contradictions: ContradictionResult[];\n /** Memory files written */\n written: string[];\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface DuplicateResult {\n /** New statement */\n newStatement: CuratedStatement;\n /** Existing memory ID that matches */\n existingId: string;\n /** Similarity score (0-1) */\n similarity: number;\n /** Recommended action */\n action: \"skip\" | \"merge\" | \"keep\";\n}\n\nexport interface ContradictionResult {\n /** New statement */\n newStatement: CuratedStatement;\n /** Conflicting memory ID */\n conflictingId: string;\n /** The conflicting content */\n conflictingContent: string;\n /** Severity */\n severity: \"high\" | \"medium\" | \"low\";\n}\n\n// ── Main function ────────────────────────────────────────────────────────────\n\nexport async function curate(options: CurateOptions): Promise<CurateResult> {\n const startTime = Date.now();\n const {\n targetPath,\n memoryDir,\n source = \"curation\",\n category: categoryOverride,\n confidence = 0.9,\n entityRef,\n tags = [],\n checkDuplicates = true,\n checkContradictions = false,\n write = true,\n } = options;\n\n const statements: CuratedStatement[] = [];\n const written: string[] = [];\n const duplicates: DuplicateResult[] = [];\n const contradictions: ContradictionResult[] = [];\n let filesProcessed = 0;\n let filesSkipped = 0;\n\n // Determine targets\n const targets = resolveTargets(targetPath);\n\n // Load existing memories for dedup/contradiction checks\n const existingMemories = checkDuplicates || checkContradictions\n ? loadExistingMemories(memoryDir)\n : new Map();\n\n // Process each file\n for (const filePath of targets) {\n const content = readFileSafe(filePath);\n if (!content) {\n filesSkipped++;\n continue;\n }\n\n if (isBinary(content)) {\n filesSkipped++;\n continue;\n }\n\n filesProcessed++;\n\n const sourceFileHash = hashContent(content);\n const fileStatements = extractStatements(\n content,\n filePath,\n targetPath,\n source,\n sourceFileHash,\n categoryOverride,\n confidence,\n entityRef,\n tags,\n );\n\n for (const stmt of fileStatements) {\n // Dedup check\n if (checkDuplicates) {\n const dup = findDuplicate(stmt, existingMemories);\n if (dup) {\n duplicates.push(dup);\n if (dup.action === \"skip\") continue;\n }\n }\n\n // Contradiction check\n if (checkContradictions) {\n const contra = findContradiction(stmt, existingMemories);\n if (contra) {\n contradictions.push(contra);\n }\n }\n\n statements.push(stmt);\n\n // Write to memory\n if (write) {\n const writtenPath = writeStatement(stmt, memoryDir);\n if (writtenPath) {\n written.push(writtenPath);\n existingMemories.set(stmt.contentHash, {\n id: stmt.id,\n content: stmt.content,\n category: stmt.category,\n });\n }\n }\n }\n }\n\n return {\n statements,\n filesProcessed,\n filesSkipped,\n duplicates,\n contradictions,\n written,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Target resolution ────────────────────────────────────────────────────────\n\nfunction resolveTargets(targetPath: string): string[] {\n const stat = fs.statSync(targetPath);\n if (stat.isFile()) return [targetPath];\n\n // Directory — walk for .md, .txt, .mdx\n const results: string[] = [];\n const extensions = new Set([\".md\", \".txt\", \".mdx\", \".rst\"]);\n\n function walk(dir: string): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== \"node_modules\" && entry.name !== \".git\") {\n walk(fullPath);\n }\n } else if (extensions.has(path.extname(entry.name).toLowerCase())) {\n results.push(fullPath);\n }\n }\n }\n\n walk(targetPath);\n return results;\n}\n\n// ── Statement extraction ─────────────────────────────────────────────────────\n\nfunction extractStatements(\n content: string,\n filePath: string,\n projectRoot: string,\n source: string,\n sourceFileHash: string,\n categoryOverride: string | undefined,\n confidence: number,\n entityRef: string | undefined,\n tags: string[],\n): CuratedStatement[] {\n const relativePath = path.relative(projectRoot, filePath);\n const statements: CuratedStatement[] = [];\n const now = new Date().toISOString();\n\n // Split content into paragraphs/lines and extract meaningful statements\n const paragraphs = content\n .split(/\\n{2,}/)\n .map((p) => p.trim())\n .filter((p) => p.length > 20 && p.length < 2000);\n\n // Also extract list items\n const listItems = content\n .split(\"\\n\")\n .filter((l) => /^\\s*[-*]\\s+/.test(l))\n .map((l) => l.replace(/^\\s*[-*]\\s+/, \"\").trim())\n .filter((l) => l.length > 10 && l.length < 500);\n\n const allItems = [...paragraphs, ...listItems];\n\n // Deduplicate within file\n const seen = new Set<string>();\n for (const item of allItems) {\n const hash = hashContent(item.toLowerCase());\n if (seen.has(hash)) continue;\n seen.add(hash);\n\n const id = generateId();\n const category = categoryOverride ?? detectCategory(item);\n\n statements.push({\n id,\n content: item,\n category,\n confidence,\n provenance: {\n sourcePath: filePath,\n relativePath,\n source,\n lineNumber: 0,\n ingestedAt: now,\n sourceFileHash,\n },\n contentHash: hash,\n tags: [...tags],\n entityRef,\n });\n }\n\n return statements;\n}\n\n// ── Category detection ───────────────────────────────────────────────────────\n\nfunction detectCategory(text: string): string {\n const lower = text.toLowerCase();\n\n if (/^(always|never|must|should|don't|avoid|ensure)/.test(lower)) return \"principle\";\n if (/^(we|team|project)\\s+(decided|chose|will|use)/.test(lower)) return \"decision\";\n if (/^(i|we)\\s+(prefer|like|want|hate|dislike)/.test(lower)) return \"preference\";\n if (/fix|bug|issue|broken|error/i.test(lower)) return \"correction\";\n if (/\\?.+$/.test(lower.trim())) return \"question\";\n\n return \"fact\";\n}\n\n// ── Dedup ────────────────────────────────────────────────────────────────────\n\ninterface ExistingMemory {\n id: string;\n content: string;\n category: string;\n}\n\nfunction findDuplicate(\n stmt: CuratedStatement,\n existing: Map<string, ExistingMemory>,\n): DuplicateResult | null {\n const stmtLower = stmt.content.toLowerCase();\n\n // Exact hash match\n const exactMatch = existing.get(stmt.contentHash);\n if (exactMatch) {\n return {\n newStatement: stmt,\n existingId: exactMatch.id,\n similarity: 1,\n action: \"skip\",\n };\n }\n\n // Fuzzy match — check substring containment\n for (const [_, mem] of existing) {\n const memLower = mem.content.toLowerCase();\n if (memLower.length > 50 && stmtLower.length > 50) {\n // Check if one contains the other\n if (memLower.includes(stmtLower.slice(0, 40)) || stmtLower.includes(memLower.slice(0, 40))) {\n return {\n newStatement: stmt,\n existingId: mem.id,\n similarity: 0.85,\n action: \"skip\",\n };\n }\n }\n }\n\n return null;\n}\n\n// ── Contradiction detection ──────────────────────────────────────────────────\n\nfunction findContradiction(\n stmt: CuratedStatement,\n existing: Map<string, ExistingMemory>,\n): ContradictionResult | null {\n const negationPatterns = [\n /\\b(not|don't|doesn't|isn't|aren't|won't|can't|never|no)\\b/i,\n ];\n\n const hasNegation = negationPatterns.some((p) => p.test(stmt.content));\n if (!hasNegation) return null;\n\n // Strip negation and look for positive version\n const stripped = stmt.content\n .toLowerCase()\n .replace(/\\b(not|don't|doesn't|isn't|aren't|won't|can't|never|no)\\b/gi, \"\")\n .trim();\n\n if (stripped.length < 20) return null;\n\n for (const [_, mem] of existing) {\n const memLower = mem.content.toLowerCase();\n // If an existing memory affirms what this statement negates\n if (memLower.includes(stripped.slice(0, Math.min(30, stripped.length)))) {\n return {\n newStatement: stmt,\n conflictingId: mem.id,\n conflictingContent: mem.content,\n severity: \"high\",\n };\n }\n }\n\n return null;\n}\n\n// ── Memory loading ───────────────────────────────────────────────────────────\n\nfunction loadExistingMemories(memoryDir: string): Map<string, ExistingMemory> {\n const result = new Map<string, ExistingMemory>();\n if (!fs.existsSync(memoryDir)) return result;\n\n // Walk all known category dirs for existing memories\n const dirs = ALL_CATEGORY_DIRS;\n for (const dir of dirs) {\n const fullDir = path.join(memoryDir, dir);\n if (!fs.existsSync(fullDir)) continue;\n\n walkFiles(fullDir, (filePath) => {\n const content = readFileSafe(filePath);\n if (!content) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id || !body) return;\n\n const hash = hashContent(body.toLowerCase());\n result.set(hash, {\n id: fm.id,\n content: body,\n category: fm.category ?? dir.slice(0, -1),\n });\n });\n }\n\n return result;\n}\n\n// ── Writing ──────────────────────────────────────────────────────────────────\n\nfunction writeStatement(stmt: CuratedStatement, memoryDir: string): string | null {\n const now = new Date();\n const dateDir = now.toISOString().split(\"T\")[0];\n const categoryDir = getCategoryDir(memoryDir, stmt.category);\n\n const dir = path.join(categoryDir, dateDir);\n fs.mkdirSync(dir, { recursive: true });\n\n const fileName = `${stmt.category}-${Date.now()}-${stmt.id.slice(0, 8)}.md`;\n const filePath = path.join(dir, fileName);\n\n const frontmatter = [\n \"---\",\n `id: ${stmt.id}`,\n `category: ${stmt.category}`,\n `created: ${stmt.provenance.ingestedAt}`,\n `updated: ${stmt.provenance.ingestedAt}`,\n `confidence: ${stmt.confidence}`,\n `confidenceTier: ${tierFromConfidence(stmt.confidence)}`,\n `source: ${stmt.provenance.source}`,\n `tags: ${JSON.stringify(stmt.tags)}`,\n stmt.entityRef ? `entityRef: ${stmt.entityRef}` : null,\n `provenanceFile: ${stmt.provenance.relativePath}`,\n `provenanceHash: ${stmt.provenance.sourceFileHash}`,\n \"---\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n const body = `${frontmatter}\\n\\n${stmt.content}\\n`;\n\n try {\n fs.writeFileSync(filePath, body);\n return filePath;\n } catch {\n return null;\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction tierFromConfidence(confidence: number): string {\n if (confidence >= 0.95) return \"explicit\";\n if (confidence >= 0.8) return \"high\";\n if (confidence >= 0.5) return \"medium\";\n return \"low\";\n}\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction isBinary(content: string): boolean {\n // Simple heuristic: if content has null bytes, it's binary\n for (let i = 0; i < Math.min(content.length, 8000); i++) {\n if (content.charCodeAt(i) === 0) return true;\n }\n return false;\n}\n\ninterface SimpleFrontmatter {\n id?: string;\n category?: string;\n [key: string]: unknown;\n}\n\nfunction parseFrontmatter(content: string): SimpleFrontmatter | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: SimpleFrontmatter = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n (fm as Record<string, unknown>)[key] = value;\n }\n return fm;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction walkFiles(dir: string, callback: (filePath: string) => void): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walkFiles(fullPath, callback);\n } else if (entry.name.endsWith(\".md\")) {\n callback(fullPath);\n }\n }\n}\n","/**\n * @remnic/core — Dedup & Contradiction Detection\n *\n * Statement-level deduplication and contradiction detection\n * against existing memories. Can be used standalone or via curation.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface MemoryEntry {\n /** Memory ID */\n id: string;\n /** Content text */\n content: string;\n /** Category */\n category: string;\n /** File path (if known) */\n filePath?: string;\n}\n\nexport interface DedupOptions {\n /** Memory root directory */\n memoryDir: string;\n /** Categories to scan (default: all) */\n categories?: string[];\n /** Similarity threshold for fuzzy matching (0-1, default: 0.85) */\n threshold?: number;\n /** Max memories to load (default: 10000) */\n maxLoad?: number;\n}\n\nexport interface DedupResult {\n /** Total memories scanned */\n scanned: number;\n /** Duplicate pairs found */\n duplicates: DuplicatePair[];\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface DuplicatePair {\n /** First memory */\n left: MemoryEntry;\n /** Second memory */\n right: MemoryEntry;\n /** Similarity score */\n similarity: number;\n /** Recommended action */\n action: \"merge\" | \"keep_left\" | \"keep_right\";\n}\n\nexport interface ContradictionOptions {\n /** Memory root directory */\n memoryDir: string;\n /** Categories to scan (default: all) */\n categories?: string[];\n /** Max memories to load (default: 10000) */\n maxLoad?: number;\n}\n\nexport interface ContradictionResult {\n /** Total memories scanned */\n scanned: number;\n /** Contradictions found */\n contradictions: ContradictionPair[];\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface ContradictionPair {\n /** First statement */\n left: MemoryEntry;\n /** Contradicting statement */\n right: MemoryEntry;\n /** Severity */\n severity: \"high\" | \"medium\" | \"low\";\n /** Reason */\n reason: string;\n}\n\n// ── Main functions ───────────────────────────────────────────────────────────\n\nexport function findDuplicates(options: DedupOptions): DedupResult {\n const startTime = Date.now();\n const { memoryDir, threshold = 0.85, maxLoad = 10000 } = options;\n\n const memories = loadMemories(memoryDir, options.categories, maxLoad);\n const duplicates: DuplicatePair[] = [];\n\n // Compare all pairs (O(n^2) but bounded by maxLoad)\n for (let i = 0; i < memories.length; i++) {\n for (let j = i + 1; j < memories.length; j++) {\n const sim = computeSimilarity(memories[i].content, memories[j].content);\n if (sim >= threshold) {\n duplicates.push({\n left: memories[i],\n right: memories[j],\n similarity: sim,\n action: sim >= 0.98 ? \"merge\" : sim >= 0.9 ? \"keep_right\" : \"keep_left\",\n });\n }\n }\n }\n\n return {\n scanned: memories.length,\n duplicates,\n durationMs: Date.now() - startTime,\n };\n}\n\nexport function findContradictions(options: ContradictionOptions): ContradictionResult {\n const startTime = Date.now();\n const { memoryDir, maxLoad = 10000 } = options;\n\n const memories = loadMemories(memoryDir, options.categories, maxLoad);\n const contradictions: ContradictionPair[] = [];\n\n for (let i = 0; i < memories.length; i++) {\n for (let j = i + 1; j < memories.length; j++) {\n const contra = detectContradiction(memories[i], memories[j]);\n if (contra) {\n contradictions.push(contra);\n }\n }\n }\n\n return {\n scanned: memories.length,\n contradictions,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Similarity computation ───────────────────────────────────────────────────\n\nfunction computeSimilarity(a: string, b: string): number {\n // Normalize\n const normA = normalize(a);\n const normB = normalize(b);\n\n // Exact match\n if (normA === normB) return 1;\n\n // Hash-based exact match\n if (hashContent(normA) === hashContent(normB)) return 0.99;\n\n // Substring containment\n if (normA.length > 50 && normB.length > 50) {\n if (normA.includes(normB.slice(0, 40)) || normB.includes(normA.slice(0, 40))) {\n return 0.9;\n }\n }\n\n // Word overlap (Jaccard)\n const wordsA = new Set(normA.split(/\\s+/));\n const wordsB = new Set(normB.split(/\\s+/));\n const intersection = new Set([...wordsA].filter((w) => wordsB.has(w)));\n const union = new Set([...wordsA, ...wordsB]);\n\n if (union.size === 0) return 0;\n return intersection.size / union.size;\n}\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s']/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n// ── Contradiction detection ──────────────────────────────────────────────────\n\nconst NEGATION_WORDS = new Set([\n \"not\", \"don't\", \"doesn't\", \"isn't\", \"aren't\", \"won't\", \"can't\",\n \"never\", \"no\", \"none\", \"neither\", \"nor\", \"nothing\", \"nowhere\",\n]);\n\nfunction detectContradiction(\n a: MemoryEntry,\n b: MemoryEntry,\n): ContradictionPair | null {\n const normA = normalize(a.content);\n const normB = normalize(b.content);\n\n // Check if one has negation and the other doesn't\n const aHasNegation = containsNegation(normA);\n const bHasNegation = containsNegation(normB);\n\n if (aHasNegation === bHasNegation) return null;\n\n // Strip negation and compare core content\n const strippedA = stripNegation(normA);\n const strippedB = stripNegation(normB);\n\n const sim = computeSimilarity(strippedA, strippedB);\n if (sim < 0.7) return null;\n\n // Check for opposite quantifiers\n const oppQuantifiers = [\n [\"always\", \"never\"],\n [\"all\", \"none\"],\n [\"every\", \"no\"],\n [\"must\", \"must not\"],\n [\"should\", \"should not\"],\n [\"can\", \"cannot\"],\n ];\n\n for (const [pos, neg] of oppQuantifiers) {\n if (\n (a.content.toLowerCase().includes(pos) && b.content.toLowerCase().includes(neg)) ||\n (a.content.toLowerCase().includes(neg) && b.content.toLowerCase().includes(pos))\n ) {\n return {\n left: a,\n right: b,\n severity: \"high\",\n reason: `Opposite quantifiers: \"${pos}\" vs \"${neg}\"`,\n };\n }\n }\n\n return {\n left: a,\n right: b,\n severity: sim >= 0.85 ? \"high\" : \"medium\",\n reason: \"Negated version of similar content\",\n };\n}\n\nfunction containsNegation(text: string): boolean {\n const words = text.split(/\\s+/);\n return words.some((w) => NEGATION_WORDS.has(w));\n}\n\nfunction stripNegation(text: string): string {\n return text\n .replace(/\\b(not|don't|doesn't|isn't|aren't|won't|can't|never|no|none|neither|nor|nothing|nowhere)\\b/gi, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n// ── Memory loading ───────────────────────────────────────────────────────────\n\nfunction loadMemories(\n memoryDir: string,\n categories?: string[],\n maxLoad = 10000,\n): MemoryEntry[] {\n const result: MemoryEntry[] = [];\n const allCategories = categories ?? ALL_CATEGORY_DIRS;\n\n for (const category of allCategories) {\n if (result.length >= maxLoad) break;\n\n const dir = path.join(memoryDir, category);\n if (!fs.existsSync(dir)) continue;\n\n walkMdFiles(dir, (filePath) => {\n if (result.length >= maxLoad) return;\n\n const content = readFileSafe(filePath);\n if (!content) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id || !body) return;\n\n result.push({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? category.slice(0, -1),\n filePath,\n });\n });\n }\n\n return result;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction parseFrontmatter(content: string): Record<string, unknown> | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: Record<string, unknown> = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n fm[key] = value;\n }\n return fm;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction walkMdFiles(dir: string, callback: (filePath: string) => void): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walkMdFiles(fullPath, callback);\n } else if (entry.name.endsWith(\".md\")) {\n callback(fullPath);\n }\n }\n}\n","/**\n * @remnic/core — Review Inbox\n *\n * Manages low-confidence memories and suggestions pending review.\n * Integrates with the existing review-queue system.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getCategoryDir, ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ReviewItem {\n /** Memory ID */\n id: string;\n /** Content text */\n content: string;\n /** Category */\n category: string;\n /** Confidence score (0-1) */\n confidence: number;\n /** Confidence tier */\n confidenceTier: string;\n /** Source */\n source: string;\n /** File path if available */\n filePath?: string;\n /** Created date */\n created: string;\n /** Reason it's in review */\n reviewReason: \"low_confidence\" | \"suggestion\" | \"contradiction\" | \"duplicate\";\n /** Additional context */\n context?: string;\n}\n\nexport type ReviewAction = \"approve\" | \"dismiss\" | \"flag\";\n\nexport interface ReviewResult {\n /** Item acted upon */\n itemId: string;\n /** Action taken */\n action: ReviewAction;\n /** Updated file path (if modified) */\n updatedPath?: string;\n /** Status message */\n message: string;\n}\n\nexport interface ReviewListResult {\n /** Items pending review */\n items: ReviewItem[];\n /** Total count */\n total: number;\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface ReviewOptions {\n /** Memory root directory */\n memoryDir: string;\n /** Filter by reason */\n reason?: ReviewItem[\"reviewReason\"];\n /** Max items to return (default: 50) */\n limit?: number;\n /** Include items with confidence below this threshold (default: 0.7) */\n confidenceThreshold?: number;\n}\n\nexport interface ReviewActionOptions {\n /** Match the threshold used when listing review items (default: 0.7) */\n confidenceThreshold?: number;\n}\n\ninterface ReviewFileMatch {\n filePath: string;\n location: \"queue\" | \"category\";\n}\n\nconst DEFAULT_CONFIDENCE_THRESHOLD = 0.7;\n\n// ── Main functions ───────────────────────────────────────────────────────────\n\n/**\n * List items pending review.\n */\nexport function listReviewItems(options: ReviewOptions): ReviewListResult {\n const startTime = Date.now();\n const {\n memoryDir,\n reason: filterReason,\n limit = 50,\n confidenceThreshold = DEFAULT_CONFIDENCE_THRESHOLD,\n } = options;\n\n const items: ReviewItem[] = [];\n const addItem = (item: ReviewItem): void => {\n if (items.length >= limit) return;\n if (filterReason && item.reviewReason !== filterReason) return;\n items.push(item);\n };\n\n // Check suggestions directory\n const suggestionsDir = path.join(memoryDir, \"suggestions\");\n if (fs.existsSync(suggestionsDir)) {\n walkMd(suggestionsDir, (filePath, content) => {\n if (items.length >= limit) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id) return;\n\n addItem({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? \"suggestion\",\n confidence: parseConfidence(fm.confidence, 0.5),\n confidenceTier: (fm.confidenceTier as string) ?? \"low\",\n source: (fm.source as string) ?? \"unknown\",\n filePath,\n created: (fm.created as string) ?? new Date().toISOString(),\n reviewReason: \"suggestion\",\n });\n });\n }\n\n // Check review directory\n const reviewDir = path.join(memoryDir, \"review\");\n if (fs.existsSync(reviewDir)) {\n walkMd(reviewDir, (filePath, content) => {\n if (items.length >= limit) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id) return;\n\n addItem({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? \"review\",\n confidence: parseConfidence(fm.confidence, 0.5),\n confidenceTier: (fm.confidenceTier as string) ?? \"low\",\n source: (fm.source as string) ?? \"unknown\",\n filePath,\n created: (fm.created as string) ?? new Date().toISOString(),\n reviewReason: (fm.reviewReason as ReviewItem[\"reviewReason\"]) ?? \"low_confidence\",\n context: fm.context as string | undefined,\n });\n });\n }\n\n // Scan all categories for low-confidence items\n const categories = ALL_CATEGORY_DIRS;\n for (const category of categories) {\n if (items.length >= limit) break;\n\n const dir = path.join(memoryDir, category);\n if (!fs.existsSync(dir)) continue;\n\n walkMd(dir, (filePath, content) => {\n if (items.length >= limit) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id) return;\n\n const confidence = parseConfidence(fm.confidence, 1);\n if (confidence >= confidenceThreshold) return;\n if (parseBoolean(fm.reviewDismissed)) return;\n\n // Skip if already in items\n if (items.some((i) => i.id === fm.id)) return;\n\n addItem({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? category.slice(0, -1),\n confidence,\n confidenceTier: (fm.confidenceTier as string) ?? \"low\",\n source: (fm.source as string) ?? \"unknown\",\n filePath,\n created: (fm.created as string) ?? new Date().toISOString(),\n reviewReason: \"low_confidence\",\n });\n });\n }\n\n return {\n items,\n total: items.length,\n durationMs: Date.now() - startTime,\n };\n}\n\n/**\n * Perform a review action on an item.\n */\nexport function performReview(\n memoryDir: string,\n itemId: string,\n action: ReviewAction,\n options: ReviewActionOptions = {},\n): ReviewResult {\n switch (action) {\n case \"approve\":\n return approveItem(memoryDir, itemId, options);\n case \"dismiss\":\n return dismissItem(memoryDir, itemId, options);\n case \"flag\":\n return flagItem(memoryDir, itemId, options);\n }\n}\n\n// ── Actions ──────────────────────────────────────────────────────────────────\n\nfunction approveItem(\n memoryDir: string,\n itemId: string,\n options: ReviewActionOptions,\n): ReviewResult {\n const found = findReviewFileById(memoryDir, itemId, options);\n if (!found) {\n return { itemId, action: \"approve\", message: \"Item not found\" };\n }\n\n const content = fs.readFileSync(found.filePath, \"utf8\");\n const fm = parseFrontmatter(content);\n if (!fm) return { itemId, action: \"approve\", message: \"Could not parse frontmatter\" };\n\n const updatedContent = updateFrontmatterFields(content, {\n confidence: \"0.9\",\n confidenceTier: \"high\",\n reviewDismissed: null,\n });\n\n if (found.location === \"category\") {\n fs.writeFileSync(found.filePath, updatedContent, \"utf8\");\n return {\n itemId,\n action: \"approve\",\n updatedPath: found.filePath,\n message: \"Approved low-confidence memory in place with confidence 0.9\",\n };\n }\n\n // Promote queued suggestions/review items to their category directory.\n const category = (fm.category as string) ?? \"fact\";\n const targetDir = getCategoryDir(memoryDir, category);\n const dateDir = new Date().toISOString().split(\"T\")[0];\n const outputPath = path.join(targetDir, dateDir, path.basename(found.filePath));\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n const promotedPath = writeFileWithoutClobber(outputPath, updatedContent, itemId);\n\n // Remove from review\n fs.unlinkSync(found.filePath);\n\n return {\n itemId,\n action: \"approve\",\n updatedPath: promotedPath,\n message: `Promoted to ${category} with confidence 0.9`,\n };\n}\n\nfunction dismissItem(\n memoryDir: string,\n itemId: string,\n options: ReviewActionOptions,\n): ReviewResult {\n const found = findReviewFileById(memoryDir, itemId, options);\n if (!found) {\n return { itemId, action: \"dismiss\", message: \"Item not found\" };\n }\n\n if (found.location === \"queue\") {\n fs.unlinkSync(found.filePath);\n return { itemId, action: \"dismiss\", message: \"Dismissed and removed\" };\n }\n\n const content = fs.readFileSync(found.filePath, \"utf8\");\n fs.writeFileSync(\n found.filePath,\n updateFrontmatterFields(content, {\n reviewDismissed: \"true\",\n reviewDismissedAt: new Date().toISOString(),\n }),\n \"utf8\",\n );\n return {\n itemId,\n action: \"dismiss\",\n updatedPath: found.filePath,\n message: \"Dismissed low-confidence memory in place\",\n };\n}\n\nfunction flagItem(\n memoryDir: string,\n itemId: string,\n options: ReviewActionOptions,\n): ReviewResult {\n const found = findReviewFileById(memoryDir, itemId, options);\n if (!found) {\n return { itemId, action: \"flag\", message: \"Item not found\" };\n }\n\n const content = fs.readFileSync(found.filePath, \"utf8\");\n const fixed = updateFrontmatterFields(content, {\n flagged: \"true\",\n flaggedAt: new Date().toISOString(),\n });\n fs.writeFileSync(found.filePath, fixed);\n return {\n itemId,\n action: \"flag\",\n updatedPath: found.filePath,\n message: \"Flagged for further review\",\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction findReviewFileById(\n memoryDir: string,\n id: string,\n options: ReviewActionOptions = {},\n): ReviewFileMatch | null {\n for (const loc of [\"suggestions\", \"review\"]) {\n const dir = path.join(memoryDir, loc);\n if (!fs.existsSync(dir)) continue;\n\n const found = findFileById(dir, id);\n if (found) return { filePath: found, location: \"queue\" };\n }\n\n for (const category of ALL_CATEGORY_DIRS) {\n const dir = path.join(memoryDir, category);\n if (!fs.existsSync(dir)) continue;\n\n const found = findFileById(dir, id, (fm) => isLowConfidenceReviewCandidate(fm, options));\n if (found) return { filePath: found, location: \"category\" };\n }\n\n return null;\n}\n\nfunction findFileById(\n dir: string,\n id: string,\n include?: (frontmatter: Record<string, unknown>) => boolean,\n): string | null {\n const files = walkMdPaths(dir);\n for (const filePath of files) {\n const content = readFileSafe(filePath);\n if (!content) continue;\n const fm = parseFrontmatter(content);\n if (fm?.id === id && (!include || include(fm))) return filePath;\n }\n return null;\n}\n\nfunction isLowConfidenceReviewCandidate(\n fm: Record<string, unknown>,\n options: ReviewActionOptions,\n): boolean {\n const threshold = options.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD;\n return (\n parseConfidence(fm.confidence, 1) < threshold &&\n !parseBoolean(fm.reviewDismissed)\n );\n}\n\nfunction parseBoolean(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value !== \"string\") return false;\n const normalized = value.trim().toLowerCase();\n return normalized === \"true\" || normalized === \"1\" || normalized === \"yes\";\n}\n\nfunction parseConfidence(value: unknown, fallback: number): number {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : fallback;\n if (typeof value === \"string\") {\n const n = parseFloat(value);\n return Number.isFinite(n) ? n : fallback;\n }\n return fallback;\n}\n\nfunction updateFrontmatterFields(\n content: string,\n fields: Record<string, string | null>,\n): string {\n const match = content.match(/^(---\\n)([\\s\\S]*?)(\\n---(?:\\n|$))/);\n if (!match) return content;\n\n const seen = new Set<string>();\n const lines = match[2].split(\"\\n\");\n const nextLines: string[] = [];\n for (const line of lines) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) {\n nextLines.push(line);\n continue;\n }\n const key = line.slice(0, colonIdx).trim();\n if (!Object.prototype.hasOwnProperty.call(fields, key)) {\n nextLines.push(line);\n continue;\n }\n seen.add(key);\n const value = fields[key];\n if (value !== null) {\n nextLines.push(`${key}: ${value}`);\n }\n }\n\n for (const [key, value] of Object.entries(fields)) {\n if (value !== null && !seen.has(key)) {\n nextLines.push(`${key}: ${value}`);\n }\n }\n\n return `${match[1]}${nextLines.join(\"\\n\")}${match[3]}${content.slice(match[0].length)}`;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction writeFileWithoutClobber(basePath: string, content: string, discriminator: string): string {\n const parsed = path.parse(basePath);\n const safeDiscriminator = sanitizeFilePart(discriminator);\n\n for (let attempt = 0; attempt < 1000; attempt++) {\n const candidate = attempt === 0\n ? basePath\n : path.join(\n parsed.dir,\n `${parsed.name}-${safeDiscriminator}${attempt === 1 ? \"\" : `-${attempt}`}${parsed.ext || \".md\"}`,\n );\n\n try {\n fs.writeFileSync(candidate, content, { encoding: \"utf8\", flag: \"wx\" });\n return candidate;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") continue;\n throw error;\n }\n }\n\n throw new Error(`Could not find a free review promotion path for ${basePath}`);\n}\n\nfunction sanitizeFilePart(value: string): string {\n const chars: string[] = [];\n let previousWasDash = false;\n\n for (const char of value) {\n const next = isSafeFilePartChar(char) ? char : \"-\";\n if (next === \"-\" && previousWasDash) continue;\n chars.push(next);\n previousWasDash = next === \"-\";\n if (chars.length >= 64) break;\n }\n\n let start = 0;\n let end = chars.length;\n while (start < end && chars[start] === \"-\") start++;\n while (end > start && chars[end - 1] === \"-\") end--;\n\n const sanitized = chars.slice(start, end).join(\"\");\n return sanitized || \"review-item\";\n}\n\nfunction isSafeFilePartChar(value: string): boolean {\n if (value.length !== 1) return false;\n const code = value.charCodeAt(0);\n return (\n (code >= 48 && code <= 57) ||\n (code >= 65 && code <= 90) ||\n (code >= 97 && code <= 122) ||\n value === \".\" ||\n value === \"_\" ||\n value === \"-\"\n );\n}\n\nfunction parseFrontmatter(content: string): Record<string, unknown> | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: Record<string, unknown> = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n fm[key] = value;\n }\n return fm;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction walkMd(dir: string, callback: (filePath: string, content: string) => void): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walkMd(fullPath, callback);\n } else if (entry.name.endsWith(\".md\")) {\n const content = readFileSafe(fullPath);\n if (content) callback(fullPath, content);\n }\n }\n}\n\nfunction walkMdPaths(dir: string): string[] {\n const results: string[] = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...walkMdPaths(fullPath));\n } else if (entry.name.endsWith(\".md\")) {\n results.push(fullPath);\n }\n }\n return results;\n}\n","/**\n * @remnic/core — Diff-Aware Sync\n *\n * Watches source files for changes and triggers re-ingestion\n * only for changed content. Uses file hashing to detect changes.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface SyncOptions {\n /** Source directory to watch */\n sourceDir: string;\n /** Memory root directory */\n memoryDir: string;\n /** State file path (stores hashes). Default: memoryDir/.sync-state.json */\n stateFile?: string;\n /** File extensions to watch (default: .md, .txt, .mdx) */\n extensions?: string[];\n /** Directories to exclude */\n excludeDirs?: string[];\n /** Whether to actually write changes (default: true) */\n dryRun?: boolean;\n}\n\nexport interface SyncResult {\n /** Files scanned */\n scanned: number;\n /** Files changed since last sync */\n changed: FileChange[];\n /** Files unchanged */\n unchanged: number;\n /** Files deleted since last sync */\n deleted: string[];\n /** Files newly added */\n added: string[];\n /** Duration in ms */\n durationMs: number;\n /** State file path */\n stateFile: string;\n}\n\nexport interface FileChange {\n /** Absolute file path */\n filePath: string;\n /** Relative path from source root */\n relativePath: string;\n /** Change type */\n type: \"added\" | \"modified\" | \"deleted\";\n /** Current content hash */\n currentHash: string;\n /** Previous content hash (if modified) */\n previousHash?: string;\n /** File size in bytes */\n size: number;\n}\n\nexport interface SyncState {\n /** Map of relative path → content hash */\n fileHashes: Record<string, string>;\n /** Last sync timestamp */\n lastSyncAt: string;\n /** Version of state format */\n version: number;\n}\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\nconst DEFAULT_EXTENSIONS = new Set([\".md\", \".txt\", \".mdx\", \".rst\"]);\nconst DEFAULT_EXCLUDE = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".engram\",\n \"coverage\",\n]);\n\n// ── Main function ────────────────────────────────────────────────────────────\n\nexport function syncChanges(options: SyncOptions): SyncResult {\n const startTime = Date.now();\n const {\n sourceDir,\n memoryDir,\n extensions = [...DEFAULT_EXTENSIONS],\n excludeDirs = [],\n dryRun = false,\n } = options;\n\n const extSet = new Set(extensions);\n const excludeSet = new Set([...DEFAULT_EXCLUDE, ...excludeDirs]);\n const stateFilePath = options.stateFile ?? path.join(memoryDir, \".sync-state.json\");\n\n // Load previous state\n const prevState = loadState(stateFilePath);\n\n // Scan current files\n const currentFiles = scanFiles(sourceDir, extSet, excludeSet);\n\n // Compute diffs\n const changes = computeDiff(currentFiles, prevState.fileHashes, sourceDir);\n\n const added = changes.filter((c) => c.type === \"added\").map((c) => c.relativePath);\n const modified = changes.filter((c) => c.type === \"modified\");\n const deleted = changes\n .filter((c) => c.type === \"deleted\")\n .map((c) => c.relativePath);\n\n // Update state (even in dry run, we want to show what would change)\n if (!dryRun) {\n const newState: SyncState = {\n fileHashes: {},\n lastSyncAt: new Date().toISOString(),\n version: 1,\n };\n\n // Build new state from current files\n for (const [relPath, hash] of Object.entries(currentFiles)) {\n newState.fileHashes[relPath] = hash;\n }\n\n // Write state\n fs.mkdirSync(path.dirname(stateFilePath), { recursive: true });\n fs.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));\n }\n\n return {\n scanned: Object.keys(currentFiles).length,\n changed: changes,\n unchanged:\n Object.keys(currentFiles).length - changes.filter((c) => c.type !== \"deleted\").length,\n deleted,\n added,\n durationMs: Date.now() - startTime,\n stateFile: stateFilePath,\n };\n}\n\n/**\n * Watch for changes and call callback on file changes.\n * Returns a stop function.\n */\nexport function watchForChanges(\n options: SyncOptions,\n onChange: (changes: FileChange[]) => void,\n): { stop: () => void } {\n const { sourceDir, extensions, excludeDirs } = options;\n const extSet = new Set(extensions ?? DEFAULT_EXTENSIONS);\n const excludeSet = new Set([...DEFAULT_EXCLUDE, ...(excludeDirs ?? [])]);\n\n let lastHashes: Record<string, string> = {};\n\n // Initial scan\n const currentFiles = scanFiles(sourceDir, extSet, excludeSet);\n lastHashes = { ...currentFiles };\n\n // Poll interval (FSWatcher doesn't reliably work for all platforms)\n const interval = setInterval(() => {\n const nowFiles = scanFiles(sourceDir, extSet, excludeSet);\n const changes = computeDiff(nowFiles, lastHashes, sourceDir);\n\n if (changes.length > 0) {\n // Update hashes\n for (const change of changes) {\n if (change.type === \"deleted\") {\n delete lastHashes[change.relativePath];\n } else {\n lastHashes[change.relativePath] = change.currentHash;\n }\n }\n onChange(changes);\n }\n }, 5000);\n\n return {\n stop: () => clearInterval(interval),\n };\n}\n\n// ── File scanning ────────────────────────────────────────────────────────────\n\nfunction scanFiles(\n root: string,\n extensions: Set<string>,\n exclude: Set<string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n\n function walk(dir: string): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (exclude.has(entry.name)) continue;\n\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name).toLowerCase();\n if (!extensions.has(ext)) continue;\n\n const relPath = path.relative(root, fullPath);\n try {\n const content = fs.readFileSync(fullPath, \"utf8\");\n result[relPath] = hashContent(content);\n } catch {\n // Can't read — skip\n }\n }\n }\n }\n\n walk(root);\n return result;\n}\n\n// ── Diff computation ─────────────────────────────────────────────────────────\n\nfunction computeDiff(\n current: Record<string, string>,\n previous: Record<string, string>,\n sourceDir: string,\n): FileChange[] {\n const changes: FileChange[] = [];\n\n // Find added and modified\n for (const [relPath, hash] of Object.entries(current)) {\n const fullPath = path.join(sourceDir, relPath);\n\n if (!(relPath in previous)) {\n // Added\n let size = 0;\n try {\n size = fs.statSync(fullPath).size;\n } catch {\n // ignore\n }\n changes.push({\n filePath: fullPath,\n relativePath: relPath,\n type: \"added\",\n currentHash: hash,\n size,\n });\n } else if (previous[relPath] !== hash) {\n // Modified\n let size = 0;\n try {\n size = fs.statSync(fullPath).size;\n } catch {\n // ignore\n }\n changes.push({\n filePath: fullPath,\n relativePath: relPath,\n type: \"modified\",\n currentHash: hash,\n previousHash: previous[relPath],\n size,\n });\n }\n }\n\n // Find deleted\n for (const relPath of Object.keys(previous)) {\n if (!(relPath in current)) {\n changes.push({\n filePath: path.join(sourceDir, relPath),\n relativePath: relPath,\n type: \"deleted\",\n currentHash: \"\",\n size: 0,\n });\n }\n }\n\n return changes;\n}\n\n// ── State management ─────────────────────────────────────────────────────────\n\nfunction loadState(stateFilePath: string): SyncState {\n try {\n const raw = fs.readFileSync(stateFilePath, \"utf8\");\n return JSON.parse(raw);\n } catch {\n return {\n fileHashes: {},\n lastSyncAt: new Date(0).toISOString(),\n version: 1,\n };\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n","/**\n * @remnic/core — Spaces + Collaboration\n *\n * First-class memory spaces (personal, project, team) with merge/conflict\n * flows, promotion workflow, and audit trail.\n *\n * Each space is an isolated memory directory. Spaces can share memories\n * through push/pull and promotion workflows.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { readEnvVar, resolveHomeDir } from \"../runtime/env.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport type SpaceKind = \"personal\" | \"project\" | \"team\";\n\nexport interface Space {\n /** Unique space ID */\n id: string;\n /** Human-readable name */\n name: string;\n /** Space type */\n kind: SpaceKind;\n /** Description */\n description?: string;\n /** Memory directory path (absolute) */\n memoryDir: string;\n /** Created at */\n createdAt: string;\n /** Updated at */\n updatedAt: string;\n /** Owner */\n owner?: string;\n /** Members (for team spaces) */\n members?: string[];\n /** Parent space (for promotion) */\n parentSpaceId?: string;\n}\n\nexport interface SpaceManifest {\n /** Current active space ID */\n activeSpaceId: string;\n /** All spaces */\n spaces: Space[];\n /** Manifest version */\n version: number;\n /** Last updated */\n updatedAt?: string;\n}\n\nexport interface SpaceSwitchResult {\n previousSpaceId: string;\n currentSpaceId: string;\n message: string;\n}\n\nexport interface SpacePushResult {\n sourceSpaceId: string;\n targetSpaceId: string;\n memoriesPushed: number;\n conflicts: ConflictEntry[];\n durationMs: number;\n}\n\nexport interface SpacePullResult {\n sourceSpaceId: string;\n targetSpaceId: string;\n memoriesPulled: number;\n conflicts: ConflictEntry[];\n durationMs: number;\n}\n\nexport interface SpaceShareResult {\n spaceId: string;\n sharedWith: string[];\n message: string;\n}\n\nexport interface SpacePromoteResult {\n sourceSpaceId: string;\n targetSpaceId: string;\n memoriesPromoted: number;\n conflicts: ConflictEntry[];\n durationMs: number;\n}\n\nexport interface ConflictEntry {\n /** Memory ID */\n memoryId: string;\n /** Source file path */\n sourcePath: string;\n /** Target file path */\n targetPath: string;\n /** Conflict type */\n conflictType: \"content_mismatch\" | \"metadata_mismatch\" | \"both\";\n /** Source content hash */\n sourceHash: string;\n /** Target content hash */\n targetHash: string;\n}\n\nexport interface MergeResult {\n merged: number;\n conflicts: ConflictEntry[];\n skipped: number;\n durationMs: number;\n}\n\nexport interface AuditEntry {\n id: string;\n timestamp: string;\n action: string;\n sourceSpaceId: string;\n targetSpaceId?: string;\n actor?: string;\n details: string;\n memoryIds?: string[];\n}\n\n// ── Manifest management ─────────────────────────────────────────────────────\n\nconst MANIFEST_VERSION = 1;\n\nexport function getSpacesDir(baseDir?: string): string {\n const homeDir = baseDir ?? resolveHomeDir();\n return path.join(homeDir, \".config\", \"engram\", \"spaces\");\n}\n\nexport function getManifestPath(baseDir?: string): string {\n return path.join(getSpacesDir(baseDir), \"manifest.json\");\n}\n\nexport function loadManifest(baseDir?: string, memoryDirOverride?: string): SpaceManifest {\n const manifestPath = getManifestPath(baseDir);\n\n if (!fs.existsSync(manifestPath)) {\n // Bootstrap with a personal space\n const personalSpace = createPersonalSpace(baseDir, memoryDirOverride);\n const manifest: SpaceManifest = {\n activeSpaceId: personalSpace.id,\n spaces: [personalSpace],\n version: MANIFEST_VERSION,\n };\n saveManifest(manifest, baseDir);\n return manifest;\n }\n\n const raw = JSON.parse(fs.readFileSync(manifestPath, \"utf8\"));\n return raw as SpaceManifest;\n}\n\nexport function saveManifest(manifest: SpaceManifest, baseDir?: string): void {\n const manifestPath = getManifestPath(baseDir);\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n}\n\nfunction createPersonalSpace(baseDir?: string, memoryDirOverride?: string): Space {\n const homeDir = baseDir ?? resolveHomeDir();\n // Priority: override > env var > existing standalone dir > existing OpenClaw dir > new standalone dir\n const standalonePath = path.join(homeDir, \".engram\", \"memory\");\n const openclawPath = path.join(homeDir, \".openclaw\", \"workspace\", \"memory\", \"local\");\n const memoryDir = memoryDirOverride\n ?? readEnvVar(\"ENGRAM_MEMORY_DIR\")\n ?? (fs.existsSync(standalonePath) ? standalonePath\n : fs.existsSync(openclawPath) ? openclawPath\n : standalonePath);\n const now = new Date().toISOString();\n\n return {\n id: \"personal\",\n name: \"Personal\",\n kind: \"personal\",\n description: \"Default personal memory space\",\n memoryDir,\n createdAt: now,\n updatedAt: now,\n owner: readEnvVar(\"USER\"),\n };\n}\n\n// ── Space CRUD ──────────────────────────────────────────────────────────────\n\nexport function listSpaces(baseDir?: string): Space[] {\n const manifest = loadManifest(baseDir);\n return manifest.spaces;\n}\n\nexport function getActiveSpace(baseDir?: string): Space {\n const manifest = loadManifest(baseDir);\n const space = manifest.spaces.find((s) => s.id === manifest.activeSpaceId);\n if (!space) throw new Error(`Active space ${manifest.activeSpaceId} not found`);\n return space;\n}\n\nexport function createSpace(options: {\n name: string;\n kind: SpaceKind;\n description?: string;\n memoryDir?: string;\n parentSpaceId?: string;\n baseDir?: string;\n}): Space {\n const manifest = loadManifest(options.baseDir);\n const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, \"-\").replace(/-+/g, \"-\");\n\n if (manifest.spaces.some((s) => s.id === id)) {\n throw new Error(`Space \"${id}\" already exists`);\n }\n\n // Validate parent space exists\n if (options.parentSpaceId && !manifest.spaces.some((s) => s.id === options.parentSpaceId)) {\n throw new Error(`Parent space \"${options.parentSpaceId}\" not found`);\n }\n\n const now = new Date().toISOString();\n const memoryDir = options.memoryDir ?? path.join(\n getSpacesDir(options.baseDir),\n id,\n \"memory\",\n );\n\n const space: Space = {\n id,\n name: options.name,\n kind: options.kind,\n description: options.description,\n memoryDir,\n createdAt: now,\n updatedAt: now,\n owner: readEnvVar(\"USER\"),\n parentSpaceId: options.parentSpaceId,\n };\n\n // Ensure memory directory exists\n fs.mkdirSync(memoryDir, { recursive: true });\n\n manifest.spaces.push(space);\n manifest.updatedAt = now;\n saveManifest(manifest, options.baseDir);\n\n // Audit\n appendAudit({\n action: \"space.create\",\n sourceSpaceId: id,\n details: `Created ${options.kind} space \"${options.name}\"`,\n }, options.baseDir);\n\n return space;\n}\n\nexport function deleteSpace(spaceId: string, baseDir?: string): void {\n const manifest = loadManifest(baseDir);\n\n if (spaceId === \"personal\") {\n throw new Error(\"Cannot delete the personal space\");\n }\n\n const idx = manifest.spaces.findIndex((s) => s.id === spaceId);\n if (idx === -1) throw new Error(`Space \"${spaceId}\" not found`);\n\n // If deleting active space, switch to personal\n if (manifest.activeSpaceId === spaceId) {\n manifest.activeSpaceId = \"personal\";\n }\n\n // Clear parentSpaceId references from children\n for (const space of manifest.spaces) {\n if (space.parentSpaceId === spaceId) {\n space.parentSpaceId = undefined;\n }\n }\n\n manifest.spaces.splice(idx, 1);\n saveManifest(manifest, baseDir);\n\n appendAudit({\n action: \"space.delete\",\n sourceSpaceId: spaceId,\n details: `Deleted space \"${spaceId}\"`,\n }, baseDir);\n}\n\n// ── Switch ───────────────────────────────────────────────────────────────────\n\nexport function switchSpace(spaceId: string, baseDir?: string): SpaceSwitchResult {\n const manifest = loadManifest(baseDir);\n const space = manifest.spaces.find((s) => s.id === spaceId);\n\n if (!space) throw new Error(`Space \"${spaceId}\" not found`);\n\n const previousId = manifest.activeSpaceId;\n manifest.activeSpaceId = spaceId;\n saveManifest(manifest, baseDir);\n\n appendAudit({\n action: \"space.switch\",\n sourceSpaceId: previousId,\n targetSpaceId: spaceId,\n details: `Switched from \"${previousId}\" to \"${spaceId}\"`,\n }, baseDir);\n\n return {\n previousSpaceId: previousId,\n currentSpaceId: spaceId,\n message: `Switched to \"${space.name}\"`,\n };\n}\n\n// ── Push / Pull ─────────────────────────────────────────────────────────────\n\nexport function pushToSpace(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { memoryIds?: string[]; force?: boolean; baseDir?: string },\n): SpacePushResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n filterIds: options?.memoryIds,\n force: options?.force,\n });\n\n appendAudit({\n action: \"space.push\",\n sourceSpaceId,\n targetSpaceId,\n details: `Pushed ${result.merged} memories, ${result.conflicts.length} conflicts`,\n }, options?.baseDir);\n\n return {\n sourceSpaceId,\n targetSpaceId,\n memoriesPushed: result.merged,\n conflicts: result.conflicts,\n durationMs: Date.now() - startTime,\n };\n}\n\nexport function pullFromSpace(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { memoryIds?: string[]; force?: boolean; baseDir?: string },\n): SpacePullResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n filterIds: options?.memoryIds,\n force: options?.force,\n });\n\n appendAudit({\n action: \"space.pull\",\n sourceSpaceId,\n targetSpaceId,\n details: `Pulled ${result.merged} memories, ${result.conflicts.length} conflicts`,\n }, options?.baseDir);\n\n return {\n sourceSpaceId,\n targetSpaceId,\n memoriesPulled: result.merged,\n conflicts: result.conflicts,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Share ────────────────────────────────────────────────────────────────────\n\nexport function shareSpace(\n spaceId: string,\n members: string[],\n baseDir?: string,\n): SpaceShareResult {\n const manifest = loadManifest(baseDir);\n const space = manifest.spaces.find((s) => s.id === spaceId);\n\n if (!space) throw new Error(`Space \"${spaceId}\" not found`);\n if (space.kind === \"personal\") throw new Error(\"Cannot share personal space\");\n\n space.members = [...new Set([...(space.members ?? []), ...members])];\n space.updatedAt = new Date().toISOString();\n saveManifest(manifest, baseDir);\n\n appendAudit({\n action: \"space.share\",\n sourceSpaceId: spaceId,\n details: `Shared with: ${members.join(\", \")}`,\n }, baseDir);\n\n return {\n spaceId,\n sharedWith: members,\n message: `Shared \"${space.name}\" with ${members.length} member(s)`,\n };\n}\n\n// ── Promote ──────────────────────────────────────────────────────────────────\n\nexport function promoteSpace(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { memoryIds?: string[]; force?: boolean; forceOverwrite?: boolean; baseDir?: string },\n): SpacePromoteResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n // Promotion requires parent-child relationship or explicit force\n if (source.parentSpaceId !== targetSpaceId && target.parentSpaceId !== sourceSpaceId) {\n if (!options?.force) {\n throw new Error(\"Spaces must have a parent-child relationship for promotion. Use --force to override.\");\n }\n }\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n filterIds: options?.memoryIds,\n force: options?.forceOverwrite !== undefined ? options.forceOverwrite : (options?.force ?? false),\n });\n\n appendAudit({\n action: \"space.promote\",\n sourceSpaceId,\n targetSpaceId,\n details: `Promoted ${result.merged} memories from \"${source.name}\" to \"${target.name}\"`,\n }, options?.baseDir);\n\n return {\n sourceSpaceId,\n targetSpaceId,\n memoriesPromoted: result.merged,\n conflicts: result.conflicts,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Merge ────────────────────────────────────────────────────────────────────\n\nexport function mergeSpaces(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { force?: boolean; baseDir?: string },\n): MergeResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n force: options?.force,\n });\n\n appendAudit({\n action: \"space.merge\",\n sourceSpaceId,\n targetSpaceId,\n details: `Merged: ${result.merged} merged, ${result.conflicts.length} conflicts, ${result.skipped} skipped`,\n }, options?.baseDir);\n\n return {\n ...result,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Audit trail ─────────────────────────────────────────────────────────────\n\nexport function getAuditLog(baseDir?: string): AuditEntry[] {\n const auditPath = path.join(getSpacesDir(baseDir), \"audit.jsonl\");\n if (!fs.existsSync(auditPath)) return [];\n\n const lines = fs.readFileSync(auditPath, \"utf8\").trim().split(\"\\n\");\n return lines\n .filter((l) => l.trim())\n .map((l) => JSON.parse(l) as AuditEntry);\n}\n\nfunction appendAudit(entry: Omit<AuditEntry, \"id\" | \"timestamp\">, baseDir?: string): void {\n const auditPath = path.join(getSpacesDir(baseDir), \"audit.jsonl\");\n fs.mkdirSync(path.dirname(auditPath), { recursive: true });\n\n const full: AuditEntry = {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n ...entry,\n };\n\n fs.appendFileSync(auditPath, JSON.stringify(full) + \"\\n\");\n}\n\n// ── Internal helpers ─────────────────────────────────────────────────────────\n\ninterface CopyOptions {\n filterIds?: string[];\n force?: boolean;\n}\n\nfunction copyMemories(\n sourceDir: string,\n targetDir: string,\n options?: CopyOptions,\n): { merged: number; conflicts: ConflictEntry[]; skipped: number } {\n let merged = 0;\n const conflicts: ConflictEntry[] = [];\n let skipped = 0;\n\n if (!fs.existsSync(sourceDir)) {\n return { merged: 0, conflicts: [], skipped: 0 };\n }\n\n fs.mkdirSync(targetDir, { recursive: true });\n\n const sourceFiles = walkMd(sourceDir);\n for (const sourcePath of sourceFiles) {\n const content = fs.readFileSync(sourcePath, \"utf8\");\n const relativePath = path.relative(sourceDir, sourcePath);\n const targetPath = path.join(targetDir, relativePath);\n\n const sourceHash = hashContent(content);\n\n // Filter by IDs if specified\n if (options?.filterIds?.length) {\n const fm = parseSimpleFrontmatter(content);\n if (!fm?.id || !options.filterIds.includes(fm.id)) {\n skipped++;\n continue;\n }\n }\n\n // Check for conflict\n if (fs.existsSync(targetPath) && !options?.force) {\n const targetContent = fs.readFileSync(targetPath, \"utf8\");\n const targetHash = hashContent(targetContent);\n\n if (sourceHash !== targetHash) {\n conflicts.push({\n memoryId: parseSimpleFrontmatter(content)?.id ?? relativePath,\n sourcePath,\n targetPath,\n conflictType: \"content_mismatch\",\n sourceHash,\n targetHash,\n });\n continue;\n }\n\n // Same content — skip\n skipped++;\n continue;\n }\n\n // Copy file\n fs.mkdirSync(path.dirname(targetPath), { recursive: true });\n fs.writeFileSync(targetPath, content);\n merged++;\n }\n\n return { merged, conflicts, skipped };\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction walkMd(dir: string): string[] {\n const results: string[] = [];\n\n function walk(d: string): void {\n for (const entry of fs.readdirSync(d, { withFileTypes: true })) {\n const fullPath = path.join(d, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.name.endsWith(\".md\")) {\n results.push(fullPath);\n }\n }\n }\n\n walk(dir);\n return results;\n}\n\ninterface SimpleFrontmatter {\n id?: string;\n [key: string]: string | undefined;\n}\n\nfunction parseSimpleFrontmatter(content: string): SimpleFrontmatter | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: SimpleFrontmatter = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n fm[key] = value;\n }\n return fm;\n}\n","/**\n * @remnic/core — Shared Instruction Blocks\n *\n * Reusable markdown fragments that every host-specific publisher can\n * compose into its instructions.md file. Keeping them here avoids\n * per-host copy-paste drift.\n */\n\n/**\n * Describes the Remnic memory types a host agent may encounter.\n */\nexport const REMNIC_SEMANTIC_OVERVIEW = `\\\n## Remnic Memory Types\n\nRemnic stores memories as plain Markdown files with YAML front-matter.\nEach memory has a **type** that describes its semantic role:\n\n| Type | Description |\n|------|-------------|\n| \\`fact\\` | An objective piece of knowledge the user confirmed or that was extracted from a session. |\n| \\`preference\\` | A stated or inferred user preference (e.g. coding style, tool choice). |\n| \\`decision\\` | An explicit decision or trade-off the user made. |\n| \\`entity\\` | A named thing the user cares about (project, service, person, API). |\n| \\`skill\\` | A reusable workflow or procedure documented for future sessions. |\n| \\`correction\\` | A fix or amendment to a previously stored memory. |\n| \\`question\\` | An open question or uncertainty flagged for future resolution. |\n| \\`observation\\` | A pattern noticed across sessions (e.g. \"user always runs tests before commits\"). |\n| \\`summary\\` | A condensed roll-up of recent sessions or a topic area. |\n\nWhen reading Remnic content, the front-matter \\`type\\` field tells you what\nkind of knowledge you are looking at and how much weight to give it.\n`;\n\n/**\n * Explains the oai-mem-citation block format hosts should use when\n * referencing Remnic-sourced content.\n */\nexport const REMNIC_CITATION_FORMAT = `\\\n## Citing Remnic Memories\n\nWhen a piece of your output draws on a Remnic file, cite it using the\nmemory citation block format so the user can trace the source:\n\n\\`\\`\\`\n<oai-mem-citation path=\"<path-relative-to-remnic-memory-base>\" />\n\\`\\`\\`\n\nThe path must be **relative to the Remnic memory base** (the directory\nnamed \\`memories/\\` under \\`<remnic-home>\\`), not absolute. Examples:\n\n- \\`<oai-mem-citation path=\"default/MEMORY.md\" />\\`\n- \\`<oai-mem-citation path=\"my-project/skills/deploy/SKILL.md\" />\\`\n- \\`<oai-mem-citation path=\"shared/memory_summary.md\" />\\`\n\nCite each distinct source once near the fact it supports. Do not invent\ncitations for files you have not actually read.\n`;\n\n/**\n * Table of MCP tools the Remnic daemon exposes. Hosts that can reach\n * the MCP server should prefer these over raw file reads.\n *\n * Tool names use the canonical `remnic.*` prefix. Legacy `engram.*`\n * aliases are also accepted by the server for backward compatibility.\n */\nexport const REMNIC_MCP_TOOL_INVENTORY = `\\\n## Remnic MCP Tools\n\nWhen the Remnic MCP server is reachable, the following tools are\navailable. Prefer MCP tools over direct file reads when the host\nsupports MCP connections.\n\n| Tool | Purpose |\n|------|---------|\n| \\`remnic.recall\\` | Retrieve contextually relevant memories for the current session. |\n| \\`remnic.recall_explain\\` | Like recall, but includes an explanation of why each memory was selected. |\n| \\`remnic.memory_store\\` | Persist a new memory (fact, preference, decision, etc.). |\n| \\`remnic.memory_get\\` | Fetch a specific memory by ID. |\n| \\`remnic.memory_search\\` | Full-text + semantic search across all memories. |\n| \\`remnic.memory_timeline\\` | Retrieve memories in chronological order within a time range. |\n| \\`remnic.observe\\` | Record an observation from the current conversation turn. |\n| \\`remnic.entity_get\\` | Look up a named entity and its relationships. |\n| \\`remnic.memory_entities_list\\` | List all known entities. |\n| \\`remnic.memory_profile\\` | Retrieve the user profile summary. |\n| \\`remnic.day_summary\\` | Generate a summary of memories from a specific day. |\n| \\`remnic.briefing\\` | Generate a structured briefing for an upcoming session. |\n| \\`remnic.memory_feedback\\` | Submit feedback on a recalled memory (useful, outdated, wrong). |\n| \\`remnic.memory_promote\\` | Promote a memory to a higher confidence tier. |\n| \\`remnic.context_checkpoint\\` | Save a conversation checkpoint for continuity. |\n| \\`remnic.suggestion_submit\\` | Submit a suggestion for a new memory to the review queue. |\n| \\`remnic.review_queue_list\\` | List pending suggestions in the review queue. |\n| \\`remnic.work_task\\` | Create or update a work task. |\n| \\`remnic.work_project\\` | Create or update a work project. |\n| \\`remnic.work_board\\` | View the work board. |\n\nLegacy \\`engram.*\\` prefixed names are accepted as aliases for all tools.\n`;\n\n/**\n * Decision rules for when a host agent should use MCP recall vs\n * reading Remnic files directly from disk.\n */\nexport const REMNIC_RECALL_DECISION_RULES = `\\\n## When to Use Recall vs Direct Read\n\n### Use \\`remnic.recall\\` (MCP) when:\n\n- The Remnic MCP server is reachable (the host has an active MCP\n connection to the Remnic daemon).\n- You want contextually relevant memories ranked by the recall planner\n (semantic search + reranking + importance scoring).\n- You need memories across multiple namespaces or topics.\n- The conversation benefits from Remnic's intent detection and\n adaptive recall depth.\n\n### Use direct file reads when:\n\n- You are in a sandboxed environment with no network or MCP access\n (e.g. Codex phase-2 consolidation).\n- You need a specific file you already know the path to.\n- The MCP server is unavailable or unhealthy.\n- You are operating on the raw memory files for maintenance or\n migration purposes.\n\n### General guidance:\n\n- Prefer MCP tools when available — they provide ranked, deduplicated,\n and context-aware results.\n- Fall back to file reads gracefully — never block on a failed MCP call\n when the data is also on disk.\n- Never write directly to the Remnic memory directory unless you are an\n authorized extraction or consolidation process.\n`;\n","/**\n * @remnic/core — Codex Memory Extension Publisher\n *\n * Writes Remnic instructions into ~/.codex/memories_extensions/remnic/\n * so the Codex agent can discover and use Remnic memories during its\n * consolidation phase.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nimport {\n REMNIC_SEMANTIC_OVERVIEW,\n REMNIC_CITATION_FORMAT,\n REMNIC_MCP_TOOL_INVENTORY,\n REMNIC_RECALL_DECISION_RULES,\n} from \"./shared-instructions.js\";\nimport { readEnvVar, resolveHomeDir } from \"../runtime/env.js\";\n\n/** Folder name Remnic installs its extension under inside memories_extensions/. */\nconst REMNIC_EXTENSION_DIR_NAME = \"remnic\";\n\n/**\n * Codex-specific publisher that knows the Codex extension layout:\n * ~/.codex/memories_extensions/remnic/instructions.md\n */\nexport class CodexMemoryExtensionPublisher implements MemoryExtensionPublisher {\n readonly hostId = \"codex\";\n\n static readonly capabilities: PublisherCapabilities = {\n instructionsMd: true,\n skillsFolder: true,\n citationFormat: true,\n readPathTemplate: true,\n };\n\n async resolveExtensionRoot(\n env?: NodeJS.ProcessEnv,\n ): Promise<string> {\n const codexHome = env === undefined\n ? readEnvVar(\"CODEX_HOME\")?.trim() || path.join(resolveHomeDir(), \".codex\")\n : env.CODEX_HOME?.trim() || path.join(env.HOME ?? env.USERPROFILE ?? os.homedir(), \".codex\");\n return path.join(codexHome, \"memories_extensions\", REMNIC_EXTENSION_DIR_NAME);\n }\n\n async isHostAvailable(): Promise<boolean> {\n try {\n const home = readEnvVar(\"CODEX_HOME\")?.trim() ||\n path.join(resolveHomeDir(), \".codex\");\n return fs.existsSync(home);\n } catch {\n return false;\n }\n }\n\n async renderInstructions(ctx: PublishContext): Promise<string> {\n const memDir = ctx.config.memoryDir;\n const ns = ctx.config.namespace ?? \"default\";\n\n const sections: string[] = [\n `# Remnic Memory Extension for Codex\\n`,\n `This document tells you how to use Remnic as an authoritative local ` +\n `memory source. Remnic is a local-first, file-backed memory system. ` +\n `All Remnic content lives on disk as plain Markdown.\\n`,\n REMNIC_SEMANTIC_OVERVIEW,\n `## Where Remnic Content Lives\\n\\n` +\n `Memory base directory: \\`${memDir}\\`\\n\\n` +\n `Namespace: \\`${ns}\\`\\n\\n` +\n `Under the base directory, memories are organized by namespace:\\n\\n` +\n \"```\\n\" +\n `${memDir}/<namespace>/\\n` +\n ` MEMORY.md # compact top-of-mind memory\\n` +\n ` memory_summary.md # optional longer summary\\n` +\n ` skills/\\n` +\n ` <skill-name>/SKILL.md # reusable workflows\\n` +\n ` rollout_summaries/\\n` +\n ` *.md # per-session rollup notes\\n` +\n \"```\\n\",\n REMNIC_CITATION_FORMAT,\n REMNIC_MCP_TOOL_INVENTORY,\n REMNIC_RECALL_DECISION_RULES,\n `## Sandboxing Rules (Codex Phase-2)\\n\\n` +\n `When running inside the Codex phase-2 consolidation sandbox:\\n\\n` +\n `- **No network.** Do not attempt HTTP calls or MCP connections.\\n` +\n `- **No CLI invocation.** Do not shell out to \\`remnic\\` or \\`engram\\`.\\n` +\n `- **No MCP tool calls.** Use filesystem reads only.\\n` +\n `- **Local writes** are allowed only where Codex's sandbox policy permits.\\n` +\n `- **Respect missing files.** If a file does not exist, move on silently.\\n`,\n ];\n\n return sections.join(\"\\n\");\n }\n\n async publish(ctx: PublishContext): Promise<PublishResult> {\n const extensionRoot = await this.resolveExtensionRoot();\n const instructionsPath = path.join(extensionRoot, \"instructions.md\");\n const filesWritten: string[] = [];\n const skipped: string[] = [];\n\n ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);\n\n // Ensure the extension root exists.\n fs.mkdirSync(extensionRoot, { recursive: true });\n\n // Render and write instructions.md using atomic write (temp + rename).\n // Per CLAUDE.md #54: never delete before write in file replace operations.\n const content = await this.renderInstructions(ctx);\n const tmpPath = `${instructionsPath}.tmp-${process.pid}-${Date.now()}`;\n\n try {\n fs.writeFileSync(tmpPath, content, \"utf-8\");\n fs.renameSync(tmpPath, instructionsPath);\n filesWritten.push(instructionsPath);\n ctx.log.info(`Wrote ${instructionsPath}`);\n } catch (err) {\n // Clean up temp file on failure.\n try {\n if (fs.existsSync(tmpPath)) {\n fs.unlinkSync(tmpPath);\n }\n } catch {\n // swallow cleanup error\n }\n throw err;\n }\n\n return {\n hostId: this.hostId,\n extensionRoot,\n filesWritten,\n skipped,\n };\n }\n\n async unpublish(): Promise<void> {\n const extensionRoot = await this.resolveExtensionRoot();\n if (fs.existsSync(extensionRoot)) {\n fs.rmSync(extensionRoot, { recursive: true, force: true });\n }\n }\n}\n","/**\n * @remnic/core — Claude Code Memory Extension Publisher (stub)\n *\n * Placeholder publisher for Claude Code. Claude Code does not yet\n * support a file-based memory extension directory, so all methods are\n * no-ops that return safe defaults.\n */\n\nimport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nexport class ClaudeCodeMemoryExtensionPublisher implements MemoryExtensionPublisher {\n readonly hostId = \"claude-code\";\n\n static readonly capabilities: PublisherCapabilities = {\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n async resolveExtensionRoot(): Promise<string> {\n // Claude Code does not have an extension directory yet.\n return \"\";\n }\n\n async isHostAvailable(): Promise<boolean> {\n return false;\n }\n\n async renderInstructions(_ctx: PublishContext): Promise<string> {\n return \"\";\n }\n\n async publish(_ctx: PublishContext): Promise<PublishResult> {\n return {\n hostId: this.hostId,\n extensionRoot: \"\",\n filesWritten: [],\n skipped: [],\n };\n }\n\n async unpublish(): Promise<void> {\n // no-op\n }\n}\n","/**\n * @remnic/core — Hermes Memory Extension Publisher (stub)\n *\n * Placeholder publisher for Hermes. Hermes uses a daemon-based\n * transport and does not currently consume file-based memory\n * extensions, so all methods are no-ops.\n */\n\nimport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nexport class HermesMemoryExtensionPublisher implements MemoryExtensionPublisher {\n readonly hostId = \"hermes\";\n\n static readonly capabilities: PublisherCapabilities = {\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n async resolveExtensionRoot(): Promise<string> {\n // Hermes does not have an extension directory.\n return \"\";\n }\n\n async isHostAvailable(): Promise<boolean> {\n return false;\n }\n\n async renderInstructions(_ctx: PublishContext): Promise<string> {\n return \"\";\n }\n\n async publish(_ctx: PublishContext): Promise<PublishResult> {\n return {\n hostId: this.hostId,\n extensionRoot: \"\",\n filesWritten: [],\n skipped: [],\n };\n }\n\n async unpublish(): Promise<void> {\n // no-op\n }\n}\n","/**\n * @remnic/core — Memory Extension Publisher Registry\n *\n * Generic registry that host adapters populate at startup via\n * `registerPublisher()`. The publisher *classes* live in core so\n * adapters can import them, but the wiring of host-specific\n * implementations into the registry happens in the host adapter\n * layer (e.g. @remnic/cli), not here. This keeps core free of\n * host-specific knowledge (CLAUDE.md gotcha #31).\n *\n * Usage (from a host adapter):\n * import { registerPublisher, CodexMemoryExtensionPublisher } from \"@remnic/core\";\n * registerPublisher(\"codex\", () => new CodexMemoryExtensionPublisher());\n */\n\nexport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nexport {\n REMNIC_SEMANTIC_OVERVIEW,\n REMNIC_CITATION_FORMAT,\n REMNIC_MCP_TOOL_INVENTORY,\n REMNIC_RECALL_DECISION_RULES,\n} from \"./shared-instructions.js\";\n\nexport { CodexMemoryExtensionPublisher } from \"./codex-publisher.js\";\nexport { ClaudeCodeMemoryExtensionPublisher } from \"./claude-code-publisher.js\";\nexport { HermesMemoryExtensionPublisher } from \"./hermes-publisher.js\";\n\nimport type { MemoryExtensionPublisher } from \"./types.js\";\n\n/**\n * Factory registry keyed by host ID. Each value is a zero-argument\n * factory that returns a fresh publisher instance.\n *\n * Starts empty — host adapters populate it via registerPublisher().\n */\nexport const PUBLISHERS: Record<string, () => MemoryExtensionPublisher> = {};\n\n/**\n * Register a publisher factory for a given host ID.\n *\n * Host adapters call this at startup to wire their host-specific\n * publisher implementations into the registry. Calling with an\n * existing hostId replaces the previous factory.\n */\nexport function registerPublisher(\n hostId: string,\n factory: () => MemoryExtensionPublisher,\n): void {\n PUBLISHERS[hostId] = factory;\n}\n\n/**\n * Maps connector IDs to publisher host IDs.\n *\n * Most connector IDs match their publisher host ID exactly (e.g.\n * \"claude-code\" -> \"claude-code\", \"hermes\" -> \"hermes\").\n * This map only needs entries for connector IDs that differ from\n * their publisher host ID. Connectors without a publisher (e.g.\n * \"cursor\", \"cline\") are intentionally absent.\n */\nconst CONNECTOR_TO_HOST: Record<string, string> = {\n \"codex-cli\": \"codex\",\n};\n\n/**\n * Resolve a connector ID to its publisher host ID.\n *\n * Returns the explicit mapping if one exists, otherwise returns\n * the connector ID itself (identity mapping covers the common case\n * where connector ID === host ID).\n */\nexport function hostIdForConnector(connectorId: string): string {\n return CONNECTOR_TO_HOST[connectorId] ?? connectorId;\n}\n\n/**\n * Look up a publisher by host ID.\n * Returns undefined for unknown host IDs rather than throwing.\n */\nexport function publisherFor(hostId: string): MemoryExtensionPublisher | undefined {\n const factory = PUBLISHERS[hostId];\n return factory ? factory() : undefined;\n}\n\n/**\n * Look up a publisher by connector ID.\n *\n * Resolves the connector ID to its host ID first (e.g. \"codex-cli\" -> \"codex\"),\n * then looks up the publisher. Returns undefined if no publisher exists for\n * the resolved host ID.\n */\nexport function publisherForConnector(connectorId: string): MemoryExtensionPublisher | undefined {\n return publisherFor(hostIdForConnector(connectorId));\n}\n","// ---------------------------------------------------------------------------\n// Shared importer base (issue #568)\n// ---------------------------------------------------------------------------\n//\n// The importer adapters defined by issue #568 (`@remnic/import-chatgpt`,\n// `@remnic/import-claude`, `@remnic/import-gemini`, `@remnic/import-mem0`) all\n// follow the same three-step shape:\n//\n// 1. parse(input) — turn a raw file / API payload into a\n// source-specific intermediate structure.\n// 2. transform(parsed) — flatten that structure into a uniform list of\n// `ImportedMemory` records with provenance\n// attached (sourceLabel, importedFromPath,\n// importedAt).\n// 3. writeTo(orchestrator) — hand each memory to the orchestrator via\n// `ingestBulkImportBatch`. Dry-run mode stops\n// after transform() and never calls writeTo().\n//\n// This file defines the shared `ImporterAdapter` interface plus a thin\n// `runImporter` helper that handles batching, dry-run plan reporting, and\n// progress callbacks so every adapter does not reimplement the same loop.\n//\n// The existing lower-level `BulkImportSourceAdapter` (weclone-shaped: produces\n// turn-structured transcripts) remains untouched and continues to drive\n// `runBulkImportCliCommand`. Importer adapters are the higher-level surface\n// intended for the four memory sources in #568 — they emit *memories*\n// (intent-level content) rather than raw conversation turns.\n\nimport type { ImportTurn } from \"../bulk-import/types.js\";\n\n/**\n * A single imported memory record with provenance.\n *\n * Every importer MUST attach a truthful `sourceLabel` (e.g. `\"chatgpt\"`,\n * `\"claude\"`, `\"gemini\"`, `\"mem0\"`) and MAY attach the export's origin path or\n * URL in `importedFromPath`. `importedAt` is always set by `runImporter` so\n * adapters do not need to timestamp records themselves.\n */\nexport interface ImportedMemory {\n /**\n * The user-facing memory content. This is the string that will eventually\n * land in the memory store (after extraction / orchestrator processing).\n */\n content: string;\n /**\n * Source-specific identifier for idempotent re-imports. Optional — when\n * present, adapters SHOULD use the source's stable id (e.g. ChatGPT memory\n * uuid); when absent, the orchestrator's own content hashing provides dedup.\n */\n sourceId?: string;\n /**\n * Source-specific timestamp at which the memory was originally created or\n * last updated. ISO 8601. Optional — adapters fall back to `importedAt`\n * when the source does not expose a creation timestamp.\n */\n sourceTimestamp?: string;\n /**\n * Human-readable short label identifying the origin platform\n * (`\"chatgpt\"`, `\"claude\"`, `\"gemini\"`, `\"mem0\"`). Required so the\n * orchestrator can attribute recalled memories correctly.\n */\n sourceLabel: string;\n /**\n * Path to the export file the memory was parsed from, OR the endpoint URL\n * for API-based imports. Optional but strongly recommended so users can\n * trace a memory back to the file they imported.\n */\n importedFromPath?: string;\n /**\n * ISO 8601 timestamp set by `runImporter` immediately before writeTo().\n * Adapters MAY populate this themselves; `runImporter` will fill in the\n * current wall-clock time when absent.\n */\n importedAt?: string;\n /**\n * Adapter-specific raw metadata preserved for debugging / future rehydration\n * (e.g. conversation id, thread id, tags). Must be JSON-serializable;\n * adapters MUST NOT stash functions or circular references here.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options threaded through every importer run. Adapters receive this object\n * from `runImporter`; the top-level CLI populates it from `--dry-run`,\n * `--batch-size`, and `--rate-limit` flags.\n */\nexport interface RunImportOptions {\n /**\n * When true, parse + transform but skip writeTo(). `runImporter` prints a\n * summary of what WOULD have been imported and returns early.\n */\n dryRun?: boolean;\n /**\n * Target batch size for writeTo(). Adapters that chunk memories into\n * smaller orchestrator calls should honor this. Defaults to\n * `DEFAULT_IMPORT_BATCH_SIZE` when undefined. Values outside the valid\n * range throw from `validateImportBatchSize`.\n */\n batchSize?: number;\n /**\n * Optional rate limit for API-backed importers (mem0). Expressed as\n * requests-per-second. Adapters that do not hit an external API may ignore\n * this. When provided, `validateImportRateLimit` enforces the positive-\n * finite contract.\n */\n rateLimit?: number;\n /**\n * Invoked after every chunk of memories is processed. `total` is the full\n * count of memories produced by `transform`; `processed` counts memories\n * already ingested (or skipped in dry-run mode). Adapters SHOULD call this\n * incrementally — `runImporter` itself calls it once per batch.\n */\n onProgress?: (progress: ImportProgress) => void;\n}\n\nexport interface ImportProgress {\n processed: number;\n total: number;\n /** Current phase for human-readable logging. */\n phase: \"parse\" | \"transform\" | \"write\" | \"dry-run\";\n}\n\n/**\n * Interface every importer package implements.\n *\n * The three-method surface intentionally mirrors the issue #568 spec:\n *\n * - `parse(input)`: raw payload → intermediate representation.\n * - `transform(parsed)`: intermediate representation → `ImportedMemory[]`.\n * - `writeTo(target, memories, options)`: actually commit to the\n * orchestrator (or whatever target the host provides).\n *\n * Adapters MAY keep `parse` and `transform` separate (e.g. ChatGPT conversions\n * that need to split saved-memories from conversation summaries) or collapse\n * them into a single operation — what matters is that the combined pipeline\n * produces an array of `ImportedMemory` records with correct provenance.\n *\n * `Parsed` is generic so strongly-typed intermediate shapes survive through\n * the adapter's own code without leaking to callers of `runImporter`.\n */\nexport interface ImporterAdapter<Parsed = unknown> {\n /** Short stable name used by `remnic import --adapter <name>`. */\n name: string;\n /** Human-readable label surfaced in CLI output and source attribution. */\n sourceLabel: string;\n\n /**\n * Parse a raw payload (file contents, API response, etc.) into the adapter\n * intermediate representation. Pure — MUST NOT call the orchestrator.\n */\n parse(input: unknown, options?: ImporterParseOptions): Parsed | Promise<Parsed>;\n\n /**\n * Flatten parsed input into an array of importable memories. Pure — MUST\n * NOT call the orchestrator. Provenance fields (`sourceLabel`,\n * `importedFromPath`) should be populated here.\n */\n transform(parsed: Parsed, options?: ImporterTransformOptions): ImportedMemory[] | Promise<ImportedMemory[]>;\n\n /**\n * Commit the transformed memories to the orchestrator (or equivalent\n * target). `runImporter` only invokes this when dryRun is false.\n *\n * Adapters typically call `orchestrator.ingestBulkImportBatch` once per\n * batch after converting `ImportedMemory` records into `ImportTurn` shapes.\n */\n writeTo(\n target: ImporterWriteTarget,\n memories: ImportedMemory[],\n options: RunImportOptions,\n ): Promise<ImporterWriteResult>;\n}\n\n/**\n * Shape of the \"target\" passed to `writeTo`.\n *\n * This is intentionally narrower than the full `Orchestrator` class: importer\n * adapters only need to push `ImportTurn[]` batches and read the active\n * bulk-import namespace. Narrowing keeps test doubles simple (the slice-1\n * integration test uses a pure in-memory mock) and prevents adapters from\n * reaching into orchestrator internals that would make them harder to move\n * between host environments.\n */\nexport interface ImporterWriteTarget {\n ingestBulkImportBatch(\n turns: ImportTurn[],\n options?: { deadlineMs?: number },\n ): Promise<void>;\n bulkImportWriteNamespace?(): string;\n}\n\nexport interface ImporterWriteResult {\n memoriesIngested: number;\n /** Optional: adapter-surfaced duplicates or skipped entries. */\n skipped?: number;\n}\n\n/** Options forwarded to `parse`. Adapter-specific; additive by design. */\nexport interface ImporterParseOptions {\n strict?: boolean;\n /** Source path passed through for provenance. */\n filePath?: string;\n /**\n * Requests-per-second throttle, forwarded from the top-level\n * `RunImportOptions.rateLimit`. Only meaningful for API-backed importers\n * (mem0) — file-based adapters ignore it. `runImporter` copies this\n * through automatically so CLI users never have to stash it on\n * `parseOptions` themselves. Cursor review on PR #602.\n */\n rateLimit?: number;\n}\n\n/** Options forwarded to `transform`. */\nexport interface ImporterTransformOptions {\n /** When true, adapters that normally skip bulk conversations should include them. */\n includeConversations?: boolean;\n /** Maximum number of memories to emit. Primarily for tests. */\n maxMemories?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_IMPORT_BATCH_SIZE = 25;\nconst MIN_IMPORT_BATCH_SIZE = 1;\nconst MAX_IMPORT_BATCH_SIZE = 500;\n\n/**\n * Coerce and validate the caller-supplied batch size. CLAUDE.md rule 14/51:\n * reject invalid CLI input rather than silently defaulting.\n */\nexport function validateImportBatchSize(value: number | undefined): number {\n if (value === undefined) return DEFAULT_IMPORT_BATCH_SIZE;\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(\n `batchSize must be a finite number, received ${String(value)}`,\n );\n }\n if (!Number.isInteger(value)) {\n throw new Error(`batchSize must be an integer, received ${value}`);\n }\n if (value < MIN_IMPORT_BATCH_SIZE || value > MAX_IMPORT_BATCH_SIZE) {\n throw new Error(\n `batchSize must be between ${MIN_IMPORT_BATCH_SIZE} and ${MAX_IMPORT_BATCH_SIZE}, ` +\n `received ${value}`,\n );\n }\n return value;\n}\n\n/**\n * Validate the `--rate-limit` (requests per second) value used by API\n * importers. Zero / negative / non-finite values throw — the CLI must reject\n * them rather than silently import at unlimited speed.\n */\nexport function validateImportRateLimit(value: number | undefined): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(\n `rateLimit must be a finite number (requests per second), received ${String(value)}`,\n );\n }\n if (value <= 0) {\n throw new Error(\n `rateLimit must be greater than 0 (requests per second), received ${value}`,\n );\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\n// Orchestration helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert an `ImportedMemory` into an `ImportTurn` suitable for\n * `orchestrator.ingestBulkImportBatch`. The resulting turn is always\n * `role=\"user\"` because every downstream source (saved memories, personal\n * context, mem0 entries) represents a first-person statement rather than an\n * assistant response.\n *\n * Keeps provenance on the turn via `participantName` so the extraction\n * pipeline's transcript renderer labels the speaker with the source platform.\n */\nexport function importedMemoryToTurn(memory: ImportedMemory): ImportTurn {\n const timestamp =\n typeof memory.sourceTimestamp === \"string\" && memory.sourceTimestamp.length > 0\n ? memory.sourceTimestamp\n : (memory.importedAt ?? new Date().toISOString());\n return {\n role: \"user\",\n content: memory.content,\n timestamp,\n participantName: memory.sourceLabel,\n ...(memory.sourceId !== undefined ? { participantId: memory.sourceId } : {}),\n };\n}\n\nexport interface RunImporterResult {\n adapter: string;\n sourceLabel: string;\n /**\n * Total memories produced by `transform`. In dry-run mode this is the same\n * as the number of memories that WOULD have been written.\n */\n memoriesPlanned: number;\n /**\n * Number of memories actually handed to writeTo(). Always zero in dry-run\n * mode.\n */\n memoriesWritten: number;\n batchesProcessed: number;\n dryRun: boolean;\n /** Mirrors the importedAt stamped on every memory. */\n importedAt: string;\n}\n\n/**\n * Orchestrate `parse → transform → writeTo` for an adapter. This is the\n * default pipeline used by the CLI; adapters are free to implement their own\n * orchestration when they need per-record rate-limiting or streaming parses\n * that would not fit in the \"produce full array before writing\" shape.\n *\n * Mutates the supplied memory records by filling in `importedAt` when the\n * adapter did not set it (CLAUDE.md rule 38: records are written in insertion\n * order to keep the provenance log stable).\n */\nexport async function runImporter<Parsed>(\n adapter: ImporterAdapter<Parsed>,\n input: unknown,\n target: ImporterWriteTarget,\n options: RunImportOptions & {\n parseOptions?: ImporterParseOptions;\n transformOptions?: ImporterTransformOptions;\n } = {},\n): Promise<RunImporterResult> {\n const batchSize = validateImportBatchSize(options.batchSize);\n validateImportRateLimit(options.rateLimit);\n\n const importedAt = new Date().toISOString();\n const dryRun = options.dryRun === true;\n const onProgress = options.onProgress;\n\n // Phase 1 — parse. Forward the validated rateLimit so API-backed adapters\n // (mem0) can throttle their own fetches. Caller-supplied parseOptions\n // takes precedence so tests can still override.\n onProgress?.({ processed: 0, total: 0, phase: \"parse\" });\n const parseOptions: ImporterParseOptions = {\n ...(options.rateLimit !== undefined ? { rateLimit: options.rateLimit } : {}),\n ...(options.parseOptions ?? {}),\n };\n const parsed = await adapter.parse(input, parseOptions);\n\n // Phase 2 — transform\n onProgress?.({ processed: 0, total: 0, phase: \"transform\" });\n const memoriesRaw = await adapter.transform(parsed, options.transformOptions);\n if (!Array.isArray(memoriesRaw)) {\n throw new Error(\n `Importer '${adapter.name}' transform() returned a non-array. ` +\n \"Adapters must produce ImportedMemory[].\",\n );\n }\n const memories = memoriesRaw.map((memory) => {\n const sourceLabel =\n typeof memory.sourceLabel === \"string\" && memory.sourceLabel.length > 0\n ? memory.sourceLabel\n : adapter.sourceLabel;\n return {\n ...memory,\n sourceLabel,\n importedAt: memory.importedAt ?? importedAt,\n };\n });\n\n // Phase 3 — write (or dry-run plan)\n if (dryRun) {\n onProgress?.({\n processed: memories.length,\n total: memories.length,\n phase: \"dry-run\",\n });\n return {\n adapter: adapter.name,\n sourceLabel: adapter.sourceLabel,\n memoriesPlanned: memories.length,\n memoriesWritten: 0,\n batchesProcessed: 0,\n dryRun: true,\n importedAt,\n };\n }\n\n let batchesProcessed = 0;\n let memoriesWritten = 0;\n for (let i = 0; i < memories.length; i += batchSize) {\n const batch = memories.slice(i, i + batchSize);\n const result = await adapter.writeTo(target, batch, options);\n memoriesWritten += result.memoriesIngested;\n batchesProcessed += 1;\n onProgress?.({\n processed: Math.min(i + batch.length, memories.length),\n total: memories.length,\n phase: \"write\",\n });\n }\n\n return {\n adapter: adapter.name,\n sourceLabel: adapter.sourceLabel,\n memoriesPlanned: memories.length,\n memoriesWritten,\n batchesProcessed,\n dryRun: false,\n importedAt,\n };\n}\n\n/**\n * Default `writeTo` implementation for importers that want to hand every\n * memory to `orchestrator.ingestBulkImportBatch` without custom routing.\n * Adapters that need per-record rate-limiting or conversation-thread linking\n * are free to implement their own `writeTo`.\n */\nexport async function defaultWriteMemoriesToOrchestrator(\n target: ImporterWriteTarget,\n memories: ImportedMemory[],\n): Promise<ImporterWriteResult> {\n if (memories.length === 0) {\n return { memoriesIngested: 0 };\n }\n const turns = memories.map(importedMemoryToTurn);\n await target.ingestBulkImportBatch(turns);\n return { memoriesIngested: memories.length };\n}\n","/**\n * Capsule fork semantics — issue #676 PR 4/6.\n *\n * A fork takes an existing capsule archive, imports it into a target memory\n * root under the `fork` conflict-resolution mode (which rebases all records\n * under `forks/<capsule-id>/`), and then writes a lineage breadcrumb at\n * `<targetRoot>/forks/<forkId>/lineage.json` recording the parent capsule's\n * identity. Subsequent forks of the same parent or of a fork produce a\n * queryable chain.\n *\n * The lineage breadcrumb is a pure JSON file — no gzip, no bundle format —\n * so downstream tooling can read it with a single `readFile` + `JSON.parse`\n * without pulling in the transfer pipeline.\n */\n\nimport { lstat, mkdir, readFile, realpath, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { importCapsule, type ImportCapsuleResult } from \"./capsule-import.js\";\nimport { assertIsDirectoryNotSymlink, assertRealpathInsideRoot } from \"./fs-utils.js\";\nimport type { CapsuleParent, ExportManifestV2 } from \"./types.js\";\nimport { CAPSULE_ID_PATTERN } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Options accepted by {@link forkCapsule}.\n *\n * `sourceArchive` — absolute or cwd-relative path to a `.capsule.json.gz`\n * archive produced by `exportCapsule`.\n *\n * `targetRoot` — absolute or cwd-relative path to the memory directory that\n * will receive the forked records. Must be an existing directory.\n *\n * `forkId` — user-chosen id for the fork. Validated against\n * {@link CAPSULE_ID_PATTERN}; must be unique under `<targetRoot>/forks/` (a\n * pre-existing `forks/<forkId>/` directory is rejected before any write).\n *\n * `versioning` — optional page-versioning config forwarded to\n * {@link importCapsule}. Only relevant when the target root already has files\n * that would be overwritten (fork mode is skip-on-exist by design, so this is\n * a no-op unless mode is changed in a future subclass).\n *\n * `now` — optional clock override (ms epoch) forwarded to `importCapsule` for\n * deterministic fork-id rewriting in tests.\n */\nexport interface ForkCapsuleOptions {\n sourceArchive: string;\n targetRoot: string;\n forkId: string;\n now?: number;\n}\n\n/**\n * The lineage breadcrumb written to `forks/<forkId>/lineage.json`.\n *\n * Fields are intentionally flat so the file is human-readable at a glance\n * and trivially diffable by `git diff`.\n *\n * `forkId` — the id supplied to `forkCapsule`.\n * `forkedAt` — ISO-8601 creation timestamp (UTC).\n * `parent` — structured linkage to the source capsule.\n * `importedRecords` — number of records written by the fork import.\n * `skippedRecords` — number of records skipped (target already existed).\n */\nexport interface ForkLineage {\n forkId: string;\n forkedAt: string;\n parent: CapsuleParent;\n importedRecords: number;\n skippedRecords: number;\n}\n\nexport interface ForkCapsuleResult {\n /** Absolute path to the source archive (unchanged, for chaining). */\n archivePath: string;\n /** The V2 manifest decoded from the source archive. */\n manifest: ExportManifestV2;\n /** Result of the underlying `importCapsule` call. */\n importResult: ImportCapsuleResult;\n /** The lineage breadcrumb that was written. */\n lineage: ForkLineage;\n /** Absolute path to the lineage breadcrumb file. */\n lineagePath: string;\n}\n\n// ---------------------------------------------------------------------------\n// Main function\n// ---------------------------------------------------------------------------\n\n/**\n * Fork a capsule archive into a target memory root.\n *\n * Sequence:\n * 1. Validate `forkId` against {@link CAPSULE_ID_PATTERN}.\n * 2. Resolve `targetRoot` and verify it is an existing directory (not a\n * symlink — mirrors {@link importCapsule}'s root validation).\n * 3. Reject if `forks/<forkId>/` already exists in the target root\n * (gotcha #49: deduplicate batch inputs; gotcha #25: don't destroy\n * old state).\n * 4. Import the archive in `\"fork\"` mode via {@link importCapsule}.\n * 5. Write the lineage breadcrumb at `forks/<forkId>/lineage.json`.\n * The breadcrumb dir is created by step 4 (importCapsule writes records\n * under `forks/<sourceId>/`); if `forkId !== sourceId` we may need to\n * create the fork dir ourselves. We always `mkdir -p` defensively.\n *\n * Error semantics:\n * - All validation errors throw before any filesystem write (fail-closed,\n * gotcha #25).\n * - If `importCapsule` throws after writing some records, the lineage\n * breadcrumb is NOT written (partial state is better than a false\n * \"fork complete\" marker — gotcha #12: write rollback data before\n * success markers).\n */\nexport async function forkCapsule(opts: ForkCapsuleOptions): Promise<ForkCapsuleResult> {\n // --- 1. Validate forkId ---\n validateForkId(opts.forkId);\n\n // --- 2. Validate targetRoot ---\n // Use the shared assertIsDirectoryNotSymlink helper from fs-utils.ts so\n // capsule-fork and capsule-import share the same symlink-safe validation\n // logic (Cursor medium #751 round 2: the fork's doc comment claims it\n // \"mirrors importCapsule's root validation\"; now it literally does).\n const rootAbs = path.resolve(opts.targetRoot);\n await assertIsDirectoryNotSymlink(rootAbs, \"forkCapsule\", \"targetRoot\");\n\n // --- 3. Reject duplicate forkId ---\n // Treat ANY existing filesystem entry at `forks/<forkId>` as occupied:\n // directory, regular file, or symlink. A previous version of this check\n // only rejected directories, so a stray file at the path would slip past\n // and `importCapsule` would still write under `forks/<sourceCapsule>/...`.\n // The later `mkdir`/`writeFile` for the breadcrumb would then fail,\n // leaving a partial fork import after reporting an error — violating the\n // documented \"reject before any write\" contract (Codex P2 #751 round 2).\n // `lstat` (not `stat`) is intentional so a symlink at the path is detected\n // even if its target is missing or otherwise unreadable.\n const forkDirAbs = path.join(rootAbs, \"forks\", opts.forkId);\n const forkEntryExists = await pathEntryExists(forkDirAbs);\n if (forkEntryExists) {\n throw new Error(\n `forkCapsule: fork path already exists — forkId \"${opts.forkId}\" is already in use at: ${forkDirAbs}`,\n );\n }\n\n // --- 4. Import in fork mode ---\n const archiveAbs = path.resolve(opts.sourceArchive);\n const importResult = await importCapsule({\n archivePath: archiveAbs,\n root: rootAbs,\n mode: \"fork\",\n now: opts.now,\n });\n\n const manifest = importResult.manifest;\n const sourceCapsule = manifest.capsule;\n\n // --- 5. Build lineage and write breadcrumb ---\n // The breadcrumb lives under the FORK's own directory, not the source\n // capsule's fork subtree. `forkId` may differ from `sourceCapsule.id`\n // (e.g. `forkCapsule({ forkId: \"my-fork\" })` with source `base-caps`\n // imports records under `forks/base-caps/` but the lineage breadcrumb\n // is written to `forks/my-fork/lineage.json` so a subsequent fork of\n // this fork can locate the breadcrumb by its own id).\n const forkedAt = new Date(opts.now ?? Date.now()).toISOString();\n\n const parent: CapsuleParent = {\n capsuleId: sourceCapsule.id,\n version: sourceCapsule.version,\n forkRoot: `forks/${sourceCapsule.id}`,\n };\n\n const lineage: ForkLineage = {\n forkId: opts.forkId,\n forkedAt,\n parent,\n importedRecords: importResult.imported.length,\n skippedRecords: importResult.skipped.length,\n };\n\n // Ensure the fork breadcrumb directory exists (it may not exist if forkId\n // differs from the source capsule's id, or if all records were skipped).\n const lineagePath = path.join(forkDirAbs, \"lineage.json\");\n\n // Guard lineage write against a symlinked `forks/` directory (or any\n // intermediate path component) pointing outside the memory root. If\n // `targetRoot/forks` is a symlink to `/tmp/evil`, a zero-record capsule\n // passes `importCapsule` without triggering per-record path checks and this\n // write would silently escape the root sandbox. We resolve the nearest\n // existing ancestor of `lineagePath` via `realpath` (same technique used by\n // `capsule-import` and `capsule-merge`) and verify the canonical path is\n // still inside `rootReal` before creating any directories or writing.\n //\n // Note: `rootReal` is computed from `rootAbs` here rather than reusing\n // `forkDirAbs` for the lexical check, because on macOS /tmp is a symlink\n // to /private/tmp and `path.relative(rootReal, forkDirAbs)` would produce\n // an absolute path if forkDirAbs was not also resolved. We skip the lexical\n // pre-check and rely solely on `assertRealpathInsideRoot` which handles\n // both the realpath walk and the containment check atomically.\n const rootReal = await realpath(rootAbs);\n // Symlink-safe containment check: walks the nearest existing ancestor and\n // confirms the canonical path is still inside rootReal.\n await assertRealpathInsideRoot(rootReal, lineagePath, `forks/${opts.forkId}/lineage.json`, \"forkCapsule\");\n\n await mkdir(path.dirname(lineagePath), { recursive: true });\n\n // Write breadcrumb AFTER import completes successfully — consistent with\n // gotcha #25 (don't destroy old state before new state is confirmed) and\n // gotcha #54 (write temp before rename / write before marker).\n await writeFile(lineagePath, JSON.stringify(lineage, null, 2) + \"\\n\", \"utf-8\");\n\n return {\n archivePath: archiveAbs,\n manifest,\n importResult,\n lineage,\n lineagePath,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Lineage query helper\n// ---------------------------------------------------------------------------\n\n/**\n * Read the lineage breadcrumb for a given fork in a memory root.\n *\n * Returns `null` when no breadcrumb exists (the directory is not a fork, or\n * was created before PR 4/6). Never throws for a missing file — callers\n * that need to distinguish \"not a fork\" from \"corrupt breadcrumb\" should\n * handle the JSON parse error themselves.\n *\n * `forkId` is validated against {@link CAPSULE_ID_PATTERN} before being\n * joined into the breadcrumb path so a malicious value like\n * `../../../../tmp` cannot escape the `forks/` namespace and resolve to\n * unrelated files outside the configured memory root (Codex P2 #751).\n * Invalid ids return `null` (same shape as \"no breadcrumb\"); callers that\n * need to distinguish \"invalid id\" from \"absent breadcrumb\" should validate\n * upstream.\n */\nexport async function readForkLineage(\n targetRoot: string,\n forkId: string,\n): Promise<ForkLineage | null> {\n // Reject any forkId that does not satisfy the capsule-id pattern. We use\n // the same constraints as `forkCapsule` so a path traversal payload like\n // `../../etc` is rejected before any filesystem access. Returning null\n // (rather than throwing) keeps the contract symmetric with the\n // \"breadcrumb not present\" case for the common get-or-default usage.\n if (\n typeof forkId !== \"string\" ||\n forkId.length === 0 ||\n forkId.length > 64 ||\n !CAPSULE_ID_PATTERN.test(forkId)\n ) {\n return null;\n }\n const rootAbs = path.resolve(targetRoot);\n // Resolve the root via realpath up-front so the symlink-safe containment\n // check below compares against the canonical path. If realpath fails\n // (root does not exist or is unreadable) we fall back to the lexical\n // root; the read will then return null for a missing breadcrumb anyway.\n const rootReal = await realpath(rootAbs).catch(() => rootAbs);\n\n const lineagePath = path.join(rootReal, \"forks\", forkId, \"lineage.json\");\n // Defensive lexical containment check: even though CAPSULE_ID_PATTERN\n // forbids `/` and `..`, an attacker who somehow bypasses the regex would\n // still be caught here. `path.relative` returns a `..`-prefixed string\n // when the target escapes the root.\n const rel = path.relative(rootReal, lineagePath);\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n return null;\n }\n\n // Symlink-safe containment check (Codex P2 #751 round 2).\n // The lexical check above cannot detect a symlinked `forks/<forkId>`\n // directory whose target is outside the memory root. We resolve the\n // nearest existing ancestor of the breadcrumb path via realpath (which\n // follows symlinks), re-append the non-existent suffix, and verify the\n // result still lives under the real root. Mirrors the\n // `assertRealpathInsideRoot` pattern used by capsule-import.ts.\n if (!(await isLineagePathContained(rootReal, lineagePath))) {\n return null;\n }\n const raw = await readFile(lineagePath, \"utf-8\").catch(() => null);\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw);\n // Basic shape check — we do not run full zod validation here so that\n // slightly malformed breadcrumbs (e.g. missing new fields added in later\n // PRs) can still be returned rather than silently dropped.\n if (typeof parsed !== \"object\" || parsed === null || typeof parsed.forkId !== \"string\") {\n return null;\n }\n return parsed as ForkLineage;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction validateForkId(forkId: unknown): void {\n if (typeof forkId !== \"string\") {\n throw new Error(\"forkCapsule: forkId must be a string\");\n }\n if (forkId.length === 0) {\n throw new Error(\"forkCapsule: forkId must not be empty\");\n }\n if (forkId.length > 64) {\n throw new Error(\"forkCapsule: forkId must be 64 characters or fewer\");\n }\n if (!CAPSULE_ID_PATTERN.test(forkId)) {\n throw new Error(\n `forkCapsule: invalid forkId \"${forkId}\". Expected alphanumeric with single dashes (no leading/trailing dashes, no consecutive dashes).`,\n );\n }\n}\n\n/**\n * Return true when ANY filesystem entry exists at {@link absPath} —\n * directory, regular file, symlink (even broken), or other special file.\n * Uses `lstat` so a symlink at the path is detected regardless of whether\n * its target resolves. Used by the duplicate-forkId check so a stray file\n * cannot bypass the \"reject before any write\" guarantee (Codex P2 #751).\n */\nasync function pathEntryExists(absPath: string): Promise<boolean> {\n const st = await lstat(absPath).catch(() => null);\n return st !== null;\n}\n\n/**\n * Symlink-safe containment check for {@link readForkLineage}.\n *\n * Walks from {@link lineagePath} toward {@link rootReal} until it finds a\n * path component that exists on disk, resolves that prefix via `realpath`\n * (which follows symlinks), then re-appends the non-existent suffix and\n * verifies the resulting canonical path is still inside {@link rootReal}.\n *\n * This catches the case where any subdirectory in the path is a symlink\n * pointing outside the intended root. Returns `true` when the path is\n * safely contained, `false` otherwise.\n *\n * Mirrors the `assertRealpathInsideRoot` helper in `capsule-import.ts`.\n * Returning a boolean (rather than throwing) keeps the contract aligned\n * with `readForkLineage`'s \"return null on any safety failure\" pattern.\n */\nasync function isLineagePathContained(\n rootReal: string,\n lineagePath: string,\n): Promise<boolean> {\n let existing = lineagePath;\n const suffix: string[] = [];\n while (existing !== path.dirname(existing)) {\n const st = await lstat(existing).catch(() => null);\n if (st !== null) break;\n suffix.unshift(path.basename(existing));\n existing = path.dirname(existing);\n }\n\n // If we walked all the way to the filesystem root without finding\n // anything that exists, the path is harmless (no symlinks to follow).\n // The subsequent readFile will simply fail and return null.\n const existingReal = await realpath(existing).catch(() => existing);\n const targetReal = suffix.length > 0 ? path.join(existingReal, ...suffix) : existingReal;\n\n const rel = path.relative(rootReal, targetReal);\n if (rel === \"\") return true;\n if (rel === \"..\") return false;\n if (rel.startsWith(`..${path.sep}`)) return false;\n if (path.isAbsolute(rel)) return false;\n return true;\n}\n"],"mappings":"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","names":["j","fsp","path","fsp","path","fsp","path","crypto","fsp","crypto","log","path","fs","path","path","fs","path","fs","path","fs","path","crypto","fs","path","parseFrontmatter","extractBody","crypto","fs","path","crypto","hashContent","path","fs","readFileSafe","parseFrontmatter","extractBody","crypto","fs","path","path","fs","parseFrontmatter","extractBody","readFileSafe","fs","path","crypto","DEFAULT_EXCLUDE","hashContent","fs","path","crypto","path","manifestPath","fs","crypto","walkMd","hashContent","fs","path","path","fs","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/coding/review-context.ts","../src/binary-lifecycle/types.ts","../src/binary-lifecycle/backend.ts","../src/binary-lifecycle/scanner.ts","../src/binary-lifecycle/manifest.ts","../src/binary-lifecycle/pipeline.ts","../src/projection/index.ts","../src/utils/category-dir.ts","../src/onboarding/index.ts","../src/curation/index.ts","../src/dedup/index.ts","../src/review/index.ts","../src/sync/index.ts","../src/spaces/index.ts","../src/memory-extension/shared-instructions.ts","../src/memory-extension/codex-publisher.ts","../src/memory-extension/claude-code-publisher.ts","../src/memory-extension/hermes-publisher.ts","../src/memory-extension/index.ts","../src/importers/base.ts","../src/transfer/capsule-fork.ts"],"sourcesContent":["/**\n * Diff-aware review-context packer (issue #569 PR 4).\n *\n * When an agent is asked \"review this PR\" / \"what changed in this diff\" /\n * \"look at this diff\", the prompt that reaches recall is short and generic\n * — the real signal is the diff itself. This module:\n *\n * 1. Detects review-intent prompts via `isReviewPrompt`.\n * 2. Extracts the touched file list from a unified diff via\n * `parseTouchedFiles`.\n * 3. Re-ranks a set of candidate memories so that memories whose\n * `entityRefs` mention a touched path float to the top. The boost is\n * additive and bounded so it doesn't obliterate the original ranking —\n * it's a bias, not a filter.\n *\n * Pure — no orchestrator, no storage. Callers inject the candidate memories\n * they already have from their normal recall pipeline. This keeps the\n * module easy to test and integrates cleanly with the existing tiered-recall\n * code in `orchestrator.ts` (the tier itself can be wired later; the pure\n * surface is what PRs 5/6/7 will call).\n */\n\n// ──────────────────────────────────────────────────────────────────────────\n// Public types\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * A memory candidate as fed into review-context ranking. The shape is a\n * deliberate subset of the core `MemorySummary` / recall result — only the\n * fields we actually need — so this module stays decoupled from the rest of\n * the codebase and can be reused by CLI tools, bench fixtures, etc.\n */\nexport interface ReviewCandidate {\n /** Opaque identifier. Echoed unchanged in the output. */\n id: string;\n /**\n * Pre-review relevance score from the upstream recall pipeline. Higher is\n * better. `0` is treated as \"no prior signal\" and gets the full review\n * boost when a path match is found.\n */\n score: number;\n /**\n * References the memory mentions (file paths, entity names, etc.). Used\n * to decide whether any touched file appears in the memory's scope.\n *\n * Accepts `undefined`/missing so callers can pass sparse records from\n * legacy storage without pre-filling.\n */\n entityRefs?: string[];\n}\n\nexport interface ReviewContext {\n /**\n * Normalized file paths touched by the diff. Each entry is forward-slashed\n * and relative to the repo root when possible.\n */\n touchedFiles: string[];\n /**\n * Candidates re-sorted so memories whose `entityRefs` mention a touched\n * path are boosted. Shape matches the input `ReviewCandidate[]` — the\n * boost is recorded on each entry as `boost` for observability.\n */\n rankedRecall: Array<ReviewCandidate & { boost: number }>;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Review-prompt heuristic\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Keyword list from the #569 design doc, plus obvious paraphrases. All\n * matching is case-insensitive and whole-word (so `reviewer` doesn't trigger\n * on `review` alone).\n */\nconst REVIEW_KEYWORD_PATTERNS: RegExp[] = [\n /\\breview\\b/i,\n /\\bdiff\\b/i,\n /\\bwhat changed\\b/i,\n /\\blook at this pr\\b/i,\n /\\bwhat('?s|\\s+is)\\s+in\\s+this\\s+(pr|patch|diff|change)\\b/i,\n /\\bcode review\\b/i,\n];\n\n/**\n * `true` when the prompt looks like a review / diff-explanation request.\n *\n * Empty / non-string input → `false` (the caller shouldn't branch on an\n * invalid prompt).\n */\nexport function isReviewPrompt(prompt: string | null | undefined): boolean {\n if (typeof prompt !== \"string\") return false;\n const trimmed = prompt.trim();\n if (!trimmed) return false;\n return REVIEW_KEYWORD_PATTERNS.some((re) => re.test(trimmed));\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Unified-diff parser — extract touched files\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Parse a unified diff and return the set of files touched. Accepts both the\n * `diff --git` form (`diff --git a/foo b/bar`) and the `--- / +++` form\n * (`--- a/foo\\n+++ b/bar`). Returns deduplicated, repo-root-relative paths\n * (with the conventional `a/` / `b/` prefixes stripped).\n *\n * Path entries of `/dev/null` (used in adds/deletes) are excluded.\n */\nexport function parseTouchedFiles(diff: string | null | undefined): string[] {\n if (typeof diff !== \"string\" || !diff.trim()) return [];\n const touched = new Set<string>();\n const lines = diff.split(/\\r?\\n/);\n\n for (const line of lines) {\n // `diff --git a/foo/bar.ts b/foo/bar.ts`\n //\n // Paths may be quoted by git when they contain spaces or non-ASCII\n // characters, e.g. `diff --git \"a/src/my file.ts\" \"b/src/my file.ts\"`.\n // The two paths are always separated by whitespace at the top level, but\n // whitespace INSIDE quoted paths must NOT split the tokens. Parse the\n // two paths with an explicit tokenizer instead of a `\\S+`-based regex\n // (which would shatter `\"a/my file.ts\"` into `\"a/my` / `file.ts\"`).\n const gitPrefix = line.match(/^diff --git\\s+/);\n if (gitPrefix) {\n const rest = line.slice(gitPrefix[0].length);\n const paths = splitDiffGitPaths(rest);\n if (paths) {\n for (const raw of paths) {\n const stripped = stripDiffPathPrefix(raw);\n if (stripped && stripped !== \"/dev/null\") touched.add(stripped);\n }\n }\n continue;\n }\n // `--- a/foo/bar.ts` or `+++ b/foo/bar.ts` — or quoted:\n // `--- \"a/foo bar.ts\"` / `+++ \"b/foo bar.ts\"` when git emits C-quoted\n // paths for whitespace or non-ASCII filenames.\n //\n // Matched with an anchored prefix and an explicit tokenizer for the\n // tail so quoted paths containing whitespace are not split on the\n // first internal space. The previous `\\S+` form silently dropped\n // everything after the first whitespace in a quoted path.\n const headerPrefix = line.match(/^(?:---|\\+\\+\\+)[ \\t]+/);\n if (headerPrefix) {\n const tail = line.slice(headerPrefix[0].length).replace(/[ \\t]+$/, \"\");\n const raw = extractSingleDiffPathToken(tail);\n if (raw) {\n const stripped = stripDiffPathPrefix(raw);\n if (stripped && stripped !== \"/dev/null\") touched.add(stripped);\n }\n }\n }\n\n return Array.from(touched).sort();\n}\n\n/**\n * Extract the single path token from the tail of a `---` / `+++` header\n * line. Returns `null` when the tail is empty or malformed (e.g. an\n * unterminated quoted path). The whole tail is consumed — trailing\n * timestamps from non-git diff frontends (`--- a/foo\t2023-01-01`) fall\n * into a leading-token extraction like the quoted-form case.\n */\nfunction extractSingleDiffPathToken(tail: string): string | null {\n if (tail.length === 0) return null;\n if (tail[0] === '\"') {\n let j = 1;\n while (j < tail.length) {\n if (tail[j] === \"\\\\\" && j + 1 < tail.length) {\n j += 2;\n continue;\n }\n if (tail[j] === '\"') break;\n j += 1;\n }\n if (j >= tail.length) return null; // unterminated quoted path\n return tail.slice(0, j + 1);\n }\n // Unquoted: consume up to the first tab or whitespace-run, so standard\n // `--- a/foo\t<timestamp>` lines surface just `a/foo`. For ordinary\n // git-style output the tail has no whitespace at all.\n let j = 0;\n while (j < tail.length && tail[j] !== \" \" && tail[j] !== \"\\t\") j += 1;\n return tail.slice(0, j);\n}\n\n/**\n * Split the `a-path b-path` portion of a `diff --git` line into exactly two\n * path tokens, respecting git's quoting convention. Returns `null` when the\n * input cannot be parsed into exactly two tokens.\n */\nfunction splitDiffGitPaths(rest: string): [string, string] | null {\n const tokens: string[] = [];\n let i = 0;\n while (i < rest.length && tokens.length < 2) {\n // Skip leading whitespace between tokens.\n while (i < rest.length && (rest[i] === \" \" || rest[i] === \"\\t\")) i += 1;\n if (i >= rest.length) break;\n if (rest[i] === '\"') {\n // Quoted path: consume up to the matching unescaped `\"`. Git escapes\n // inner quotes as `\\\"`, so we respect backslash escaping.\n let j = i + 1;\n while (j < rest.length) {\n if (rest[j] === \"\\\\\" && j + 1 < rest.length) {\n j += 2;\n continue;\n }\n if (rest[j] === '\"') break;\n j += 1;\n }\n if (j >= rest.length) return null; // unterminated quoted path\n tokens.push(rest.slice(i, j + 1));\n i = j + 1;\n } else {\n // Unquoted path: run of non-whitespace.\n let j = i;\n while (j < rest.length && rest[j] !== \" \" && rest[j] !== \"\\t\") j += 1;\n tokens.push(rest.slice(i, j));\n i = j;\n }\n }\n if (tokens.length !== 2) return null;\n return [tokens[0]!, tokens[1]!];\n}\n\nfunction stripDiffPathPrefix(raw: string): string {\n // Git conventionally prefixes paths with `a/` or `b/` in diffs, and\n // quotes the whole path (including the prefix) when it contains spaces or\n // non-ASCII bytes. Quote-stripping must therefore happen BEFORE the\n // prefix check — otherwise `\"a/path with spaces.ts\"` still starts with\n // `\"a` and the prefix is never recognized.\n let s = raw;\n if (s.length >= 2 && s[0] === '\"' && s[s.length - 1] === '\"') {\n s = s.slice(1, -1);\n // Git octal-escapes non-ASCII and escapes inner backslashes/quotes via\n // the C-quoted path form. We handle the common cases (`\\\"`, `\\\\`) so\n // the rest of the pipeline gets the raw filename. Octal escapes for\n // non-ASCII are left as-is here — they are extremely rare and the\n // touched-file set is only used for lexical matches against\n // entityRefs, where matching either form produces the same hit.\n s = s.replace(/\\\\([\"\\\\])/g, \"$1\");\n }\n // Normalize Windows-style backslashes. Must happen AFTER quote stripping\n // so that the `\\\"` and `\\\\` escape pairs above aren't corrupted.\n s = s.replace(/\\\\/g, \"/\");\n if (s.startsWith(\"a/\") || s.startsWith(\"b/\")) s = s.slice(2);\n return s;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Ranking\n// ──────────────────────────────────────────────────────────────────────────\n\n/**\n * Additive boost per matching touched-file. Tuned so that a single exact\n * match is enough to float a `score=0` candidate above a `score=0.4`\n * unmatched peer, but not so large it buries multi-signal results. `0.5`\n * per match, capped at `1.0` so three matches don't eclipse strong recall.\n */\nconst BOOST_PER_MATCH = 0.5;\nconst MAX_BOOST = 1.0;\n\n/**\n * Count how many touched files appear in a memory's entityRefs. Matches are\n * literal substring matches on either direction — either the ref contains\n * the path, or the path contains the ref — so both\n * - `\"src/foo.ts\"` refs matching a touched `\"src/foo.ts\"`, and\n * - `\"foo.ts\"` refs matching a touched `\"src/foo.ts\"`\n * succeed.\n */\nfunction countPathHits(entityRefs: string[] | undefined, touchedFiles: string[]): number {\n if (!entityRefs || entityRefs.length === 0) return 0;\n if (touchedFiles.length === 0) return 0;\n let hits = 0;\n for (const ref of entityRefs) {\n if (typeof ref !== \"string\" || !ref) continue;\n const lowered = ref.toLowerCase();\n for (const file of touchedFiles) {\n const flower = file.toLowerCase();\n if (lowered === flower) {\n hits += 1;\n break;\n }\n if (lowered.includes(flower) || flower.includes(lowered)) {\n hits += 1;\n break;\n }\n }\n }\n return hits;\n}\n\n/**\n * Build a review-context ranking for a set of candidate memories.\n *\n * Contract:\n * - `touchedFiles` is the parsed diff file list.\n * - `candidates` is passed through unchanged when no boost applies.\n * - When a boost applies, the result is sorted by `(score + boost)` desc,\n * with a stable secondary sort on the original `id` for determinism\n * (CLAUDE.md #19 — comparators must return 0 for equal items).\n */\nexport function rankReviewCandidates(\n candidates: ReviewCandidate[],\n touchedFiles: string[],\n): Array<ReviewCandidate & { boost: number }> {\n const annotated: Array<ReviewCandidate & { boost: number }> = candidates.map((c) => {\n const hits = countPathHits(c.entityRefs, touchedFiles);\n const boost = Math.min(MAX_BOOST, hits * BOOST_PER_MATCH);\n return { ...c, boost };\n });\n\n annotated.sort((a, b) => {\n const adjA = a.score + a.boost;\n const adjB = b.score + b.boost;\n if (adjA !== adjB) return adjB - adjA;\n // Stable secondary sort for deterministic ordering on ties.\n if (a.id < b.id) return -1;\n if (a.id > b.id) return 1;\n return 0;\n });\n\n return annotated;\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// Packer entry point\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface PackReviewContextInput {\n /** Unified diff, as produced by `git diff`. */\n diff: string | null | undefined;\n /** Candidate memories from the upstream recall pipeline. */\n candidates: ReviewCandidate[];\n}\n\n/**\n * Top-level entry point used by the orchestrator (and CLI / bench) when a\n * review-intent prompt is detected.\n *\n * Parses the diff, re-ranks the candidates, and returns both artefacts so\n * the caller can surface `touchedFiles` as context and `rankedRecall` as\n * the recall result.\n */\nexport function packReviewContext(input: PackReviewContextInput): ReviewContext {\n const touchedFiles = parseTouchedFiles(input.diff);\n const rankedRecall = rankReviewCandidates(input.candidates, touchedFiles);\n return { touchedFiles, rankedRecall };\n}\n","/**\n * Binary file lifecycle management types.\n *\n * Defines the configuration, manifest, and record structures for the\n * three-stage binary lifecycle pipeline: mirror, redirect, clean.\n */\n\nexport interface BinaryLifecycleConfig {\n /** Master toggle. Default: false. */\n enabled: boolean;\n /** Days after mirror before local copy is eligible for cleanup. Default: 7. */\n gracePeriodDays: number;\n /** Files larger than this are skipped during scan. Default: 50 MB. */\n maxBinarySizeBytes: number;\n /** Glob patterns for binary file types to manage. */\n scanPatterns: string[];\n /** Backend configuration for binary storage. */\n backend: BinaryStorageBackendConfig;\n}\n\nexport interface BinaryStorageBackendConfig {\n /** Backend type. \"filesystem\" copies to a local directory. \"none\" is a no-op (dry-run/testing). */\n type: \"filesystem\" | \"s3\" | \"none\";\n /** Destination directory for the filesystem backend. */\n basePath?: string;\n /** S3 bucket name (future). */\n s3Bucket?: string;\n /** S3 region (future). */\n s3Region?: string;\n /** S3 key prefix (future). */\n s3Prefix?: string;\n}\n\nexport type BinaryAssetStatus =\n | \"pending\"\n | \"mirrored\"\n | \"redirected\"\n | \"cleaned\"\n | \"error\";\n\nexport interface BinaryAssetRecord {\n /** Relative path from memoryDir to the original file. */\n originalPath: string;\n /** Path (or URL) in the backend storage. */\n mirroredPath: string;\n /** SHA-256 hex digest of file content. */\n contentHash: string;\n /** File size in bytes. */\n sizeBytes: number;\n /** MIME type (e.g. \"image/png\"). */\n mimeType: string;\n /** ISO 8601 timestamp when the file was mirrored. */\n mirroredAt: string;\n /** ISO 8601 timestamp when markdown references were rewritten. */\n redirectedAt?: string;\n /** ISO 8601 timestamp when the local copy was deleted. */\n cleanedAt?: string;\n /** Current lifecycle status. */\n status: BinaryAssetStatus;\n}\n\nexport interface BinaryLifecycleManifest {\n version: 1;\n assets: BinaryAssetRecord[];\n lastScanAt?: string;\n}\n\nexport interface PipelineResult {\n scanned: number;\n mirrored: number;\n redirected: number;\n cleaned: number;\n errors: string[];\n dryRun: boolean;\n}\n\nexport const DEFAULT_SCAN_PATTERNS = [\n \"*.png\",\n \"*.jpg\",\n \"*.jpeg\",\n \"*.gif\",\n \"*.pdf\",\n \"*.mp3\",\n \"*.mp4\",\n \"*.wav\",\n];\n\nexport const DEFAULT_MAX_BINARY_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB\nexport const DEFAULT_GRACE_PERIOD_DAYS = 7;\n","/**\n * Binary storage backend interface and implementations.\n *\n * Backends handle the actual persistence of binary files to an external\n * location. The pipeline calls upload/exists/delete through this interface\n * so swapping storage providers requires no pipeline changes.\n */\n\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { BinaryStorageBackendConfig } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Interface\n// ---------------------------------------------------------------------------\n\nexport interface BinaryStorageBackend {\n /** Discriminator for the backend type. */\n readonly type: string;\n /**\n * Upload a local file to the backend.\n * @returns The backend path or URL where the file was stored.\n */\n upload(localPath: string, remotePath: string): Promise<string>;\n /** Check whether a remote path already exists in the backend. */\n exists(remotePath: string): Promise<boolean>;\n /** Delete a file from the backend. */\n delete(remotePath: string): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Filesystem backend\n// ---------------------------------------------------------------------------\n\nexport class FilesystemBackend implements BinaryStorageBackend {\n readonly type = \"filesystem\";\n private readonly basePath: string;\n\n constructor(basePath: string) {\n if (!basePath || basePath.trim().length === 0) {\n throw new Error(\"FilesystemBackend requires a non-empty basePath\");\n }\n this.basePath = basePath;\n }\n\n async upload(localPath: string, remotePath: string): Promise<string> {\n const dest = path.join(this.basePath, remotePath);\n const destDir = path.dirname(dest);\n await fsp.mkdir(destDir, { recursive: true });\n await fsp.copyFile(localPath, dest);\n return dest;\n }\n\n async exists(remotePath: string): Promise<boolean> {\n const dest = path.join(this.basePath, remotePath);\n try {\n await fsp.access(dest, fs.constants.F_OK);\n return true;\n } catch {\n return false;\n }\n }\n\n async delete(remotePath: string): Promise<void> {\n const dest = path.join(this.basePath, remotePath);\n try {\n await fsp.unlink(dest);\n } catch (err: unknown) {\n // Ignore ENOENT (already deleted); rethrow everything else.\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// None backend (no-op, for dry-run / testing)\n// ---------------------------------------------------------------------------\n\nexport class NoneBackend implements BinaryStorageBackend {\n readonly type = \"none\";\n\n async upload(_localPath: string, remotePath: string): Promise<string> {\n return remotePath;\n }\n\n async exists(_remotePath: string): Promise<boolean> {\n return false;\n }\n\n async delete(_remotePath: string): Promise<void> {\n // intentional no-op\n }\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\nexport function createBackend(cfg: BinaryStorageBackendConfig): BinaryStorageBackend {\n switch (cfg.type) {\n case \"filesystem\": {\n if (!cfg.basePath) {\n throw new Error(\n \"BinaryStorageBackendConfig.basePath is required when type is \\\"filesystem\\\"\",\n );\n }\n return new FilesystemBackend(cfg.basePath);\n }\n case \"s3\":\n throw new Error(\"S3 binary storage backend is not yet implemented\");\n case \"none\":\n return new NoneBackend();\n default:\n throw new Error(`Unknown binary storage backend type: ${String((cfg as { type: string }).type)}`);\n }\n}\n","/**\n * Binary file scanner.\n *\n * Recursively walks the memory directory, matches files against configured\n * glob patterns, skips files already tracked in the manifest, and respects\n * the max-size limit.\n */\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { BinaryLifecycleConfig, BinaryLifecycleManifest } from \"./types.js\";\n\n/**\n * Test whether a filename matches any of the provided glob patterns.\n * Supports simple `*.ext` patterns (the default scan patterns).\n * For more complex globs a proper library should be used; this covers\n * the 95% case without adding a dependency.\n */\nexport function matchesPatterns(filename: string, patterns: string[]): boolean {\n const lower = filename.toLowerCase();\n for (const pattern of patterns) {\n // Simple *.ext matching\n if (pattern.startsWith(\"*.\")) {\n const ext = pattern.slice(1).toLowerCase(); // e.g. \".png\"\n if (lower.endsWith(ext)) return true;\n } else if (lower === pattern.toLowerCase()) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Scan memoryDir recursively for binary files matching the configured patterns.\n * Returns relative paths (relative to memoryDir) for files not yet tracked.\n */\nexport async function scanForBinaries(\n memoryDir: string,\n config: BinaryLifecycleConfig,\n manifest: BinaryLifecycleManifest,\n): Promise<string[]> {\n const tracked = new Set(manifest.assets.map((a) => a.originalPath));\n const results: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fsp.readdir(dir, { withFileTypes: true });\n } catch {\n return; // skip unreadable directories\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n // Normalize to POSIX separators so redirect matching works on Windows\n // (markdown links always use forward slashes).\n const relativePath = path.relative(memoryDir, fullPath).split(path.sep).join(\"/\");\n\n if (entry.isDirectory()) {\n // Skip the manifest directory itself and hidden dirs starting with .\n if (entry.name === \".binary-lifecycle\") continue;\n await walk(fullPath);\n continue;\n }\n\n if (!entry.isFile()) continue;\n\n // Check pattern match\n if (!matchesPatterns(entry.name, config.scanPatterns)) continue;\n\n // Skip already tracked\n if (tracked.has(relativePath)) continue;\n\n // Check file size\n try {\n const stat = await fsp.stat(fullPath);\n if (stat.size > config.maxBinarySizeBytes) continue;\n if (stat.size === 0) continue; // skip empty files\n } catch {\n continue; // stat failure, skip\n }\n\n results.push(relativePath);\n }\n }\n\n await walk(memoryDir);\n return results;\n}\n","/**\n * Binary lifecycle manifest — read/write operations.\n *\n * The manifest lives at `${memoryDir}/.binary-lifecycle/manifest.json`.\n * Writes use the atomic temp-then-rename pattern (CLAUDE.md #54).\n */\n\nimport fs from \"node:fs\";\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport type { BinaryLifecycleManifest } from \"./types.js\";\n\nconst MANIFEST_DIR = \".binary-lifecycle\";\nconst MANIFEST_FILE = \"manifest.json\";\n\nexport function manifestDir(memoryDir: string): string {\n return path.join(memoryDir, MANIFEST_DIR);\n}\n\nexport function manifestPath(memoryDir: string): string {\n return path.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);\n}\n\n/**\n * Read the manifest from disk. Returns a fresh empty manifest if the file\n * does not exist or contains invalid JSON (CLAUDE.md #18).\n */\nexport async function readManifest(memoryDir: string): Promise<BinaryLifecycleManifest> {\n const filePath = manifestPath(memoryDir);\n try {\n const raw = await fsp.readFile(filePath, \"utf-8\");\n const parsed: unknown = JSON.parse(raw);\n // CLAUDE.md #18: validate the parsed result is a non-null object.\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return emptyManifest();\n }\n const obj = parsed as Record<string, unknown>;\n if (obj.version !== 1 || !Array.isArray(obj.assets)) {\n return emptyManifest();\n }\n return parsed as BinaryLifecycleManifest;\n } catch {\n return emptyManifest();\n }\n}\n\n/**\n * Write the manifest atomically: write to a temp file, then rename.\n * CLAUDE.md #54: never delete before write. Write temp first, rename atomically.\n */\nexport async function writeManifest(\n memoryDir: string,\n manifest: BinaryLifecycleManifest,\n): Promise<void> {\n const dir = manifestDir(memoryDir);\n await fsp.mkdir(dir, { recursive: true });\n const dest = manifestPath(memoryDir);\n const tmpSuffix = crypto.randomBytes(8).toString(\"hex\");\n const tmpPath = `${dest}.${tmpSuffix}.tmp`;\n // Sort keys for deterministic output (CLAUDE.md #38).\n const content = JSON.stringify(manifest, null, 2) + \"\\n\";\n await fsp.writeFile(tmpPath, content, \"utf-8\");\n try {\n await fsp.rename(tmpPath, dest);\n } catch (renameErr) {\n // Clean up temp on rename failure (cross-device edge case).\n try {\n await fsp.unlink(tmpPath);\n } catch {\n // ignore cleanup failure\n }\n throw renameErr;\n }\n}\n\nexport function emptyManifest(): BinaryLifecycleManifest {\n return { version: 1, assets: [] };\n}\n","/**\n * Binary lifecycle pipeline — mirror, redirect, clean.\n *\n * Three-stage pipeline:\n * 1. Mirror: upload binary to backend, record in manifest\n * 2. Redirect: scan markdown for inline refs, replace with redirect path\n * 3. Clean: after grace period, delete local copy\n */\n\nimport fsp from \"node:fs/promises\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport type {\n BinaryAssetRecord,\n BinaryLifecycleConfig,\n PipelineResult,\n} from \"./types.js\";\nimport type { BinaryStorageBackend } from \"./backend.js\";\nimport { readManifest, writeManifest } from \"./manifest.js\";\nimport { scanForBinaries } from \"./scanner.js\";\n\n/** Minimal logger interface so we don't depend on the full logger module. */\ninterface PipelineLogger {\n info(msg: string): void;\n warn(msg: string): void;\n error(msg: string): void;\n}\n\ninterface PipelineOptions {\n dryRun?: boolean;\n /** Force-clean all files past grace period, ignoring redirect status. */\n forceClean?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nasync function hashFile(filePath: string): Promise<string> {\n const content = await fsp.readFile(filePath);\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nfunction guessMimeType(ext: string): string {\n const map: Record<string, string> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".pdf\": \"application/pdf\",\n \".mp3\": \"audio/mpeg\",\n \".mp4\": \"video/mp4\",\n \".wav\": \"audio/wav\",\n };\n return map[ext.toLowerCase()] ?? \"application/octet-stream\";\n}\n\n/**\n * Escape special regex characters in a string.\n * CLAUDE.md #46: always escapeRegex on user-derived parts.\n */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n// ---------------------------------------------------------------------------\n// Pipeline stages\n// ---------------------------------------------------------------------------\n\nasync function stageMirror(\n memoryDir: string,\n newPaths: string[],\n backend: BinaryStorageBackend,\n assets: BinaryAssetRecord[],\n log: PipelineLogger,\n dryRun: boolean,\n): Promise<{ mirrored: number; errors: string[] }> {\n let mirrored = 0;\n const errors: string[] = [];\n\n for (const relPath of newPaths) {\n const fullPath = path.join(memoryDir, relPath);\n try {\n const stat = await fsp.stat(fullPath);\n const contentHash = await hashFile(fullPath);\n const ext = path.extname(relPath);\n const mimeType = guessMimeType(ext);\n const remotePath = relPath;\n\n let backendLocation = remotePath;\n if (!dryRun) {\n backendLocation = await backend.upload(fullPath, remotePath);\n }\n\n const record: BinaryAssetRecord = {\n originalPath: relPath,\n mirroredPath: backendLocation,\n contentHash,\n sizeBytes: stat.size,\n mimeType,\n mirroredAt: new Date().toISOString(),\n status: \"mirrored\",\n };\n\n assets.push(record);\n mirrored++;\n log.info(`[binary-lifecycle] mirrored: ${relPath} (${stat.size} bytes)${dryRun ? \" [dry-run]\" : \"\"}`);\n } catch (err) {\n const msg = `mirror failed for ${relPath}: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`[binary-lifecycle] ${msg}`);\n errors.push(msg);\n }\n }\n\n return { mirrored, errors };\n}\n\nasync function stageRedirect(\n memoryDir: string,\n assets: BinaryAssetRecord[],\n log: PipelineLogger,\n dryRun: boolean,\n): Promise<{ redirected: number; errors: string[] }> {\n let redirected = 0;\n const errors: string[] = [];\n\n // Only redirect assets that are mirrored but not yet redirected.\n const candidates = assets.filter((a) => a.status === \"mirrored\");\n if (candidates.length === 0) return { redirected, errors };\n\n // Find all markdown files in memoryDir (recursive).\n const mdFiles = await findMarkdownFiles(memoryDir);\n\n for (const asset of candidates) {\n let matchCount = 0;\n let writeFailCount = 0;\n for (const mdPath of mdFiles) {\n try {\n const content = await fsp.readFile(mdPath, \"utf-8\");\n\n // Build the match path relative to this markdown file's directory.\n // Markdown links like `` are file-relative, but\n // asset.originalPath is memory-root relative (e.g. `sub/image.png`).\n // Resolve the asset path relative to the markdown file's directory\n // so both forms match correctly.\n const mdDir = path.dirname(mdPath);\n const assetAbsolute = path.join(memoryDir, asset.originalPath);\n const relativeToMd = path.relative(mdDir, assetAbsolute);\n // Normalise to forward slashes for regex matching (markdown uses /).\n const relativeForward = relativeToMd.split(path.sep).join(\"/\");\n const escaped = escapeRegex(relativeForward);\n\n // Build a regex that matches markdown image/link references to the file.\n // Handles:  ,  , [text](./path)\n const pattern = new RegExp(\n `(!?\\\\[[^\\\\]]*\\\\]\\\\()(\\\\.\\\\/)?(${escaped})(\\\\))`,\n \"g\",\n );\n\n if (!pattern.test(content)) continue;\n matchCount++;\n\n if (!dryRun) {\n // Reset lastIndex after test().\n pattern.lastIndex = 0;\n const updated = content.replace(pattern, (_match, open, _dotSlash, _file, close) => {\n return `${open as string}${asset.mirroredPath}${close as string}`;\n });\n await fsp.writeFile(mdPath, updated, \"utf-8\");\n }\n } catch (err) {\n // Track write failures separately so we don't transition status\n // when some markdown rewrites failed (P1: block redirect on failure).\n writeFailCount++;\n const msg = `redirect scan failed for ${mdPath}: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`[binary-lifecycle] ${msg}`);\n errors.push(msg);\n }\n }\n\n // Only transition to \"redirected\" when at least one reference was found\n // AND all matched files were rewritten successfully.\n if (matchCount > 0 && writeFailCount === 0) {\n asset.status = \"redirected\";\n asset.redirectedAt = new Date().toISOString();\n redirected++;\n log.info(`[binary-lifecycle] redirected: ${asset.originalPath}${dryRun ? \" [dry-run]\" : \"\"}`);\n } else if (matchCount > 0 && writeFailCount > 0) {\n // Some rewrites failed — set error status so the asset is not cleaned\n // prematurely. It can be retried on the next pipeline run.\n asset.status = \"error\";\n log.warn(\n `[binary-lifecycle] redirect partial failure for ${asset.originalPath}: ` +\n `${matchCount} match(es), ${writeFailCount} write failure(s) — status set to error`,\n );\n }\n }\n\n return { redirected, errors };\n}\n\nasync function stageClean(\n memoryDir: string,\n assets: BinaryAssetRecord[],\n gracePeriodDays: number,\n log: PipelineLogger,\n dryRun: boolean,\n forceClean: boolean,\n): Promise<{ cleaned: number; errors: string[] }> {\n let cleaned = 0;\n const errors: string[] = [];\n const now = Date.now();\n const graceMs = gracePeriodDays * 24 * 60 * 60 * 1000;\n\n // Clean only assets that have been redirected (markdown refs already rewritten).\n // Mirrored-only assets must NOT be cleaned — their markdown refs still point\n // to the local file, so deletion would break links.\n const candidates = assets.filter(\n (a) => a.status === \"redirected\",\n );\n\n for (const asset of candidates) {\n const mirroredMs = new Date(asset.mirroredAt).getTime();\n const ageMs = now - mirroredMs;\n\n if (!forceClean && ageMs < graceMs) {\n // Not yet past grace period.\n continue;\n }\n\n const fullPath = path.join(memoryDir, asset.originalPath);\n try {\n if (!dryRun) {\n await fsp.unlink(fullPath);\n }\n asset.status = \"cleaned\";\n asset.cleanedAt = new Date().toISOString();\n cleaned++;\n log.info(`[binary-lifecycle] cleaned: ${asset.originalPath}${dryRun ? \" [dry-run]\" : \"\"}`);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") {\n // Already gone — mark as cleaned.\n asset.status = \"cleaned\";\n asset.cleanedAt = new Date().toISOString();\n cleaned++;\n } else {\n const msg = `clean failed for ${asset.originalPath}: ${err instanceof Error ? err.message : String(err)}`;\n log.error(`[binary-lifecycle] ${msg}`);\n errors.push(msg);\n }\n }\n }\n\n return { cleaned, errors };\n}\n\n// ---------------------------------------------------------------------------\n// Markdown file discovery\n// ---------------------------------------------------------------------------\n\nasync function findMarkdownFiles(dir: string): Promise<string[]> {\n const results: string[] = [];\n\n async function walk(current: string): Promise<void> {\n let entries: import(\"node:fs\").Dirent[];\n try {\n entries = await fsp.readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = path.join(current, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === \".binary-lifecycle\") continue;\n await walk(full);\n } else if (entry.isFile() && entry.name.endsWith(\".md\")) {\n results.push(full);\n }\n }\n }\n\n await walk(dir);\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Main pipeline entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Run the binary lifecycle pipeline: scan, mirror, redirect, clean.\n */\nexport async function runBinaryLifecyclePipeline(\n memoryDir: string,\n config: BinaryLifecycleConfig,\n backend: BinaryStorageBackend,\n log: PipelineLogger,\n opts?: PipelineOptions,\n): Promise<PipelineResult> {\n const dryRun = opts?.dryRun ?? false;\n const forceClean = opts?.forceClean ?? false;\n\n const manifest = await readManifest(memoryDir);\n\n // Stage 0: Scan\n const newPaths = await scanForBinaries(memoryDir, config, manifest);\n const scanned = newPaths.length;\n\n // Stage 1: Mirror\n const mirrorResult = await stageMirror(\n memoryDir,\n newPaths,\n backend,\n manifest.assets,\n log,\n dryRun,\n );\n\n // Stage 2: Redirect\n const redirectResult = await stageRedirect(memoryDir, manifest.assets, log, dryRun);\n\n // Stage 3: Clean\n const cleanResult = await stageClean(\n memoryDir,\n manifest.assets,\n config.gracePeriodDays,\n log,\n dryRun,\n forceClean,\n );\n\n // Persist manifest (unless dry-run).\n manifest.lastScanAt = new Date().toISOString();\n if (!dryRun) {\n await writeManifest(memoryDir, manifest);\n }\n\n const allErrors = [\n ...mirrorResult.errors,\n ...redirectResult.errors,\n ...cleanResult.errors,\n ];\n\n return {\n scanned,\n mirrored: mirrorResult.mirrored,\n redirected: redirectResult.redirected,\n cleaned: cleanResult.cleaned,\n errors: allErrors,\n dryRun,\n };\n}\n","/**\n * @remnic/core — Workspace Tree Projection\n *\n * Generates a human-readable `.engram/context-tree/` from canonical memory.\n * Each node is a `.md` file with rich metadata, * (provenance, trust, confidence, source anchors).\n * Manual edits are preserved in fenced blocks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getCategoryDir, ALL_CATEGORY_KEYS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface TreeNode {\n /** Relative path from context-tree root, e.g. \"entities/claude.md\" */\n path: string;\n /** Category from canonical memory */\n category: string;\n /** Human-readable title */\n title: string;\n /** File content (rendered markdown) */\n content: string;\n /** Source memory IDs that contributed to this node */\n sourceAnchors: string[];\n /** Confidence (0-1) */\n confidence: number;\n /** Trust zone classification */\n confidenceTier: string;\n /** When this node was generated */\n generatedAt: string;\n /** Provenance chain */\n provenance: ProvenanceEntry[];\n}\n\nexport interface ProvenanceEntry {\n memoryId: string;\n source: string;\n extracted: string;\n}\n\nexport interface GenerateOptions {\n /** Memory root directory (e.g. ~/.openclaw/workspace/memory/local) */\n memoryDir: string;\n /** Output directory (e.g. .engram/context-tree) */\n outputDir: string;\n /** Categories to include (default: all) */\n categories?: string[];\n /** Whether to include entity graph */\n includeEntities?: boolean;\n /** Whether to include orphaned questions */\n includeQuestions?: boolean;\n /** Max nodes per category (default: unlimited) */\n maxPerCategory?: number;\n /** Whether to watch for changes and regenerate incrementally */\n watch?: boolean;\n}\n\nexport interface GenerateResult {\n nodesGenerated: number;\n nodesSkipped: number;\n categories: Record<string, number>;\n durationMs: number;\n outputDir: string;\n}\n\n// ── Generation ──────────────────────────────────────────────────────────────\n\n/**\n * Generate a context tree from canonical memory.\n *\n * Reads memory `.md` files from the source directory, * and projects them into a clean, * human-readable tree structure at `outputDir`.\n */\nexport async function generateContextTree(options: GenerateOptions): Promise<GenerateResult> {\n const startTime = Date.now();\n const {\n memoryDir,\n outputDir,\n categories: filterCategories,\n includeEntities = true,\n includeQuestions = true,\n maxPerCategory = Infinity,\n } = options;\n\n let nodesGenerated = 0;\n let nodesSkipped = 0;\n const categoryCounts: Record<string, number> = {};\n\n // Ensure output directory exists\n fs.mkdirSync(outputDir, { recursive: true });\n\n // Process each category (exclude 'question' — handled by separate includeQuestions pass)\n const allCategories = filterCategories ?? ALL_CATEGORY_KEYS.filter((c) => c !== \"question\");\n\n for (const category of allCategories) {\n const categoryDir = getCategoryDir(memoryDir, category);\n if (!fs.existsSync(categoryDir)) continue;\n\n categoryCounts[category] = 0;\n const files = walkR(categoryDir);\n let count = 0;\n\n for (const filePath of files) {\n if (count >= maxPerCategory) {\n nodesSkipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf8\");\n const fm = parseFrontmatter(content);\n if (!fm) {\n nodesSkipped++;\n continue;\n }\n\n const node = projectNode(filePath, category, fm, content);\n if (!node) {\n nodesSkipped++;\n continue;\n }\n\n // Write node to output\n const outputPath = path.join(outputDir, node.path);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, node.content);\n\n nodesGenerated++;\n categoryCounts[category] = (categoryCounts[category] ?? 0) + 1;\n count++;\n }\n }\n\n // Process entities\n if (includeEntities) {\n const entitiesDir = path.join(memoryDir, \"entities\");\n if (fs.existsSync(entitiesDir)) {\n categoryCounts[\"entity\"] = 0;\n const entityFiles = walkR(entitiesDir);\n let count = 0;\n\n for (const filePath of entityFiles) {\n if (count >= maxPerCategory) {\n nodesSkipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf8\");\n const fileName = path.basename(filePath, \".md\");\n const node = projectEntityNode(fileName, content);\n\n const outputPath = path.join(outputDir, \"entities\", `${fileName}.md`);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, node.content);\n\n nodesGenerated++;\n categoryCounts[\"entity\"] = (categoryCounts[\"entity\"] ?? 0) + 1;\n count++;\n }\n }\n }\n\n // Process questions\n if (includeQuestions) {\n const questionsDir = path.join(memoryDir, \"questions\");\n if (fs.existsSync(questionsDir)) {\n categoryCounts[\"question\"] = 0;\n const qFiles = walkR(questionsDir);\n let count = 0;\n\n for (const filePath of qFiles) {\n if (count >= maxPerCategory) {\n nodesSkipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf8\");\n const fm = parseFrontmatter(content);\n if (!fm) {\n nodesSkipped++;\n continue;\n }\n\n const node = projectNode(filePath, \"question\", fm, content);\n if (!node) {\n nodesSkipped++;\n continue;\n }\n\n const outputPath = path.join(outputDir, node.path);\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n fs.writeFileSync(outputPath, node.content);\n\n nodesGenerated++;\n categoryCounts[\"question\"] = (categoryCounts[\"question\"] ?? 0) + 1;\n count++;\n }\n }\n }\n\n // Write index\n const index = generateIndex(categoryCounts, outputDir);\n fs.writeFileSync(path.join(outputDir, \"INDEX.md\"), index);\n\n return {\n nodesGenerated,\n nodesSkipped,\n categories: categoryCounts,\n durationMs: Date.now() - startTime,\n outputDir,\n };\n}\n\n// ── Helpers ──────────────────────────────────────────────────────────────────\n\n\nfunction walkR(dir: string): string[] {\n const results: string[] = [];\n function walk(directory: string): void {\n for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {\n const fullPath = path.join(directory, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.name.endsWith(\".md\")) {\n results.push(fullPath);\n }\n }\n }\n walk(dir);\n return results;\n}\n\ninterface Frontmatter {\n id: string;\n category: string;\n created: string;\n updated: string;\n confidence: number;\n confidenceTier: string;\n tags: string[];\n source: string;\n entityRef?: string;\n lifecycleState?: string;\n}\n\nfunction parseFrontmatter(content: string): Frontmatter | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fmText = match[1];\n const fm: Record<string, unknown> = {};\n for (const line of fmText.split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n if (key === \"tags\") {\n try {\n fm[key] = JSON.parse(value) as string[];\n } catch {\n fm[key] = [] as string[];\n }\n } else if (key === \"confidence\") {\n const parsed = parseFloat(value);\n fm[key] = Number.isFinite(parsed) ? parsed : 0;\n } else {\n fm[key] = value;\n }\n }\n return fm as unknown as Frontmatter;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction projectNode(\n filePath: string,\n category: string,\n fm: Frontmatter,\n rawContent: string,\n): TreeNode | null {\n const body = extractBody(rawContent);\n const fileName = path.basename(filePath, \".md\");\n const dateDir = path.basename(path.dirname(filePath));\n\n // Build relative path: category/date/file or just category/file\n let relPath: string;\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(dateDir)) {\n relPath = path.join(category, dateDir, `${fileName}.md`);\n } else {\n relPath = path.join(category, `${fileName}.md`);\n }\n\n const generatedAt = new Date().toISOString();\n\n const md = `# ${fm.id}\n\n> **Category:** ${fm.category}\n> **Created:** ${fm.created}\n> **Updated:** ${fm.updated ?? fm.created}\n> **Confidence:** ${fm.confidence} (${fm.confidenceTier}${fm.lifecycleState ? `, ${fm.lifecycleState}` : \"\"})\n${fm.tags?.length ? `\\n> **Tags:** ${fm.tags.join(\", \")}` : \"\"}\n${fm.entityRef ? `\\n> **Entity:** ${fm.entityRef}` : \"\"}\n> **Source:** ${fm.source ?? \"unknown\"}\n> **Projected:** ${generatedAt}\n\n---\n\n${body}\n`;\n\n return {\n path: relPath,\n category,\n title: fm.id,\n content: md,\n sourceAnchors: [fm.id],\n confidence: fm.confidence ?? 0,\n confidenceTier: fm.confidenceTier ?? \"unknown\",\n generatedAt,\n provenance: [{\n memoryId: fm.id,\n source: fm.source ?? \"unknown\",\n extracted: fm.created,\n }],\n };\n}\n\nfunction projectEntityNode(fileName: string, content: string): TreeNode {\n const generatedAt = new Date().toISOString();\n\n const md = `> **Projected:** ${generatedAt}\n> **Source:** canonical\n\n---\n\n${content}\n`;\n\n return {\n path: path.join(\"entities\", `${fileName}.md`),\n category: \"entity\",\n title: fileName,\n content: md,\n sourceAnchors: [fileName],\n confidence: 1,\n confidenceTier: \"explicit\",\n generatedAt,\n provenance: [{\n memoryId: fileName,\n source: \"canonical\",\n extracted: generatedAt,\n }],\n };\n}\n\nfunction generateIndex(\n categoryCounts: Record<string, number>,\n outputDir: string,\n): string {\n const lines = [\n \"# Context Tree Index\",\n \"\",\n `Generated: ${new Date().toISOString()}`,\n \"\",\n \"## Summary\",\n \"\",\n `| Category | Count |`,\n `|----------|-------|`,\n ];\n\n let total = 0;\n for (const [cat, count] of Object.entries(categoryCounts).sort()) {\n lines.push(`| ${cat} | ${count} |`);\n total += count;\n }\n\n lines.push(\"\");\n lines.push(`**Total:** ${total} nodes`);\n lines.push(\"\");\n lines.push(\"## Structure\");\n lines.push(\"\");\n lines.push(\"```\");\n lines.push(\"context-tree/\");\n lines.push(\"├── entities/ # Entity knowledge graph\");\n lines.push(\"├── fact/ # Factual memories (date-partitioned)\");\n lines.push(\"├── correction/ # Correction memories\");\n lines.push(\"├── decision/ # Decisions\");\n lines.push(\"├── moment/ # Notable moments\");\n lines.push(\"├── preference/ # Preferences\");\n lines.push(\"├── principle/ # Principles\");\n lines.push(\"├── question/ # Open questions\");\n lines.push(\"└── INDEX.md # This file\");\n lines.push(\"```\");\n\n return lines.join(\"\\n\") + \"\\n\";\n}\n","/**\n * @remnic/core — Category Directory Map\n *\n * Shared mapping of memory category names to directory names.\n * Single source of truth — import from here instead of copy-pasting.\n */\n\nimport path from \"node:path\";\n\nexport const CATEGORY_DIR_MAP: Record<string, string> = {\n correction: \"corrections\",\n question: \"questions\",\n preference: \"preferences\",\n decision: \"decisions\",\n moment: \"moments\",\n commitment: \"commitments\",\n principle: \"principles\",\n rule: \"rules\",\n skill: \"skills\",\n relationship: \"relationships\",\n procedure: \"procedures\",\n reasoning_trace: \"reasoning-traces\",\n};\n\n/** All directory names derived from CATEGORY_DIR_MAP, plus \"facts\" (the default). */\nexport const ALL_CATEGORY_DIRS: string[] = [\n \"facts\",\n ...Object.values(CATEGORY_DIR_MAP),\n];\n\n/** All category keys (singular form) — used when iterating categories and calling getCategoryDir. */\nexport const ALL_CATEGORY_KEYS: string[] = [\n \"fact\",\n ...Object.keys(CATEGORY_DIR_MAP),\n];\n\n/**\n * Resolve a category name to its directory path under memoryDir.\n * Falls back to `facts/` for unknown categories.\n */\nexport function getCategoryDir(memoryDir: string, category: string): string {\n const dir = CATEGORY_DIR_MAP[category];\n return dir ? path.join(memoryDir, dir) : path.join(memoryDir, \"facts\");\n}\n","/**\n * @remnic/core — Onboarding\n *\n * Detects project language, shape, and documentation to produce\n * an onboarding plan for memory ingestion.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface OnboardOptions {\n /** Directory to scan (defaults to cwd) */\n directory: string;\n /** Max depth to walk (default: 6) */\n maxDepth?: number;\n /** Directories to skip */\n excludeDirs?: string[];\n}\n\nexport interface LanguageInfo {\n /** Language name (e.g. \"TypeScript\", \"Python\") */\n language: string;\n /** Confidence in detection (0-1) */\n confidence: number;\n /** Evidence (e.g. [\"package.json\", \"tsconfig.json\", \"*.ts files\"]) */\n evidence: string[];\n}\n\nexport interface DocFile {\n /** Absolute path */\n path: string;\n /** Relative path from project root */\n relativePath: string;\n /** Estimated type */\n kind: \"readme\" | \"changelog\" | \"contributing\" | \"license\" | \"config\" | \"docs\" | \"other\";\n /** File size in bytes */\n size: number;\n}\n\nexport type ProjectShape = \"app\" | \"library\" | \"monorepo\" | \"workspace\" | \"script\" | \"unknown\";\n\nexport interface OnboardResult {\n /** Project root */\n directory: string;\n /** Detected languages (sorted by confidence) */\n languages: LanguageInfo[];\n /** Detected project shape */\n shape: ProjectShape;\n /** Shape evidence */\n shapeEvidence: string[];\n /** Discovered documentation files */\n docs: DocFile[];\n /** Total files scanned */\n totalFiles: number;\n /** Duration in ms */\n durationMs: number;\n /** Suggested ingestion plan */\n plan: IngestionPlan;\n}\n\nexport interface IngestionPlan {\n /** Priority files to ingest first */\n priorityFiles: DocFile[];\n /** Estimated total files to ingest */\n estimatedFiles: number;\n /** Recommended categories */\n categories: string[];\n /** Suggested memory namespace */\n suggestedNamespace: string;\n}\n\n// ── Language detection rules ─────────────────────────────────────────────────\n\ninterface LanguageRule {\n language: string;\n extensions: string[];\n manifests: string[];\n configFiles: string[];\n}\n\nconst LANGUAGE_RULES: LanguageRule[] = [\n {\n language: \"TypeScript\",\n extensions: [\".ts\", \".tsx\"],\n manifests: [\"package.json\"],\n configFiles: [\"tsconfig.json\", \"tsup.config.ts\"],\n },\n {\n language: \"JavaScript\",\n extensions: [\".js\", \".jsx\", \".mjs\", \".cjs\"],\n manifests: [\"package.json\"],\n configFiles: [\".eslintrc\", \".prettierrc\"],\n },\n {\n language: \"Python\",\n extensions: [\".py\", \".pyi\"],\n manifests: [\"pyproject.toml\", \"setup.py\", \"setup.cfg\", \"requirements.txt\"],\n configFiles: [\"mypy.ini\", \".flake8\", \"tox.ini\"],\n },\n {\n language: \"Go\",\n extensions: [\".go\"],\n manifests: [\"go.mod\", \"go.sum\"],\n configFiles: [],\n },\n {\n language: \"Rust\",\n extensions: [\".rs\"],\n manifests: [\"Cargo.toml\"],\n configFiles: [],\n },\n {\n language: \"Ruby\",\n extensions: [\".rb\"],\n manifests: [\"Gemfile\", \"*.gemspec\"],\n configFiles: [\".rubocop.yml\"],\n },\n {\n language: \"PHP\",\n extensions: [\".php\"],\n manifests: [\"composer.json\"],\n configFiles: [\"phpcs.xml\"],\n },\n {\n language: \"Java\",\n extensions: [\".java\", \".kt\"],\n manifests: [\"pom.xml\", \"build.gradle\", \"build.gradle.kts\"],\n configFiles: [],\n },\n {\n language: \"Swift\",\n extensions: [\".swift\"],\n manifests: [\"Package.swift\", \"Podfile\"],\n configFiles: [],\n },\n {\n language: \"C#\",\n extensions: [\".cs\"],\n manifests: [\"*.csproj\", \"*.sln\"],\n configFiles: [],\n },\n {\n language: \"Shell\",\n extensions: [\".sh\", \".bash\", \".zsh\"],\n manifests: [],\n configFiles: [],\n },\n {\n language: \"Dart\",\n extensions: [\".dart\"],\n manifests: [\"pubspec.yaml\"],\n configFiles: [],\n },\n {\n language: \"Elixir\",\n extensions: [\".ex\", \".exs\"],\n manifests: [\"mix.exs\"],\n configFiles: [],\n },\n];\n\nconst DEFAULT_EXCLUDE = new Set([\n \"node_modules\",\n \".git\",\n \"vendor\",\n \"__pycache__\",\n \".venv\",\n \"venv\",\n \"dist\",\n \"build\",\n \".next\",\n \".nuxt\",\n \"target\",\n \"coverage\",\n \".engram\",\n]);\n\n// ── Main function ────────────────────────────────────────────────────────────\n\nexport function onboard(options: OnboardOptions): OnboardResult {\n const startTime = Date.now();\n const {\n directory,\n maxDepth = 6,\n excludeDirs = [],\n } = options;\n\n const exclude = new Set([...DEFAULT_EXCLUDE, ...excludeDirs]);\n\n // Collect all files\n const files = walkDir(directory, exclude, maxDepth);\n\n // Detect languages\n const languages = detectLanguages(files, directory);\n\n // Detect shape\n const { shape, evidence: shapeEvidence } = detectShape(files, directory);\n\n // Discover docs\n const docs = discoverDocs(files, directory);\n\n // Build plan\n const plan = buildPlan(languages, shape, docs, directory);\n\n return {\n directory,\n languages,\n shape,\n shapeEvidence,\n docs,\n totalFiles: files.length,\n durationMs: Date.now() - startTime,\n plan,\n };\n}\n\n// ── Walk directory ───────────────────────────────────────────────────────────\n\nfunction walkDir(\n root: string,\n exclude: Set<string>,\n maxDepth: number,\n): string[] {\n const results: string[] = [];\n\n function walk(dir: string, depth: number): void {\n if (depth > maxDepth) return;\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (exclude.has(entry.name)) continue;\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath, depth + 1);\n } else if (entry.isFile()) {\n results.push(fullPath);\n }\n }\n }\n\n walk(root, 0);\n return results;\n}\n\n// ── Language detection ───────────────────────────────────────────────────────\n\nfunction detectLanguages(files: string[], root: string): LanguageInfo[] {\n const results: LanguageInfo[] = [];\n\n // Count extensions\n const extCounts = new Map<string, number>();\n for (const f of files) {\n const ext = path.extname(f).toLowerCase();\n if (ext) extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);\n }\n\n // Check manifests at root level\n const rootFiles = new Set(\n files\n .filter((f) => path.dirname(f) === root)\n .map((f) => path.basename(f)),\n );\n\n for (const rule of LANGUAGE_RULES) {\n const evidence: string[] = [];\n let score = 0;\n\n // Extension matches\n let extMatch = 0;\n for (const ext of rule.extensions) {\n const count = extCounts.get(ext) ?? 0;\n if (count > 0) {\n extMatch += count;\n evidence.push(`${ext} files (${count})`);\n }\n }\n score += Math.min(extMatch * 0.05, 0.5);\n\n // Manifest matches\n for (const manifest of rule.manifests) {\n if (manifest.includes(\"*\")) {\n // Glob pattern — e.g. \"*.gemspec\" matches files ending with \".gemspec\"\n const suffix = manifest.replaceAll(/\\*/g, \"\");\n if ([...rootFiles].some((f) => f.endsWith(suffix))) {\n score += 0.2;\n evidence.push(manifest);\n }\n } else if (rootFiles.has(manifest)) {\n score += 0.2;\n evidence.push(manifest);\n }\n }\n\n // Config matches\n for (const cfg of rule.configFiles) {\n if (rootFiles.has(cfg)) {\n score += 0.1;\n evidence.push(cfg);\n }\n }\n\n if (score > 0) {\n results.push({\n language: rule.language,\n confidence: Math.min(score, 1),\n evidence,\n });\n }\n }\n\n return results.sort((a, b) => b.confidence - a.confidence);\n}\n\n// ── Shape detection ──────────────────────────────────────────────────────────\n\nfunction detectShape(\n files: string[],\n root: string,\n): { shape: ProjectShape; evidence: string[] } {\n const rootFiles = new Set(\n files\n .filter((f) => path.dirname(f) === root)\n .map((f) => path.basename(f)),\n );\n\n const rootDirs = new Set<string>();\n try {\n for (const entry of fs.readdirSync(root, { withFileTypes: true })) {\n if (entry.isDirectory()) rootDirs.add(entry.name);\n }\n } catch {\n // ignore\n }\n\n const evidence: string[] = [];\n\n // Monorepo: workspace packages/ or libs/ dirs + root package.json with workspaces\n if (rootFiles.has(\"package.json\")) {\n const pkg = readJsonSafe(path.join(root, \"package.json\"));\n if (pkg?.workspaces) {\n evidence.push(\"package.json has workspaces\");\n return { shape: \"monorepo\", evidence };\n }\n }\n\n if (rootDirs.has(\"packages\") || rootDirs.has(\"libs\")) {\n evidence.push(\"has packages/ or libs/ directory\");\n return { shape: \"monorepo\", evidence };\n }\n\n // Workspace: pnpm-workspace.yaml, Cargo workspace, go.work\n if (rootFiles.has(\"pnpm-workspace.yaml\") || rootFiles.has(\"go.work\")) {\n evidence.push(\"workspace manifest found\");\n return { shape: \"workspace\", evidence };\n }\n\n const cargoToml = readTomlWorkspace(path.join(root, \"Cargo.toml\"));\n if (cargoToml) {\n evidence.push(\"Cargo.toml has workspace\");\n return { shape: \"workspace\", evidence };\n }\n\n // Library: has lib/ or src/index.*, exports in package.json\n if (rootFiles.has(\"package.json\")) {\n const pkg = readJsonSafe(path.join(root, \"package.json\"));\n if (pkg?.exports || pkg?.main) {\n // If it also has \"bin\", it's more of an app\n if (pkg?.bin) {\n evidence.push(\"package.json has bin\");\n return { shape: \"app\", evidence };\n }\n evidence.push(\"package.json has exports/main\");\n return { shape: \"library\", evidence };\n }\n }\n\n // Check for app indicators\n if (\n rootFiles.has(\"Dockerfile\") ||\n rootFiles.has(\"docker-compose.yml\") ||\n rootFiles.has(\"docker-compose.yaml\") ||\n rootDirs.has(\"app\") ||\n rootDirs.has(\"src\") && rootDirs.has(\"public\")\n ) {\n evidence.push(\"app-like structure detected\");\n return { shape: \"app\", evidence };\n }\n\n // Script: few files, no package manifest\n if (files.length <= 5 && !rootFiles.has(\"package.json\") && !rootFiles.has(\"pyproject.toml\")) {\n evidence.push(\"few files, no manifest\");\n return { shape: \"script\", evidence };\n }\n\n return { shape: \"unknown\", evidence: [\"no strong shape signal\"] };\n}\n\n// ── Doc discovery ────────────────────────────────────────────────────────────\n\nfunction discoverDocs(files: string[], root: string): DocFile[] {\n const docs: DocFile[] = [];\n const docPatterns: Array<{ pattern: RegExp; kind: DocFile[\"kind\"] }> = [\n { pattern: /^readme(\\.\\w+)?$/i, kind: \"readme\" },\n { pattern: /^changelog(\\.\\w+)?$/i, kind: \"changelog\" },\n { pattern: /^changes(\\.\\w+)?$/i, kind: \"changelog\" },\n { pattern: /^contributing(\\.\\w+)?$/i, kind: \"contributing\" },\n { pattern: /^code[_-]of[_-]conduct(\\.\\w+)?$/i, kind: \"contributing\" },\n { pattern: /^license(\\.\\w+)?$/i, kind: \"license\" },\n { pattern: /^copying(\\.\\w+)?$/i, kind: \"license\" },\n { pattern: /^\\.env\\.example$/i, kind: \"config\" },\n { pattern: /^\\.editorconfig$/i, kind: \"config\" },\n ];\n\n for (const filePath of files) {\n const basename = path.basename(filePath).toLowerCase();\n const relPath = path.relative(root, filePath);\n let kind: DocFile[\"kind\"] | undefined;\n\n // Check against patterns\n for (const { pattern, kind: k } of docPatterns) {\n if (pattern.test(basename)) {\n kind = k;\n break;\n }\n }\n\n // Check docs/ directories\n if (!kind && isUnderDocsDir(relPath)) {\n kind = \"docs\";\n }\n\n // Check markdown files at root or in docs/\n if (!kind && (basename.endsWith(\".md\") || basename.endsWith(\".mdx\"))) {\n if (path.dirname(relPath) === \".\" || isUnderDocsDir(relPath)) {\n kind = \"docs\";\n }\n }\n\n if (kind) {\n let size = 0;\n try {\n size = fs.statSync(filePath).size;\n } catch {\n // ignore\n }\n docs.push({\n path: filePath,\n relativePath: relPath,\n kind,\n size,\n });\n }\n }\n\n return docs;\n}\n\nfunction isUnderDocsDir(relPath: string): boolean {\n const parts = relPath.split(path.sep);\n return parts[0] === \"docs\" || parts[0] === \"doc\" || parts[0] === \"documentation\";\n}\n\n// ── Plan generation ──────────────────────────────────────────────────────────\n\nfunction buildPlan(\n languages: LanguageInfo[],\n shape: ProjectShape,\n docs: DocFile[],\n _root: string,\n): IngestionPlan {\n // Priority: README, CONTRIBUTING, then other docs\n const priorityOrder: Record<DocFile[\"kind\"], number> = {\n readme: 0,\n contributing: 1,\n changelog: 2,\n license: 3,\n docs: 4,\n config: 5,\n other: 6,\n };\n\n const priorityFiles = [...docs]\n .filter((d) => d.size > 0)\n .sort((a, b) => priorityOrder[a.kind] - priorityOrder[b.kind]);\n\n // Recommended categories based on project shape\n const categories: string[] = [\"fact\", \"preference\", \"decision\", \"principle\"];\n if (shape === \"monorepo\" || shape === \"workspace\") {\n categories.push(\"entity\");\n }\n\n // Namespace suggestion from primary language\n const suggestedNamespace = languages.length > 0\n ? languages[0].language.toLowerCase()\n : \"project\";\n\n return {\n priorityFiles,\n estimatedFiles: docs.filter((d) => d.size > 0).length,\n categories,\n suggestedNamespace,\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction readJsonSafe(filePath: string): Record<string, unknown> | null {\n try {\n return JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nfunction readTomlWorkspace(filePath: string): boolean {\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n return content.includes(\"[workspace]\");\n } catch {\n return false;\n }\n}\n","/**\n * @remnic/core — Curation\n *\n * Deliberate ingestion of files into memory with provenance tracking.\n * Supports statement-level extraction, dedup, and contradiction checks.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { getCategoryDir, ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface CurateOptions {\n /** File or directory path to curate */\n targetPath: string;\n /** Memory root directory for writing */\n memoryDir: string;\n /** Source label (e.g. \"manual\", \"docs\", \"onboarding\") */\n source?: string;\n /** Category override (default: auto-detect) */\n category?: string;\n /** Confidence to assign (default: 0.9 for curated items) */\n confidence?: number;\n /** Entity reference to attach */\n entityRef?: string;\n /** Tags to add */\n tags?: string[];\n /** Whether to perform dedup check against existing memories */\n checkDuplicates?: boolean;\n /** Whether to detect contradictions */\n checkContradictions?: boolean;\n /** Whether to write files (default: true). False = dry run */\n write?: boolean;\n}\n\nexport interface CuratedStatement {\n /** Unique ID for this statement */\n id: string;\n /** The extracted statement text */\n content: string;\n /** Category */\n category: string;\n /** Confidence */\n confidence: number;\n /** Provenance info */\n provenance: StatementProvenance;\n /** Hash of content for dedup */\n contentHash: string;\n /** Tags */\n tags: string[];\n /** Entity reference */\n entityRef?: string;\n}\n\nexport interface StatementProvenance {\n /** Source file path */\n sourcePath: string;\n /** Relative path from project root */\n relativePath: string;\n /** Source label */\n source: string;\n /** Line number if extractable (0 = unknown) */\n lineNumber: number;\n /** Timestamp of ingestion */\n ingestedAt: string;\n /** Hash of the source file for diff tracking */\n sourceFileHash: string;\n}\n\nexport interface CurateResult {\n /** Statements extracted */\n statements: CuratedStatement[];\n /** Files processed */\n filesProcessed: number;\n /** Files skipped (empty, binary, etc.) */\n filesSkipped: number;\n /** Duplicate statements found (if checkDuplicates) */\n duplicates: DuplicateResult[];\n /** Contradictions found (if checkContradictions) */\n contradictions: ContradictionResult[];\n /** Memory files written */\n written: string[];\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface DuplicateResult {\n /** New statement */\n newStatement: CuratedStatement;\n /** Existing memory ID that matches */\n existingId: string;\n /** Similarity score (0-1) */\n similarity: number;\n /** Recommended action */\n action: \"skip\" | \"merge\" | \"keep\";\n}\n\nexport interface ContradictionResult {\n /** New statement */\n newStatement: CuratedStatement;\n /** Conflicting memory ID */\n conflictingId: string;\n /** The conflicting content */\n conflictingContent: string;\n /** Severity */\n severity: \"high\" | \"medium\" | \"low\";\n}\n\n// ── Main function ────────────────────────────────────────────────────────────\n\nexport async function curate(options: CurateOptions): Promise<CurateResult> {\n const startTime = Date.now();\n const {\n targetPath,\n memoryDir,\n source = \"curation\",\n category: categoryOverride,\n confidence = 0.9,\n entityRef,\n tags = [],\n checkDuplicates = true,\n checkContradictions = false,\n write = true,\n } = options;\n\n const statements: CuratedStatement[] = [];\n const written: string[] = [];\n const duplicates: DuplicateResult[] = [];\n const contradictions: ContradictionResult[] = [];\n let filesProcessed = 0;\n let filesSkipped = 0;\n\n // Determine targets\n const targets = resolveTargets(targetPath);\n\n // Load existing memories for dedup/contradiction checks\n const existingMemories = checkDuplicates || checkContradictions\n ? loadExistingMemories(memoryDir)\n : new Map();\n\n // Process each file\n for (const filePath of targets) {\n const content = readFileSafe(filePath);\n if (!content) {\n filesSkipped++;\n continue;\n }\n\n if (isBinary(content)) {\n filesSkipped++;\n continue;\n }\n\n filesProcessed++;\n\n const sourceFileHash = hashContent(content);\n const fileStatements = extractStatements(\n content,\n filePath,\n targetPath,\n source,\n sourceFileHash,\n categoryOverride,\n confidence,\n entityRef,\n tags,\n );\n\n for (const stmt of fileStatements) {\n // Dedup check\n if (checkDuplicates) {\n const dup = findDuplicate(stmt, existingMemories);\n if (dup) {\n duplicates.push(dup);\n if (dup.action === \"skip\") continue;\n }\n }\n\n // Contradiction check\n if (checkContradictions) {\n const contra = findContradiction(stmt, existingMemories);\n if (contra) {\n contradictions.push(contra);\n }\n }\n\n statements.push(stmt);\n\n // Write to memory\n if (write) {\n const writtenPath = writeStatement(stmt, memoryDir);\n if (writtenPath) {\n written.push(writtenPath);\n existingMemories.set(stmt.contentHash, {\n id: stmt.id,\n content: stmt.content,\n category: stmt.category,\n });\n }\n }\n }\n }\n\n return {\n statements,\n filesProcessed,\n filesSkipped,\n duplicates,\n contradictions,\n written,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Target resolution ────────────────────────────────────────────────────────\n\nfunction resolveTargets(targetPath: string): string[] {\n const stat = fs.statSync(targetPath);\n if (stat.isFile()) return [targetPath];\n\n // Directory — walk for .md, .txt, .mdx\n const results: string[] = [];\n const extensions = new Set([\".md\", \".txt\", \".mdx\", \".rst\"]);\n\n function walk(dir: string): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== \"node_modules\" && entry.name !== \".git\") {\n walk(fullPath);\n }\n } else if (extensions.has(path.extname(entry.name).toLowerCase())) {\n results.push(fullPath);\n }\n }\n }\n\n walk(targetPath);\n return results;\n}\n\n// ── Statement extraction ─────────────────────────────────────────────────────\n\nfunction extractStatements(\n content: string,\n filePath: string,\n projectRoot: string,\n source: string,\n sourceFileHash: string,\n categoryOverride: string | undefined,\n confidence: number,\n entityRef: string | undefined,\n tags: string[],\n): CuratedStatement[] {\n const relativePath = path.relative(projectRoot, filePath);\n const statements: CuratedStatement[] = [];\n const now = new Date().toISOString();\n\n // Split content into paragraphs/lines and extract meaningful statements\n const paragraphs = content\n .split(/\\n{2,}/)\n .map((p) => p.trim())\n .filter((p) => p.length > 20 && p.length < 2000);\n\n // Also extract list items\n const listItems = content\n .split(\"\\n\")\n .filter((l) => /^\\s*[-*]\\s+/.test(l))\n .map((l) => l.replace(/^\\s*[-*]\\s+/, \"\").trim())\n .filter((l) => l.length > 10 && l.length < 500);\n\n const allItems = [...paragraphs, ...listItems];\n\n // Deduplicate within file\n const seen = new Set<string>();\n for (const item of allItems) {\n const hash = hashContent(item.toLowerCase());\n if (seen.has(hash)) continue;\n seen.add(hash);\n\n const id = generateId();\n const category = categoryOverride ?? detectCategory(item);\n\n statements.push({\n id,\n content: item,\n category,\n confidence,\n provenance: {\n sourcePath: filePath,\n relativePath,\n source,\n lineNumber: 0,\n ingestedAt: now,\n sourceFileHash,\n },\n contentHash: hash,\n tags: [...tags],\n entityRef,\n });\n }\n\n return statements;\n}\n\n// ── Category detection ───────────────────────────────────────────────────────\n\nfunction detectCategory(text: string): string {\n const lower = text.toLowerCase();\n\n if (/^(always|never|must|should|don't|avoid|ensure)/.test(lower)) return \"principle\";\n if (/^(we|team|project)\\s+(decided|chose|will|use)/.test(lower)) return \"decision\";\n if (/^(i|we)\\s+(prefer|like|want|hate|dislike)/.test(lower)) return \"preference\";\n if (/fix|bug|issue|broken|error/i.test(lower)) return \"correction\";\n if (/\\?.+$/.test(lower.trim())) return \"question\";\n\n return \"fact\";\n}\n\n// ── Dedup ────────────────────────────────────────────────────────────────────\n\ninterface ExistingMemory {\n id: string;\n content: string;\n category: string;\n}\n\nfunction findDuplicate(\n stmt: CuratedStatement,\n existing: Map<string, ExistingMemory>,\n): DuplicateResult | null {\n const stmtLower = stmt.content.toLowerCase();\n\n // Exact hash match\n const exactMatch = existing.get(stmt.contentHash);\n if (exactMatch) {\n return {\n newStatement: stmt,\n existingId: exactMatch.id,\n similarity: 1,\n action: \"skip\",\n };\n }\n\n // Fuzzy match — check substring containment\n for (const [_, mem] of existing) {\n const memLower = mem.content.toLowerCase();\n if (memLower.length > 50 && stmtLower.length > 50) {\n // Check if one contains the other\n if (memLower.includes(stmtLower.slice(0, 40)) || stmtLower.includes(memLower.slice(0, 40))) {\n return {\n newStatement: stmt,\n existingId: mem.id,\n similarity: 0.85,\n action: \"skip\",\n };\n }\n }\n }\n\n return null;\n}\n\n// ── Contradiction detection ──────────────────────────────────────────────────\n\nfunction findContradiction(\n stmt: CuratedStatement,\n existing: Map<string, ExistingMemory>,\n): ContradictionResult | null {\n const negationPatterns = [\n /\\b(not|don't|doesn't|isn't|aren't|won't|can't|never|no)\\b/i,\n ];\n\n const hasNegation = negationPatterns.some((p) => p.test(stmt.content));\n if (!hasNegation) return null;\n\n // Strip negation and look for positive version\n const stripped = stmt.content\n .toLowerCase()\n .replace(/\\b(not|don't|doesn't|isn't|aren't|won't|can't|never|no)\\b/gi, \"\")\n .trim();\n\n if (stripped.length < 20) return null;\n\n for (const [_, mem] of existing) {\n const memLower = mem.content.toLowerCase();\n // If an existing memory affirms what this statement negates\n if (memLower.includes(stripped.slice(0, Math.min(30, stripped.length)))) {\n return {\n newStatement: stmt,\n conflictingId: mem.id,\n conflictingContent: mem.content,\n severity: \"high\",\n };\n }\n }\n\n return null;\n}\n\n// ── Memory loading ───────────────────────────────────────────────────────────\n\nfunction loadExistingMemories(memoryDir: string): Map<string, ExistingMemory> {\n const result = new Map<string, ExistingMemory>();\n if (!fs.existsSync(memoryDir)) return result;\n\n // Walk all known category dirs for existing memories\n const dirs = ALL_CATEGORY_DIRS;\n for (const dir of dirs) {\n const fullDir = path.join(memoryDir, dir);\n if (!fs.existsSync(fullDir)) continue;\n\n walkFiles(fullDir, (filePath) => {\n const content = readFileSafe(filePath);\n if (!content) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id || !body) return;\n\n const hash = hashContent(body.toLowerCase());\n result.set(hash, {\n id: fm.id,\n content: body,\n category: fm.category ?? dir.slice(0, -1),\n });\n });\n }\n\n return result;\n}\n\n// ── Writing ──────────────────────────────────────────────────────────────────\n\nfunction writeStatement(stmt: CuratedStatement, memoryDir: string): string | null {\n const now = new Date();\n const dateDir = now.toISOString().split(\"T\")[0];\n const categoryDir = getCategoryDir(memoryDir, stmt.category);\n\n const dir = path.join(categoryDir, dateDir);\n fs.mkdirSync(dir, { recursive: true });\n\n const fileName = `${stmt.category}-${Date.now()}-${stmt.id.slice(0, 8)}.md`;\n const filePath = path.join(dir, fileName);\n\n const frontmatter = [\n \"---\",\n `id: ${stmt.id}`,\n `category: ${stmt.category}`,\n `created: ${stmt.provenance.ingestedAt}`,\n `updated: ${stmt.provenance.ingestedAt}`,\n `confidence: ${stmt.confidence}`,\n `confidenceTier: ${tierFromConfidence(stmt.confidence)}`,\n `source: ${stmt.provenance.source}`,\n `tags: ${JSON.stringify(stmt.tags)}`,\n stmt.entityRef ? `entityRef: ${stmt.entityRef}` : null,\n `provenanceFile: ${stmt.provenance.relativePath}`,\n `provenanceHash: ${stmt.provenance.sourceFileHash}`,\n \"---\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n\n const body = `${frontmatter}\\n\\n${stmt.content}\\n`;\n\n try {\n fs.writeFileSync(filePath, body);\n return filePath;\n } catch {\n return null;\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction generateId(): string {\n return crypto.randomUUID();\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction tierFromConfidence(confidence: number): string {\n if (confidence >= 0.95) return \"explicit\";\n if (confidence >= 0.8) return \"high\";\n if (confidence >= 0.5) return \"medium\";\n return \"low\";\n}\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction isBinary(content: string): boolean {\n // Simple heuristic: if content has null bytes, it's binary\n for (let i = 0; i < Math.min(content.length, 8000); i++) {\n if (content.charCodeAt(i) === 0) return true;\n }\n return false;\n}\n\ninterface SimpleFrontmatter {\n id?: string;\n category?: string;\n [key: string]: unknown;\n}\n\nfunction parseFrontmatter(content: string): SimpleFrontmatter | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: SimpleFrontmatter = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n (fm as Record<string, unknown>)[key] = value;\n }\n return fm;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction walkFiles(dir: string, callback: (filePath: string) => void): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walkFiles(fullPath, callback);\n } else if (entry.name.endsWith(\".md\")) {\n callback(fullPath);\n }\n }\n}\n","/**\n * @remnic/core — Dedup & Contradiction Detection\n *\n * Statement-level deduplication and contradiction detection\n * against existing memories. Can be used standalone or via curation.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface MemoryEntry {\n /** Memory ID */\n id: string;\n /** Content text */\n content: string;\n /** Category */\n category: string;\n /** File path (if known) */\n filePath?: string;\n}\n\nexport interface DedupOptions {\n /** Memory root directory */\n memoryDir: string;\n /** Categories to scan (default: all) */\n categories?: string[];\n /** Similarity threshold for fuzzy matching (0-1, default: 0.85) */\n threshold?: number;\n /** Max memories to load (default: 10000) */\n maxLoad?: number;\n}\n\nexport interface DedupResult {\n /** Total memories scanned */\n scanned: number;\n /** Duplicate pairs found */\n duplicates: DuplicatePair[];\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface DuplicatePair {\n /** First memory */\n left: MemoryEntry;\n /** Second memory */\n right: MemoryEntry;\n /** Similarity score */\n similarity: number;\n /** Recommended action */\n action: \"merge\" | \"keep_left\" | \"keep_right\";\n}\n\nexport interface ContradictionOptions {\n /** Memory root directory */\n memoryDir: string;\n /** Categories to scan (default: all) */\n categories?: string[];\n /** Max memories to load (default: 10000) */\n maxLoad?: number;\n}\n\nexport interface ContradictionResult {\n /** Total memories scanned */\n scanned: number;\n /** Contradictions found */\n contradictions: ContradictionPair[];\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface ContradictionPair {\n /** First statement */\n left: MemoryEntry;\n /** Contradicting statement */\n right: MemoryEntry;\n /** Severity */\n severity: \"high\" | \"medium\" | \"low\";\n /** Reason */\n reason: string;\n}\n\n// ── Main functions ───────────────────────────────────────────────────────────\n\nexport function findDuplicates(options: DedupOptions): DedupResult {\n const startTime = Date.now();\n const { memoryDir, threshold = 0.85, maxLoad = 10000 } = options;\n\n const memories = loadMemories(memoryDir, options.categories, maxLoad);\n const duplicates: DuplicatePair[] = [];\n\n // Compare all pairs (O(n^2) but bounded by maxLoad)\n for (let i = 0; i < memories.length; i++) {\n for (let j = i + 1; j < memories.length; j++) {\n const sim = computeSimilarity(memories[i].content, memories[j].content);\n if (sim >= threshold) {\n duplicates.push({\n left: memories[i],\n right: memories[j],\n similarity: sim,\n action: sim >= 0.98 ? \"merge\" : sim >= 0.9 ? \"keep_right\" : \"keep_left\",\n });\n }\n }\n }\n\n return {\n scanned: memories.length,\n duplicates,\n durationMs: Date.now() - startTime,\n };\n}\n\nexport function findContradictions(options: ContradictionOptions): ContradictionResult {\n const startTime = Date.now();\n const { memoryDir, maxLoad = 10000 } = options;\n\n const memories = loadMemories(memoryDir, options.categories, maxLoad);\n const contradictions: ContradictionPair[] = [];\n\n for (let i = 0; i < memories.length; i++) {\n for (let j = i + 1; j < memories.length; j++) {\n const contra = detectContradiction(memories[i], memories[j]);\n if (contra) {\n contradictions.push(contra);\n }\n }\n }\n\n return {\n scanned: memories.length,\n contradictions,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Similarity computation ───────────────────────────────────────────────────\n\nfunction computeSimilarity(a: string, b: string): number {\n // Normalize\n const normA = normalize(a);\n const normB = normalize(b);\n\n // Exact match\n if (normA === normB) return 1;\n\n // Hash-based exact match\n if (hashContent(normA) === hashContent(normB)) return 0.99;\n\n // Substring containment\n if (normA.length > 50 && normB.length > 50) {\n if (normA.includes(normB.slice(0, 40)) || normB.includes(normA.slice(0, 40))) {\n return 0.9;\n }\n }\n\n // Word overlap (Jaccard)\n const wordsA = new Set(normA.split(/\\s+/));\n const wordsB = new Set(normB.split(/\\s+/));\n const intersection = new Set([...wordsA].filter((w) => wordsB.has(w)));\n const union = new Set([...wordsA, ...wordsB]);\n\n if (union.size === 0) return 0;\n return intersection.size / union.size;\n}\n\nfunction normalize(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^\\w\\s']/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n// ── Contradiction detection ──────────────────────────────────────────────────\n\nconst NEGATION_WORDS = new Set([\n \"not\", \"don't\", \"doesn't\", \"isn't\", \"aren't\", \"won't\", \"can't\",\n \"never\", \"no\", \"none\", \"neither\", \"nor\", \"nothing\", \"nowhere\",\n]);\n\nfunction detectContradiction(\n a: MemoryEntry,\n b: MemoryEntry,\n): ContradictionPair | null {\n const normA = normalize(a.content);\n const normB = normalize(b.content);\n\n // Check if one has negation and the other doesn't\n const aHasNegation = containsNegation(normA);\n const bHasNegation = containsNegation(normB);\n\n if (aHasNegation === bHasNegation) return null;\n\n // Strip negation and compare core content\n const strippedA = stripNegation(normA);\n const strippedB = stripNegation(normB);\n\n const sim = computeSimilarity(strippedA, strippedB);\n if (sim < 0.7) return null;\n\n // Check for opposite quantifiers\n const oppQuantifiers = [\n [\"always\", \"never\"],\n [\"all\", \"none\"],\n [\"every\", \"no\"],\n [\"must\", \"must not\"],\n [\"should\", \"should not\"],\n [\"can\", \"cannot\"],\n ];\n\n for (const [pos, neg] of oppQuantifiers) {\n if (\n (a.content.toLowerCase().includes(pos) && b.content.toLowerCase().includes(neg)) ||\n (a.content.toLowerCase().includes(neg) && b.content.toLowerCase().includes(pos))\n ) {\n return {\n left: a,\n right: b,\n severity: \"high\",\n reason: `Opposite quantifiers: \"${pos}\" vs \"${neg}\"`,\n };\n }\n }\n\n return {\n left: a,\n right: b,\n severity: sim >= 0.85 ? \"high\" : \"medium\",\n reason: \"Negated version of similar content\",\n };\n}\n\nfunction containsNegation(text: string): boolean {\n const words = text.split(/\\s+/);\n return words.some((w) => NEGATION_WORDS.has(w));\n}\n\nfunction stripNegation(text: string): string {\n return text\n .replace(/\\b(not|don't|doesn't|isn't|aren't|won't|can't|never|no|none|neither|nor|nothing|nowhere)\\b/gi, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n// ── Memory loading ───────────────────────────────────────────────────────────\n\nfunction loadMemories(\n memoryDir: string,\n categories?: string[],\n maxLoad = 10000,\n): MemoryEntry[] {\n const result: MemoryEntry[] = [];\n const allCategories = categories ?? ALL_CATEGORY_DIRS;\n\n for (const category of allCategories) {\n if (result.length >= maxLoad) break;\n\n const dir = path.join(memoryDir, category);\n if (!fs.existsSync(dir)) continue;\n\n walkMdFiles(dir, (filePath) => {\n if (result.length >= maxLoad) return;\n\n const content = readFileSafe(filePath);\n if (!content) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id || !body) return;\n\n result.push({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? category.slice(0, -1),\n filePath,\n });\n });\n }\n\n return result;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction parseFrontmatter(content: string): Record<string, unknown> | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: Record<string, unknown> = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n fm[key] = value;\n }\n return fm;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction walkMdFiles(dir: string, callback: (filePath: string) => void): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walkMdFiles(fullPath, callback);\n } else if (entry.name.endsWith(\".md\")) {\n callback(fullPath);\n }\n }\n}\n","/**\n * @remnic/core — Review Inbox\n *\n * Manages low-confidence memories and suggestions pending review.\n * Integrates with the existing review-queue system.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getCategoryDir, ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface ReviewItem {\n /** Memory ID */\n id: string;\n /** Content text */\n content: string;\n /** Category */\n category: string;\n /** Confidence score (0-1) */\n confidence: number;\n /** Confidence tier */\n confidenceTier: string;\n /** Source */\n source: string;\n /** File path if available */\n filePath?: string;\n /** Created date */\n created: string;\n /** Reason it's in review */\n reviewReason: \"low_confidence\" | \"suggestion\" | \"contradiction\" | \"duplicate\";\n /** Additional context */\n context?: string;\n}\n\nexport type ReviewAction = \"approve\" | \"dismiss\" | \"flag\";\n\nexport interface ReviewResult {\n /** Item acted upon */\n itemId: string;\n /** Action taken */\n action: ReviewAction;\n /** Updated file path (if modified) */\n updatedPath?: string;\n /** Status message */\n message: string;\n}\n\nexport interface ReviewListResult {\n /** Items pending review */\n items: ReviewItem[];\n /** Total count */\n total: number;\n /** Duration in ms */\n durationMs: number;\n}\n\nexport interface ReviewOptions {\n /** Memory root directory */\n memoryDir: string;\n /** Filter by reason */\n reason?: ReviewItem[\"reviewReason\"];\n /** Max items to return (default: 50) */\n limit?: number;\n /** Include items with confidence below this threshold (default: 0.7) */\n confidenceThreshold?: number;\n}\n\nexport interface ReviewActionOptions {\n /** Match the threshold used when listing review items (default: 0.7) */\n confidenceThreshold?: number;\n}\n\ninterface ReviewFileMatch {\n filePath: string;\n location: \"queue\" | \"category\";\n}\n\nconst DEFAULT_CONFIDENCE_THRESHOLD = 0.7;\n\n// ── Main functions ───────────────────────────────────────────────────────────\n\n/**\n * List items pending review.\n */\nexport function listReviewItems(options: ReviewOptions): ReviewListResult {\n const startTime = Date.now();\n const {\n memoryDir,\n reason: filterReason,\n limit = 50,\n confidenceThreshold = DEFAULT_CONFIDENCE_THRESHOLD,\n } = options;\n\n const items: ReviewItem[] = [];\n const addItem = (item: ReviewItem): void => {\n if (items.length >= limit) return;\n if (filterReason && item.reviewReason !== filterReason) return;\n items.push(item);\n };\n\n // Check suggestions directory\n const suggestionsDir = path.join(memoryDir, \"suggestions\");\n if (fs.existsSync(suggestionsDir)) {\n walkMd(suggestionsDir, (filePath, content) => {\n if (items.length >= limit) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id) return;\n\n addItem({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? \"suggestion\",\n confidence: parseConfidence(fm.confidence, 0.5),\n confidenceTier: (fm.confidenceTier as string) ?? \"low\",\n source: (fm.source as string) ?? \"unknown\",\n filePath,\n created: (fm.created as string) ?? new Date().toISOString(),\n reviewReason: \"suggestion\",\n });\n });\n }\n\n // Check review directory\n const reviewDir = path.join(memoryDir, \"review\");\n if (fs.existsSync(reviewDir)) {\n walkMd(reviewDir, (filePath, content) => {\n if (items.length >= limit) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id) return;\n\n addItem({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? \"review\",\n confidence: parseConfidence(fm.confidence, 0.5),\n confidenceTier: (fm.confidenceTier as string) ?? \"low\",\n source: (fm.source as string) ?? \"unknown\",\n filePath,\n created: (fm.created as string) ?? new Date().toISOString(),\n reviewReason: (fm.reviewReason as ReviewItem[\"reviewReason\"]) ?? \"low_confidence\",\n context: fm.context as string | undefined,\n });\n });\n }\n\n // Scan all categories for low-confidence items\n const categories = ALL_CATEGORY_DIRS;\n for (const category of categories) {\n if (items.length >= limit) break;\n\n const dir = path.join(memoryDir, category);\n if (!fs.existsSync(dir)) continue;\n\n walkMd(dir, (filePath, content) => {\n if (items.length >= limit) return;\n\n const fm = parseFrontmatter(content);\n const body = extractBody(content);\n if (!fm?.id) return;\n\n const confidence = parseConfidence(fm.confidence, 1);\n if (confidence >= confidenceThreshold) return;\n if (parseBoolean(fm.reviewDismissed)) return;\n\n // Skip if already in items\n if (items.some((i) => i.id === fm.id)) return;\n\n addItem({\n id: fm.id as string,\n content: body,\n category: (fm.category as string) ?? category.slice(0, -1),\n confidence,\n confidenceTier: (fm.confidenceTier as string) ?? \"low\",\n source: (fm.source as string) ?? \"unknown\",\n filePath,\n created: (fm.created as string) ?? new Date().toISOString(),\n reviewReason: \"low_confidence\",\n });\n });\n }\n\n return {\n items,\n total: items.length,\n durationMs: Date.now() - startTime,\n };\n}\n\n/**\n * Perform a review action on an item.\n */\nexport function performReview(\n memoryDir: string,\n itemId: string,\n action: ReviewAction,\n options: ReviewActionOptions = {},\n): ReviewResult {\n switch (action) {\n case \"approve\":\n return approveItem(memoryDir, itemId, options);\n case \"dismiss\":\n return dismissItem(memoryDir, itemId, options);\n case \"flag\":\n return flagItem(memoryDir, itemId, options);\n }\n}\n\n// ── Actions ──────────────────────────────────────────────────────────────────\n\nfunction approveItem(\n memoryDir: string,\n itemId: string,\n options: ReviewActionOptions,\n): ReviewResult {\n const found = findReviewFileById(memoryDir, itemId, options);\n if (!found) {\n return { itemId, action: \"approve\", message: \"Item not found\" };\n }\n\n const content = fs.readFileSync(found.filePath, \"utf8\");\n const fm = parseFrontmatter(content);\n if (!fm) return { itemId, action: \"approve\", message: \"Could not parse frontmatter\" };\n\n const updatedContent = updateFrontmatterFields(content, {\n confidence: \"0.9\",\n confidenceTier: \"high\",\n reviewDismissed: null,\n });\n\n if (found.location === \"category\") {\n fs.writeFileSync(found.filePath, updatedContent, \"utf8\");\n return {\n itemId,\n action: \"approve\",\n updatedPath: found.filePath,\n message: \"Approved low-confidence memory in place with confidence 0.9\",\n };\n }\n\n // Promote queued suggestions/review items to their category directory.\n const category = (fm.category as string) ?? \"fact\";\n const targetDir = getCategoryDir(memoryDir, category);\n const dateDir = new Date().toISOString().split(\"T\")[0];\n const outputPath = path.join(targetDir, dateDir, path.basename(found.filePath));\n\n fs.mkdirSync(path.dirname(outputPath), { recursive: true });\n const promotedPath = writeFileWithoutClobber(outputPath, updatedContent, itemId);\n\n // Remove from review\n fs.unlinkSync(found.filePath);\n\n return {\n itemId,\n action: \"approve\",\n updatedPath: promotedPath,\n message: `Promoted to ${category} with confidence 0.9`,\n };\n}\n\nfunction dismissItem(\n memoryDir: string,\n itemId: string,\n options: ReviewActionOptions,\n): ReviewResult {\n const found = findReviewFileById(memoryDir, itemId, options);\n if (!found) {\n return { itemId, action: \"dismiss\", message: \"Item not found\" };\n }\n\n if (found.location === \"queue\") {\n fs.unlinkSync(found.filePath);\n return { itemId, action: \"dismiss\", message: \"Dismissed and removed\" };\n }\n\n const content = fs.readFileSync(found.filePath, \"utf8\");\n fs.writeFileSync(\n found.filePath,\n updateFrontmatterFields(content, {\n reviewDismissed: \"true\",\n reviewDismissedAt: new Date().toISOString(),\n }),\n \"utf8\",\n );\n return {\n itemId,\n action: \"dismiss\",\n updatedPath: found.filePath,\n message: \"Dismissed low-confidence memory in place\",\n };\n}\n\nfunction flagItem(\n memoryDir: string,\n itemId: string,\n options: ReviewActionOptions,\n): ReviewResult {\n const found = findReviewFileById(memoryDir, itemId, options);\n if (!found) {\n return { itemId, action: \"flag\", message: \"Item not found\" };\n }\n\n const content = fs.readFileSync(found.filePath, \"utf8\");\n const fixed = updateFrontmatterFields(content, {\n flagged: \"true\",\n flaggedAt: new Date().toISOString(),\n });\n fs.writeFileSync(found.filePath, fixed);\n return {\n itemId,\n action: \"flag\",\n updatedPath: found.filePath,\n message: \"Flagged for further review\",\n };\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction findReviewFileById(\n memoryDir: string,\n id: string,\n options: ReviewActionOptions = {},\n): ReviewFileMatch | null {\n for (const loc of [\"suggestions\", \"review\"]) {\n const dir = path.join(memoryDir, loc);\n if (!fs.existsSync(dir)) continue;\n\n const found = findFileById(dir, id);\n if (found) return { filePath: found, location: \"queue\" };\n }\n\n for (const category of ALL_CATEGORY_DIRS) {\n const dir = path.join(memoryDir, category);\n if (!fs.existsSync(dir)) continue;\n\n const found = findFileById(dir, id, (fm) => isLowConfidenceReviewCandidate(fm, options));\n if (found) return { filePath: found, location: \"category\" };\n }\n\n return null;\n}\n\nfunction findFileById(\n dir: string,\n id: string,\n include?: (frontmatter: Record<string, unknown>) => boolean,\n): string | null {\n const files = walkMdPaths(dir);\n for (const filePath of files) {\n const content = readFileSafe(filePath);\n if (!content) continue;\n const fm = parseFrontmatter(content);\n if (fm?.id === id && (!include || include(fm))) return filePath;\n }\n return null;\n}\n\nfunction isLowConfidenceReviewCandidate(\n fm: Record<string, unknown>,\n options: ReviewActionOptions,\n): boolean {\n const threshold = options.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD;\n return (\n parseConfidence(fm.confidence, 1) < threshold &&\n !parseBoolean(fm.reviewDismissed)\n );\n}\n\nfunction parseBoolean(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n if (typeof value !== \"string\") return false;\n const normalized = value.trim().toLowerCase();\n return normalized === \"true\" || normalized === \"1\" || normalized === \"yes\";\n}\n\nfunction parseConfidence(value: unknown, fallback: number): number {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : fallback;\n if (typeof value === \"string\") {\n const n = parseFloat(value);\n return Number.isFinite(n) ? n : fallback;\n }\n return fallback;\n}\n\nfunction updateFrontmatterFields(\n content: string,\n fields: Record<string, string | null>,\n): string {\n const match = content.match(/^(---\\n)([\\s\\S]*?)(\\n---(?:\\n|$))/);\n if (!match) return content;\n\n const seen = new Set<string>();\n const lines = match[2].split(\"\\n\");\n const nextLines: string[] = [];\n for (const line of lines) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) {\n nextLines.push(line);\n continue;\n }\n const key = line.slice(0, colonIdx).trim();\n if (!Object.prototype.hasOwnProperty.call(fields, key)) {\n nextLines.push(line);\n continue;\n }\n seen.add(key);\n const value = fields[key];\n if (value !== null) {\n nextLines.push(`${key}: ${value}`);\n }\n }\n\n for (const [key, value] of Object.entries(fields)) {\n if (value !== null && !seen.has(key)) {\n nextLines.push(`${key}: ${value}`);\n }\n }\n\n return `${match[1]}${nextLines.join(\"\\n\")}${match[3]}${content.slice(match[0].length)}`;\n}\n\nfunction readFileSafe(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf8\");\n } catch {\n return null;\n }\n}\n\nfunction writeFileWithoutClobber(basePath: string, content: string, discriminator: string): string {\n const parsed = path.parse(basePath);\n const safeDiscriminator = sanitizeFilePart(discriminator);\n\n for (let attempt = 0; attempt < 1000; attempt++) {\n const candidate = attempt === 0\n ? basePath\n : path.join(\n parsed.dir,\n `${parsed.name}-${safeDiscriminator}${attempt === 1 ? \"\" : `-${attempt}`}${parsed.ext || \".md\"}`,\n );\n\n try {\n fs.writeFileSync(candidate, content, { encoding: \"utf8\", flag: \"wx\" });\n return candidate;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\") continue;\n throw error;\n }\n }\n\n throw new Error(`Could not find a free review promotion path for ${basePath}`);\n}\n\nfunction sanitizeFilePart(value: string): string {\n const chars: string[] = [];\n let previousWasDash = false;\n\n for (const char of value) {\n const next = isSafeFilePartChar(char) ? char : \"-\";\n if (next === \"-\" && previousWasDash) continue;\n chars.push(next);\n previousWasDash = next === \"-\";\n if (chars.length >= 64) break;\n }\n\n let start = 0;\n let end = chars.length;\n while (start < end && chars[start] === \"-\") start++;\n while (end > start && chars[end - 1] === \"-\") end--;\n\n const sanitized = chars.slice(start, end).join(\"\");\n return sanitized || \"review-item\";\n}\n\nfunction isSafeFilePartChar(value: string): boolean {\n if (value.length !== 1) return false;\n const code = value.charCodeAt(0);\n return (\n (code >= 48 && code <= 57) ||\n (code >= 65 && code <= 90) ||\n (code >= 97 && code <= 122) ||\n value === \".\" ||\n value === \"_\" ||\n value === \"-\"\n );\n}\n\nfunction parseFrontmatter(content: string): Record<string, unknown> | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: Record<string, unknown> = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n fm[key] = value;\n }\n return fm;\n}\n\nfunction extractBody(content: string): string {\n const match = content.match(/^---\\n[\\s\\S]*?\\n---\\n([\\s\\S]*)/);\n return match ? match[1].trim() : content.trim();\n}\n\nfunction walkMd(dir: string, callback: (filePath: string, content: string) => void): void {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walkMd(fullPath, callback);\n } else if (entry.name.endsWith(\".md\")) {\n const content = readFileSafe(fullPath);\n if (content) callback(fullPath, content);\n }\n }\n}\n\nfunction walkMdPaths(dir: string): string[] {\n const results: string[] = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(...walkMdPaths(fullPath));\n } else if (entry.name.endsWith(\".md\")) {\n results.push(fullPath);\n }\n }\n return results;\n}\n","/**\n * @remnic/core — Diff-Aware Sync\n *\n * Watches source files for changes and triggers re-ingestion\n * only for changed content. Uses file hashing to detect changes.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport interface SyncOptions {\n /** Source directory to watch */\n sourceDir: string;\n /** Memory root directory */\n memoryDir: string;\n /** State file path (stores hashes). Default: memoryDir/.sync-state.json */\n stateFile?: string;\n /** File extensions to watch (default: .md, .txt, .mdx) */\n extensions?: string[];\n /** Directories to exclude */\n excludeDirs?: string[];\n /** Whether to actually write changes (default: true) */\n dryRun?: boolean;\n}\n\nexport interface SyncResult {\n /** Files scanned */\n scanned: number;\n /** Files changed since last sync */\n changed: FileChange[];\n /** Files unchanged */\n unchanged: number;\n /** Files deleted since last sync */\n deleted: string[];\n /** Files newly added */\n added: string[];\n /** Duration in ms */\n durationMs: number;\n /** State file path */\n stateFile: string;\n}\n\nexport interface FileChange {\n /** Absolute file path */\n filePath: string;\n /** Relative path from source root */\n relativePath: string;\n /** Change type */\n type: \"added\" | \"modified\" | \"deleted\";\n /** Current content hash */\n currentHash: string;\n /** Previous content hash (if modified) */\n previousHash?: string;\n /** File size in bytes */\n size: number;\n}\n\nexport interface SyncState {\n /** Map of relative path → content hash */\n fileHashes: Record<string, string>;\n /** Last sync timestamp */\n lastSyncAt: string;\n /** Version of state format */\n version: number;\n}\n\n// ── Constants ────────────────────────────────────────────────────────────────\n\nconst DEFAULT_EXTENSIONS = new Set([\".md\", \".txt\", \".mdx\", \".rst\"]);\nconst DEFAULT_EXCLUDE = new Set([\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".engram\",\n \"coverage\",\n]);\n\n// ── Main function ────────────────────────────────────────────────────────────\n\nexport function syncChanges(options: SyncOptions): SyncResult {\n const startTime = Date.now();\n const {\n sourceDir,\n memoryDir,\n extensions = [...DEFAULT_EXTENSIONS],\n excludeDirs = [],\n dryRun = false,\n } = options;\n\n const extSet = new Set(extensions);\n const excludeSet = new Set([...DEFAULT_EXCLUDE, ...excludeDirs]);\n const stateFilePath = options.stateFile ?? path.join(memoryDir, \".sync-state.json\");\n\n // Load previous state\n const prevState = loadState(stateFilePath);\n\n // Scan current files\n const currentFiles = scanFiles(sourceDir, extSet, excludeSet);\n\n // Compute diffs\n const changes = computeDiff(currentFiles, prevState.fileHashes, sourceDir);\n\n const added = changes.filter((c) => c.type === \"added\").map((c) => c.relativePath);\n const modified = changes.filter((c) => c.type === \"modified\");\n const deleted = changes\n .filter((c) => c.type === \"deleted\")\n .map((c) => c.relativePath);\n\n // Update state (even in dry run, we want to show what would change)\n if (!dryRun) {\n const newState: SyncState = {\n fileHashes: {},\n lastSyncAt: new Date().toISOString(),\n version: 1,\n };\n\n // Build new state from current files\n for (const [relPath, hash] of Object.entries(currentFiles)) {\n newState.fileHashes[relPath] = hash;\n }\n\n // Write state\n fs.mkdirSync(path.dirname(stateFilePath), { recursive: true });\n fs.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));\n }\n\n return {\n scanned: Object.keys(currentFiles).length,\n changed: changes,\n unchanged:\n Object.keys(currentFiles).length - changes.filter((c) => c.type !== \"deleted\").length,\n deleted,\n added,\n durationMs: Date.now() - startTime,\n stateFile: stateFilePath,\n };\n}\n\n/**\n * Watch for changes and call callback on file changes.\n * Returns a stop function.\n */\nexport function watchForChanges(\n options: SyncOptions,\n onChange: (changes: FileChange[]) => void,\n): { stop: () => void } {\n const { sourceDir, extensions, excludeDirs } = options;\n const extSet = new Set(extensions ?? DEFAULT_EXTENSIONS);\n const excludeSet = new Set([...DEFAULT_EXCLUDE, ...(excludeDirs ?? [])]);\n\n let lastHashes: Record<string, string> = {};\n\n // Initial scan\n const currentFiles = scanFiles(sourceDir, extSet, excludeSet);\n lastHashes = { ...currentFiles };\n\n // Poll interval (FSWatcher doesn't reliably work for all platforms)\n const interval = setInterval(() => {\n const nowFiles = scanFiles(sourceDir, extSet, excludeSet);\n const changes = computeDiff(nowFiles, lastHashes, sourceDir);\n\n if (changes.length > 0) {\n // Update hashes\n for (const change of changes) {\n if (change.type === \"deleted\") {\n delete lastHashes[change.relativePath];\n } else {\n lastHashes[change.relativePath] = change.currentHash;\n }\n }\n onChange(changes);\n }\n }, 5000);\n\n return {\n stop: () => clearInterval(interval),\n };\n}\n\n// ── File scanning ────────────────────────────────────────────────────────────\n\nfunction scanFiles(\n root: string,\n extensions: Set<string>,\n exclude: Set<string>,\n): Record<string, string> {\n const result: Record<string, string> = {};\n\n function walk(dir: string): void {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return;\n }\n\n for (const entry of entries) {\n if (exclude.has(entry.name)) continue;\n\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name).toLowerCase();\n if (!extensions.has(ext)) continue;\n\n const relPath = path.relative(root, fullPath);\n try {\n const content = fs.readFileSync(fullPath, \"utf8\");\n result[relPath] = hashContent(content);\n } catch {\n // Can't read — skip\n }\n }\n }\n }\n\n walk(root);\n return result;\n}\n\n// ── Diff computation ─────────────────────────────────────────────────────────\n\nfunction computeDiff(\n current: Record<string, string>,\n previous: Record<string, string>,\n sourceDir: string,\n): FileChange[] {\n const changes: FileChange[] = [];\n\n // Find added and modified\n for (const [relPath, hash] of Object.entries(current)) {\n const fullPath = path.join(sourceDir, relPath);\n\n if (!(relPath in previous)) {\n // Added\n let size = 0;\n try {\n size = fs.statSync(fullPath).size;\n } catch {\n // ignore\n }\n changes.push({\n filePath: fullPath,\n relativePath: relPath,\n type: \"added\",\n currentHash: hash,\n size,\n });\n } else if (previous[relPath] !== hash) {\n // Modified\n let size = 0;\n try {\n size = fs.statSync(fullPath).size;\n } catch {\n // ignore\n }\n changes.push({\n filePath: fullPath,\n relativePath: relPath,\n type: \"modified\",\n currentHash: hash,\n previousHash: previous[relPath],\n size,\n });\n }\n }\n\n // Find deleted\n for (const relPath of Object.keys(previous)) {\n if (!(relPath in current)) {\n changes.push({\n filePath: path.join(sourceDir, relPath),\n relativePath: relPath,\n type: \"deleted\",\n currentHash: \"\",\n size: 0,\n });\n }\n }\n\n return changes;\n}\n\n// ── State management ─────────────────────────────────────────────────────────\n\nfunction loadState(stateFilePath: string): SyncState {\n try {\n const raw = fs.readFileSync(stateFilePath, \"utf8\");\n return JSON.parse(raw);\n } catch {\n return {\n fileHashes: {},\n lastSyncAt: new Date(0).toISOString(),\n version: 1,\n };\n }\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n","/**\n * @remnic/core — Spaces + Collaboration\n *\n * First-class memory spaces (personal, project, team) with merge/conflict\n * flows, promotion workflow, and audit trail.\n *\n * Each space is an isolated memory directory. Spaces can share memories\n * through push/pull and promotion workflows.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport crypto from \"node:crypto\";\nimport { readEnvVar, resolveHomeDir } from \"../runtime/env.js\";\n\n// ── Types ────────────────────────────────────────────────────────────────────\n\nexport type SpaceKind = \"personal\" | \"project\" | \"team\";\n\nexport interface Space {\n /** Unique space ID */\n id: string;\n /** Human-readable name */\n name: string;\n /** Space type */\n kind: SpaceKind;\n /** Description */\n description?: string;\n /** Memory directory path (absolute) */\n memoryDir: string;\n /** Created at */\n createdAt: string;\n /** Updated at */\n updatedAt: string;\n /** Owner */\n owner?: string;\n /** Members (for team spaces) */\n members?: string[];\n /** Parent space (for promotion) */\n parentSpaceId?: string;\n}\n\nexport interface SpaceManifest {\n /** Current active space ID */\n activeSpaceId: string;\n /** All spaces */\n spaces: Space[];\n /** Manifest version */\n version: number;\n /** Last updated */\n updatedAt?: string;\n}\n\nexport interface SpaceSwitchResult {\n previousSpaceId: string;\n currentSpaceId: string;\n message: string;\n}\n\nexport interface SpacePushResult {\n sourceSpaceId: string;\n targetSpaceId: string;\n memoriesPushed: number;\n conflicts: ConflictEntry[];\n durationMs: number;\n}\n\nexport interface SpacePullResult {\n sourceSpaceId: string;\n targetSpaceId: string;\n memoriesPulled: number;\n conflicts: ConflictEntry[];\n durationMs: number;\n}\n\nexport interface SpaceShareResult {\n spaceId: string;\n sharedWith: string[];\n message: string;\n}\n\nexport interface SpacePromoteResult {\n sourceSpaceId: string;\n targetSpaceId: string;\n memoriesPromoted: number;\n conflicts: ConflictEntry[];\n durationMs: number;\n}\n\nexport interface ConflictEntry {\n /** Memory ID */\n memoryId: string;\n /** Source file path */\n sourcePath: string;\n /** Target file path */\n targetPath: string;\n /** Conflict type */\n conflictType: \"content_mismatch\" | \"metadata_mismatch\" | \"both\";\n /** Source content hash */\n sourceHash: string;\n /** Target content hash */\n targetHash: string;\n}\n\nexport interface MergeResult {\n merged: number;\n conflicts: ConflictEntry[];\n skipped: number;\n durationMs: number;\n}\n\nexport interface AuditEntry {\n id: string;\n timestamp: string;\n action: string;\n sourceSpaceId: string;\n targetSpaceId?: string;\n actor?: string;\n details: string;\n memoryIds?: string[];\n}\n\n// ── Manifest management ─────────────────────────────────────────────────────\n\nconst MANIFEST_VERSION = 1;\n\nexport function getSpacesDir(baseDir?: string): string {\n const homeDir = baseDir ?? resolveHomeDir();\n return path.join(homeDir, \".config\", \"engram\", \"spaces\");\n}\n\nexport function getManifestPath(baseDir?: string): string {\n return path.join(getSpacesDir(baseDir), \"manifest.json\");\n}\n\nexport function loadManifest(baseDir?: string, memoryDirOverride?: string): SpaceManifest {\n const manifestPath = getManifestPath(baseDir);\n\n if (!fs.existsSync(manifestPath)) {\n // Bootstrap with a personal space\n const personalSpace = createPersonalSpace(baseDir, memoryDirOverride);\n const manifest: SpaceManifest = {\n activeSpaceId: personalSpace.id,\n spaces: [personalSpace],\n version: MANIFEST_VERSION,\n };\n saveManifest(manifest, baseDir);\n return manifest;\n }\n\n const raw = JSON.parse(fs.readFileSync(manifestPath, \"utf8\"));\n return raw as SpaceManifest;\n}\n\nexport function saveManifest(manifest: SpaceManifest, baseDir?: string): void {\n const manifestPath = getManifestPath(baseDir);\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n}\n\nfunction createPersonalSpace(baseDir?: string, memoryDirOverride?: string): Space {\n const homeDir = baseDir ?? resolveHomeDir();\n // Priority: override > env var > existing standalone dir > existing OpenClaw dir > new standalone dir\n const standalonePath = path.join(homeDir, \".engram\", \"memory\");\n const openclawPath = path.join(homeDir, \".openclaw\", \"workspace\", \"memory\", \"local\");\n const memoryDir = memoryDirOverride\n ?? readEnvVar(\"ENGRAM_MEMORY_DIR\")\n ?? (fs.existsSync(standalonePath) ? standalonePath\n : fs.existsSync(openclawPath) ? openclawPath\n : standalonePath);\n const now = new Date().toISOString();\n\n return {\n id: \"personal\",\n name: \"Personal\",\n kind: \"personal\",\n description: \"Default personal memory space\",\n memoryDir,\n createdAt: now,\n updatedAt: now,\n owner: readEnvVar(\"USER\"),\n };\n}\n\n// ── Space CRUD ──────────────────────────────────────────────────────────────\n\nexport function listSpaces(baseDir?: string): Space[] {\n const manifest = loadManifest(baseDir);\n return manifest.spaces;\n}\n\nexport function getActiveSpace(baseDir?: string): Space {\n const manifest = loadManifest(baseDir);\n const space = manifest.spaces.find((s) => s.id === manifest.activeSpaceId);\n if (!space) throw new Error(`Active space ${manifest.activeSpaceId} not found`);\n return space;\n}\n\nexport function createSpace(options: {\n name: string;\n kind: SpaceKind;\n description?: string;\n memoryDir?: string;\n parentSpaceId?: string;\n baseDir?: string;\n}): Space {\n const manifest = loadManifest(options.baseDir);\n const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, \"-\").replace(/-+/g, \"-\");\n\n if (manifest.spaces.some((s) => s.id === id)) {\n throw new Error(`Space \"${id}\" already exists`);\n }\n\n // Validate parent space exists\n if (options.parentSpaceId && !manifest.spaces.some((s) => s.id === options.parentSpaceId)) {\n throw new Error(`Parent space \"${options.parentSpaceId}\" not found`);\n }\n\n const now = new Date().toISOString();\n const memoryDir = options.memoryDir ?? path.join(\n getSpacesDir(options.baseDir),\n id,\n \"memory\",\n );\n\n const space: Space = {\n id,\n name: options.name,\n kind: options.kind,\n description: options.description,\n memoryDir,\n createdAt: now,\n updatedAt: now,\n owner: readEnvVar(\"USER\"),\n parentSpaceId: options.parentSpaceId,\n };\n\n // Ensure memory directory exists\n fs.mkdirSync(memoryDir, { recursive: true });\n\n manifest.spaces.push(space);\n manifest.updatedAt = now;\n saveManifest(manifest, options.baseDir);\n\n // Audit\n appendAudit({\n action: \"space.create\",\n sourceSpaceId: id,\n details: `Created ${options.kind} space \"${options.name}\"`,\n }, options.baseDir);\n\n return space;\n}\n\nexport function deleteSpace(spaceId: string, baseDir?: string): void {\n const manifest = loadManifest(baseDir);\n\n if (spaceId === \"personal\") {\n throw new Error(\"Cannot delete the personal space\");\n }\n\n const idx = manifest.spaces.findIndex((s) => s.id === spaceId);\n if (idx === -1) throw new Error(`Space \"${spaceId}\" not found`);\n\n // If deleting active space, switch to personal\n if (manifest.activeSpaceId === spaceId) {\n manifest.activeSpaceId = \"personal\";\n }\n\n // Clear parentSpaceId references from children\n for (const space of manifest.spaces) {\n if (space.parentSpaceId === spaceId) {\n space.parentSpaceId = undefined;\n }\n }\n\n manifest.spaces.splice(idx, 1);\n saveManifest(manifest, baseDir);\n\n appendAudit({\n action: \"space.delete\",\n sourceSpaceId: spaceId,\n details: `Deleted space \"${spaceId}\"`,\n }, baseDir);\n}\n\n// ── Switch ───────────────────────────────────────────────────────────────────\n\nexport function switchSpace(spaceId: string, baseDir?: string): SpaceSwitchResult {\n const manifest = loadManifest(baseDir);\n const space = manifest.spaces.find((s) => s.id === spaceId);\n\n if (!space) throw new Error(`Space \"${spaceId}\" not found`);\n\n const previousId = manifest.activeSpaceId;\n manifest.activeSpaceId = spaceId;\n saveManifest(manifest, baseDir);\n\n appendAudit({\n action: \"space.switch\",\n sourceSpaceId: previousId,\n targetSpaceId: spaceId,\n details: `Switched from \"${previousId}\" to \"${spaceId}\"`,\n }, baseDir);\n\n return {\n previousSpaceId: previousId,\n currentSpaceId: spaceId,\n message: `Switched to \"${space.name}\"`,\n };\n}\n\n// ── Push / Pull ─────────────────────────────────────────────────────────────\n\nexport function pushToSpace(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { memoryIds?: string[]; force?: boolean; baseDir?: string },\n): SpacePushResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n filterIds: options?.memoryIds,\n force: options?.force,\n });\n\n appendAudit({\n action: \"space.push\",\n sourceSpaceId,\n targetSpaceId,\n details: `Pushed ${result.merged} memories, ${result.conflicts.length} conflicts`,\n }, options?.baseDir);\n\n return {\n sourceSpaceId,\n targetSpaceId,\n memoriesPushed: result.merged,\n conflicts: result.conflicts,\n durationMs: Date.now() - startTime,\n };\n}\n\nexport function pullFromSpace(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { memoryIds?: string[]; force?: boolean; baseDir?: string },\n): SpacePullResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n filterIds: options?.memoryIds,\n force: options?.force,\n });\n\n appendAudit({\n action: \"space.pull\",\n sourceSpaceId,\n targetSpaceId,\n details: `Pulled ${result.merged} memories, ${result.conflicts.length} conflicts`,\n }, options?.baseDir);\n\n return {\n sourceSpaceId,\n targetSpaceId,\n memoriesPulled: result.merged,\n conflicts: result.conflicts,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Share ────────────────────────────────────────────────────────────────────\n\nexport function shareSpace(\n spaceId: string,\n members: string[],\n baseDir?: string,\n): SpaceShareResult {\n const manifest = loadManifest(baseDir);\n const space = manifest.spaces.find((s) => s.id === spaceId);\n\n if (!space) throw new Error(`Space \"${spaceId}\" not found`);\n if (space.kind === \"personal\") throw new Error(\"Cannot share personal space\");\n\n space.members = [...new Set([...(space.members ?? []), ...members])];\n space.updatedAt = new Date().toISOString();\n saveManifest(manifest, baseDir);\n\n appendAudit({\n action: \"space.share\",\n sourceSpaceId: spaceId,\n details: `Shared with: ${members.join(\", \")}`,\n }, baseDir);\n\n return {\n spaceId,\n sharedWith: members,\n message: `Shared \"${space.name}\" with ${members.length} member(s)`,\n };\n}\n\n// ── Promote ──────────────────────────────────────────────────────────────────\n\nexport function promoteSpace(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { memoryIds?: string[]; force?: boolean; forceOverwrite?: boolean; baseDir?: string },\n): SpacePromoteResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n // Promotion requires parent-child relationship or explicit force\n if (source.parentSpaceId !== targetSpaceId && target.parentSpaceId !== sourceSpaceId) {\n if (!options?.force) {\n throw new Error(\"Spaces must have a parent-child relationship for promotion. Use --force to override.\");\n }\n }\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n filterIds: options?.memoryIds,\n force: options?.forceOverwrite !== undefined ? options.forceOverwrite : (options?.force ?? false),\n });\n\n appendAudit({\n action: \"space.promote\",\n sourceSpaceId,\n targetSpaceId,\n details: `Promoted ${result.merged} memories from \"${source.name}\" to \"${target.name}\"`,\n }, options?.baseDir);\n\n return {\n sourceSpaceId,\n targetSpaceId,\n memoriesPromoted: result.merged,\n conflicts: result.conflicts,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Merge ────────────────────────────────────────────────────────────────────\n\nexport function mergeSpaces(\n sourceSpaceId: string,\n targetSpaceId: string,\n options?: { force?: boolean; baseDir?: string },\n): MergeResult {\n const startTime = Date.now();\n const manifest = loadManifest(options?.baseDir);\n\n const source = manifest.spaces.find((s) => s.id === sourceSpaceId);\n const target = manifest.spaces.find((s) => s.id === targetSpaceId);\n\n if (!source) throw new Error(`Source space \"${sourceSpaceId}\" not found`);\n if (!target) throw new Error(`Target space \"${targetSpaceId}\" not found`);\n\n const result = copyMemories(source.memoryDir, target.memoryDir, {\n force: options?.force,\n });\n\n appendAudit({\n action: \"space.merge\",\n sourceSpaceId,\n targetSpaceId,\n details: `Merged: ${result.merged} merged, ${result.conflicts.length} conflicts, ${result.skipped} skipped`,\n }, options?.baseDir);\n\n return {\n ...result,\n durationMs: Date.now() - startTime,\n };\n}\n\n// ── Audit trail ─────────────────────────────────────────────────────────────\n\nexport function getAuditLog(baseDir?: string): AuditEntry[] {\n const auditPath = path.join(getSpacesDir(baseDir), \"audit.jsonl\");\n if (!fs.existsSync(auditPath)) return [];\n\n const lines = fs.readFileSync(auditPath, \"utf8\").trim().split(\"\\n\");\n return lines\n .filter((l) => l.trim())\n .map((l) => JSON.parse(l) as AuditEntry);\n}\n\nfunction appendAudit(entry: Omit<AuditEntry, \"id\" | \"timestamp\">, baseDir?: string): void {\n const auditPath = path.join(getSpacesDir(baseDir), \"audit.jsonl\");\n fs.mkdirSync(path.dirname(auditPath), { recursive: true });\n\n const full: AuditEntry = {\n id: crypto.randomUUID(),\n timestamp: new Date().toISOString(),\n ...entry,\n };\n\n fs.appendFileSync(auditPath, JSON.stringify(full) + \"\\n\");\n}\n\n// ── Internal helpers ─────────────────────────────────────────────────────────\n\ninterface CopyOptions {\n filterIds?: string[];\n force?: boolean;\n}\n\nfunction copyMemories(\n sourceDir: string,\n targetDir: string,\n options?: CopyOptions,\n): { merged: number; conflicts: ConflictEntry[]; skipped: number } {\n let merged = 0;\n const conflicts: ConflictEntry[] = [];\n let skipped = 0;\n\n if (!fs.existsSync(sourceDir)) {\n return { merged: 0, conflicts: [], skipped: 0 };\n }\n\n fs.mkdirSync(targetDir, { recursive: true });\n\n const sourceFiles = walkMd(sourceDir);\n for (const sourcePath of sourceFiles) {\n const content = fs.readFileSync(sourcePath, \"utf8\");\n const relativePath = path.relative(sourceDir, sourcePath);\n const targetPath = path.join(targetDir, relativePath);\n\n const sourceHash = hashContent(content);\n\n // Filter by IDs if specified\n if (options?.filterIds?.length) {\n const fm = parseSimpleFrontmatter(content);\n if (!fm?.id || !options.filterIds.includes(fm.id)) {\n skipped++;\n continue;\n }\n }\n\n // Check for conflict\n if (fs.existsSync(targetPath) && !options?.force) {\n const targetContent = fs.readFileSync(targetPath, \"utf8\");\n const targetHash = hashContent(targetContent);\n\n if (sourceHash !== targetHash) {\n conflicts.push({\n memoryId: parseSimpleFrontmatter(content)?.id ?? relativePath,\n sourcePath,\n targetPath,\n conflictType: \"content_mismatch\",\n sourceHash,\n targetHash,\n });\n continue;\n }\n\n // Same content — skip\n skipped++;\n continue;\n }\n\n // Copy file\n fs.mkdirSync(path.dirname(targetPath), { recursive: true });\n fs.writeFileSync(targetPath, content);\n merged++;\n }\n\n return { merged, conflicts, skipped };\n}\n\nfunction hashContent(content: string): string {\n return crypto.createHash(\"sha256\").update(content).digest(\"hex\").slice(0, 16);\n}\n\nfunction walkMd(dir: string): string[] {\n const results: string[] = [];\n\n function walk(d: string): void {\n for (const entry of fs.readdirSync(d, { withFileTypes: true })) {\n const fullPath = path.join(d, entry.name);\n if (entry.isDirectory()) {\n walk(fullPath);\n } else if (entry.name.endsWith(\".md\")) {\n results.push(fullPath);\n }\n }\n }\n\n walk(dir);\n return results;\n}\n\ninterface SimpleFrontmatter {\n id?: string;\n [key: string]: string | undefined;\n}\n\nfunction parseSimpleFrontmatter(content: string): SimpleFrontmatter | null {\n const match = content.match(/^---\\n([\\s\\S]*?)\\n---/);\n if (!match) return null;\n\n const fm: SimpleFrontmatter = {};\n for (const line of match[1].split(\"\\n\")) {\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const key = line.slice(0, colonIdx).trim();\n const value = line.slice(colonIdx + 1).trim();\n fm[key] = value;\n }\n return fm;\n}\n","/**\n * @remnic/core — Shared Instruction Blocks\n *\n * Reusable markdown fragments that every host-specific publisher can\n * compose into its instructions.md file. Keeping them here avoids\n * per-host copy-paste drift.\n */\n\n/**\n * Describes the Remnic memory types a host agent may encounter.\n */\nexport const REMNIC_SEMANTIC_OVERVIEW = `\\\n## Remnic Memory Types\n\nRemnic stores memories as plain Markdown files with YAML front-matter.\nEach memory has a **type** that describes its semantic role:\n\n| Type | Description |\n|------|-------------|\n| \\`fact\\` | An objective piece of knowledge the user confirmed or that was extracted from a session. |\n| \\`preference\\` | A stated or inferred user preference (e.g. coding style, tool choice). |\n| \\`decision\\` | An explicit decision or trade-off the user made. |\n| \\`entity\\` | A named thing the user cares about (project, service, person, API). |\n| \\`skill\\` | A reusable workflow or procedure documented for future sessions. |\n| \\`correction\\` | A fix or amendment to a previously stored memory. |\n| \\`question\\` | An open question or uncertainty flagged for future resolution. |\n| \\`observation\\` | A pattern noticed across sessions (e.g. \"user always runs tests before commits\"). |\n| \\`summary\\` | A condensed roll-up of recent sessions or a topic area. |\n\nWhen reading Remnic content, the front-matter \\`type\\` field tells you what\nkind of knowledge you are looking at and how much weight to give it.\n`;\n\n/**\n * Explains the oai-mem-citation block format hosts should use when\n * referencing Remnic-sourced content.\n */\nexport const REMNIC_CITATION_FORMAT = `\\\n## Citing Remnic Memories\n\nWhen a piece of your output draws on a Remnic file, cite it using the\nmemory citation block format so the user can trace the source:\n\n\\`\\`\\`\n<oai-mem-citation path=\"<path-relative-to-remnic-memory-base>\" />\n\\`\\`\\`\n\nThe path must be **relative to the Remnic memory base** (the directory\nnamed \\`memories/\\` under \\`<remnic-home>\\`), not absolute. Examples:\n\n- \\`<oai-mem-citation path=\"default/MEMORY.md\" />\\`\n- \\`<oai-mem-citation path=\"my-project/skills/deploy/SKILL.md\" />\\`\n- \\`<oai-mem-citation path=\"shared/memory_summary.md\" />\\`\n\nCite each distinct source once near the fact it supports. Do not invent\ncitations for files you have not actually read.\n`;\n\n/**\n * Table of MCP tools the Remnic daemon exposes. Hosts that can reach\n * the MCP server should prefer these over raw file reads.\n *\n * Tool names use the canonical `remnic.*` prefix. Legacy `engram.*`\n * aliases are also accepted by the server for backward compatibility.\n */\nexport const REMNIC_MCP_TOOL_INVENTORY = `\\\n## Remnic MCP Tools\n\nWhen the Remnic MCP server is reachable, the following tools are\navailable. Prefer MCP tools over direct file reads when the host\nsupports MCP connections.\n\n| Tool | Purpose |\n|------|---------|\n| \\`remnic.recall\\` | Retrieve contextually relevant memories for the current session. |\n| \\`remnic.recall_explain\\` | Like recall, but includes an explanation of why each memory was selected. |\n| \\`remnic.memory_store\\` | Persist a new memory (fact, preference, decision, etc.). |\n| \\`remnic.memory_get\\` | Fetch a specific memory by ID. |\n| \\`remnic.memory_search\\` | Full-text + semantic search across all memories. |\n| \\`remnic.memory_timeline\\` | Retrieve memories in chronological order within a time range. |\n| \\`remnic.observe\\` | Record an observation from the current conversation turn. |\n| \\`remnic.entity_get\\` | Look up a named entity and its relationships. |\n| \\`remnic.memory_entities_list\\` | List all known entities. |\n| \\`remnic.memory_profile\\` | Retrieve the user profile summary. |\n| \\`remnic.day_summary\\` | Generate a summary of memories from a specific day. |\n| \\`remnic.briefing\\` | Generate a structured briefing for an upcoming session. |\n| \\`remnic.memory_feedback\\` | Submit feedback on a recalled memory (useful, outdated, wrong). |\n| \\`remnic.memory_promote\\` | Promote a memory to a higher confidence tier. |\n| \\`remnic.context_checkpoint\\` | Save a conversation checkpoint for continuity. |\n| \\`remnic.suggestion_submit\\` | Submit a suggestion for a new memory to the review queue. |\n| \\`remnic.review_queue_list\\` | List pending suggestions in the review queue. |\n| \\`remnic.work_task\\` | Create or update a work task. |\n| \\`remnic.work_project\\` | Create or update a work project. |\n| \\`remnic.work_board\\` | View the work board. |\n\nLegacy \\`engram.*\\` prefixed names are accepted as aliases for all tools.\n`;\n\n/**\n * Decision rules for when a host agent should use MCP recall vs\n * reading Remnic files directly from disk.\n */\nexport const REMNIC_RECALL_DECISION_RULES = `\\\n## When to Use Recall vs Direct Read\n\n### Use \\`remnic.recall\\` (MCP) when:\n\n- The Remnic MCP server is reachable (the host has an active MCP\n connection to the Remnic daemon).\n- You want contextually relevant memories ranked by the recall planner\n (semantic search + reranking + importance scoring).\n- You need memories across multiple namespaces or topics.\n- The conversation benefits from Remnic's intent detection and\n adaptive recall depth.\n\n### Use direct file reads when:\n\n- You are in a sandboxed environment with no network or MCP access\n (e.g. Codex phase-2 consolidation).\n- You need a specific file you already know the path to.\n- The MCP server is unavailable or unhealthy.\n- You are operating on the raw memory files for maintenance or\n migration purposes.\n\n### General guidance:\n\n- Prefer MCP tools when available — they provide ranked, deduplicated,\n and context-aware results.\n- Fall back to file reads gracefully — never block on a failed MCP call\n when the data is also on disk.\n- Never write directly to the Remnic memory directory unless you are an\n authorized extraction or consolidation process.\n`;\n","/**\n * @remnic/core — Codex Memory Extension Publisher\n *\n * Writes Remnic instructions into ~/.codex/memories_extensions/remnic/\n * so the Codex agent can discover and use Remnic memories during its\n * consolidation phase.\n */\n\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nimport {\n REMNIC_SEMANTIC_OVERVIEW,\n REMNIC_CITATION_FORMAT,\n REMNIC_MCP_TOOL_INVENTORY,\n REMNIC_RECALL_DECISION_RULES,\n} from \"./shared-instructions.js\";\nimport { readEnvVar, resolveHomeDir } from \"../runtime/env.js\";\n\n/** Folder name Remnic installs its extension under inside memories_extensions/. */\nconst REMNIC_EXTENSION_DIR_NAME = \"remnic\";\n\n/**\n * Codex-specific publisher that knows the Codex extension layout:\n * ~/.codex/memories_extensions/remnic/instructions.md\n */\nexport class CodexMemoryExtensionPublisher implements MemoryExtensionPublisher {\n readonly hostId = \"codex\";\n\n static readonly capabilities: PublisherCapabilities = {\n instructionsMd: true,\n skillsFolder: true,\n citationFormat: true,\n readPathTemplate: true,\n };\n\n async resolveExtensionRoot(\n env?: NodeJS.ProcessEnv,\n ): Promise<string> {\n const codexHome = env === undefined\n ? readEnvVar(\"CODEX_HOME\")?.trim() || path.join(resolveHomeDir(), \".codex\")\n : env.CODEX_HOME?.trim() || path.join(env.HOME ?? env.USERPROFILE ?? os.homedir(), \".codex\");\n return path.join(codexHome, \"memories_extensions\", REMNIC_EXTENSION_DIR_NAME);\n }\n\n async isHostAvailable(): Promise<boolean> {\n try {\n const home = readEnvVar(\"CODEX_HOME\")?.trim() ||\n path.join(resolveHomeDir(), \".codex\");\n return fs.existsSync(home);\n } catch {\n return false;\n }\n }\n\n async renderInstructions(ctx: PublishContext): Promise<string> {\n const memDir = ctx.config.memoryDir;\n const ns = ctx.config.namespace ?? \"default\";\n\n const sections: string[] = [\n `# Remnic Memory Extension for Codex\\n`,\n `This document tells you how to use Remnic as an authoritative local ` +\n `memory source. Remnic is a local-first, file-backed memory system. ` +\n `All Remnic content lives on disk as plain Markdown.\\n`,\n REMNIC_SEMANTIC_OVERVIEW,\n `## Where Remnic Content Lives\\n\\n` +\n `Memory base directory: \\`${memDir}\\`\\n\\n` +\n `Namespace: \\`${ns}\\`\\n\\n` +\n `Under the base directory, memories are organized by namespace:\\n\\n` +\n \"```\\n\" +\n `${memDir}/<namespace>/\\n` +\n ` MEMORY.md # compact top-of-mind memory\\n` +\n ` memory_summary.md # optional longer summary\\n` +\n ` skills/\\n` +\n ` <skill-name>/SKILL.md # reusable workflows\\n` +\n ` rollout_summaries/\\n` +\n ` *.md # per-session rollup notes\\n` +\n \"```\\n\",\n REMNIC_CITATION_FORMAT,\n REMNIC_MCP_TOOL_INVENTORY,\n REMNIC_RECALL_DECISION_RULES,\n `## Sandboxing Rules (Codex Phase-2)\\n\\n` +\n `When running inside the Codex phase-2 consolidation sandbox:\\n\\n` +\n `- **No network.** Do not attempt HTTP calls or MCP connections.\\n` +\n `- **No CLI invocation.** Do not shell out to \\`remnic\\` or \\`engram\\`.\\n` +\n `- **No MCP tool calls.** Use filesystem reads only.\\n` +\n `- **Local writes** are allowed only where Codex's sandbox policy permits.\\n` +\n `- **Respect missing files.** If a file does not exist, move on silently.\\n`,\n ];\n\n return sections.join(\"\\n\");\n }\n\n async publish(ctx: PublishContext): Promise<PublishResult> {\n const extensionRoot = await this.resolveExtensionRoot();\n const instructionsPath = path.join(extensionRoot, \"instructions.md\");\n const filesWritten: string[] = [];\n const skipped: string[] = [];\n\n ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);\n\n // Ensure the extension root exists.\n fs.mkdirSync(extensionRoot, { recursive: true });\n\n // Render and write instructions.md using atomic write (temp + rename).\n // Per CLAUDE.md #54: never delete before write in file replace operations.\n const content = await this.renderInstructions(ctx);\n const tmpPath = `${instructionsPath}.tmp-${process.pid}-${Date.now()}`;\n\n try {\n fs.writeFileSync(tmpPath, content, \"utf-8\");\n fs.renameSync(tmpPath, instructionsPath);\n filesWritten.push(instructionsPath);\n ctx.log.info(`Wrote ${instructionsPath}`);\n } catch (err) {\n // Clean up temp file on failure.\n try {\n if (fs.existsSync(tmpPath)) {\n fs.unlinkSync(tmpPath);\n }\n } catch {\n // swallow cleanup error\n }\n throw err;\n }\n\n return {\n hostId: this.hostId,\n extensionRoot,\n filesWritten,\n skipped,\n };\n }\n\n async unpublish(): Promise<void> {\n const extensionRoot = await this.resolveExtensionRoot();\n if (fs.existsSync(extensionRoot)) {\n fs.rmSync(extensionRoot, { recursive: true, force: true });\n }\n }\n}\n","/**\n * @remnic/core — Claude Code Memory Extension Publisher (stub)\n *\n * Placeholder publisher for Claude Code. Claude Code does not yet\n * support a file-based memory extension directory, so all methods are\n * no-ops that return safe defaults.\n */\n\nimport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nexport class ClaudeCodeMemoryExtensionPublisher implements MemoryExtensionPublisher {\n readonly hostId = \"claude-code\";\n\n static readonly capabilities: PublisherCapabilities = {\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n async resolveExtensionRoot(): Promise<string> {\n // Claude Code does not have an extension directory yet.\n return \"\";\n }\n\n async isHostAvailable(): Promise<boolean> {\n return false;\n }\n\n async renderInstructions(_ctx: PublishContext): Promise<string> {\n return \"\";\n }\n\n async publish(_ctx: PublishContext): Promise<PublishResult> {\n return {\n hostId: this.hostId,\n extensionRoot: \"\",\n filesWritten: [],\n skipped: [],\n };\n }\n\n async unpublish(): Promise<void> {\n // no-op\n }\n}\n","/**\n * @remnic/core — Hermes Memory Extension Publisher (stub)\n *\n * Placeholder publisher for Hermes. Hermes uses a daemon-based\n * transport and does not currently consume file-based memory\n * extensions, so all methods are no-ops.\n */\n\nimport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nexport class HermesMemoryExtensionPublisher implements MemoryExtensionPublisher {\n readonly hostId = \"hermes\";\n\n static readonly capabilities: PublisherCapabilities = {\n instructionsMd: false,\n skillsFolder: false,\n citationFormat: false,\n readPathTemplate: false,\n };\n\n async resolveExtensionRoot(): Promise<string> {\n // Hermes does not have an extension directory.\n return \"\";\n }\n\n async isHostAvailable(): Promise<boolean> {\n return false;\n }\n\n async renderInstructions(_ctx: PublishContext): Promise<string> {\n return \"\";\n }\n\n async publish(_ctx: PublishContext): Promise<PublishResult> {\n return {\n hostId: this.hostId,\n extensionRoot: \"\",\n filesWritten: [],\n skipped: [],\n };\n }\n\n async unpublish(): Promise<void> {\n // no-op\n }\n}\n","/**\n * @remnic/core — Memory Extension Publisher Registry\n *\n * Generic registry that host adapters populate at startup via\n * `registerPublisher()`. The publisher *classes* live in core so\n * adapters can import them, but the wiring of host-specific\n * implementations into the registry happens in the host adapter\n * layer (e.g. @remnic/cli), not here. This keeps core free of\n * host-specific knowledge (CLAUDE.md gotcha #31).\n *\n * Usage (from a host adapter):\n * import { registerPublisher, CodexMemoryExtensionPublisher } from \"@remnic/core\";\n * registerPublisher(\"codex\", () => new CodexMemoryExtensionPublisher());\n */\n\nexport type {\n MemoryExtensionPublisher,\n PublishContext,\n PublishResult,\n PublisherCapabilities,\n} from \"./types.js\";\n\nexport {\n REMNIC_SEMANTIC_OVERVIEW,\n REMNIC_CITATION_FORMAT,\n REMNIC_MCP_TOOL_INVENTORY,\n REMNIC_RECALL_DECISION_RULES,\n} from \"./shared-instructions.js\";\n\nexport { CodexMemoryExtensionPublisher } from \"./codex-publisher.js\";\nexport { ClaudeCodeMemoryExtensionPublisher } from \"./claude-code-publisher.js\";\nexport { HermesMemoryExtensionPublisher } from \"./hermes-publisher.js\";\n\nimport type { MemoryExtensionPublisher } from \"./types.js\";\n\n/**\n * Factory registry keyed by host ID. Each value is a zero-argument\n * factory that returns a fresh publisher instance.\n *\n * Starts empty — host adapters populate it via registerPublisher().\n */\nexport const PUBLISHERS: Record<string, () => MemoryExtensionPublisher> = {};\n\n/**\n * Register a publisher factory for a given host ID.\n *\n * Host adapters call this at startup to wire their host-specific\n * publisher implementations into the registry. Calling with an\n * existing hostId replaces the previous factory.\n */\nexport function registerPublisher(\n hostId: string,\n factory: () => MemoryExtensionPublisher,\n): void {\n PUBLISHERS[hostId] = factory;\n}\n\n/**\n * Maps connector IDs to publisher host IDs.\n *\n * Most connector IDs match their publisher host ID exactly (e.g.\n * \"claude-code\" -> \"claude-code\", \"hermes\" -> \"hermes\").\n * This map only needs entries for connector IDs that differ from\n * their publisher host ID. Connectors without a publisher (e.g.\n * \"cursor\", \"cline\") are intentionally absent.\n */\nconst CONNECTOR_TO_HOST: Record<string, string> = {\n \"codex-cli\": \"codex\",\n};\n\n/**\n * Resolve a connector ID to its publisher host ID.\n *\n * Returns the explicit mapping if one exists, otherwise returns\n * the connector ID itself (identity mapping covers the common case\n * where connector ID === host ID).\n */\nexport function hostIdForConnector(connectorId: string): string {\n return CONNECTOR_TO_HOST[connectorId] ?? connectorId;\n}\n\n/**\n * Look up a publisher by host ID.\n * Returns undefined for unknown host IDs rather than throwing.\n */\nexport function publisherFor(hostId: string): MemoryExtensionPublisher | undefined {\n const factory = PUBLISHERS[hostId];\n return factory ? factory() : undefined;\n}\n\n/**\n * Look up a publisher by connector ID.\n *\n * Resolves the connector ID to its host ID first (e.g. \"codex-cli\" -> \"codex\"),\n * then looks up the publisher. Returns undefined if no publisher exists for\n * the resolved host ID.\n */\nexport function publisherForConnector(connectorId: string): MemoryExtensionPublisher | undefined {\n return publisherFor(hostIdForConnector(connectorId));\n}\n","// ---------------------------------------------------------------------------\n// Shared importer base (issue #568)\n// ---------------------------------------------------------------------------\n//\n// The importer adapters defined by issue #568 (`@remnic/import-chatgpt`,\n// `@remnic/import-claude`, `@remnic/import-gemini`, `@remnic/import-mem0`) all\n// follow the same three-step shape:\n//\n// 1. parse(input) — turn a raw file / API payload into a\n// source-specific intermediate structure.\n// 2. transform(parsed) — flatten that structure into a uniform list of\n// `ImportedMemory` records with provenance\n// attached (sourceLabel, importedFromPath,\n// importedAt).\n// 3. writeTo(orchestrator) — hand each memory to the orchestrator via\n// `ingestBulkImportBatch`. Dry-run mode stops\n// after transform() and never calls writeTo().\n//\n// This file defines the shared `ImporterAdapter` interface plus a thin\n// `runImporter` helper that handles batching, dry-run plan reporting, and\n// progress callbacks so every adapter does not reimplement the same loop.\n//\n// The existing lower-level `BulkImportSourceAdapter` (weclone-shaped: produces\n// turn-structured transcripts) remains untouched and continues to drive\n// `runBulkImportCliCommand`. Importer adapters are the higher-level surface\n// intended for the four memory sources in #568 — they emit *memories*\n// (intent-level content) rather than raw conversation turns.\n\nimport type { ImportTurn } from \"../bulk-import/types.js\";\n\n/**\n * A single imported memory record with provenance.\n *\n * Every importer MUST attach a truthful `sourceLabel` (e.g. `\"chatgpt\"`,\n * `\"claude\"`, `\"gemini\"`, `\"mem0\"`) and MAY attach the export's origin path or\n * URL in `importedFromPath`. `importedAt` is always set by `runImporter` so\n * adapters do not need to timestamp records themselves.\n */\nexport interface ImportedMemory {\n /**\n * The user-facing memory content. This is the string that will eventually\n * land in the memory store (after extraction / orchestrator processing).\n */\n content: string;\n /**\n * Source-specific identifier for idempotent re-imports. Optional — when\n * present, adapters SHOULD use the source's stable id (e.g. ChatGPT memory\n * uuid); when absent, the orchestrator's own content hashing provides dedup.\n */\n sourceId?: string;\n /**\n * Source-specific timestamp at which the memory was originally created or\n * last updated. ISO 8601. Optional — adapters fall back to `importedAt`\n * when the source does not expose a creation timestamp.\n */\n sourceTimestamp?: string;\n /**\n * Human-readable short label identifying the origin platform\n * (`\"chatgpt\"`, `\"claude\"`, `\"gemini\"`, `\"mem0\"`). Required so the\n * orchestrator can attribute recalled memories correctly.\n */\n sourceLabel: string;\n /**\n * Path to the export file the memory was parsed from, OR the endpoint URL\n * for API-based imports. Optional but strongly recommended so users can\n * trace a memory back to the file they imported.\n */\n importedFromPath?: string;\n /**\n * ISO 8601 timestamp set by `runImporter` immediately before writeTo().\n * Adapters MAY populate this themselves; `runImporter` will fill in the\n * current wall-clock time when absent.\n */\n importedAt?: string;\n /**\n * Adapter-specific raw metadata preserved for debugging / future rehydration\n * (e.g. conversation id, thread id, tags). Must be JSON-serializable;\n * adapters MUST NOT stash functions or circular references here.\n */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options threaded through every importer run. Adapters receive this object\n * from `runImporter`; the top-level CLI populates it from `--dry-run`,\n * `--batch-size`, and `--rate-limit` flags.\n */\nexport interface RunImportOptions {\n /**\n * When true, parse + transform but skip writeTo(). `runImporter` prints a\n * summary of what WOULD have been imported and returns early.\n */\n dryRun?: boolean;\n /**\n * Target batch size for writeTo(). Adapters that chunk memories into\n * smaller orchestrator calls should honor this. Defaults to\n * `DEFAULT_IMPORT_BATCH_SIZE` when undefined. Values outside the valid\n * range throw from `validateImportBatchSize`.\n */\n batchSize?: number;\n /**\n * Optional rate limit for API-backed importers (mem0). Expressed as\n * requests-per-second. Adapters that do not hit an external API may ignore\n * this. When provided, `validateImportRateLimit` enforces the positive-\n * finite contract.\n */\n rateLimit?: number;\n /**\n * Invoked after every chunk of memories is processed. `total` is the full\n * count of memories produced by `transform`; `processed` counts memories\n * already ingested (or skipped in dry-run mode). Adapters SHOULD call this\n * incrementally — `runImporter` itself calls it once per batch.\n */\n onProgress?: (progress: ImportProgress) => void;\n}\n\nexport interface ImportProgress {\n processed: number;\n total: number;\n /** Current phase for human-readable logging. */\n phase: \"parse\" | \"transform\" | \"write\" | \"dry-run\";\n}\n\n/**\n * Interface every importer package implements.\n *\n * The three-method surface intentionally mirrors the issue #568 spec:\n *\n * - `parse(input)`: raw payload → intermediate representation.\n * - `transform(parsed)`: intermediate representation → `ImportedMemory[]`.\n * - `writeTo(target, memories, options)`: actually commit to the\n * orchestrator (or whatever target the host provides).\n *\n * Adapters MAY keep `parse` and `transform` separate (e.g. ChatGPT conversions\n * that need to split saved-memories from conversation summaries) or collapse\n * them into a single operation — what matters is that the combined pipeline\n * produces an array of `ImportedMemory` records with correct provenance.\n *\n * `Parsed` is generic so strongly-typed intermediate shapes survive through\n * the adapter's own code without leaking to callers of `runImporter`.\n */\nexport interface ImporterAdapter<Parsed = unknown> {\n /** Short stable name used by `remnic import --adapter <name>`. */\n name: string;\n /** Human-readable label surfaced in CLI output and source attribution. */\n sourceLabel: string;\n\n /**\n * Parse a raw payload (file contents, API response, etc.) into the adapter\n * intermediate representation. Pure — MUST NOT call the orchestrator.\n */\n parse(input: unknown, options?: ImporterParseOptions): Parsed | Promise<Parsed>;\n\n /**\n * Flatten parsed input into an array of importable memories. Pure — MUST\n * NOT call the orchestrator. Provenance fields (`sourceLabel`,\n * `importedFromPath`) should be populated here.\n */\n transform(parsed: Parsed, options?: ImporterTransformOptions): ImportedMemory[] | Promise<ImportedMemory[]>;\n\n /**\n * Commit the transformed memories to the orchestrator (or equivalent\n * target). `runImporter` only invokes this when dryRun is false.\n *\n * Adapters typically call `orchestrator.ingestBulkImportBatch` once per\n * batch after converting `ImportedMemory` records into `ImportTurn` shapes.\n */\n writeTo(\n target: ImporterWriteTarget,\n memories: ImportedMemory[],\n options: RunImportOptions,\n ): Promise<ImporterWriteResult>;\n}\n\n/**\n * Shape of the \"target\" passed to `writeTo`.\n *\n * This is intentionally narrower than the full `Orchestrator` class: importer\n * adapters only need to push `ImportTurn[]` batches and read the active\n * bulk-import namespace. Narrowing keeps test doubles simple (the slice-1\n * integration test uses a pure in-memory mock) and prevents adapters from\n * reaching into orchestrator internals that would make them harder to move\n * between host environments.\n */\nexport interface ImporterWriteTarget {\n ingestBulkImportBatch(\n turns: ImportTurn[],\n options?: { deadlineMs?: number },\n ): Promise<void>;\n bulkImportWriteNamespace?(): string;\n}\n\nexport interface ImporterWriteResult {\n memoriesIngested: number;\n /** Optional: adapter-surfaced duplicates or skipped entries. */\n skipped?: number;\n}\n\n/** Options forwarded to `parse`. Adapter-specific; additive by design. */\nexport interface ImporterParseOptions {\n strict?: boolean;\n /** Source path passed through for provenance. */\n filePath?: string;\n /**\n * Requests-per-second throttle, forwarded from the top-level\n * `RunImportOptions.rateLimit`. Only meaningful for API-backed importers\n * (mem0) — file-based adapters ignore it. `runImporter` copies this\n * through automatically so CLI users never have to stash it on\n * `parseOptions` themselves. Cursor review on PR #602.\n */\n rateLimit?: number;\n}\n\n/** Options forwarded to `transform`. */\nexport interface ImporterTransformOptions {\n /** When true, adapters that normally skip bulk conversations should include them. */\n includeConversations?: boolean;\n /** Maximum number of memories to emit. Primarily for tests. */\n maxMemories?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_IMPORT_BATCH_SIZE = 25;\nconst MIN_IMPORT_BATCH_SIZE = 1;\nconst MAX_IMPORT_BATCH_SIZE = 500;\n\n/**\n * Coerce and validate the caller-supplied batch size. CLAUDE.md rule 14/51:\n * reject invalid CLI input rather than silently defaulting.\n */\nexport function validateImportBatchSize(value: number | undefined): number {\n if (value === undefined) return DEFAULT_IMPORT_BATCH_SIZE;\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(\n `batchSize must be a finite number, received ${String(value)}`,\n );\n }\n if (!Number.isInteger(value)) {\n throw new Error(`batchSize must be an integer, received ${value}`);\n }\n if (value < MIN_IMPORT_BATCH_SIZE || value > MAX_IMPORT_BATCH_SIZE) {\n throw new Error(\n `batchSize must be between ${MIN_IMPORT_BATCH_SIZE} and ${MAX_IMPORT_BATCH_SIZE}, ` +\n `received ${value}`,\n );\n }\n return value;\n}\n\n/**\n * Validate the `--rate-limit` (requests per second) value used by API\n * importers. Zero / negative / non-finite values throw — the CLI must reject\n * them rather than silently import at unlimited speed.\n */\nexport function validateImportRateLimit(value: number | undefined): number | undefined {\n if (value === undefined) return undefined;\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(\n `rateLimit must be a finite number (requests per second), received ${String(value)}`,\n );\n }\n if (value <= 0) {\n throw new Error(\n `rateLimit must be greater than 0 (requests per second), received ${value}`,\n );\n }\n return value;\n}\n\n// ---------------------------------------------------------------------------\n// Orchestration helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert an `ImportedMemory` into an `ImportTurn` suitable for\n * `orchestrator.ingestBulkImportBatch`. The resulting turn is always\n * `role=\"user\"` because every downstream source (saved memories, personal\n * context, mem0 entries) represents a first-person statement rather than an\n * assistant response.\n *\n * Keeps provenance on the turn via `participantName` so the extraction\n * pipeline's transcript renderer labels the speaker with the source platform.\n */\nexport function importedMemoryToTurn(memory: ImportedMemory): ImportTurn {\n const timestamp =\n typeof memory.sourceTimestamp === \"string\" && memory.sourceTimestamp.length > 0\n ? memory.sourceTimestamp\n : (memory.importedAt ?? new Date().toISOString());\n return {\n role: \"user\",\n content: memory.content,\n timestamp,\n participantName: memory.sourceLabel,\n ...(memory.sourceId !== undefined ? { participantId: memory.sourceId } : {}),\n };\n}\n\nexport interface RunImporterResult {\n adapter: string;\n sourceLabel: string;\n /**\n * Total memories produced by `transform`. In dry-run mode this is the same\n * as the number of memories that WOULD have been written.\n */\n memoriesPlanned: number;\n /**\n * Number of memories actually handed to writeTo(). Always zero in dry-run\n * mode.\n */\n memoriesWritten: number;\n batchesProcessed: number;\n dryRun: boolean;\n /** Mirrors the importedAt stamped on every memory. */\n importedAt: string;\n}\n\n/**\n * Orchestrate `parse → transform → writeTo` for an adapter. This is the\n * default pipeline used by the CLI; adapters are free to implement their own\n * orchestration when they need per-record rate-limiting or streaming parses\n * that would not fit in the \"produce full array before writing\" shape.\n *\n * Mutates the supplied memory records by filling in `importedAt` when the\n * adapter did not set it (CLAUDE.md rule 38: records are written in insertion\n * order to keep the provenance log stable).\n */\nexport async function runImporter<Parsed>(\n adapter: ImporterAdapter<Parsed>,\n input: unknown,\n target: ImporterWriteTarget,\n options: RunImportOptions & {\n parseOptions?: ImporterParseOptions;\n transformOptions?: ImporterTransformOptions;\n } = {},\n): Promise<RunImporterResult> {\n const batchSize = validateImportBatchSize(options.batchSize);\n validateImportRateLimit(options.rateLimit);\n\n const importedAt = new Date().toISOString();\n const dryRun = options.dryRun === true;\n const onProgress = options.onProgress;\n\n // Phase 1 — parse. Forward the validated rateLimit so API-backed adapters\n // (mem0) can throttle their own fetches. Caller-supplied parseOptions\n // takes precedence so tests can still override.\n onProgress?.({ processed: 0, total: 0, phase: \"parse\" });\n const parseOptions: ImporterParseOptions = {\n ...(options.rateLimit !== undefined ? { rateLimit: options.rateLimit } : {}),\n ...(options.parseOptions ?? {}),\n };\n const parsed = await adapter.parse(input, parseOptions);\n\n // Phase 2 — transform\n onProgress?.({ processed: 0, total: 0, phase: \"transform\" });\n const memoriesRaw = await adapter.transform(parsed, options.transformOptions);\n if (!Array.isArray(memoriesRaw)) {\n throw new Error(\n `Importer '${adapter.name}' transform() returned a non-array. ` +\n \"Adapters must produce ImportedMemory[].\",\n );\n }\n const memories = memoriesRaw.map((memory) => {\n const sourceLabel =\n typeof memory.sourceLabel === \"string\" && memory.sourceLabel.length > 0\n ? memory.sourceLabel\n : adapter.sourceLabel;\n return {\n ...memory,\n sourceLabel,\n importedAt: memory.importedAt ?? importedAt,\n };\n });\n\n // Phase 3 — write (or dry-run plan)\n if (dryRun) {\n onProgress?.({\n processed: memories.length,\n total: memories.length,\n phase: \"dry-run\",\n });\n return {\n adapter: adapter.name,\n sourceLabel: adapter.sourceLabel,\n memoriesPlanned: memories.length,\n memoriesWritten: 0,\n batchesProcessed: 0,\n dryRun: true,\n importedAt,\n };\n }\n\n let batchesProcessed = 0;\n let memoriesWritten = 0;\n for (let i = 0; i < memories.length; i += batchSize) {\n const batch = memories.slice(i, i + batchSize);\n const result = await adapter.writeTo(target, batch, options);\n memoriesWritten += result.memoriesIngested;\n batchesProcessed += 1;\n onProgress?.({\n processed: Math.min(i + batch.length, memories.length),\n total: memories.length,\n phase: \"write\",\n });\n }\n\n return {\n adapter: adapter.name,\n sourceLabel: adapter.sourceLabel,\n memoriesPlanned: memories.length,\n memoriesWritten,\n batchesProcessed,\n dryRun: false,\n importedAt,\n };\n}\n\n/**\n * Default `writeTo` implementation for importers that want to hand every\n * memory to `orchestrator.ingestBulkImportBatch` without custom routing.\n * Adapters that need per-record rate-limiting or conversation-thread linking\n * are free to implement their own `writeTo`.\n */\nexport async function defaultWriteMemoriesToOrchestrator(\n target: ImporterWriteTarget,\n memories: ImportedMemory[],\n): Promise<ImporterWriteResult> {\n if (memories.length === 0) {\n return { memoriesIngested: 0 };\n }\n const turns = memories.map(importedMemoryToTurn);\n await target.ingestBulkImportBatch(turns);\n return { memoriesIngested: memories.length };\n}\n","/**\n * Capsule fork semantics — issue #676 PR 4/6.\n *\n * A fork takes an existing capsule archive, imports it into a target memory\n * root under the `fork` conflict-resolution mode (which rebases all records\n * under `forks/<capsule-id>/`), and then writes a lineage breadcrumb at\n * `<targetRoot>/forks/<forkId>/lineage.json` recording the parent capsule's\n * identity. Subsequent forks of the same parent or of a fork produce a\n * queryable chain.\n *\n * The lineage breadcrumb is a pure JSON file — no gzip, no bundle format —\n * so downstream tooling can read it with a single `readFile` + `JSON.parse`\n * without pulling in the transfer pipeline.\n */\n\nimport { lstat, mkdir, readFile, realpath, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { importCapsule, type ImportCapsuleResult } from \"./capsule-import.js\";\nimport { assertIsDirectoryNotSymlink, assertRealpathInsideRoot } from \"./fs-utils.js\";\nimport type { CapsuleParent, ExportManifestV2 } from \"./types.js\";\nimport { CAPSULE_ID_PATTERN } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Options accepted by {@link forkCapsule}.\n *\n * `sourceArchive` — absolute or cwd-relative path to a `.capsule.json.gz`\n * archive produced by `exportCapsule`.\n *\n * `targetRoot` — absolute or cwd-relative path to the memory directory that\n * will receive the forked records. Must be an existing directory.\n *\n * `forkId` — user-chosen id for the fork. Validated against\n * {@link CAPSULE_ID_PATTERN}; must be unique under `<targetRoot>/forks/` (a\n * pre-existing `forks/<forkId>/` directory is rejected before any write).\n *\n * `versioning` — optional page-versioning config forwarded to\n * {@link importCapsule}. Only relevant when the target root already has files\n * that would be overwritten (fork mode is skip-on-exist by design, so this is\n * a no-op unless mode is changed in a future subclass).\n *\n * `now` — optional clock override (ms epoch) forwarded to `importCapsule` for\n * deterministic fork-id rewriting in tests.\n */\nexport interface ForkCapsuleOptions {\n sourceArchive: string;\n targetRoot: string;\n forkId: string;\n now?: number;\n}\n\n/**\n * The lineage breadcrumb written to `forks/<forkId>/lineage.json`.\n *\n * Fields are intentionally flat so the file is human-readable at a glance\n * and trivially diffable by `git diff`.\n *\n * `forkId` — the id supplied to `forkCapsule`.\n * `forkedAt` — ISO-8601 creation timestamp (UTC).\n * `parent` — structured linkage to the source capsule.\n * `importedRecords` — number of records written by the fork import.\n * `skippedRecords` — number of records skipped (target already existed).\n */\nexport interface ForkLineage {\n forkId: string;\n forkedAt: string;\n parent: CapsuleParent;\n importedRecords: number;\n skippedRecords: number;\n}\n\nexport interface ForkCapsuleResult {\n /** Absolute path to the source archive (unchanged, for chaining). */\n archivePath: string;\n /** The V2 manifest decoded from the source archive. */\n manifest: ExportManifestV2;\n /** Result of the underlying `importCapsule` call. */\n importResult: ImportCapsuleResult;\n /** The lineage breadcrumb that was written. */\n lineage: ForkLineage;\n /** Absolute path to the lineage breadcrumb file. */\n lineagePath: string;\n}\n\n// ---------------------------------------------------------------------------\n// Main function\n// ---------------------------------------------------------------------------\n\n/**\n * Fork a capsule archive into a target memory root.\n *\n * Sequence:\n * 1. Validate `forkId` against {@link CAPSULE_ID_PATTERN}.\n * 2. Resolve `targetRoot` and verify it is an existing directory (not a\n * symlink — mirrors {@link importCapsule}'s root validation).\n * 3. Reject if `forks/<forkId>/` already exists in the target root\n * (gotcha #49: deduplicate batch inputs; gotcha #25: don't destroy\n * old state).\n * 4. Import the archive in `\"fork\"` mode via {@link importCapsule}.\n * 5. Write the lineage breadcrumb at `forks/<forkId>/lineage.json`.\n * The breadcrumb dir is created by step 4 (importCapsule writes records\n * under `forks/<sourceId>/`); if `forkId !== sourceId` we may need to\n * create the fork dir ourselves. We always `mkdir -p` defensively.\n *\n * Error semantics:\n * - All validation errors throw before any filesystem write (fail-closed,\n * gotcha #25).\n * - If `importCapsule` throws after writing some records, the lineage\n * breadcrumb is NOT written (partial state is better than a false\n * \"fork complete\" marker — gotcha #12: write rollback data before\n * success markers).\n */\nexport async function forkCapsule(opts: ForkCapsuleOptions): Promise<ForkCapsuleResult> {\n // --- 1. Validate forkId ---\n validateForkId(opts.forkId);\n\n // --- 2. Validate targetRoot ---\n // Use the shared assertIsDirectoryNotSymlink helper from fs-utils.ts so\n // capsule-fork and capsule-import share the same symlink-safe validation\n // logic (Cursor medium #751 round 2: the fork's doc comment claims it\n // \"mirrors importCapsule's root validation\"; now it literally does).\n const rootAbs = path.resolve(opts.targetRoot);\n await assertIsDirectoryNotSymlink(rootAbs, \"forkCapsule\", \"targetRoot\");\n\n // --- 3. Reject duplicate forkId ---\n // Treat ANY existing filesystem entry at `forks/<forkId>` as occupied:\n // directory, regular file, or symlink. A previous version of this check\n // only rejected directories, so a stray file at the path would slip past\n // and `importCapsule` would still write under `forks/<sourceCapsule>/...`.\n // The later `mkdir`/`writeFile` for the breadcrumb would then fail,\n // leaving a partial fork import after reporting an error — violating the\n // documented \"reject before any write\" contract (Codex P2 #751 round 2).\n // `lstat` (not `stat`) is intentional so a symlink at the path is detected\n // even if its target is missing or otherwise unreadable.\n const forkDirAbs = path.join(rootAbs, \"forks\", opts.forkId);\n const forkEntryExists = await pathEntryExists(forkDirAbs);\n if (forkEntryExists) {\n throw new Error(\n `forkCapsule: fork path already exists — forkId \"${opts.forkId}\" is already in use at: ${forkDirAbs}`,\n );\n }\n\n // --- 4. Import in fork mode ---\n const archiveAbs = path.resolve(opts.sourceArchive);\n const importResult = await importCapsule({\n archivePath: archiveAbs,\n root: rootAbs,\n mode: \"fork\",\n now: opts.now,\n });\n\n const manifest = importResult.manifest;\n const sourceCapsule = manifest.capsule;\n\n // --- 5. Build lineage and write breadcrumb ---\n // The breadcrumb lives under the FORK's own directory, not the source\n // capsule's fork subtree. `forkId` may differ from `sourceCapsule.id`\n // (e.g. `forkCapsule({ forkId: \"my-fork\" })` with source `base-caps`\n // imports records under `forks/base-caps/` but the lineage breadcrumb\n // is written to `forks/my-fork/lineage.json` so a subsequent fork of\n // this fork can locate the breadcrumb by its own id).\n const forkedAt = new Date(opts.now ?? Date.now()).toISOString();\n\n const parent: CapsuleParent = {\n capsuleId: sourceCapsule.id,\n version: sourceCapsule.version,\n forkRoot: `forks/${sourceCapsule.id}`,\n };\n\n const lineage: ForkLineage = {\n forkId: opts.forkId,\n forkedAt,\n parent,\n importedRecords: importResult.imported.length,\n skippedRecords: importResult.skipped.length,\n };\n\n // Ensure the fork breadcrumb directory exists (it may not exist if forkId\n // differs from the source capsule's id, or if all records were skipped).\n const lineagePath = path.join(forkDirAbs, \"lineage.json\");\n\n // Guard lineage write against a symlinked `forks/` directory (or any\n // intermediate path component) pointing outside the memory root. If\n // `targetRoot/forks` is a symlink to `/tmp/evil`, a zero-record capsule\n // passes `importCapsule` without triggering per-record path checks and this\n // write would silently escape the root sandbox. We resolve the nearest\n // existing ancestor of `lineagePath` via `realpath` (same technique used by\n // `capsule-import` and `capsule-merge`) and verify the canonical path is\n // still inside `rootReal` before creating any directories or writing.\n //\n // Note: `rootReal` is computed from `rootAbs` here rather than reusing\n // `forkDirAbs` for the lexical check, because on macOS /tmp is a symlink\n // to /private/tmp and `path.relative(rootReal, forkDirAbs)` would produce\n // an absolute path if forkDirAbs was not also resolved. We skip the lexical\n // pre-check and rely solely on `assertRealpathInsideRoot` which handles\n // both the realpath walk and the containment check atomically.\n const rootReal = await realpath(rootAbs);\n // Symlink-safe containment check: walks the nearest existing ancestor and\n // confirms the canonical path is still inside rootReal.\n await assertRealpathInsideRoot(rootReal, lineagePath, `forks/${opts.forkId}/lineage.json`, \"forkCapsule\");\n\n await mkdir(path.dirname(lineagePath), { recursive: true });\n\n // Write breadcrumb AFTER import completes successfully — consistent with\n // gotcha #25 (don't destroy old state before new state is confirmed) and\n // gotcha #54 (write temp before rename / write before marker).\n await writeFile(lineagePath, JSON.stringify(lineage, null, 2) + \"\\n\", \"utf-8\");\n\n return {\n archivePath: archiveAbs,\n manifest,\n importResult,\n lineage,\n lineagePath,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Lineage query helper\n// ---------------------------------------------------------------------------\n\n/**\n * Read the lineage breadcrumb for a given fork in a memory root.\n *\n * Returns `null` when no breadcrumb exists (the directory is not a fork, or\n * was created before PR 4/6). Never throws for a missing file — callers\n * that need to distinguish \"not a fork\" from \"corrupt breadcrumb\" should\n * handle the JSON parse error themselves.\n *\n * `forkId` is validated against {@link CAPSULE_ID_PATTERN} before being\n * joined into the breadcrumb path so a malicious value like\n * `../../../../tmp` cannot escape the `forks/` namespace and resolve to\n * unrelated files outside the configured memory root (Codex P2 #751).\n * Invalid ids return `null` (same shape as \"no breadcrumb\"); callers that\n * need to distinguish \"invalid id\" from \"absent breadcrumb\" should validate\n * upstream.\n */\nexport async function readForkLineage(\n targetRoot: string,\n forkId: string,\n): Promise<ForkLineage | null> {\n // Reject any forkId that does not satisfy the capsule-id pattern. We use\n // the same constraints as `forkCapsule` so a path traversal payload like\n // `../../etc` is rejected before any filesystem access. Returning null\n // (rather than throwing) keeps the contract symmetric with the\n // \"breadcrumb not present\" case for the common get-or-default usage.\n if (\n typeof forkId !== \"string\" ||\n forkId.length === 0 ||\n forkId.length > 64 ||\n !CAPSULE_ID_PATTERN.test(forkId)\n ) {\n return null;\n }\n const rootAbs = path.resolve(targetRoot);\n // Resolve the root via realpath up-front so the symlink-safe containment\n // check below compares against the canonical path. If realpath fails\n // (root does not exist or is unreadable) we fall back to the lexical\n // root; the read will then return null for a missing breadcrumb anyway.\n const rootReal = await realpath(rootAbs).catch(() => rootAbs);\n\n const lineagePath = path.join(rootReal, \"forks\", forkId, \"lineage.json\");\n // Defensive lexical containment check: even though CAPSULE_ID_PATTERN\n // forbids `/` and `..`, an attacker who somehow bypasses the regex would\n // still be caught here. `path.relative` returns a `..`-prefixed string\n // when the target escapes the root.\n const rel = path.relative(rootReal, lineagePath);\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n return null;\n }\n\n // Symlink-safe containment check (Codex P2 #751 round 2).\n // The lexical check above cannot detect a symlinked `forks/<forkId>`\n // directory whose target is outside the memory root. We resolve the\n // nearest existing ancestor of the breadcrumb path via realpath (which\n // follows symlinks), re-append the non-existent suffix, and verify the\n // result still lives under the real root. Mirrors the\n // `assertRealpathInsideRoot` pattern used by capsule-import.ts.\n if (!(await isLineagePathContained(rootReal, lineagePath))) {\n return null;\n }\n const raw = await readFile(lineagePath, \"utf-8\").catch(() => null);\n if (raw === null) return null;\n try {\n const parsed = JSON.parse(raw);\n // Basic shape check — we do not run full zod validation here so that\n // slightly malformed breadcrumbs (e.g. missing new fields added in later\n // PRs) can still be returned rather than silently dropped.\n if (typeof parsed !== \"object\" || parsed === null || typeof parsed.forkId !== \"string\") {\n return null;\n }\n return parsed as ForkLineage;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Private helpers\n// ---------------------------------------------------------------------------\n\nfunction validateForkId(forkId: unknown): void {\n if (typeof forkId !== \"string\") {\n throw new Error(\"forkCapsule: forkId must be a string\");\n }\n if (forkId.length === 0) {\n throw new Error(\"forkCapsule: forkId must not be empty\");\n }\n if (forkId.length > 64) {\n throw new Error(\"forkCapsule: forkId must be 64 characters or fewer\");\n }\n if (!CAPSULE_ID_PATTERN.test(forkId)) {\n throw new Error(\n `forkCapsule: invalid forkId \"${forkId}\". Expected alphanumeric with single dashes (no leading/trailing dashes, no consecutive dashes).`,\n );\n }\n}\n\n/**\n * Return true when ANY filesystem entry exists at {@link absPath} —\n * directory, regular file, symlink (even broken), or other special file.\n * Uses `lstat` so a symlink at the path is detected regardless of whether\n * its target resolves. Used by the duplicate-forkId check so a stray file\n * cannot bypass the \"reject before any write\" guarantee (Codex P2 #751).\n */\nasync function pathEntryExists(absPath: string): Promise<boolean> {\n const st = await lstat(absPath).catch(() => null);\n return st !== null;\n}\n\n/**\n * Symlink-safe containment check for {@link readForkLineage}.\n *\n * Walks from {@link lineagePath} toward {@link rootReal} until it finds a\n * path component that exists on disk, resolves that prefix via `realpath`\n * (which follows symlinks), then re-appends the non-existent suffix and\n * verifies the resulting canonical path is still inside {@link rootReal}.\n *\n * This catches the case where any subdirectory in the path is a symlink\n * pointing outside the intended root. Returns `true` when the path is\n * safely contained, `false` otherwise.\n *\n * Mirrors the `assertRealpathInsideRoot` helper in `capsule-import.ts`.\n * Returning a boolean (rather than throwing) keeps the contract aligned\n * with `readForkLineage`'s \"return null on any safety failure\" pattern.\n */\nasync function isLineagePathContained(\n rootReal: string,\n lineagePath: string,\n): Promise<boolean> {\n let existing = lineagePath;\n const suffix: string[] = [];\n while (existing !== path.dirname(existing)) {\n const st = await lstat(existing).catch(() => null);\n if (st !== null) break;\n suffix.unshift(path.basename(existing));\n existing = path.dirname(existing);\n }\n\n // If we walked all the way to the filesystem root without finding\n // anything that exists, the path is harmless (no symlinks to follow).\n // The subsequent readFile will simply fail and return null.\n const existingReal = await realpath(existing).catch(() => existing);\n const targetReal = suffix.length > 0 ? path.join(existingReal, ...suffix) : existingReal;\n\n const rel = path.relative(rootReal, targetReal);\n if (rel === \"\") return true;\n if (rel === \"..\") return false;\n if (rel.startsWith(`..${path.sep}`)) return false;\n if (path.isAbsolute(rel)) return false;\n return true;\n}\n"],"mappings":"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","names":["j","fsp","path","fsp","path","fsp","path","crypto","fsp","crypto","log","path","fs","path","path","fs","path","fs","path","fs","path","crypto","fs","path","parseFrontmatter","extractBody","crypto","fs","path","crypto","hashContent","path","fs","readFileSafe","parseFrontmatter","extractBody","crypto","fs","path","path","fs","parseFrontmatter","extractBody","readFileSafe","fs","path","crypto","DEFAULT_EXCLUDE","hashContent","fs","path","crypto","path","manifestPath","fs","crypto","walkMd","hashContent","fs","path","path","fs","path","path"]}
|