nexusmem 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +337 -0
- package/dist/cli/index.js +2686 -0
- package/dist/cli/index.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts","../../src/config/workspace.ts","../../src/git/exec.ts","../../src/git/repo.ts","../../src/hooks/install.ts","../../src/shell/paths.ts","../../src/hooks/powershell.ts","../../src/cli/commands/hook.ts","../../src/cli/commands/init.ts","../../src/core/ids.ts","../../src/core/project.ts","../../src/store/store.ts","../../src/store/fts.ts","../../src/store/schema.ts","../../src/mcp/server.ts","../../src/core/text.ts","../../src/retrieval/pack.ts","../../src/retrieval/fuse.ts","../../src/retrieval/rank.ts","../../src/retrieval/query-pipeline.ts","../../src/vector/embed.ts","../../src/cli/commands/sync.ts","../../src/conversation/chunk.ts","../../src/conversation/redact.ts","../../src/collectors/conversation.ts","../../src/collectors/docs.ts","../../src/git/parse.ts","../../src/git/log.ts","../../src/collectors/git-commits.ts","../../src/collectors/shell-history.ts","../../src/conversation/claude-code-reader.ts","../../src/conversation/paths.ts","../../src/docs/read.ts","../../src/shell/detect.ts","../../src/shell/hook-log.ts","../../src/shell/parse-bash.ts","../../src/shell/parse-psreadline.ts","../../src/shell/parse-zsh.ts","../../src/vector/sync.ts","../../src/cli/context.ts","../../src/mcp/tools.ts","../../src/cli/commands/query.ts","../../src/cli/commands/scan-conversation.ts","../../src/cli/format.ts","../../src/cli/commands/scan-docs.ts","../../src/cli/commands/scan-git.ts","../../src/cli/commands/scan-shell.ts","../../src/cli/commands/status.ts"],"sourcesContent":["import { Command } from 'commander';\r\nimport pc from 'picocolors';\r\nimport { ConfigError } from '../config/workspace.js';\r\nimport { GitCrashError, GitSpawnError } from '../git/exec.js';\r\nimport { NotAGitRepositoryError } from '../git/repo.js';\r\nimport { ProfileNotFoundError } from '../hooks/install.js';\r\nimport { runHookInstall, runHookRemove, runHookStatus } from './commands/hook.js';\r\nimport { runInit } from './commands/init.js';\r\nimport { runMcpServer } from '../mcp/server.js';\r\nimport { runQuery } from './commands/query.js';\r\nimport { runScanConversation } from './commands/scan-conversation.js';\r\nimport { runScanDocs } from './commands/scan-docs.js';\r\nimport { runScanGit } from './commands/scan-git.js';\r\nimport { runScanShell } from './commands/scan-shell.js';\r\nimport { runStatus } from './commands/status.js';\r\nimport { runSync } from './commands/sync.js';\r\n\r\n/** Failures the user can act on, reported without a stack trace. */\r\nfunction isExpected(err: unknown): err is Error {\r\n return (\r\n err instanceof NotAGitRepositoryError ||\r\n // \"git isn't installed\" and \"the spawn failed, try again\" are both things\r\n // the user fixes, not stack traces they debug.\r\n err instanceof GitSpawnError ||\r\n // Survived every retry, so git is genuinely unstable on this machine\r\n // (antivirus, a bad install). Actionable, and not our stack to print.\r\n err instanceof GitCrashError ||\r\n err instanceof ConfigError ||\r\n err instanceof ProfileNotFoundError\r\n );\r\n}\r\n\r\n/** Wrap a command so expected failures exit 1 with a clean message. */\r\nfunction guard(run: () => Promise<number>): () => Promise<void> {\r\n return async () => {\r\n try {\r\n process.exitCode = await run();\r\n } catch (err) {\r\n if (isExpected(err)) {\r\n process.stderr.write(`${pc.red('error')} ${err.message}\\n`);\r\n process.exitCode = 1;\r\n return;\r\n }\r\n throw err;\r\n }\r\n };\r\n}\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('nexusmem')\r\n .description('NexusMem — local-first persistent memory for AI coding agents')\r\n .version('0.1.0');\r\n\r\nprogram\r\n .command('init')\r\n .description('Create the .nexusmem workspace and database for this repository')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('--force', 'overwrite an existing config (the database is kept)', false)\r\n .option('--hook', 'also install the opt-in PowerShell hook (cwd + exit code + timestamp)', false)\r\n .option('--enable-conversation', 'opt in to the conversation-transcript source (off by default -- see docs/phase-2-spec.md)', false)\r\n .action((options) =>\r\n guard(() =>\r\n runInit({ cwd: options.cwd, force: options.force, hook: options.hook, enableConversation: options.enableConversation }),\r\n )(),\r\n );\r\n\r\nprogram\r\n .command('sync')\r\n .description('Ingest new history into the local database')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('--full', 'ignore the stored cursor and re-walk all history', false)\r\n .option('--rebuild', 'drop this project\\'s nodes and re-ingest from scratch', false)\r\n .option('--since <date>', 'override the configured git cutoff, e.g. 1.year.ago')\r\n .option('--shell-lines <count>', 'override the configured shell tail-window size', (v) => Number.parseInt(v, 10))\r\n .option('--conversation', 'force the conversation source on for this run, without persisting it to config', false)\r\n .option('--no-embed', 'skip the vector-embedding pass for this run')\r\n .option('-q, --quiet', 'only print the final summary', false)\r\n .action((options) =>\r\n guard(() =>\r\n runSync({\r\n cwd: options.cwd,\r\n full: options.full,\r\n rebuild: options.rebuild,\r\n since: options.since,\r\n shellTailLines: options.shellLines,\r\n conversationOverride: options.conversation ? true : undefined,\r\n noEmbed: !options.embed,\r\n quiet: options.quiet,\r\n }),\r\n )(),\r\n );\r\n\r\nprogram\r\n .command('hook')\r\n .description('Manage the opt-in PowerShell hook that logs cwd + exit code + timestamp')\r\n .addCommand(\r\n new Command('install')\r\n .description('Install (or update) the hook in your PowerShell profile')\r\n .option('--profile <path>', 'override the auto-detected $PROFILE path')\r\n .action((options) => guard(() => runHookInstall({ profile: options.profile }))()),\r\n )\r\n .addCommand(\r\n new Command('remove')\r\n .description('Remove the hook block from your PowerShell profile')\r\n .option('--profile <path>', 'override the auto-detected $PROFILE path')\r\n .action((options) => guard(() => runHookRemove({ profile: options.profile }))()),\r\n )\r\n .addCommand(\r\n new Command('status')\r\n .description('Show whether the hook is installed')\r\n .option('--profile <path>', 'override the auto-detected $PROFILE path')\r\n .action((options) => guard(() => runHookStatus({ profile: options.profile }))()),\r\n );\r\n\r\nprogram\r\n .command('status')\r\n .description('Show what is currently remembered for this repository')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .action((options) => guard(() => runStatus({ cwd: options.cwd }))());\r\n\r\nprogram\r\n .command('query')\r\n .description('Search remembered history and print a token-budgeted context block')\r\n .argument('<text>', 'free-text query')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('-b, --budget <tokens>', 'max tokens in the packed context', (v) => Number.parseInt(v, 10), 2000)\r\n .option('-n, --candidates <count>', 'how many search hits to rank before packing', (v) => Number.parseInt(v, 10), 30)\r\n .option('--half-life <days>', 'days for a node\\'s recency weight to halve', (v) => Number.parseFloat(v))\r\n .option('--no-vector', 'BM25 only -- skip embedding the query and vector search')\r\n .option('--json', 'emit the packed result as JSON on stdout', false)\r\n .action((text: string, options) =>\r\n guard(() =>\r\n runQuery({\r\n cwd: options.cwd,\r\n query: text,\r\n budget: options.budget,\r\n candidates: options.candidates,\r\n halfLifeDays: options.halfLife,\r\n noVector: !options.vector,\r\n json: options.json,\r\n }),\r\n )(),\r\n );\r\n\r\nprogram\r\n .command('scan-git')\r\n .description('Preview the MemoryNodes git history would produce (writes nothing)')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('--since <date>', 'only commits newer than this git date expression, e.g. 90.days.ago')\r\n .option('-n, --limit <count>', 'stop after N commits', (v) => Number.parseInt(v, 10))\r\n .option('--no-merges', 'skip merge commits')\r\n .option('--min-signal <score>', 'drop nodes below this signal', (v) => Number.parseFloat(v), 0)\r\n .option('--json', 'emit MemoryNodes as JSON on stdout', false)\r\n .action((options) =>\r\n guard(() =>\r\n runScanGit({\r\n cwd: options.cwd,\r\n since: options.since,\r\n limit: options.limit,\r\n merges: options.merges,\r\n json: options.json,\r\n minSignal: options.minSignal,\r\n }),\r\n )(),\r\n );\r\n\r\nprogram\r\n .command('scan-shell')\r\n .description('Preview the MemoryNodes shell history would produce (writes nothing)')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('-n, --tail-lines <count>', 'lines kept from each scrape-based source', (v) => Number.parseInt(v, 10), 300)\r\n .option('--min-signal <score>', 'drop nodes below this signal', (v) => Number.parseFloat(v), 0)\r\n .option('--json', 'emit MemoryNodes as JSON on stdout', false)\r\n .action((options) =>\r\n guard(() =>\r\n runScanShell({ cwd: options.cwd, tailLines: options.tailLines, minSignal: options.minSignal, json: options.json }),\r\n )(),\r\n );\r\n\r\nprogram\r\n .command('scan-conversation')\r\n .description('Preview the MemoryNodes the conversation transcript would produce (writes nothing)')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('--min-signal <score>', 'drop nodes below this signal', (v) => Number.parseFloat(v), 0)\r\n .option('--json', 'emit MemoryNodes as JSON on stdout', false)\r\n .action((options) =>\r\n guard(() => runScanConversation({ cwd: options.cwd, minSignal: options.minSignal, json: options.json }))(),\r\n );\r\n\r\nprogram\r\n .command('scan-docs')\r\n .description('Preview the MemoryNodes tracked .md files would produce (writes nothing)')\r\n .option('-C, --cwd <path>', 'repository path', process.cwd())\r\n .option('--min-signal <score>', 'drop nodes below this signal', (v) => Number.parseFloat(v), 0)\r\n .option('--json', 'emit MemoryNodes as JSON on stdout', false)\r\n .action((options) => guard(() => runScanDocs({ cwd: options.cwd, minSignal: options.minSignal, json: options.json }))());\r\n\r\nprogram\r\n .command('mcp')\r\n .description('Start the MCP server (stdio transport) for Claude Desktop, Cursor, Windsurf, etc.')\r\n .action(() => guard(() => runMcpServer().then(() => 0))());\r\n\r\nprogram.parseAsync(process.argv).catch((err: unknown) => {\r\n const message = err instanceof Error ? err.message : String(err);\r\n process.stderr.write(`${pc.red('error')} ${message}\\n`);\r\n process.exitCode = 1;\r\n});\r\n","import { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { z } from 'zod';\n\n/** Everything NexusMem stores lives under this directory in the repo root. */\nexport const WORKSPACE_DIR = '.nexusmem';\n\nexport interface Workspace {\n /** Repository root. */\n root: string;\n /** `<root>/.nexusmem` */\n dir: string;\n dbPath: string;\n configPath: string;\n}\n\nexport function resolveWorkspace(repoRoot: string): Workspace {\n const dir = join(repoRoot, WORKSPACE_DIR);\n return {\n root: repoRoot,\n dir,\n dbPath: join(dir, 'memory.db'),\n configPath: join(dir, 'config.json'),\n };\n}\n\nexport function isInitialized(ws: Workspace): boolean {\n return existsSync(ws.configPath);\n}\n\nexport const ConfigSchema = z.object({\n version: z.literal(1),\n projectId: z.string().min(1),\n sources: z\n .object({\n git: z\n .object({\n enabled: z.boolean().default(true),\n /** Git date expression bounding how far back to ingest; null = all history. */\n since: z.string().nullable().default(null),\n includeMerges: z.boolean().default(true),\n })\n .default({ enabled: true, since: null, includeMerges: true }),\n shell: z\n .object({\n enabled: z.boolean().default(true),\n /** Lines kept from scrape-based (no-hook) history files each sync. */\n tailLines: z.number().int().positive().default(300),\n })\n .default({ enabled: true, tailLines: 300 }),\n /**\n * Opt-in, unlike git/shell: conversation transcripts are the source\n * most likely to contain something sensitive (a pasted credential,\n * confidential discussion), so this must be a deliberate choice, not\n * an automatic default. See docs/phase-2-spec.md.\n */\n conversation: z\n .object({\n enabled: z.boolean().default(false),\n })\n .default({ enabled: false }),\n /** Tracked `.md` files -- README, architecture docs. On by default like git/shell: no secrets risk, just project prose. */\n docs: z\n .object({\n enabled: z.boolean().default(true),\n /** git pathspecs passed to `git ls-files`. */\n include: z.array(z.string()).default(['*.md']),\n })\n .default({ enabled: true, include: ['*.md'] }),\n })\n .default({\n git: { enabled: true, since: null, includeMerges: true },\n shell: { enabled: true, tailLines: 300 },\n conversation: { enabled: false },\n docs: { enabled: true, include: ['*.md'] },\n }),\n limits: z\n .object({\n maxFilesPerNode: z.number().int().positive().default(40),\n maxBodyChars: z.number().int().positive().default(4000),\n })\n .default({ maxFilesPerNode: 40, maxBodyChars: 4000 }),\n});\n\nexport type NexusConfig = z.infer<typeof ConfigSchema>;\n\nexport function defaultConfig(projectId: string): NexusConfig {\n return ConfigSchema.parse({ version: 1, projectId });\n}\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nexport async function readConfig(ws: Workspace): Promise<NexusConfig> {\n let raw: string;\n try {\n raw = await readFile(ws.configPath, 'utf8');\n } catch {\n throw new ConfigError(`Not initialized: ${ws.configPath} not found. Run \\`nexusmem init\\` first.`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new ConfigError(`${ws.configPath} is not valid JSON: ${(err as Error).message}`);\n }\n\n const result = ConfigSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` ${i.path.join('.') || '(root)'}: ${i.message}`).join('\\n');\n throw new ConfigError(`${ws.configPath} is invalid:\\n${issues}`);\n }\n return result.data;\n}\n\nexport async function writeConfig(ws: Workspace, config: NexusConfig): Promise<void> {\n await mkdir(ws.dir, { recursive: true });\n await writeFile(ws.configPath, `${JSON.stringify(config, null, 2)}\\n`, 'utf8');\n}\n\n/**\n * Make the workspace ignore itself.\n *\n * A self-ignoring directory means `init` never has to edit the user's own\n * .gitignore -- one less surprising write into a repo we do not own.\n */\nexport async function writeWorkspaceGitignore(ws: Workspace): Promise<void> {\n await mkdir(ws.dir, { recursive: true });\n await writeFile(join(ws.dir, '.gitignore'), '# Machine-local derived data.\\n*\\n', 'utf8');\n}\n","import { spawn } from 'node:child_process';\r\n\r\nexport class GitError extends Error {\r\n constructor(\r\n message: string,\r\n readonly args: string[],\r\n readonly exitCode: number,\r\n readonly stderr: string,\r\n ) {\r\n super(message);\r\n this.name = 'GitError';\r\n }\r\n}\r\n\r\n/**\r\n * `git` could not be started at all -- distinct from git starting and then\r\n * exiting non-zero (`GitError`).\r\n *\r\n * Worth its own type because the two have nothing in common diagnostically:\r\n * a `GitError` is git's own verdict about the repository, while this means we\r\n * never got git's opinion. Collapsing them is what previously let a failed\r\n * process spawn be reported as \"not a git repository\", pointing the user at\r\n * their repo when the repo was fine.\r\n */\r\nexport class GitSpawnError extends Error {\r\n constructor(\r\n message: string,\r\n readonly args: string[],\r\n /** libuv errno string (`ENOENT`, `EPERM`, ...), or `undefined` if the platform gave none. */\r\n readonly code: string | undefined,\r\n /** Whether retrying the identical command could plausibly succeed. */\r\n readonly transient: boolean,\r\n override readonly cause: unknown,\r\n ) {\r\n super(message);\r\n this.name = 'GitSpawnError';\r\n }\r\n}\r\n\r\n/**\r\n * `git` started, then died without reaching an exit of its own -- a segfault\r\n * or an access violation, not a verdict.\r\n *\r\n * Distinct from `GitError` for the same reason `GitSpawnError` is: a\r\n * `GitError` carries git's opinion about the repository and must be shown to\r\n * the user as such, while this means git never formed one. Reported as a\r\n * `GitError` it reads as \"git rev-parse exited with code 3221225477\", which\r\n * sends the reader looking for a git problem that does not exist.\r\n */\r\nexport class GitCrashError extends Error {\r\n constructor(\r\n message: string,\r\n readonly args: string[],\r\n /** `0xC0000005` on Windows, or the POSIX signal name. */\r\n readonly status: string,\r\n readonly exitCode: number,\r\n readonly signal: NodeJS.Signals | null,\r\n ) {\r\n super(message);\r\n this.name = 'GitCrashError';\r\n }\r\n}\r\n\r\n/**\r\n * A Windows process torn down by an unhandled exception exits with its\r\n * NTSTATUS value, and every failure NTSTATUS sits at or above this bound.\r\n * git's own exit codes are small (1, 2, 128, 129), so the boundary separates\r\n * \"the OS killed git\" from \"git ran and disagreed\" without guesswork.\r\n *\r\n * Observed here as 0xC0000005 (STATUS_ACCESS_VIOLATION) from `git init` and\r\n * `git commit` in test fixtures, at roughly two runs in five on this machine.\r\n */\r\nconst NTSTATUS_FAILURE_BASE = 0xc0000000;\r\n\r\n/**\r\n * Ctrl+C arrives as an NTSTATUS too, and is the one that must not be retried:\r\n * it is the user's instruction to stop, not a fault.\r\n */\r\nconst STATUS_CONTROL_C_EXIT = 0xc000013a;\r\n\r\n/** POSIX counterpart -- the process died on a signal instead of returning a code. */\r\nconst FATAL_SIGNALS = new Set<string>(['SIGSEGV', 'SIGBUS', 'SIGABRT', 'SIGILL', 'SIGFPE']);\r\n\r\n/** A short label for how git died, or `null` if it exited normally (however unhappily). */\r\nfunction crashStatus(code: number, signal: NodeJS.Signals | null): string | null {\r\n if (signal) return FATAL_SIGNALS.has(signal) ? signal : null;\r\n if (code >= NTSTATUS_FAILURE_BASE && code !== STATUS_CONTROL_C_EXIT) {\r\n return `0x${code.toString(16).toUpperCase()}`;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Spawn failures that are worth retrying rather than reporting as a broken\r\n * setup.\r\n *\r\n * Windows is the reason this list exists: under process-creation pressure\r\n * (antivirus scanning a new image, handle exhaustion) `uv_spawn` intermittently\r\n * returns `EPERM` or `EAGAIN` for a binary that is present and runnable, and\r\n * succeeds on an immediate retry. `ENOENT` is deliberately absent -- git being\r\n * missing, or the cwd not existing, does not fix itself.\r\n */\r\nconst TRANSIENT_SPAWN_CODES = new Set(['EAGAIN', 'EPERM', 'EACCES', 'EMFILE', 'ENFILE', 'ENOMEM', 'EBUSY', 'ETXTBSY']);\r\n\r\nfunction toSpawnError(err: unknown, cwd: string, args: string[]): GitSpawnError {\r\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\r\n\r\n if (code === 'ENOENT') {\r\n // Ambiguous by design in libuv: the missing thing is either the binary or\r\n // the cwd, and the error carries nothing that tells them apart.\r\n return new GitSpawnError(\r\n `Could not run git: either git is not on PATH, or the directory does not exist: ${cwd}`,\r\n args,\r\n code,\r\n false,\r\n err,\r\n );\r\n }\r\n\r\n const transient = code !== undefined && TRANSIENT_SPAWN_CODES.has(code);\r\n const suffix = transient ? ' This is usually transient on Windows -- retrying the same command often succeeds.' : '';\r\n\r\n return new GitSpawnError(\r\n `Could not start git (${code ?? 'unknown spawn failure'}) in ${cwd}.${suffix}`,\r\n args,\r\n code,\r\n transient,\r\n err,\r\n );\r\n}\r\n\r\n/**\r\n * Flags applied to every invocation.\r\n *\r\n * - `core.quotePath=false` keeps non-ASCII paths readable instead of `\\303\\251`.\r\n * - `--no-pager` / `core.pager=` stops git from ever trying to spawn `less`.\r\n */\r\nconst BASE_ARGS = ['-c', 'core.quotePath=false', '-c', 'core.pager=', '--no-pager'];\r\n\r\n/**\r\n * Backoff between spawn retries, in milliseconds. Length sets the retry count.\r\n *\r\n * The failures this covers are short-lived contention (an antivirus scanner\r\n * holding a new image, momentary handle exhaustion), so the useful waits are\r\n * tens to low hundreds of milliseconds. A worst-case run adds ~600ms before\r\n * giving up, and only on a path that was going to fail outright before.\r\n */\r\nconst RETRY_DELAYS_MS = [50, 150, 400];\r\n\r\nconst realSleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\r\n\r\nexport interface GitExecOptions {\r\n /** Injectable for deterministic tests; defaults to `child_process.spawn`. */\r\n spawn?: typeof spawn;\r\n /** Injectable for deterministic tests; defaults to a real timer. */\r\n sleep?: (ms: number) => Promise<void>;\r\n}\r\n\r\n/**\r\n * Stream `git <args>` stdout as UTF-8 chunks, retrying the two failures that\r\n * are the environment's fault rather than git's: a transient failure to start\r\n * (`GitSpawnError`), and git being killed mid-run (`GitCrashError`).\r\n *\r\n * A non-zero exit is never retried. That is git's own answer, and running the\r\n * same command again will get the same one.\r\n *\r\n * The two retryable cases differ in where they can strike. A spawn failure is\r\n * necessarily before any output exists; a crash can happen part-way through a\r\n * long `git log`. The `produced` guard is what makes retrying safe in both:\r\n * once a single chunk has reached the consumer, re-running would replay output\r\n * into a parser that has already consumed it, so from that point the error\r\n * propagates instead.\r\n *\r\n * Retrying is also safe at the command level here because every git invocation\r\n * in this codebase reads (`log`, `rev-parse`, `merge-base`, `ls-files`,\r\n * `remote get-url`). Nothing writes, so a half-finished attempt leaves no lock\r\n * file or partial state for the next one to trip over. A future write command\r\n * would need that reasoning revisited.\r\n */\r\nexport async function* gitStream(cwd: string, args: string[], opts: GitExecOptions = {}): AsyncGenerator<string> {\r\n const sleep = opts.sleep ?? realSleep;\r\n\r\n for (let attempt = 0; ; attempt += 1) {\r\n let produced = false;\r\n try {\r\n for await (const chunk of runGitOnce(cwd, args, opts)) {\r\n produced = true;\r\n yield chunk;\r\n }\r\n return;\r\n } catch (err) {\r\n const retryable = (err instanceof GitSpawnError && err.transient) || err instanceof GitCrashError;\r\n if (produced || !retryable || attempt >= RETRY_DELAYS_MS.length) throw err;\r\n await sleep(RETRY_DELAYS_MS[attempt]!);\r\n }\r\n }\r\n}\r\n\r\nasync function* runGitOnce(cwd: string, args: string[], opts: GitExecOptions): AsyncGenerator<string> {\r\n const fullArgs = [...BASE_ARGS, ...args];\r\n const child = (opts.spawn ?? spawn)('git', fullArgs, { cwd, windowsHide: true });\r\n\r\n child.stdout.setEncoding('utf8');\r\n child.stderr.setEncoding('utf8');\r\n\r\n let stderr = '';\r\n child.stderr.on('data', (chunk: string) => {\r\n // Bounded, so a pathological repo cannot blow up memory via stderr.\r\n if (stderr.length < 64 * 1024) stderr += chunk;\r\n });\r\n\r\n const exited = new Promise<{ code: number; signal: NodeJS.Signals | null }>((resolve, reject) => {\r\n child.once('error', (err) => reject(toSpawnError(err, cwd, fullArgs)));\r\n child.once('close', (code, signal) => resolve({ code: code ?? 0, signal: signal ?? null }));\r\n });\r\n // A spawn failure rejects `exited` before the stdout loop below has a\r\n // consumer for it; without this the rejection is unhandled for a tick even\r\n // though we do await it further down.\r\n exited.catch(() => {});\r\n\r\n try {\r\n for await (const chunk of child.stdout) {\r\n yield chunk as string;\r\n }\r\n } finally {\r\n // Consumer broke out early (e.g. hit --limit): don't leave git running.\r\n if (child.exitCode === null) child.kill();\r\n }\r\n\r\n const { code, signal } = await exited;\r\n\r\n const crash = crashStatus(code, signal);\r\n if (crash) {\r\n throw new GitCrashError(\r\n `git ${args.join(' ')} was killed before it could answer (${crash}) in ${cwd}.` +\r\n ' This is an environment fault, not a problem with the repository.',\r\n fullArgs,\r\n crash,\r\n code,\r\n signal,\r\n );\r\n }\r\n\r\n if (code !== 0) {\r\n const trimmed = stderr.trim();\r\n // Lead with git's own first line: now that callers no longer rewrite every\r\n // failure into \"not a git repository\", this message is what the user sees\r\n // for the cases that aren't specifically handled.\r\n const detail = trimmed.split('\\n')[0];\r\n throw new GitError(\r\n `git ${args.join(' ')} exited with code ${code}${detail ? `: ${detail}` : ''}`,\r\n fullArgs,\r\n code,\r\n trimmed,\r\n );\r\n }\r\n}\r\n\r\n/** Buffered variant, for commands with small, bounded output. */\r\nexport async function git(cwd: string, args: string[], opts: GitExecOptions = {}): Promise<string> {\r\n let out = '';\r\n for await (const chunk of gitStream(cwd, args, opts)) out += chunk;\r\n return out;\r\n}\r\n\r\n/** Buffered variant that returns `null` instead of throwing (e.g. no origin remote). */\r\nexport async function gitOrNull(cwd: string, args: string[], opts: GitExecOptions = {}): Promise<string | null> {\r\n try {\r\n return await git(cwd, args, opts);\r\n } catch (err) {\r\n if (err instanceof GitError) return null;\r\n throw err;\r\n }\r\n}\r\n","import { resolve } from 'node:path';\r\nimport { git, gitOrNull, GitError } from './exec.js';\r\n\r\nexport class NotAGitRepositoryError extends Error {\r\n constructor(readonly cwd: string) {\r\n super(`Not a git repository: ${cwd}`);\r\n this.name = 'NotAGitRepositoryError';\r\n }\r\n}\r\n\r\n/**\r\n * git's own wording when the path simply isn't inside a work tree, e.g.\r\n * `fatal: not a git repository (or any of the parent directories): .git`.\r\n *\r\n * Matching on it is what keeps this error meaning exactly one thing. Every\r\n * other non-zero exit from `rev-parse` -- dubious ownership, a corrupt object\r\n * store, an unreadable config -- is a different problem with a different fix,\r\n * and is now surfaced with git's own message instead of being relabelled.\r\n */\r\nconst NOT_A_REPO = /not a git repository/i;\r\n\r\nexport interface RepoInfo {\r\n /** Absolute, platform-native path to the work tree root. */\r\n root: string;\r\n /** `null` on a detached HEAD. */\r\n branch: string | null;\r\n /** `null` in a repo with no commits yet. */\r\n head: string | null;\r\n originUrl: string | null;\r\n}\r\n\r\n/**\r\n * Whether `ancestor` is reachable from `descendant`.\r\n *\r\n * Used to validate a stored sync cursor: after a rebase, amend or branch\r\n * switch the old HEAD may no longer be in history, and `cursor..HEAD` would\r\n * then silently skip commits. Falling back to a full resync is the safe move.\r\n */\r\nexport async function isAncestor(cwd: string, ancestor: string, descendant: string): Promise<boolean> {\r\n try {\r\n await git(cwd, ['merge-base', '--is-ancestor', ancestor, descendant]);\r\n return true;\r\n } catch (err) {\r\n if (err instanceof GitError) return false;\r\n throw err;\r\n }\r\n}\r\n\r\nexport async function readRepoInfo(cwd: string): Promise<RepoInfo> {\r\n let rootRaw: string;\r\n try {\r\n rootRaw = await git(cwd, ['rev-parse', '--show-toplevel']);\r\n } catch (err) {\r\n if (err instanceof GitError && NOT_A_REPO.test(err.stderr)) {\r\n throw new NotAGitRepositoryError(cwd);\r\n }\r\n // Anything else -- a `GitSpawnError` (git missing, or a transient Windows\r\n // spawn failure), or git failing for some reason of its own -- keeps its\r\n // own type and message. Reporting those as \"not a git repository\" sent\r\n // the reader to inspect a repository that was never the problem.\r\n throw err;\r\n }\r\n\r\n // git always prints forward slashes; resolve() gives us a native path back.\r\n const root = resolve(rootRaw.trim());\r\n\r\n const [branchRaw, headRaw, originRaw] = await Promise.all([\r\n gitOrNull(root, ['rev-parse', '--abbrev-ref', 'HEAD']),\r\n gitOrNull(root, ['rev-parse', 'HEAD']),\r\n gitOrNull(root, ['remote', 'get-url', 'origin']),\r\n ]);\r\n\r\n const branch = branchRaw?.trim() ?? null;\r\n\r\n return {\r\n root,\r\n branch: branch && branch !== 'HEAD' ? branch : null,\r\n head: headRaw?.trim() || null,\r\n originUrl: originRaw?.trim() || null,\r\n };\r\n}\r\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { hookLogPath, resolvePowerShellProfilePath } from '../shell/paths.js';\nimport { isHookInstalled, stripHookSnippet, upsertHookSnippet } from './powershell.js';\n\nexport interface HookTarget {\n profilePath: string;\n logPath: string;\n}\n\nexport class ProfileNotFoundError extends Error {\n constructor() {\n super('Could not resolve a PowerShell profile path (tried `powershell -Command $PROFILE`). Pass --profile explicitly.');\n this.name = 'ProfileNotFoundError';\n }\n}\n\nexport async function resolveHookTarget(profileOverride?: string, logPathOverride?: string): Promise<HookTarget> {\n const profilePath = profileOverride ?? (await resolvePowerShellProfilePath());\n if (!profilePath) throw new ProfileNotFoundError();\n return { profilePath, logPath: logPathOverride ?? hookLogPath() };\n}\n\nasync function readProfile(path: string): Promise<string> {\n try {\n return await readFile(path, 'utf8');\n } catch {\n return '';\n }\n}\n\nexport async function installHook(target: HookTarget): Promise<{ changed: boolean; alreadyInstalled: boolean }> {\n const current = await readProfile(target.profilePath);\n const alreadyInstalled = isHookInstalled(current);\n const next = upsertHookSnippet(current, target.logPath);\n\n if (next === current) return { changed: false, alreadyInstalled };\n\n await mkdir(dirname(target.profilePath), { recursive: true });\n await writeFile(target.profilePath, next, 'utf8');\n return { changed: true, alreadyInstalled };\n}\n\nexport async function removeHook(target: HookTarget): Promise<{ changed: boolean }> {\n const current = await readProfile(target.profilePath);\n if (!isHookInstalled(current)) return { changed: false };\n\n await writeFile(target.profilePath, stripHookSnippet(current), 'utf8');\n return { changed: true };\n}\n\nexport async function hookStatus(target: HookTarget): Promise<{ installed: boolean }> {\n const current = await readProfile(target.profilePath);\n return { installed: isHookInstalled(current) };\n}\n","import { execFile } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport function psReadLineHistoryPath(): string {\n const appData = process.env.APPDATA ?? join(homedir(), 'AppData', 'Roaming');\n return join(appData, 'Microsoft', 'Windows', 'PowerShell', 'PSReadLine', 'ConsoleHost_history.txt');\n}\n\nexport function bashHistoryPath(): string {\n return process.env.HISTFILE_BASH ?? join(homedir(), '.bash_history');\n}\n\nexport function zshHistoryPath(): string {\n return process.env.HISTFILE ?? join(homedir(), '.zsh_history');\n}\n\n/**\n * The user-scoped (not repo-scoped) NexusMem directory.\n *\n * The hook log lives here rather than under any one repo's `.nexusmem/`,\n * because a shell session moves between projects -- the log is one growing\n * stream shared across every repo, filtered to each repo's cwd at read time.\n */\nexport function globalWorkspaceDir(): string {\n return join(homedir(), '.nexusmem');\n}\n\nexport function hookLogPath(): string {\n return join(globalWorkspaceDir(), 'shell-history.jsonl');\n}\n\n/**\n * Resolve `$PROFILE` by asking PowerShell itself.\n *\n * The exact path depends on host (Windows PowerShell vs. PowerShell 7) and\n * is not worth hardcoding when the shell will just tell us.\n */\nexport async function resolvePowerShellProfilePath(exe: 'pwsh' | 'powershell' = 'powershell'): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync(exe, ['-NoLogo', '-NoProfile', '-Command', '$PROFILE'], {\n windowsHide: true,\n });\n const path = stdout.trim();\n return path.length > 0 ? path : null;\n } catch {\n return null;\n }\n}\n","/**\n * Generates and manages the block NexusMem inserts into a PowerShell profile\n * to log every command with its real timestamp, cwd and exit code.\n *\n * The block wraps the existing `prompt` function rather than replacing it,\n * so an already-customized prompt (oh-my-posh, posh-git, ...) keeps\n * rendering exactly as before -- logging piggybacks on the fact that\n * `prompt` runs once per command, it does not own the prompt's appearance.\n */\n\nconst MARK_START = '# >>> nexusmem shell hook >>>';\nconst MARK_END = '# <<< nexusmem shell hook <<<';\n\n/**\n * PowerShell single-quoted strings have exactly one escape rule (a literal\n * `'` doubles to `''`) and no backslash processing at all -- unlike a JSON\n * or JS string. `JSON.stringify` would leave a Windows path's backslashes\n * doubled in the resulting PowerShell literal, since JSON escaping and\n * PowerShell escaping are different rules applied to the same character.\n */\nfunction toPowerShellLiteral(s: string): string {\n return `'${s.replace(/'/g, \"''\")}'`;\n}\n\nexport function renderHookSnippet(logPath: string): string {\n return [\n MARK_START,\n 'if (Test-Path Function:\\\\prompt) { $function:__ssd_original_prompt = $function:prompt }',\n '$global:__ssd_last_history_id = -1',\n `$global:__ssd_log_path = ${toPowerShellLiteral(logPath)}`,\n 'function global:prompt {',\n ' $__ssd_h = Get-History -Count 1 -ErrorAction SilentlyContinue',\n ' if ($__ssd_h -and $__ssd_h.Id -ne $global:__ssd_last_history_id) {',\n ' $global:__ssd_last_history_id = $__ssd_h.Id',\n ' try {',\n ' $__ssd_entry = [ordered]@{',\n ' ts = (Get-Date).ToString(\"o\")',\n ' cwd = (Get-Location).Path',\n ' exitCode = $LASTEXITCODE',\n ' durationMs = [int](($__ssd_h.EndExecutionTime - $__ssd_h.StartExecutionTime).TotalMilliseconds)',\n ' command = $__ssd_h.CommandLine',\n ' }',\n ' $__ssd_dir = Split-Path -Parent $global:__ssd_log_path',\n ' if (-not (Test-Path $__ssd_dir)) { New-Item -ItemType Directory -Force -Path $__ssd_dir | Out-Null }',\n ' Add-Content -LiteralPath $global:__ssd_log_path -Value ($__ssd_entry | ConvertTo-Json -Compress) -Encoding utf8',\n ' } catch {}',\n ' }',\n ' if (Test-Path Function:\\\\__ssd_original_prompt) { & $function:__ssd_original_prompt }',\n \" else { \\\"PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) \\\" }\",\n '}',\n MARK_END,\n '',\n ].join('\\n');\n}\n\nexport function isHookInstalled(profileContent: string): boolean {\n return profileContent.includes(MARK_START);\n}\n\nexport function stripHookSnippet(profileContent: string): string {\n const startIdx = profileContent.indexOf(MARK_START);\n const endIdx = profileContent.indexOf(MARK_END);\n if (startIdx === -1 || endIdx === -1) return profileContent;\n\n const afterBlock = profileContent.slice(endIdx + MARK_END.length).replace(/^\\r?\\n/, '');\n return profileContent.slice(0, startIdx) + afterBlock;\n}\n\n/** Idempotent: strips any existing block first, so re-running with a new log path updates cleanly. */\nexport function upsertHookSnippet(profileContent: string, logPath: string): string {\n const stripped = stripHookSnippet(profileContent).replace(/\\s+$/, '');\n const prefix = stripped.length > 0 ? `${stripped}\\n\\n` : '';\n return `${prefix}${renderHookSnippet(logPath)}`;\n}\n","import pc from 'picocolors';\nimport { hookStatus, installHook, removeHook, resolveHookTarget } from '../../hooks/install.js';\n\nexport interface HookOptions {\n profile?: string;\n logPath?: string;\n}\n\nexport async function runHookInstall(opts: HookOptions): Promise<number> {\n const target = await resolveHookTarget(opts.profile, opts.logPath);\n const result = await installHook(target);\n\n process.stdout.write(\n [\n result.changed\n ? `${pc.green(result.alreadyInstalled ? 'updated' : 'installed')} shell hook`\n : `${pc.dim('already up to date')}`,\n ` profile ${target.profilePath}`,\n ` log ${target.logPath}`,\n '',\n `New commands in any PowerShell session using this profile will now log their timestamp, cwd and exit code.`,\n `Open a new PowerShell window (or run \\`. $PROFILE\\`) for it to take effect.`,\n `Run ${pc.bold('nexusmem hook remove')} to undo this.`,\n '',\n ].join('\\n'),\n );\n\n return 0;\n}\n\nexport async function runHookRemove(opts: HookOptions): Promise<number> {\n const target = await resolveHookTarget(opts.profile, opts.logPath);\n const result = await removeHook(target);\n\n process.stdout.write(\n result.changed\n ? `${pc.green('removed')} shell hook from ${target.profilePath}\\n`\n : `${pc.dim('nothing to remove')} — no hook block found in ${target.profilePath}\\n`,\n );\n\n return 0;\n}\n\nexport async function runHookStatus(opts: HookOptions): Promise<number> {\n const target = await resolveHookTarget(opts.profile, opts.logPath);\n const result = await hookStatus(target);\n\n process.stdout.write(\n [\n `${pc.dim('profile')} ${target.profilePath}`,\n `${pc.dim('log ')} ${target.logPath}`,\n `${pc.dim('status ')} ${result.installed ? pc.green('installed') : pc.yellow('not installed')}`,\n '',\n ].join('\\n'),\n );\n\n return 0;\n}\n","import { relative } from 'node:path';\r\nimport pc from 'picocolors';\r\nimport {\r\n defaultConfig,\r\n isInitialized,\r\n readConfig,\r\n resolveWorkspace,\r\n writeConfig,\r\n writeWorkspaceGitignore,\r\n} from '../../config/workspace.js';\r\nimport { makeProjectId } from '../../core/project.js';\r\nimport { readRepoInfo } from '../../git/repo.js';\r\nimport { installHook, ProfileNotFoundError, resolveHookTarget } from '../../hooks/install.js';\r\nimport { MemoryStore } from '../../store/store.js';\r\nimport { LATEST_SCHEMA_VERSION } from '../../store/schema.js';\r\n\r\nexport interface InitOptions {\r\n cwd: string;\r\n force: boolean;\r\n /** Also install the opt-in PowerShell hook that logs cwd + exit code + timestamp. */\r\n hook: boolean;\r\n /** Persist `sources.conversation.enabled = true` in config.json. */\r\n enableConversation: boolean;\r\n /**\r\n * Where the result summary goes. Defaults to real stdout for the CLI.\r\n *\r\n * Callers that are not a terminal pass their own sink. The MCP server is\r\n * the reason this exists rather than a capture wrapper: there, `stdout` is\r\n * the JSON-RPC transport, so borrowing it for human-readable output is not\r\n * a formatting choice but a protocol hazard.\r\n */\r\n out?: (chunk: string) => void;\r\n}\r\n\r\nexport async function runInit(opts: InitOptions): Promise<number> {\r\n const out = opts.out ?? ((chunk: string) => void process.stdout.write(chunk));\r\n const repo = await readRepoInfo(opts.cwd);\r\n const ws = resolveWorkspace(repo.root);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n\r\n const already = isInitialized(ws);\r\n if (already && !opts.force) {\r\n const existing = await readConfig(ws);\r\n process.stderr.write(\r\n `${pc.yellow('already initialized')} ${relative(process.cwd(), ws.configPath) || ws.configPath}\\n` +\r\n ` project ${pc.cyan(existing.projectId)}\\n` +\r\n ` use ${pc.bold('--force')} to reset the config (the database is kept)\\n`,\r\n );\r\n return 0;\r\n }\r\n\r\n await writeWorkspaceGitignore(ws);\r\n const config = defaultConfig(projectId);\r\n if (opts.enableConversation) config.sources.conversation.enabled = true;\r\n await writeConfig(ws, config);\r\n\r\n // Creating the database here means `init` surfaces native-module or\r\n // filesystem problems immediately, rather than halfway through a long sync.\r\n const store = MemoryStore.open(ws.dbPath);\r\n try {\r\n store.upsertProject({ id: projectId, root: repo.root, originUrl: repo.originUrl });\r\n } finally {\r\n store.close();\r\n }\r\n\r\n const lines = [\r\n `${pc.green('initialized')} ${ws.dir}`,\r\n ` project ${pc.cyan(projectId)}`,\r\n ` repo ${repo.root}`,\r\n ` branch ${repo.branch ?? pc.yellow('(detached)')}`,\r\n ` schema v${LATEST_SCHEMA_VERSION}`,\r\n ];\r\n\r\n if (opts.enableConversation) {\r\n lines.push(` ${pc.yellow('conversation source enabled')} -- transcripts will be redacted-but-indexed on sync`);\r\n }\r\n\r\n if (opts.hook) {\r\n try {\r\n const target = await resolveHookTarget();\r\n const result = await installHook(target);\r\n lines.push(\r\n '',\r\n `${pc.green(result.changed ? 'installed' : 'already installed')} shell hook`,\r\n ` profile ${target.profilePath}`,\r\n ` log ${target.logPath}`,\r\n ` open a new PowerShell window (or run \\`. $PROFILE\\`) for it to take effect`,\r\n );\r\n } catch (err) {\r\n if (err instanceof ProfileNotFoundError) {\r\n lines.push('', `${pc.yellow('hook not installed')} ${err.message}`);\r\n } else {\r\n throw err;\r\n }\r\n }\r\n }\r\n\r\n lines.push('', `Next: ${pc.bold('nexusmem sync')}`, '');\r\n out(lines.join('\\n'));\r\n\r\n return 0;\r\n}\r\n","import { createHash } from 'node:crypto';\nimport type { NodeKind } from './types.js';\n\n/** NUL cannot appear in any of our key components, so it is a safe joiner. */\nconst KEY_SEP = '\\u0000';\n\nexport function sha256Hex(input: string): string {\n return createHash('sha256').update(input, 'utf8').digest('hex');\n}\n\n/**\n * Content-addressed node id.\n *\n * `naturalKey` must be whatever uniquely identifies the event at its source\n * (a commit sha, a `timestamp:command` pair, ...). Re-running `sync` then\n * re-derives the exact same id, which makes ingestion idempotent without\n * relying on a cursor being correct.\n */\nexport function makeNodeId(projectId: string, kind: NodeKind, naturalKey: string): string {\n return sha256Hex([projectId, kind, naturalKey].join(KEY_SEP)).slice(0, 24);\n}\n","import { sha256Hex } from './ids.js';\n\n/**\n * Normalise a git remote URL so that the same repo yields the same id whether\n * it was cloned over ssh, https, with or without a `.git` suffix.\n *\n * git@github.com:acme/Repo.git -> github.com/acme/repo\n * https://github.com/acme/repo -> github.com/acme/repo\n */\nexport function normalizeGitUrl(url: string): string {\n let s = url.trim();\n\n // scp-like syntax: [user@]host:path\n const scp = /^(?:[^@/]+@)?([^/:]+):(.+)$/.exec(s);\n if (scp && !s.includes('://')) {\n s = `${scp[1]}/${scp[2]}`;\n } else {\n s = s.replace(/^[a-z+]+:\\/\\//i, '').replace(/^[^@/]+@/, '');\n }\n\n return s\n .replace(/\\/+$/, '')\n .replace(/\\.git$/i, '')\n .replace(/\\/+$/, '')\n .replace(/\\/{2,}/g, '/')\n .toLowerCase();\n}\n\nexport interface ProjectIdentity {\n /** Absolute path to the repo root on this machine. */\n root: string;\n originUrl?: string | null;\n}\n\n/**\n * Stable identity for a project.\n *\n * Prefers the origin URL so that the same repo checked out twice (or on two\n * machines) shares a memory namespace; falls back to the absolute path for\n * repos with no remote.\n */\nexport function makeProjectId({ root, originUrl }: ProjectIdentity): string {\n const basis = originUrl ? `remote:${normalizeGitUrl(originUrl)}` : `path:${root.replace(/\\\\/g, '/').toLowerCase()}`;\n return sha256Hex(basis).slice(0, 16);\n}\n","import Database from 'better-sqlite3';\r\nimport { mkdirSync } from 'node:fs';\r\nimport { dirname } from 'node:path';\r\nimport * as sqliteVec from 'sqlite-vec';\r\nimport type { MemoryNode, NodeKind } from '../core/types.js';\r\nimport { toMatchQuery } from './fts.js';\r\nimport { migrate } from './schema.js';\r\n\r\nexport interface IngestStats {\r\n inserted: number;\r\n updated: number;\r\n unchanged: number;\r\n}\r\n\r\nexport interface ProjectRecord {\r\n id: string;\r\n root: string;\r\n originUrl: string | null;\r\n}\r\n\r\nexport interface StoreStats {\r\n total: number;\r\n byKind: Record<string, number>;\r\n oldest: string | null;\r\n newest: string | null;\r\n distinctFiles: number;\r\n}\r\n\r\nexport interface SearchHit {\r\n id: string;\r\n kind: NodeKind;\r\n ts: string;\r\n title: string;\r\n body: string;\r\n signal: number;\r\n /** bm25 score; lower is a better lexical match. */\r\n rank: number;\r\n}\r\n\r\ninterface NodeRow {\r\n id: string;\r\n kind: NodeKind;\r\n ts: string;\r\n title: string;\r\n body: string;\r\n signal: number;\r\n rank: number;\r\n}\r\n\r\nexport interface VectorHit {\r\n id: string;\r\n kind: NodeKind;\r\n ts: string;\r\n title: string;\r\n body: string;\r\n signal: number;\r\n /** Euclidean distance from the query vector; lower is closer. */\r\n distance: number;\r\n}\r\n\r\nexport interface EmbeddableNode {\r\n rowid: number;\r\n id: string;\r\n title: string;\r\n body: string;\r\n}\r\n\r\nfunction epochOf(ts: string): number {\r\n const parsed = Date.parse(ts);\r\n return Number.isNaN(parsed) ? Date.now() : parsed;\r\n}\r\n\r\nexport class MemoryStore {\r\n private constructor(private readonly db: Database.Database) {}\r\n\r\n static open(dbPath: string): MemoryStore {\r\n mkdirSync(dirname(dbPath), { recursive: true });\r\n const db = new Database(dbPath);\r\n\r\n // WAL lets a long `sync` write while an agent reads via `query`.\r\n db.pragma('journal_mode = WAL');\r\n // NORMAL is the right durability trade for a rebuildable derived index:\r\n // worst case after a crash we re-run sync, which is idempotent anyway.\r\n db.pragma('synchronous = NORMAL');\r\n db.pragma('foreign_keys = ON');\r\n\r\n // Must load before migrate(): the nodes_vec migration's CREATE VIRTUAL\r\n // TABLE ... USING vec0 needs the module registered first.\r\n sqliteVec.load(db);\r\n\r\n migrate(db);\r\n return new MemoryStore(db);\r\n }\r\n\r\n close(): void {\r\n this.db.close();\r\n }\r\n\r\n upsertProject(project: ProjectRecord): void {\r\n this.db\r\n .prepare(\r\n `INSERT INTO projects (id, root, origin_url, created_at)\r\n VALUES (@id, @root, @originUrl, @now)\r\n ON CONFLICT(id) DO UPDATE SET root = excluded.root, origin_url = excluded.origin_url`,\r\n )\r\n .run({ ...project, now: Date.now() });\r\n }\r\n\r\n markSynced(projectId: string): void {\r\n this.db.prepare('UPDATE projects SET last_synced_at = ? WHERE id = ?').run(Date.now(), projectId);\r\n }\r\n\r\n /**\r\n * Write a batch of nodes in one transaction.\r\n *\r\n * Ids are content-addressed, so re-ingesting the same event is a no-op --\r\n * a node is only rewritten when the derived content actually changed (which\r\n * happens when scoring or body composition is improved between releases).\r\n */\r\n upsertNodes(nodes: readonly MemoryNode[]): IngestStats {\r\n const exists = this.db.prepare('SELECT body, signal, title FROM nodes WHERE id = ?');\r\n // vec0 has no triggers to keep itself in sync (see schema.ts) -- when a\r\n // node's indexed text actually changes, its old embedding is stale and\r\n // must be dropped so the embedding pass in vector/embed.ts re-embeds it.\r\n const dropStaleEmbedding = this.db.prepare(\r\n 'DELETE FROM nodes_vec WHERE rowid = (SELECT rowid FROM nodes WHERE id = ?)',\r\n );\r\n const insertNode = this.db.prepare(\r\n `INSERT INTO nodes (id, kind, project_id, ts, ts_epoch, source, title, body, signal, meta, created_at)\r\n VALUES (@id, @kind, @projectId, @ts, @tsEpoch, @source, @title, @body, @signal, @meta, @now)\r\n ON CONFLICT(id) DO UPDATE SET\r\n ts = excluded.ts, ts_epoch = excluded.ts_epoch, source = excluded.source,\r\n title = excluded.title, body = excluded.body, signal = excluded.signal, meta = excluded.meta`,\r\n );\r\n const clearFiles = this.db.prepare('DELETE FROM node_files WHERE node_id = ?');\r\n const insertFile = this.db.prepare(\r\n `INSERT INTO node_files (node_id, path, previous_path, insertions, deletions, is_binary)\r\n VALUES (@nodeId, @path, @previousPath, @insertions, @deletions, @isBinary)\r\n ON CONFLICT(node_id, path) DO UPDATE SET\r\n previous_path = excluded.previous_path, insertions = excluded.insertions,\r\n deletions = excluded.deletions, is_binary = excluded.is_binary`,\r\n );\r\n\r\n const stats: IngestStats = { inserted: 0, updated: 0, unchanged: 0 };\r\n\r\n const run = this.db.transaction((batch: readonly MemoryNode[]) => {\r\n const now = Date.now();\r\n\r\n for (const node of batch) {\r\n const prior = exists.get(node.id) as { body: string; signal: number; title: string } | undefined;\r\n\r\n if (prior) {\r\n if (prior.body === node.body && prior.signal === node.signal && prior.title === node.title) {\r\n stats.unchanged += 1;\r\n continue;\r\n }\r\n stats.updated += 1;\r\n dropStaleEmbedding.run(node.id);\r\n } else {\r\n stats.inserted += 1;\r\n }\r\n\r\n insertNode.run({\r\n id: node.id,\r\n kind: node.kind,\r\n projectId: node.projectId,\r\n ts: node.ts,\r\n tsEpoch: epochOf(node.ts),\r\n source: node.source,\r\n title: node.title,\r\n body: node.body,\r\n signal: node.signal,\r\n meta: JSON.stringify(node.meta),\r\n now,\r\n });\r\n\r\n clearFiles.run(node.id);\r\n for (const file of node.files) {\r\n insertFile.run({\r\n nodeId: node.id,\r\n path: file.path,\r\n previousPath: file.previousPath ?? null,\r\n insertions: file.insertions,\r\n deletions: file.deletions,\r\n isBinary: file.binary ? 1 : 0,\r\n });\r\n }\r\n }\r\n });\r\n\r\n run(nodes);\r\n return stats;\r\n }\r\n\r\n getSyncCursor(projectId: string, source: string): string | null {\r\n const row = this.db\r\n .prepare('SELECT cursor FROM sync_state WHERE project_id = ? AND source = ?')\r\n .get(projectId, source) as { cursor: string | null } | undefined;\r\n return row?.cursor ?? null;\r\n }\r\n\r\n setSyncCursor(projectId: string, source: string, cursor: string | null): void {\r\n this.db\r\n .prepare(\r\n `INSERT INTO sync_state (project_id, source, cursor, last_run_at)\r\n VALUES (?, ?, ?, ?)\r\n ON CONFLICT(project_id, source) DO UPDATE SET cursor = excluded.cursor, last_run_at = excluded.last_run_at`,\r\n )\r\n .run(projectId, source, cursor, Date.now());\r\n }\r\n\r\n /** Every source that has ever synced for this project, most recently run first. */\r\n listSyncState(projectId: string): Array<{ source: string; cursor: string | null; lastRunAt: number | null }> {\r\n return this.db\r\n .prepare('SELECT source, cursor, last_run_at AS lastRunAt FROM sync_state WHERE project_id = ? ORDER BY last_run_at DESC')\r\n .all(projectId) as Array<{ source: string; cursor: string | null; lastRunAt: number | null }>;\r\n }\r\n\r\n /** Drop every node for a project. Used by `sync --rebuild`. */\r\n clearProject(projectId: string): number {\r\n // nodes_vec has no FK/trigger relationship to nodes (see schema.ts) --\r\n // clean it up explicitly, before the rows it points at disappear.\r\n this.db\r\n .prepare('DELETE FROM nodes_vec WHERE rowid IN (SELECT rowid FROM nodes WHERE project_id = ?)')\r\n .run(projectId);\r\n const info = this.db.prepare('DELETE FROM nodes WHERE project_id = ?').run(projectId);\r\n this.db.prepare('DELETE FROM sync_state WHERE project_id = ?').run(projectId);\r\n return info.changes;\r\n }\r\n\r\n /**\r\n * Delete the nodes of one source that its latest full scan did not produce.\r\n *\r\n * Needed by any source whose node ids are derived from content that can be\r\n * *edited in place* rather than only appended to. A `doc_section` id comes\r\n * from `path + heading slug`, so renaming a markdown heading mints a new node\r\n * and strands the old one: `sync` reports `+1 new`, and the corpus then holds\r\n * two contradictory versions of the same section, both of which come back for\r\n * the same query. Git and shell nodes describe events that already happened\r\n * and are never restated, so they have nothing to prune.\r\n *\r\n * Scoping is the whole safety story here, and it is deliberately narrow:\r\n *\r\n * - `project_id` -- never reaches another repository's memory.\r\n * - `source` -- an exact match on the collector's own key, so pruning `docs`\r\n * cannot touch `conversation:claude-code`, `shell:pwsh` or `git` nodes even\r\n * though they share the table.\r\n * - `keepIds` -- everything this scan produced.\r\n * - `keepPaths` -- files the scan could not read. Their nodes are kept\r\n * because an unreadable file is not evidence that its sections are gone.\r\n *\r\n * Callers must pass the ids from a *complete* scan of the source. A partial\r\n * or filtered scan would read as \"these nodes no longer exist\" and delete\r\n * real history.\r\n */\r\n pruneSourceNodes(\r\n projectId: string,\r\n source: string,\r\n keepIds: readonly string[],\r\n opts: { keepPaths?: readonly string[] } = {},\r\n ): number {\r\n // json_each keeps this one statement regardless of how many sections a\r\n // repo has, instead of an id list that grows into SQLite's parameter cap.\r\n const scope = `project_id = @projectId AND source = @source\r\n AND id NOT IN (SELECT value FROM json_each(@keepIds))\r\n AND id NOT IN (SELECT node_id FROM node_files WHERE path IN (SELECT value FROM json_each(@keepPaths)))`;\r\n\r\n const params = {\r\n projectId,\r\n source,\r\n keepIds: JSON.stringify(keepIds),\r\n keepPaths: JSON.stringify(opts.keepPaths ?? []),\r\n };\r\n\r\n return this.db.transaction(() => {\r\n // Same ordering constraint as clearProject: nodes_vec is not reachable by\r\n // FK or trigger, so its rows must go while their rowids still resolve.\r\n // nodes_fts *is* trigger-backed (schema.ts) and cleans itself up on\r\n // DELETE, and node_files cascades.\r\n this.db.prepare(`DELETE FROM nodes_vec WHERE rowid IN (SELECT rowid FROM nodes WHERE ${scope})`).run(params);\r\n return this.db.prepare(`DELETE FROM nodes WHERE ${scope}`).run(params).changes;\r\n })();\r\n }\r\n\r\n /** Nodes for this project that have no embedding yet (new, or invalidated by a content change). */\r\n findNodesNeedingEmbedding(projectId: string, limit = 200): EmbeddableNode[] {\r\n return this.db\r\n .prepare(\r\n `SELECT n.rowid AS rowid, n.id AS id, n.title AS title, n.body AS body\r\n FROM nodes n\r\n LEFT JOIN nodes_vec v ON v.rowid = n.rowid\r\n WHERE n.project_id = ? AND v.rowid IS NULL\r\n LIMIT ?`,\r\n )\r\n .all(projectId, limit) as EmbeddableNode[];\r\n }\r\n\r\n upsertEmbedding(rowid: number, embedding: Float32Array): void {\r\n this.db\r\n .prepare('INSERT OR REPLACE INTO nodes_vec (rowid, embedding) VALUES (?, ?)')\r\n .run(BigInt(rowid), embedding);\r\n }\r\n\r\n /**\r\n * Nearest-neighbour search over the corpus.\r\n *\r\n * `nodes_vec` has no `project_id` column of its own (embeddings are\r\n * generic; project scoping lives on `nodes`), so this over-fetches `k`\r\n * before joining and filtering, then caps to `limit`. Simple and correct;\r\n * not the efficient way to do this at a scale this project isn't at yet.\r\n */\r\n vectorSearch(projectId: string, embedding: Float32Array, limit = 20): VectorHit[] {\r\n const overfetch = Math.max(limit * 8, 50);\r\n return this.db\r\n .prepare(\r\n `SELECT n.id, n.kind, n.ts, n.title, n.body, n.signal, v.distance AS distance\r\n FROM nodes_vec v\r\n JOIN nodes n ON n.rowid = v.rowid\r\n WHERE v.embedding MATCH ? AND k = ? AND n.project_id = ?\r\n ORDER BY v.distance\r\n LIMIT ?`,\r\n )\r\n .all(embedding, overfetch, projectId, limit) as VectorHit[];\r\n }\r\n\r\n stats(projectId: string): StoreStats {\r\n const kinds = this.db\r\n .prepare('SELECT kind, COUNT(*) AS n FROM nodes WHERE project_id = ? GROUP BY kind')\r\n .all(projectId) as Array<{ kind: string; n: number }>;\r\n\r\n const range = this.db\r\n .prepare('SELECT MIN(ts) AS oldest, MAX(ts) AS newest FROM nodes WHERE project_id = ?')\r\n .get(projectId) as { oldest: string | null; newest: string | null };\r\n\r\n const files = this.db\r\n .prepare(\r\n `SELECT COUNT(DISTINCT f.path) AS n\r\n FROM node_files f JOIN nodes n ON n.id = f.node_id\r\n WHERE n.project_id = ?`,\r\n )\r\n .get(projectId) as { n: number };\r\n\r\n return {\r\n total: kinds.reduce((sum, k) => sum + k.n, 0),\r\n byKind: Object.fromEntries(kinds.map((k) => [k.kind, k.n])),\r\n oldest: range.oldest,\r\n newest: range.newest,\r\n distinctFiles: files.n,\r\n };\r\n }\r\n\r\n /**\r\n * Lexical search over the corpus.\r\n *\r\n * Title is weighted 10x body: a commit subject that names the thing you asked\r\n * about is far stronger evidence than the same word buried in a file list.\r\n * Ranking by `relevance x signal` happens a layer up, in retrieval.\r\n */\r\n search(projectId: string, query: string, limit = 20): SearchHit[] {\r\n const match = toMatchQuery(query);\r\n if (!match) return [];\r\n\r\n const rows = this.db\r\n .prepare(\r\n `SELECT n.id, n.kind, n.ts, n.title, n.body, n.signal,\r\n bm25(nodes_fts, 10.0, 1.0) AS rank\r\n FROM nodes_fts\r\n JOIN nodes n ON n.rowid = nodes_fts.rowid\r\n WHERE nodes_fts MATCH ? AND n.project_id = ?\r\n ORDER BY rank\r\n LIMIT ?`,\r\n )\r\n .all(match, projectId, limit) as NodeRow[];\r\n\r\n return rows;\r\n }\r\n\r\n /** Escape hatch for tests and future modules. */\r\n get raw(): Database.Database {\r\n return this.db;\r\n }\r\n}\r\n","/**\n * FTS5 has its own query language (`AND`, `OR`, `NEAR`, `*`, `^`, `:` column\n * filters). User input must never reach it raw -- a stray `\"` or a bare `OR`\n * turns a search into a syntax error.\n */\n\n/** Characters FTS5 treats as syntax rather than text. */\nconst FTS_SYNTAX = /[\"'()*:^{}[\\]-]/g;\n\n/**\n * Turn free-form user text into a safe FTS5 MATCH expression.\n *\n * Each token becomes a quoted prefix term, and tokens are OR-ed so that a\n * multi-word question still finds partially matching nodes -- bm25 ranking\n * then rewards the nodes that matched more of them.\n */\nexport function toMatchQuery(input: string): string | null {\n const tokens = input\n .replace(FTS_SYNTAX, ' ')\n .split(/\\s+/)\n .map((t) => t.trim())\n .filter((t) => t.length > 0);\n\n if (tokens.length === 0) return null;\n\n return tokens.map((t) => `\"${t}\"*`).join(' OR ');\n}\n","import type { Database } from 'better-sqlite3';\n\n/**\n * Schema migrations, applied in order and tracked via `PRAGMA user_version`.\n *\n * Migrations are append-only: never edit a shipped migration, add a new one.\n */\n\nconst V1 = `\nCREATE TABLE meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n\nCREATE TABLE projects (\n id TEXT PRIMARY KEY,\n root TEXT NOT NULL,\n origin_url TEXT,\n created_at INTEGER NOT NULL,\n last_synced_at INTEGER\n);\n\nCREATE TABLE nodes (\n id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n project_id TEXT NOT NULL,\n -- Human-readable ISO-8601 with offset, kept verbatim from the source event.\n ts TEXT NOT NULL,\n -- Same instant as epoch ms, so range scans and ordering never parse strings.\n ts_epoch INTEGER NOT NULL,\n source TEXT NOT NULL,\n title TEXT NOT NULL,\n body TEXT NOT NULL,\n signal REAL NOT NULL,\n meta TEXT NOT NULL DEFAULT '{}',\n created_at INTEGER NOT NULL\n);\n\nCREATE INDEX idx_nodes_project_ts ON nodes (project_id, ts_epoch DESC);\nCREATE INDEX idx_nodes_project_kind ON nodes (project_id, kind, ts_epoch DESC);\n\nCREATE TABLE node_files (\n node_id TEXT NOT NULL REFERENCES nodes (id) ON DELETE CASCADE,\n path TEXT NOT NULL,\n previous_path TEXT,\n insertions INTEGER,\n deletions INTEGER,\n is_binary INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (node_id, path)\n);\n\n-- Path-scoped recall (\"what happened to src/store/db.ts?\") is a first-class\n-- query, so it gets its own index rather than a scan over node_files.\nCREATE INDEX idx_node_files_path ON node_files (path);\n\n-- External-content FTS: the index stores no copy of the text, it points back\n-- at nodes.rowid. Halves the on-disk footprint of the searchable corpus.\nCREATE VIRTUAL TABLE nodes_fts USING fts5 (\n title,\n body,\n content = 'nodes',\n content_rowid = 'rowid',\n tokenize = 'unicode61 remove_diacritics 2'\n);\n\nCREATE TRIGGER nodes_fts_ai AFTER INSERT ON nodes BEGIN\n INSERT INTO nodes_fts (rowid, title, body) VALUES (new.rowid, new.title, new.body);\nEND;\n\nCREATE TRIGGER nodes_fts_ad AFTER DELETE ON nodes BEGIN\n INSERT INTO nodes_fts (nodes_fts, rowid, title, body) VALUES ('delete', old.rowid, old.title, old.body);\nEND;\n\nCREATE TRIGGER nodes_fts_au AFTER UPDATE ON nodes BEGIN\n INSERT INTO nodes_fts (nodes_fts, rowid, title, body) VALUES ('delete', old.rowid, old.title, old.body);\n INSERT INTO nodes_fts (rowid, title, body) VALUES (new.rowid, new.title, new.body);\nEND;\n\nCREATE TABLE sync_state (\n project_id TEXT NOT NULL,\n -- Collector identity, e.g. 'git' or 'shell:pwsh'.\n source TEXT NOT NULL,\n -- Opaque to the store; for git this is the HEAD sha at last successful sync.\n cursor TEXT,\n last_run_at INTEGER,\n PRIMARY KEY (project_id, source)\n);\n`;\n\n/**\n * nomic-embed-text produces 768-dimensional vectors (confirmed against a\n * live Ollama call, not assumed). `vec0` fixes a table's dimension at\n * creation time, so switching embedding models later means a new\n * migration and a re-embed, not an in-place change to this one.\n */\nexport const EMBEDDING_DIM = 768;\n\nconst V2 = `\n-- Unlike nodes_fts, this is NOT trigger-populated: computing an embedding\n-- means an async call to an external model, which a synchronous SQL trigger\n-- cannot make. Rows are written explicitly by the embedding pass in\n-- vector/embed.ts, keyed by the same rowid nodes_fts already uses.\nCREATE VIRTUAL TABLE nodes_vec USING vec0 (\n embedding float[${EMBEDDING_DIM}]\n);\n`;\n\ninterface Migration {\n version: number;\n up: (db: Database) => void;\n}\n\nconst MIGRATIONS: Migration[] = [\n { version: 1, up: (db) => db.exec(V1) },\n { version: 2, up: (db) => db.exec(V2) },\n];\n\nexport const LATEST_SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1]?.version ?? 0;\n\nexport function currentSchemaVersion(db: Database): number {\n return Number(db.pragma('user_version', { simple: true }) ?? 0);\n}\n\nexport function migrate(db: Database): { from: number; to: number } {\n const from = currentSchemaVersion(db);\n\n for (const migration of MIGRATIONS) {\n if (migration.version <= from) continue;\n db.transaction(() => {\n migration.up(db);\n db.pragma(`user_version = ${migration.version}`);\n })();\n }\n\n return { from, to: currentSchemaVersion(db) };\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\r\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\r\nimport { z } from 'zod';\r\nimport { getStatus, searchMemory, syncProject } from './tools.js';\r\n\r\n/**\r\n * Local-only, stdio-transport MCP server exposing NexusMem's memory to any\r\n * MCP-capable client (Claude Desktop, Cursor, Windsurf, ...). No auth layer:\r\n * a client that can spawn this process already has the same filesystem\r\n * access the process itself would use.\r\n */\r\nexport function createServer(): McpServer {\r\n const server = new McpServer({ name: 'nexusmem', version: '0.1.0' });\r\n\r\n server.registerTool(\r\n 'search_memory',\r\n {\r\n title: 'Search remembered project history',\r\n description:\r\n 'Search a NexusMem-tracked repository\\'s remembered history: git commits, shell commands, tracked markdown docs, and (if enabled) conversation transcripts. Returns a token-budgeted, ranked context block -- not raw search results.',\r\n inputSchema: {\r\n projectRoot: z.string().describe('Absolute path to the repository root'),\r\n query: z.string().describe('Free-text question or search terms'),\r\n budget: z.number().int().positive().optional().describe('Max tokens in the returned context block. Default 2000.'),\r\n },\r\n },\r\n async ({ projectRoot, query, budget }) => {\r\n const result = await searchMemory({ projectRoot, query, budget });\r\n // The packed context block goes in BOTH fields: clients differ on which\r\n // one they surface to the model, and a client that prefers\r\n // structuredContent would otherwise see only the match stats -- the\r\n // block itself (the tool's entire value) silently dropped.\r\n return {\r\n content: [{ type: 'text', text: result.text }],\r\n structuredContent: {\r\n text: result.text,\r\n matched: result.matched,\r\n bm25Matched: result.bm25Matched,\r\n vectorMatched: result.vectorMatched,\r\n tokensUsed: result.tokensUsed,\r\n tokensBudget: result.tokensBudget,\r\n } as Record<string, unknown>,\r\n };\r\n },\r\n );\r\n\r\n server.registerTool(\r\n 'sync_project',\r\n {\r\n title: 'Sync remembered history',\r\n description:\r\n 'Ingest new git, shell, docs and (if enabled) conversation history for a NexusMem-tracked repository into its local database.',\r\n inputSchema: {\r\n projectRoot: z.string().describe('Absolute path to the repository root'),\r\n },\r\n },\r\n async ({ projectRoot }) => {\r\n const result = await syncProject({ projectRoot });\r\n return { content: [{ type: 'text', text: result.summary }] };\r\n },\r\n );\r\n\r\n server.registerTool(\r\n 'get_status',\r\n {\r\n title: 'Show what is remembered',\r\n description: 'Report how many nodes NexusMem currently remembers for a repository, broken down by kind and source.',\r\n inputSchema: {\r\n projectRoot: z.string().describe('Absolute path to the repository root'),\r\n },\r\n },\r\n async ({ projectRoot }) => {\r\n const result = await getStatus({ projectRoot });\r\n return {\r\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\r\n structuredContent: result as unknown as Record<string, unknown>,\r\n };\r\n },\r\n );\r\n\r\n return server;\r\n}\r\n\r\nexport async function runMcpServer(): Promise<void> {\r\n const server = createServer();\r\n const transport = new StdioServerTransport();\r\n await server.connect(transport);\r\n}\r\n","export function truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1).trimEnd()}…`;\n}\n\n/** Rough heuristic for English-dominant text: ~4 chars per token. */\nexport function approxTokens(text: string): number {\n return Math.ceil(text.length / 4);\n}\n","import { approxTokens, truncate } from '../core/text.js';\nimport type { RankedHit } from './rank.js';\n\nexport interface PackedNode {\n id: string;\n kind: string;\n ts: string;\n title: string;\n signal: number;\n score: number;\n summary: string;\n tokens: number;\n}\n\nexport interface PackResult {\n nodes: PackedNode[];\n tokensUsed: number;\n tokensBudget: number;\n consideredNodes: number;\n droppedForBudget: number;\n}\n\nconst DEFAULT_SUMMARY_CHARS = 320;\n/** Formatting overhead per node (date prefix, bullet, line breaks) counted as tokens. */\nconst NODE_OVERHEAD_TOKENS = 8;\n\nconst CONVERSATION_ANSWER_MARKER = '\\n\\nA: ';\n\nfunction summarize(hit: RankedHit, maxChars: number): string {\n // Conversation nodes always shape their body as \"Q: <question>\\n\\nA:\n // <answer>\" (collectors/conversation.ts repeats the full original question\n // in every chunk so each node is self-contained). The answer is the part\n // worth showing -- truncating from the start of the body would otherwise\n // spend the whole summary on a long question and never reach it.\n const answerIdx = hit.body.indexOf(CONVERSATION_ANSWER_MARKER);\n if (answerIdx !== -1) {\n const answer = hit.body.slice(answerIdx + CONVERSATION_ANSWER_MARKER.length).trim();\n if (answer) return truncate(answer, maxChars);\n }\n\n // Body already leads with the title; skip straight to whatever follows it\n // so the summary doesn't repeat text that's already shown as the heading.\n const rest = hit.body.startsWith(hit.title) ? hit.body.slice(hit.title.length).trim() : hit.body;\n return truncate(rest || hit.title, maxChars);\n}\n\n/**\n * Greedily fill a token budget with the highest-scoring hits.\n *\n * Nodes are tried strictly in score order. One that doesn't fit is skipped,\n * not a stopping point -- a later, smaller, lower-priority node may still\n * fit the remaining budget. This is best-effort packing, not knapsack-\n * optimal, but it keeps \"why is node X included\" answerable by score alone.\n */\nexport function packContext(\n ranked: readonly RankedHit[],\n tokensBudget: number,\n opts: { summaryChars?: number } = {},\n): PackResult {\n const summaryChars = opts.summaryChars ?? DEFAULT_SUMMARY_CHARS;\n const nodes: PackedNode[] = [];\n let tokensUsed = 0;\n let droppedForBudget = 0;\n\n for (const hit of ranked) {\n const summary = summarize(hit, summaryChars);\n const tokens = approxTokens(hit.title) + approxTokens(summary) + NODE_OVERHEAD_TOKENS;\n\n if (tokensUsed + tokens > tokensBudget) {\n droppedForBudget += 1;\n continue;\n }\n\n nodes.push({\n id: hit.id,\n kind: hit.kind,\n ts: hit.ts,\n title: hit.title,\n signal: hit.signal,\n score: hit.score,\n summary,\n tokens,\n });\n tokensUsed += tokens;\n }\n\n return { nodes, tokensUsed, tokensBudget, consideredNodes: ranked.length, droppedForBudget };\n}\n\n/** Render a packed result as plain text, ready to paste into an agent's context. */\nexport function renderContextBlock(query: string, result: PackResult): string {\n if (result.nodes.length === 0) return `No remembered context matched \"${query}\".`;\n\n const lines = [`Relevant history for: ${query}`, ''];\n for (const node of result.nodes) {\n lines.push(`- ${node.ts.slice(0, 10)} ${node.title}`);\n if (node.summary && node.summary !== node.title) {\n lines.push(` ${node.summary.replace(/\\n+/g, ' ')}`);\n }\n }\n return lines.join('\\n');\n}\n","import type { SearchHit, VectorHit } from '../store/store.js';\n\n/**\n * Reciprocal Rank Fusion: combine several best-first ranked lists into one\n * relevance score per item, using only each item's *position* in each list,\n * never the raw scores.\n *\n * This is what makes fusing BM25 (a cost, lower is better) with vector\n * distance (also lower is better, but on a completely different, unbounded\n * scale) safe without any manual normalization between them -- position is\n * the only thing the two scales agree on.\n */\n\n/** Standard RRF constant. Large enough that rank 1 doesn't overwhelmingly dominate rank 2. */\nconst RRF_K = 60;\n\nexport interface RankedItem {\n id: string;\n}\n\n/**\n * `lists` are each assumed already sorted best-first. An id absent from a\n * list simply contributes nothing from it -- appearing in multiple lists\n * compounds, which is the intended behavior: a node both BM25 *and* vector\n * search agree on should outrank one only one of them found.\n */\nexport function reciprocalRankFusion(lists: readonly (readonly RankedItem[])[]): Map<string, number> {\n const scores = new Map<string, number>();\n\n for (const list of lists) {\n list.forEach((item, index) => {\n const contribution = 1 / (RRF_K + index + 1);\n scores.set(item.id, (scores.get(item.id) ?? 0) + contribution);\n });\n }\n\n return scores;\n}\n\n/**\n * Union two hit sets by id for a combined ranking pass.\n *\n * A vector-only match (found by semantic similarity, sharing no keywords\n * with the query at all) has no real BM25 rank -- it gets a placeholder\n * `rank` of 0, which is never read: once `relevanceScores` (from\n * `reciprocalRankFusion`) is passed to `rankHits`, the BM25-derived\n * relevance path is bypassed entirely for every hit in the set, fused or not.\n */\nexport function mergeSearchAndVectorHits(bm25Hits: readonly SearchHit[], vectorHits: readonly VectorHit[]): SearchHit[] {\n const byId = new Map<string, SearchHit>();\n\n for (const hit of bm25Hits) byId.set(hit.id, hit);\n\n for (const hit of vectorHits) {\n if (byId.has(hit.id)) continue;\n byId.set(hit.id, { id: hit.id, kind: hit.kind, ts: hit.ts, title: hit.title, body: hit.body, signal: hit.signal, rank: 0 });\n }\n\n return [...byId.values()];\n}\n","import type { SearchHit } from '../store/store.js';\r\n\r\nexport interface RankedHit extends SearchHit {\r\n /** 0..1, normalized from bm25 within this result set. 1 = best lexical match. */\r\n relevance: number;\r\n /** 0..1, structural importance rescaled so it can never zero out relevance. */\r\n signalWeight: number;\r\n /** 0..1, decays with age but never below the floor. */\r\n recencyFactor: number;\r\n ageDays: number;\r\n /**\r\n * `relevance * signalWeight**SIGNAL_EXPONENT * recencyFactor**RECENCY_EXPONENT`.\r\n * Sort key, best first. The reported `signalWeight`/`recencyFactor` are the\r\n * raw factors, not the exponentiated ones, so they stay readable as \"how\r\n * important\" and \"how fresh\" independent of how much weight ranking gives them.\r\n */\r\n score: number;\r\n}\r\n\r\nexport interface RankOptions {\r\n /** Days for the recency factor to halve. Default 30. */\r\n halfLifeDays?: number;\r\n /** Injectable for deterministic tests; defaults to the real clock. */\r\n now?: Date;\r\n /**\r\n * Pre-fused relevance (e.g. from `reciprocalRankFusion` over BM25 +\r\n * vector search), keyed by node id, higher-is-better. When provided, this\r\n * replaces the BM25-only `relevance` derivation entirely -- vector search\r\n * changes what counts as relevant, not the rest of the ranking formula.\r\n */\r\n relevanceScores?: ReadonlyMap<string, number>;\r\n}\r\n\r\n/**\r\n * Floors on each factor.\r\n *\r\n * Every factor lives in [floor, 1] rather than [0, 1]. Multiplying three\r\n * [0,1] terms lets any single dimension crush the other two to zero -- an\r\n * old-but-perfect match would lose to a recent-but-mediocre one purely on\r\n * age. Floors keep the combination a *reordering* within each dimension\r\n * instead of an on/off gate.\r\n */\r\nconst RELEVANCE_FLOOR = 0.15;\r\nconst SIGNAL_FLOOR = 0.2;\r\nconst RECENCY_FLOOR = 0.3;\r\nconst DEFAULT_HALF_LIFE_DAYS = 30;\r\nconst MS_PER_DAY = 86_400_000;\r\n\r\n/**\r\n * How far a *prior* may overturn the *query*.\r\n *\r\n * `relevance` is the only factor derived from what was asked; `signal` and\r\n * `recency` are query-independent priors that hold before any query exists.\r\n * Multiplying all three as equals let the priors win outright: signal spans\r\n * 5x (0.2 -> 1) and recency 3.33x (0.3 -> 1), while relevance spans 6.7x, so a\r\n * well-scored recent commit could outrank a document that matched the question\r\n * far better. Observed on a real query: a `fix:` commit (signal .9) took rank 1\r\n * from the top-fused doc section (signal .55) on a 44% signal edge against a\r\n * 15% relevance deficit.\r\n *\r\n * Exponents bound that instead of banning it. Each prior is raised to the power\r\n * that makes its *entire* range worth at most a `MAX_PRIOR_OVERTURN`-fold\r\n * relevance gap -- solving `span^exponent = MAX_PRIOR_OVERTURN`. Priors still\r\n * order equally-relevant hits exactly as before (the transform is monotonic);\r\n * they simply can no longer overturn a large relevance gap.\r\n */\r\nconst MAX_PRIOR_OVERTURN = 2;\r\nconst SIGNAL_EXPONENT = Math.log(MAX_PRIOR_OVERTURN) / Math.log(1 / SIGNAL_FLOOR);\r\nconst RECENCY_EXPONENT = Math.log(MAX_PRIOR_OVERTURN) / Math.log(1 / RECENCY_FLOOR);\r\n\r\n/**\r\n * bm25() in SQLite is a *cost*: smaller (more negative) is a better match.\r\n * Min-max normalize within this result set so the scale is comparable across\r\n * queries, then rescale into [RELEVANCE_FLOOR, 1].\r\n */\r\nfunction normalizeRelevance(hits: readonly SearchHit[]): number[] {\r\n const costs = hits.map((h) => h.rank);\r\n const min = Math.min(...costs);\r\n const max = Math.max(...costs);\r\n\r\n if (min === max) return hits.map(() => 1);\r\n\r\n return costs.map((cost) => {\r\n const normalized = (max - cost) / (max - min); // best cost -> 1, worst -> 0\r\n return RELEVANCE_FLOOR + (1 - RELEVANCE_FLOOR) * normalized;\r\n });\r\n}\r\n\r\n/**\r\n * Same rescale-into-[floor,1] treatment as `normalizeRelevance`, but for an\r\n * externally supplied score where *higher* is better (RRF's convention),\r\n * unlike bm25's cost convention.\r\n */\r\nfunction normalizeExternalRelevance(hits: readonly SearchHit[], scores: ReadonlyMap<string, number>): number[] {\r\n const values = hits.map((h) => scores.get(h.id) ?? 0);\r\n const min = Math.min(...values);\r\n const max = Math.max(...values);\r\n\r\n if (min === max) return hits.map(() => 1);\r\n\r\n return values.map((v) => {\r\n const normalized = (v - min) / (max - min); // best (highest) -> 1\r\n return RELEVANCE_FLOOR + (1 - RELEVANCE_FLOOR) * normalized;\r\n });\r\n}\r\n\r\nfunction ageDaysOf(ts: string, now: Date): number {\r\n const parsed = Date.parse(ts);\r\n if (Number.isNaN(parsed)) return 0;\r\n return Math.max(0, (now.getTime() - parsed) / MS_PER_DAY);\r\n}\r\n\r\n/**\r\n * Combine lexical match quality, structural importance and recency into one\r\n * score, and return hits sorted best-first.\r\n *\r\n * This is the layer the whole \"signal at ingest time\" design pays off in:\r\n * `signalWeight` is what keeps a `fix:` commit ahead of an equally-relevant\r\n * `chore:` one, without a query-time re-analysis of either.\r\n */\r\nexport function rankHits(hits: readonly SearchHit[], opts: RankOptions = {}): RankedHit[] {\r\n if (hits.length === 0) return [];\r\n\r\n const halfLife = opts.halfLifeDays ?? DEFAULT_HALF_LIFE_DAYS;\r\n const now = opts.now ?? new Date();\r\n const relevances = opts.relevanceScores ? normalizeExternalRelevance(hits, opts.relevanceScores) : normalizeRelevance(hits);\r\n\r\n const ranked = hits.map((hit, i) => {\r\n const relevance = relevances[i] ?? RELEVANCE_FLOOR;\r\n const signalWeight = SIGNAL_FLOOR + (1 - SIGNAL_FLOOR) * hit.signal;\r\n const ageDays = ageDaysOf(hit.ts, now);\r\n const recencyFactor = RECENCY_FLOOR + (1 - RECENCY_FLOOR) * 2 ** (-ageDays / halfLife);\r\n\r\n const score = relevance * signalWeight ** SIGNAL_EXPONENT * recencyFactor ** RECENCY_EXPONENT;\r\n\r\n return { ...hit, relevance, signalWeight, ageDays, recencyFactor, score };\r\n });\r\n\r\n return ranked.sort((a, b) => b.score - a.score);\r\n}\r\n","import type { MemoryStore, SearchHit, VectorHit } from '../store/store.js';\nimport type { EmbeddingProvider } from '../vector/embed.js';\nimport { mergeSearchAndVectorHits, reciprocalRankFusion } from './fuse.js';\nimport { packContext, type PackResult } from './pack.js';\nimport { rankHits } from './rank.js';\n\nexport interface HybridQueryOptions {\n budget: number;\n candidates: number;\n halfLifeDays?: number;\n /** `null`/omitted skips vector search entirely -- BM25-only, same behavior as before hybrid retrieval existed. */\n embeddingProvider?: EmbeddingProvider | null;\n}\n\nexport interface HybridQueryResult {\n bm25Count: number;\n vectorCount: number;\n /** The full candidate set that was ranked (bm25 ∪ vector, deduped) -- for callers that need the raw, unpacked bodies (e.g. a \"tokens if sent unpacked\" comparison). */\n hits: SearchHit[];\n packed: PackResult;\n}\n\n/**\n * The one retrieval pipeline both `nexusmem query` and the MCP `search_memory`\n * tool run -- BM25 search, optional vector search, RRF fusion when both\n * fired, then rank and pack. Kept in one place so the CLI and the MCP server\n * can never quietly drift into answering the same query differently.\n */\nexport async function runHybridQuery(\n store: MemoryStore,\n projectId: string,\n query: string,\n opts: HybridQueryOptions,\n): Promise<HybridQueryResult> {\n const bm25Hits = store.search(projectId, query, opts.candidates);\n\n let vectorHits: VectorHit[] = [];\n if (opts.embeddingProvider) {\n const queryVector = await opts.embeddingProvider.embed(query);\n if (queryVector) vectorHits = store.vectorSearch(projectId, queryVector, opts.candidates);\n }\n\n const hits = vectorHits.length > 0 ? mergeSearchAndVectorHits(bm25Hits, vectorHits) : bm25Hits;\n const relevanceScores = vectorHits.length > 0 ? reciprocalRankFusion([bm25Hits, vectorHits]) : undefined;\n\n const ranked = rankHits(hits, { halfLifeDays: opts.halfLifeDays, relevanceScores });\n const packed = packContext(ranked, opts.budget);\n\n return { bm25Count: bm25Hits.length, vectorCount: vectorHits.length, hits, packed };\n}\n","/**\n * Embedding provider abstraction.\n *\n * The real implementation calls a local Ollama server; tests and any\n * environment without Ollama running use a fake. `embed` returns `null`\n * (never throws) on any failure -- connection refused, model not pulled,\n * malformed response -- so a missing embedding provider degrades the whole\n * system to BM25-only search, not a broken `sync`.\n */\nexport interface EmbeddingProvider {\n readonly dimension: number;\n embed(text: string): Promise<Float32Array | null>;\n}\n\nexport interface OllamaEmbeddingProviderOptions {\n baseUrl?: string;\n model?: string;\n dimension?: number;\n /** Milliseconds before giving up on a single embed call. Default 10s. */\n timeoutMs?: number;\n}\n\nconst DEFAULT_BASE_URL = 'http://127.0.0.1:11434';\nconst DEFAULT_MODEL = 'nomic-embed-text';\n/** Confirmed against a live Ollama call -- see store/schema.ts's EMBEDDING_DIM. */\nconst DEFAULT_DIMENSION = 768;\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nexport class OllamaEmbeddingProvider implements EmbeddingProvider {\n readonly dimension: number;\n private readonly baseUrl: string;\n private readonly model: string;\n private readonly timeoutMs: number;\n\n constructor(opts: OllamaEmbeddingProviderOptions = {}) {\n this.baseUrl = opts.baseUrl ?? DEFAULT_BASE_URL;\n this.model = opts.model ?? DEFAULT_MODEL;\n this.dimension = opts.dimension ?? DEFAULT_DIMENSION;\n this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n async embed(text: string): Promise<Float32Array | null> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const res = await fetch(`${this.baseUrl}/api/embeddings`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ model: this.model, prompt: text }),\n signal: controller.signal,\n });\n\n if (!res.ok) return null;\n\n const data = (await res.json()) as { embedding?: unknown };\n if (!Array.isArray(data.embedding) || data.embedding.length !== this.dimension) return null;\n\n return new Float32Array(data.embedding as number[]);\n } catch {\n return null; // Ollama not running, model not pulled, network hiccup -- all degrade the same way\n } finally {\n clearTimeout(timeout);\n }\n }\n}\n\n/** Deterministic, network-free provider for tests. */\nexport class FakeEmbeddingProvider implements EmbeddingProvider {\n constructor(readonly dimension = 8) {}\n\n async embed(text: string): Promise<Float32Array | null> {\n // A cheap hash-based vector: stable per input, distinct enough across\n // different inputs to exercise real KNN ordering in tests.\n const v = new Float32Array(this.dimension);\n for (let i = 0; i < text.length; i += 1) {\n const idx = i % this.dimension;\n v[idx] = (v[idx] ?? 0) + text.charCodeAt(i);\n }\n return v;\n }\n}\n","import pc from 'picocolors';\r\nimport { collectConversationTurns } from '../../collectors/conversation.js';\r\nimport { collectDocFiles } from '../../collectors/docs.js';\r\nimport { collectGitCommits } from '../../collectors/git-commits.js';\r\nimport { collectShellHistory } from '../../collectors/shell-history.js';\r\nimport { collectClaudeCodeTranscripts } from '../../conversation/claude-code-reader.js';\r\nimport type { MemoryNode } from '../../core/types.js';\r\nimport { readDocFiles } from '../../docs/read.js';\r\nimport { isAncestor } from '../../git/repo.js';\r\nimport { collectAvailableShellHistory } from '../../shell/detect.js';\r\nimport { MemoryStore, type IngestStats } from '../../store/store.js';\r\nimport { OllamaEmbeddingProvider } from '../../vector/embed.js';\r\nimport { embedPendingNodes } from '../../vector/sync.js';\r\nimport { loadContext } from '../context.js';\r\n\r\nexport interface SyncOptions {\r\n cwd: string;\r\n /** Ignore the stored cursor and re-walk all history (still deduplicated). */\r\n full: boolean;\r\n /** Delete this project's nodes first, then re-ingest from scratch. */\r\n rebuild: boolean;\r\n /** Overrides `sources.git.since` from config for this run. */\r\n since?: string;\r\n /** Overrides `sources.shell.tailLines` from config for this run. */\r\n shellTailLines?: number;\r\n /** Forces the (opt-in) conversation source on for this run without persisting it to config. */\r\n conversationOverride?: boolean;\r\n /** Skip the embedding pass entirely -- useful when Ollama isn't running and you don't want to wait out its timeout. */\r\n noEmbed?: boolean;\r\n quiet: boolean;\r\n /**\r\n * Where the final summary goes. Defaults to real stdout for the CLI.\r\n *\r\n * Progress lines keep going to stderr regardless (see `log` below); this is\r\n * only the result. The MCP server passes its own sink because there `stdout`\r\n * carries the JSON-RPC transport -- see `InitOptions.out`.\r\n */\r\n out?: (chunk: string) => void;\r\n}\r\n\r\n/**\r\n * Rows per transaction.\r\n *\r\n * Large enough that per-transaction overhead disappears, small enough that a\r\n * huge repository does not hold every node in memory before the first write.\r\n */\r\nconst BATCH_SIZE = 500;\r\n\r\nconst GIT_SOURCE = 'git';\r\n\r\nfunction addStats(into: IngestStats, from: IngestStats): void {\r\n into.inserted += from.inserted;\r\n into.updated += from.updated;\r\n into.unchanged += from.unchanged;\r\n}\r\n\r\nasync function syncGit(\r\n store: MemoryStore,\r\n projectId: string,\r\n opts: SyncOptions,\r\n repo: Awaited<ReturnType<typeof loadContext>>['repo'],\r\n config: Awaited<ReturnType<typeof loadContext>>['config'],\r\n log: (line: string) => void,\r\n): Promise<{ totals: IngestStats; seen: number }> {\r\n const totals: IngestStats = { inserted: 0, updated: 0, unchanged: 0 };\r\n\r\n if (!repo.head) {\r\n log(`${pc.yellow('git')} skipped -- repository has no commits yet`);\r\n return { totals, seen: 0 };\r\n }\r\n if (!config.sources.git.enabled) {\r\n log(`${pc.dim('git')} disabled in config`);\r\n return { totals, seen: 0 };\r\n }\r\n\r\n let cursor = opts.full || opts.rebuild ? null : store.getSyncCursor(projectId, GIT_SOURCE);\r\n\r\n if (cursor && !(await isAncestor(repo.root, cursor, repo.head))) {\r\n log(`${pc.yellow('git cursor stale')} ${cursor.slice(0, 7)} is not an ancestor of HEAD — falling back to a full walk`);\r\n cursor = null;\r\n }\r\n\r\n if (cursor === repo.head) {\r\n log(`${pc.green('git up to date')} at ${repo.head.slice(0, 7)}`);\r\n store.setSyncCursor(projectId, GIT_SOURCE, repo.head);\r\n return { totals, seen: 0 };\r\n }\r\n\r\n log(\r\n `${pc.dim('git syncing')} ${repo.branch ?? 'HEAD'} ${cursor ? `${cursor.slice(0, 7)}..${repo.head.slice(0, 7)}` : '(full history)'}`,\r\n );\r\n\r\n let batch: MemoryNode[] = [];\r\n let seen = 0;\r\n\r\n const flush = () => {\r\n if (batch.length === 0) return;\r\n addStats(totals, store.upsertNodes(batch));\r\n batch = [];\r\n log(` ${pc.dim(`${seen} commits read, ${totals.inserted} new`)}`);\r\n };\r\n\r\n const nodes = collectGitCommits(repo.root, projectId, {\r\n afterCommit: cursor,\r\n since: opts.since ?? config.sources.git.since,\r\n includeMerges: config.sources.git.includeMerges,\r\n maxFilesPerNode: config.limits.maxFilesPerNode,\r\n maxBodyChars: config.limits.maxBodyChars,\r\n });\r\n\r\n for await (const node of nodes) {\r\n batch.push(node);\r\n seen += 1;\r\n if (batch.length >= BATCH_SIZE) flush();\r\n }\r\n flush();\r\n\r\n // Only advance the cursor once the walk completed without throwing -- a\r\n // crash mid-sync leaves the old cursor, and the next run redoes the range\r\n // (harmlessly, because ingestion is idempotent).\r\n store.setSyncCursor(projectId, GIT_SOURCE, repo.head);\r\n return { totals, seen };\r\n}\r\n\r\nasync function syncShell(\r\n store: MemoryStore,\r\n projectId: string,\r\n opts: SyncOptions,\r\n repoRoot: string,\r\n config: Awaited<ReturnType<typeof loadContext>>['config'],\r\n log: (line: string) => void,\r\n): Promise<{ totals: IngestStats; seen: number }> {\r\n const totals: IngestStats = { inserted: 0, updated: 0, unchanged: 0 };\r\n\r\n if (!config.sources.shell.enabled) {\r\n log(`${pc.dim('shell')} disabled in config`);\r\n return { totals, seen: 0 };\r\n }\r\n\r\n const results = await collectAvailableShellHistory({\r\n tailLines: opts.shellTailLines ?? config.sources.shell.tailLines,\r\n repoRoot,\r\n hookCursor: store.getSyncCursor(projectId, 'shell:pwsh-hook'),\r\n });\r\n\r\n if (results.length === 0) {\r\n log(`${pc.dim('shell')} no history source found on this machine`);\r\n return { totals, seen: 0 };\r\n }\r\n\r\n let seen = 0;\r\n for (const result of results) {\r\n const sourceKey = `shell:${result.name}`;\r\n const nodes = collectShellHistory(result.entries, projectId, { maxBodyChars: config.limits.maxBodyChars });\r\n seen += nodes.length;\r\n\r\n if (nodes.length > 0) {\r\n addStats(totals, store.upsertNodes(nodes));\r\n }\r\n\r\n // Hook source is a real append-only log: advance a walk-forward cursor.\r\n // Scrape sources re-read their tail window every run (bounded, cheap,\r\n // and self-deduplicating via content-addressed ids) so their \"cursor\" is\r\n // informational only, for `status` to show a last-synced marker.\r\n store.setSyncCursor(projectId, sourceKey, result.cursorAfter ?? `scanned:${result.entries.length}`);\r\n log(` ${pc.dim(`${sourceKey}: ${nodes.length} entr${nodes.length === 1 ? 'y' : 'ies'} read`)}`);\r\n }\r\n\r\n return { totals, seen };\r\n}\r\n\r\nconst CONVERSATION_SOURCE = 'conversation:claude-code';\r\n\r\nasync function syncConversation(\r\n store: MemoryStore,\r\n projectId: string,\r\n repoRoot: string,\r\n config: Awaited<ReturnType<typeof loadContext>>['config'],\r\n log: (line: string) => void,\r\n forceEnabled: boolean | undefined,\r\n): Promise<{ totals: IngestStats; seen: number }> {\r\n const totals: IngestStats = { inserted: 0, updated: 0, unchanged: 0 };\r\n const enabled = forceEnabled ?? config.sources.conversation.enabled;\r\n\r\n if (!enabled) {\r\n // Opt-in and silent by default -- this source is off for almost every\r\n // sync, and it would be noise to announce that on every single run.\r\n return { totals, seen: 0 };\r\n }\r\n\r\n const turns = await collectClaudeCodeTranscripts(repoRoot);\r\n if (turns.length === 0) {\r\n log(`${pc.dim('conversation')} no transcripts found`);\r\n return { totals, seen: 0 };\r\n }\r\n\r\n const nodes = collectConversationTurns(turns, projectId, { maxBodyChars: config.limits.maxBodyChars });\r\n if (nodes.length > 0) addStats(totals, store.upsertNodes(nodes));\r\n\r\n // Re-read in full each sync (see claude-code-reader.ts) -- the cursor here\r\n // is informational only, matching the shell scrape sources.\r\n store.setSyncCursor(projectId, CONVERSATION_SOURCE, `scanned:${nodes.length}`);\r\n log(` ${pc.dim(`${CONVERSATION_SOURCE}: ${nodes.length} of ${turns.length} exchange(s) kept`)}`);\r\n\r\n return { totals, seen: nodes.length };\r\n}\r\n\r\nconst DOCS_SOURCE = 'docs';\r\n\r\nasync function syncDocs(\r\n store: MemoryStore,\r\n projectId: string,\r\n repoRoot: string,\r\n config: Awaited<ReturnType<typeof loadContext>>['config'],\r\n log: (line: string) => void,\r\n): Promise<{ totals: IngestStats; seen: number }> {\r\n const totals: IngestStats = { inserted: 0, updated: 0, unchanged: 0 };\r\n\r\n if (!config.sources.docs.enabled) {\r\n log(`${pc.dim('docs')} disabled in config`);\r\n return { totals, seen: 0 };\r\n }\r\n\r\n const { files, unreadable } = await readDocFiles(repoRoot, { include: config.sources.docs.include });\r\n\r\n const nodes = collectDocFiles(files, projectId, { maxBodyChars: config.limits.maxBodyChars });\r\n if (nodes.length > 0) addStats(totals, store.upsertNodes(nodes));\r\n\r\n // Prune *after* the upsert, so a renamed heading's replacement is already in\r\n // place and only the stranded original is left to remove.\r\n //\r\n // This scan is always a complete one -- every tracked .md file, re-read in\r\n // full -- which is what makes the delete safe: anything of this source not in\r\n // `nodes` genuinely no longer exists in the repository. An empty scan is a\r\n // legitimate outcome (every .md file deleted) and prunes accordingly; files\r\n // that could not be read are excluded rather than treated as gone.\r\n const pruned = store.pruneSourceNodes(\r\n projectId,\r\n DOCS_SOURCE,\r\n nodes.map((node) => node.id),\r\n { keepPaths: unreadable },\r\n );\r\n\r\n // Re-read in full each sync, the same trade the conversation source makes:\r\n // content-addressed ids make it idempotent, and a doc file has no cheap\r\n // append-only cursor to walk incrementally.\r\n store.setSyncCursor(projectId, DOCS_SOURCE, `scanned:${nodes.length}`);\r\n\r\n if (files.length === 0 && unreadable.length === 0) {\r\n log(`${pc.dim('docs')} no tracked .md files found`);\r\n } else {\r\n const prunedPart = pruned > 0 ? `, ${pc.yellow(`${pruned} stale removed`)}` : '';\r\n const skippedPart = unreadable.length > 0 ? `, ${unreadable.length} unreadable (kept)` : '';\r\n log(` ${pc.dim(`${DOCS_SOURCE}: ${nodes.length} section(s) from ${files.length} file(s)`)}${prunedPart}${pc.dim(skippedPart)}`);\r\n }\r\n\r\n return { totals, seen: nodes.length };\r\n}\r\n\r\nexport async function runSync(opts: SyncOptions): Promise<number> {\r\n const { repo, ws, projectId, config } = await loadContext(opts.cwd);\r\n const log = (line: string) => {\r\n if (!opts.quiet) process.stderr.write(`${line}\\n`);\r\n };\r\n const out = opts.out ?? ((chunk: string) => void process.stdout.write(chunk));\r\n\r\n const store = MemoryStore.open(ws.dbPath);\r\n const started = Date.now();\r\n\r\n try {\r\n store.upsertProject({ id: projectId, root: repo.root, originUrl: repo.originUrl });\r\n\r\n if (opts.rebuild) {\r\n const removed = store.clearProject(projectId);\r\n log(`${pc.dim('rebuild')} dropped ${removed} existing node(s)`);\r\n }\r\n\r\n const git = await syncGit(store, projectId, opts, repo, config, log);\r\n const shell = await syncShell(store, projectId, opts, repo.root, config, log);\r\n const conversation = await syncConversation(store, projectId, repo.root, config, log, opts.conversationOverride);\r\n const docs = await syncDocs(store, projectId, repo.root, config, log);\r\n\r\n let embedLine = '';\r\n if (!opts.noEmbed) {\r\n const result = await embedPendingNodes(store, new OllamaEmbeddingProvider(), projectId);\r\n if (result.embedded > 0) {\r\n embedLine = ` ${pc.dim(`vector: ${result.embedded} node(s) embedded`)}${result.skipped > 0 ? pc.dim(`, ${result.skipped} skipped`) : ''}\\n`;\r\n } else if (result.providerUnavailable) {\r\n log(`${pc.dim('vector')} embedding provider unavailable (is Ollama running with nomic-embed-text pulled?) -- BM25-only for now`);\r\n }\r\n }\r\n\r\n store.markSynced(projectId);\r\n\r\n const totals: IngestStats = { inserted: 0, updated: 0, unchanged: 0 };\r\n addStats(totals, git.totals);\r\n addStats(totals, shell.totals);\r\n addStats(totals, conversation.totals);\r\n addStats(totals, docs.totals);\r\n\r\n const stats = store.stats(projectId);\r\n const elapsed = ((Date.now() - started) / 1000).toFixed(2);\r\n\r\n const conversationEnabled = opts.conversationOverride ?? config.sources.conversation.enabled;\r\n const conversationPart = conversationEnabled ? `, ${conversation.seen} conversation exchange(s)` : '';\r\n const docsPart = config.sources.docs.enabled ? `, ${docs.seen} doc section(s)` : '';\r\n\r\n out(\r\n [\r\n `${pc.green('synced')} ${git.seen} commit(s), ${shell.seen} shell entr${shell.seen === 1 ? 'y' : 'ies'}${conversationPart}${docsPart} in ${elapsed}s`,\r\n ` ${pc.green(`+${totals.inserted} new`)} ${pc.yellow(`~${totals.updated} updated`)} ${pc.dim(`=${totals.unchanged} unchanged`)}`,\r\n ` ${pc.dim(`${stats.total} node(s) total across ${stats.distinctFiles} file path(s)`)}`,\r\n '',\r\n ].join('\\n') + embedLine,\r\n );\r\n\r\n return 0;\r\n } finally {\r\n store.close();\r\n }\r\n}\r\n","import { truncate } from '../core/text.js';\r\n\r\n/**\r\n * Splits a long assistant reply into topic-sized chunks instead of one node\r\n * per whole exchange.\r\n *\r\n * The acceptance-test failure this fixes was diagnosed against the real\r\n * database, not guessed: a specific technical explanation was present in\r\n * the index but buried (and sometimes truncated) inside a single 4000-char\r\n * node covering several unrelated points. The fix targets how this\r\n * project's own replies are actually written -- long-form prose with\r\n * **bold lead sentences** marking each point, not literal `#` markdown\r\n * headings (those show up in files this assistant writes, like README.md,\r\n * not in its own chat responses). Both are treated as section boundaries;\r\n * plain paragraphs accumulate into the current chunk until it would exceed\r\n * `maxChars`.\r\n */\r\n\r\nexport interface AssistantChunk {\r\n /** The literal heading/bold-lead text that opened this chunk, if any. */\r\n heading: string | null;\r\n text: string;\r\n}\r\n\r\nconst HEADING_LINE = /^#{1,6}\\s+(.+)$/;\r\nconst BOLD_LEAD = /^\\*\\*([^*]+?)\\*\\*/;\r\n\r\n/** Returns the section title if `paragraph` opens a new logical section, else null. */\r\nfunction sectionStart(paragraph: string): string | null {\r\n const firstLine = paragraph.split('\\n')[0] ?? '';\r\n const heading = HEADING_LINE.exec(firstLine);\r\n if (heading) return (heading[1] ?? '').trim();\r\n\r\n const bold = BOLD_LEAD.exec(paragraph);\r\n if (bold) return (bold[1] ?? '').trim();\r\n\r\n return null;\r\n}\r\n\r\nexport function chunkAssistantText(text: string, maxChars: number): AssistantChunk[] {\r\n const paragraphs = text\r\n // Normalize first: every rule below is written against `\\n`, and a CRLF\r\n // file defeats all of them at once. `\\r\\n\\r\\n` holds no two *consecutive*\r\n // `\\n`, so the paragraph split silently returns the whole document as one\r\n // paragraph, headings are never detected, and the file degrades into a few\r\n // coarse size-based chunks. Docs are read straight off a Windows working\r\n // tree where git checks files out as CRLF, so this is the normal case\r\n // there, not an edge one -- it cut this repo's README from 37 sections to 8.\r\n .replace(/\\r\\n?/g, '\\n')\r\n .split(/\\n{2,}/)\r\n .map((p) => p.trim())\r\n .filter(Boolean);\r\n\r\n if (paragraphs.length === 0) return [];\r\n\r\n const chunks: AssistantChunk[] = [];\r\n let buffer: string[] = [];\r\n let bufferHeading: string | null = null;\r\n\r\n const bufferChars = () => buffer.reduce((n, p) => n + p.length, 0) + Math.max(0, buffer.length - 1) * 2;\r\n\r\n const flush = () => {\r\n if (buffer.length === 0) return;\r\n chunks.push({ heading: bufferHeading, text: truncate(buffer.join('\\n\\n'), maxChars) });\r\n buffer = [];\r\n bufferHeading = null;\r\n };\r\n\r\n for (const paragraph of paragraphs) {\r\n const heading = sectionStart(paragraph);\r\n const startsNewSection = heading !== null && buffer.length > 0;\r\n const wouldOverflow = buffer.length > 0 && bufferChars() + 2 + paragraph.length > maxChars;\r\n\r\n if (startsNewSection || wouldOverflow) flush();\r\n if (heading !== null) bufferHeading = heading;\r\n\r\n buffer.push(paragraph);\r\n }\r\n flush();\r\n\r\n return chunks;\r\n}\r\n","/**\r\n * Best-effort secret redaction for conversation text before it is ever\r\n * written to the FTS index.\r\n *\r\n * This is a safety net, not a guarantee -- pattern-based redaction cannot\r\n * catch every shape a secret can take. It exists because conversation text\r\n * is the collector most likely to contain something sensitive (a pasted\r\n * credential, a key a user asked for help debugging), unlike git diffs or\r\n * shell commands which rarely carry raw secret values verbatim.\r\n */\r\n\r\ninterface Rule {\r\n name: string;\r\n pattern: RegExp;\r\n}\r\n\r\nconst RULES: Rule[] = [\r\n { name: 'private-key-block', pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\\s\\S]*?-----END [A-Z ]*PRIVATE KEY-----/g },\r\n { name: 'aws-access-key', pattern: /\\bAKIA[0-9A-Z]{16}\\b/g },\r\n { name: 'github-token', pattern: /\\bgh[pousr]_[A-Za-z0-9]{20,}\\b/g },\r\n { name: 'slack-token', pattern: /\\bxox[baprs]-[A-Za-z0-9-]{10,}\\b/g },\r\n { name: 'jwt', pattern: /\\beyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\b/g },\r\n // key/token/secret/password = \"value\" or : value, in code, JSON, env-file or prose form.\r\n {\r\n name: 'key-value-secret',\r\n pattern: /\\b((?:api[_-]?key|secret|password|passwd|token|access[_-]?key)s?)\\s*[:=]\\s*['\"]?[A-Za-z0-9_\\-./+=]{8,}['\"]?/gi,\r\n },\r\n];\r\n\r\nexport interface RedactResult {\r\n text: string;\r\n redactedCount: number;\r\n}\r\n\r\nexport function redact(text: string): RedactResult {\r\n let redactedCount = 0;\r\n let out = text;\r\n\r\n for (const rule of RULES) {\r\n out = out.replace(rule.pattern, (_match: string, ...rest: unknown[]) => {\r\n redactedCount += 1;\r\n // `String.replace` passes (match, ...groups, offset, wholeString), so for\r\n // a rule with no capture group `rest[0]` is the *offset* -- a number, and\r\n // truthy at any position but the very first. Testing the type rather than\r\n // truthiness is what keeps a match position out of the indexed corpus.\r\n const key = typeof rest[0] === 'string' ? rest[0] : null;\r\n // Keep the key name for key/value matches so the redaction is legible\r\n // (\"apiKey: [redacted]\" reads better than a bare \"[redacted]\").\r\n return key ? `${key}: [redacted]` : '[redacted]';\r\n });\r\n }\r\n\r\n return { text: out, redactedCount };\r\n}\r\n","import { makeNodeId } from '../core/ids.js';\nimport { truncate } from '../core/text.js';\nimport type { FileTouch, MemoryNode } from '../core/types.js';\nimport { chunkAssistantText } from '../conversation/chunk.js';\nimport { redact } from '../conversation/redact.js';\nimport type { RawConversationTurn } from '../conversation/types.js';\n\nexport interface ConversationCollectorOptions {\n /** Default 2500 -- final safety cap on one chunk's assembled Q+A body. */\n maxBodyChars?: number;\n /**\n * Default 900 -- target size for one assistant-reply chunk before it's\n * closed out and a new node started. Small enough that a single\n * explanatory point stays retrievable on its own, not buried inside\n * several unrelated ones in the same long reply.\n */\n maxChunkChars?: number;\n}\n\nconst DEFAULT_MAX_BODY_CHARS = 2500;\nconst DEFAULT_MAX_CHUNK_CHARS = 900;\nconst MAX_TITLE_CHARS = 200;\nconst MAX_FILES_PER_NODE = 20;\n\nconst EXPLANATION_MARKERS =\n /\\b(because|the reason|design decision|trade-?off|instead of|rationale|so that)\\b|เพราะ|ทำไม|เหตุผล/i;\nconst TRIVIAL_ACK = /^(ok|okay|thanks?|ขอบคุณ|ครับ|ค่ะ|got it|sounds good|👍|done)\\.?!?$/i;\n\n/**\n * Prior importance of one chunk of a conversation exchange.\n *\n * Scored per chunk, not per whole exchange -- a genuinely explanatory point\n * should outrank a routine one even when they came from the same reply.\n * A much noisier signal than a commit type, so scores stay closer to the\n * middle. The intuition mirrors `scoreCommit`: text that explains a *why*\n * is worth far more to a future query than text that just confirms\n * something happened.\n */\nexport function scoreConversationTurn(userText: string, replyText: string): number {\n const text = `${userText}\\n${replyText}`;\n let score = 0.3;\n\n if (EXPLANATION_MARKERS.test(text)) score += 0.25;\n if (replyText.length > 400) score += 0.15;\n else if (replyText.length < 80) score -= 0.1;\n\n if (TRIVIAL_ACK.test(userText.trim())) score -= 0.2;\n if (userText.trim().endsWith('?')) score += 0.05;\n\n return Number(Math.min(1, Math.max(0.05, score)).toFixed(3));\n}\n\nconst FILE_PATH = /\\b(?:[\\w.-]+\\/)*[\\w-]+\\.(?:ts|tsx|js|jsx|json|md|py|rs|go|java|c|cpp|h|hpp|css|html|ya?ml|toml|sql|sh|ps1)\\b/g;\n\n/**\n * Heuristic file-mention extraction, not a parsed diff -- lets a\n * conversation node join the `node_files` path index like git/shell nodes\n * do, so \"why is this file the way it is\" can be answered from either\n * direction. False positives (a path-shaped word in prose) are possible and\n * acceptable for a best-effort cross-reference.\n */\nfunction extractMentionedFiles(text: string): FileTouch[] {\n const seen = new Set<string>();\n const files: FileTouch[] = [];\n\n for (const match of text.matchAll(FILE_PATH)) {\n const path = match[0];\n if (seen.has(path)) continue;\n seen.add(path);\n files.push({ path, insertions: null, deletions: null, binary: false });\n if (files.length >= MAX_FILES_PER_NODE) break;\n }\n\n return files;\n}\n\n/**\n * Truncating `${userFirstLine}${suffix}` as one string would silently drop\n * the suffix whenever the question alone already reached MAX_TITLE_CHARS --\n * exactly the case for a long original question, which is also exactly the\n * case where a reply is most likely to have been split into many chunks\n * that then all render with one indistinguishable title. Truncate the\n * question first, leaving guaranteed room for whatever comes after it.\n */\nfunction withSuffix(base: string, suffix: string): string {\n const budget = Math.max(20, MAX_TITLE_CHARS - suffix.length);\n return `${truncate(base, budget)}${suffix}`;\n}\n\nfunction chunkTitle(userFirstLine: string, heading: string | null, index: number, count: number): string {\n if (heading) return withSuffix(userFirstLine, ` — ${heading}`);\n if (count > 1) return withSuffix(userFirstLine, ` (part ${index + 1}/${count})`);\n return truncate(userFirstLine, MAX_TITLE_CHARS);\n}\n\n/**\n * One exchange becomes one node per chunk of its assistant reply (see\n * `chunkAssistantText`), each pairing the *original* question with just\n * that chunk -- so every node is independently self-contained and\n * searchable, not dependent on a sibling node for context. Short exchanges\n * (the common case) produce exactly one chunk, unchanged from before.\n */\nexport function toMemoryNodes(\n turn: RawConversationTurn,\n projectId: string,\n opts: ConversationCollectorOptions = {},\n): MemoryNode[] {\n const maxBody = opts.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;\n const maxChunk = opts.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;\n\n const userRedacted = redact(turn.userText);\n const userFirstLine = userRedacted.text.split(/\\r?\\n/)[0] ?? userRedacted.text;\n\n const assistantRedacted = redact(turn.assistantText);\n const chunks = chunkAssistantText(assistantRedacted.text, maxChunk);\n // Redaction found something but chunking produced nothing to attach it to\n // (a reply that was pure whitespace after redaction) -- shouldn't happen\n // since callers filter out empty assistantText first, but stay defensive.\n if (chunks.length === 0) return [];\n\n return chunks.map((chunk, index) => {\n const body = [`Q: ${userRedacted.text}`, '', `A: ${chunk.text}`].join('\\n');\n\n return {\n id: makeNodeId(projectId, 'conversation_turn', `${turn.naturalKey}:${index}`),\n kind: 'conversation_turn',\n projectId,\n ts: turn.ts,\n source: `conversation:${turn.source}`,\n title: chunkTitle(userFirstLine, chunk.heading, index, chunks.length),\n body: truncate(body, maxBody),\n files: extractMentionedFiles(`${userRedacted.text}\\n${chunk.text}`),\n signal: scoreConversationTurn(userRedacted.text, chunk.text),\n meta: {\n cwd: turn.cwd,\n source: turn.source,\n chunkIndex: index,\n chunkCount: chunks.length,\n heading: chunk.heading,\n // Redaction runs once over the whole reply before chunking (so a\n // secret can never straddle a chunk boundary and slip through) --\n // this is the turn's total, repeated on every chunk it produced,\n // not a per-chunk count.\n redactedCount: userRedacted.redactedCount + assistantRedacted.redactedCount,\n },\n };\n });\n}\n\nexport function collectConversationTurns(\n turns: readonly RawConversationTurn[],\n projectId: string,\n opts: ConversationCollectorOptions = {},\n): MemoryNode[] {\n return turns.filter((t) => t.assistantText.length > 0).flatMap((turn) => toMemoryNodes(turn, projectId, opts));\n}\n","import { chunkAssistantText } from '../conversation/chunk.js';\nimport { makeNodeId } from '../core/ids.js';\nimport { truncate } from '../core/text.js';\nimport type { MemoryNode } from '../core/types.js';\nimport type { RawDocFile } from '../docs/types.js';\n\n/**\n * Maps a repo's tracked `.md` files onto MemoryNodes, one per section.\n *\n * The gap this closes was found by dogfooding the live MCP server (see\n * README.md's Phase 3 row): a design-rationale question answered from\n * README.md prose came back empty, because git/shell/conversation are the\n * only sources that were ever read. Chunking reuses `chunkAssistantText`\n * from the conversation collector rather than inventing a second heading\n * splitter -- it already treats literal `#`..`######` lines as section\n * boundaries, which is exactly what a real markdown file is made of (the\n * bold-lead-paragraph case it also handles just never triggers here).\n */\n\nexport interface DocsCollectorOptions {\n /** Default 2000 -- final safety cap on one section's body. */\n maxBodyChars?: number;\n /** Default 1200 -- target size for one section chunk before it's split further. */\n maxChunkChars?: number;\n}\n\nconst DEFAULT_MAX_BODY_CHARS = 2000;\nconst DEFAULT_MAX_CHUNK_CHARS = 1200;\nconst MAX_TITLE_CHARS = 200;\n\nconst EXPLANATION_MARKERS = /\\b(because|the reason|design decision|trade-?off|instead of|rationale|why)\\b/i;\n\n/**\n * Prior importance of one doc section.\n *\n * Deliberately close to the middle, like a conversation chunk's score: a\n * doc file mixes genuine design rationale with routine scaffolding (a table\n * of contents entry, a install-step list), and only the source text itself\n * tells them apart.\n */\nexport function scoreDocSection(path: string, heading: string | null, text: string): number {\n let score = 0.45;\n\n if (EXPLANATION_MARKERS.test(text)) score += 0.25;\n if (/(^|\\/)readme\\.md$/i.test(path)) score += 0.1;\n if (heading === null) score -= 0.1; // preamble text with no section of its own\n if (text.length < 80) score -= 0.15;\n\n return Number(Math.min(1, Math.max(0.05, score)).toFixed(3));\n}\n\nfunction slugify(heading: string | null, index: number): string {\n if (heading === null) return `_preamble-${index}`;\n const slug = heading\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n return slug || `_section-${index}`;\n}\n\nfunction sectionTitle(path: string, heading: string | null, index: number, count: number): string {\n if (heading) return truncate(`${path} — ${heading}`, MAX_TITLE_CHARS);\n if (count > 1) return truncate(`${path} (part ${index + 1}/${count})`, MAX_TITLE_CHARS);\n return truncate(path, MAX_TITLE_CHARS);\n}\n\nexport function toMemoryNodes(file: RawDocFile, projectId: string, opts: DocsCollectorOptions = {}): MemoryNode[] {\n const maxBody = opts.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;\n const maxChunk = opts.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;\n\n const chunks = chunkAssistantText(file.content, maxChunk);\n if (chunks.length === 0) return [];\n\n // Stable ids keyed by heading text, not raw index -- a section added\n // earlier in the file must not silently reshuffle every node after it.\n // Duplicate headings (e.g. two \"Why\" sections) fall back to an occurrence\n // count so they still get distinct, deterministic keys.\n const seenSlugs = new Map<string, number>();\n\n return chunks.map((chunk, index) => {\n const baseSlug = slugify(chunk.heading, index);\n const occurrence = seenSlugs.get(baseSlug) ?? 0;\n seenSlugs.set(baseSlug, occurrence + 1);\n const naturalKey = occurrence === 0 ? `${file.path}#${baseSlug}` : `${file.path}#${baseSlug}:${occurrence}`;\n\n return {\n id: makeNodeId(projectId, 'doc_section', naturalKey),\n kind: 'doc_section',\n projectId,\n ts: file.ts,\n source: 'docs',\n title: sectionTitle(file.path, chunk.heading, index, chunks.length),\n body: truncate(chunk.text, maxBody),\n files: [{ path: file.path, insertions: null, deletions: null, binary: false }],\n signal: scoreDocSection(file.path, chunk.heading, chunk.text),\n meta: {\n path: file.path,\n heading: chunk.heading,\n chunkIndex: index,\n chunkCount: chunks.length,\n },\n };\n });\n}\n\nexport function collectDocFiles(files: readonly RawDocFile[], projectId: string, opts: DocsCollectorOptions = {}): MemoryNode[] {\n return files.flatMap((file) => toMemoryNodes(file, projectId, opts));\n}\n","import type { FileTouch } from '../core/types.js';\n\n/**\n * Pure parsing layer for `git log` output. No I/O here, so it is cheap to\n * unit-test against the ugly real-world cases (renames, binaries, messages\n * containing newlines / tabs / arrows).\n */\n\nexport const RECORD_SEP = '\\x1E';\nexport const UNIT_SEP = '\\x1F';\n\n/**\n * Field order must stay in sync with `parseCommitRecord`.\n *\n * ASCII RS/US are used as delimiters rather than a made-up token: they are\n * control characters that git never emits itself and that essentially never\n * occur in commit messages.\n */\nexport const GIT_LOG_FORMAT =\n '%x1e%H%x1f%h%x1f%P%x1f%an%x1f%ae%x1f%aI%x1f%cI%x1f%s%x1f%B%x1f';\n\nconst FIELD_COUNT = 10; // 9 format fields + the trailing --numstat block\n\nexport interface RawCommit {\n sha: string;\n shortSha: string;\n parents: string[];\n authorName: string;\n authorEmail: string;\n /** ISO-8601 with offset (`%aI`). */\n authoredAt: string;\n committedAt: string;\n subject: string;\n /** Full message including the subject line (`%B`), trimmed. */\n message: string;\n /** Message with the subject line stripped; empty for one-line commits. */\n messageBody: string;\n files: FileTouch[];\n isMerge: boolean;\n}\n\n/**\n * Split a stdout buffer into complete records.\n *\n * Returns the trailing partial record so the caller can carry it into the next\n * chunk. `flush` treats whatever remains as complete.\n */\nexport function splitRecords(buffer: string, flush = false): { records: string[]; rest: string } {\n const parts = buffer.split(RECORD_SEP);\n // parts[0] is whatever preceded the first RS -- empty on a fresh stream.\n const rest = flush ? '' : (parts.pop() ?? '');\n const records = parts.filter((p) => p.trim().length > 0);\n if (flush && rest.trim().length > 0) records.push(rest);\n return { records, rest };\n}\n\nexport function parseCommitRecord(record: string): RawCommit | null {\n // Tolerate a leading separator so callers can pass raw `git log` slices\n // without going through `splitRecords` first.\n let payload = record;\n while (payload.startsWith(RECORD_SEP)) payload = payload.slice(RECORD_SEP.length);\n\n const parts = payload.split(UNIT_SEP);\n if (parts.length < FIELD_COUNT) return null;\n\n const sha = parts[0] ?? '';\n if (!/^[0-9a-f]{7,64}$/i.test(sha)) return null;\n\n // A commit message containing a literal US would add fields; everything\n // between the subject and the final block still belongs to the body.\n const numstatBlock = parts[parts.length - 1] ?? '';\n const message = parts.slice(8, parts.length - 1).join(UNIT_SEP).trim();\n const subject = (parts[7] ?? '').trim();\n const parents = (parts[2] ?? '').trim().split(/\\s+/).filter(Boolean);\n\n return {\n sha,\n shortSha: parts[1] ?? '',\n parents,\n authorName: parts[3] ?? '',\n authorEmail: parts[4] ?? '',\n authoredAt: parts[5] ?? '',\n committedAt: parts[6] ?? '',\n subject,\n message,\n messageBody: stripSubject(message, subject),\n files: parseNumstat(numstatBlock),\n isMerge: parents.length > 1,\n };\n}\n\nfunction stripSubject(message: string, subject: string): string {\n if (!subject || !message.startsWith(subject)) return message;\n return message.slice(subject.length).trim();\n}\n\nconst NUMSTAT_LINE = /^(\\d+|-)\\t(\\d+|-)\\t(.*)$/;\n\nexport function parseNumstat(block: string): FileTouch[] {\n const files: FileTouch[] = [];\n\n for (const line of block.split('\\n')) {\n const m = NUMSTAT_LINE.exec(line.trimEnd());\n if (!m) continue;\n\n const [, addRaw = '', delRaw = '', pathRaw = ''] = m;\n const binary = addRaw === '-' || delRaw === '-';\n const { path, previousPath } = resolveRenamePath(unquoteGitPath(pathRaw));\n\n const touch: FileTouch = {\n path,\n insertions: binary ? null : Number(addRaw),\n deletions: binary ? null : Number(delRaw),\n binary,\n };\n if (previousPath) touch.previousPath = previousPath;\n files.push(touch);\n }\n\n return files;\n}\n\n/**\n * `--numstat` reports renames in two shapes:\n * src/{old => new}/file.ts (shared prefix/suffix factored out)\n * old/file.ts => new/file.ts (no shared parts)\n */\nexport function resolveRenamePath(raw: string): { path: string; previousPath?: string } {\n const braced = /^(.*)\\{(.*?) => (.*?)\\}(.*)$/.exec(raw);\n if (braced) {\n const [, prefix = '', from = '', to = '', suffix = ''] = braced;\n return {\n path: collapseSlashes(prefix + to + suffix),\n previousPath: collapseSlashes(prefix + from + suffix),\n };\n }\n\n const plain = raw.split(' => ');\n if (plain.length === 2) {\n return { path: (plain[1] ?? '').trim(), previousPath: (plain[0] ?? '').trim() };\n }\n\n return { path: raw };\n}\n\nfunction collapseSlashes(p: string): string {\n return p.replace(/\\/{2,}/g, '/').replace(/^\\//, '');\n}\n\n/**\n * git still quotes paths containing `\"`, backslashes or control characters\n * even with `core.quotePath=false`.\n */\nexport function unquoteGitPath(p: string): string {\n if (p.length < 2 || !p.startsWith('\"') || !p.endsWith('\"')) return p;\n const inner = p.slice(1, -1);\n\n // Octal escapes are raw *bytes*, so decode into a byte buffer and let UTF-8\n // decoding reassemble multi-byte characters.\n const bytes: number[] = [];\n\n for (let i = 0; i < inner.length; i += 1) {\n const ch = inner[i] ?? '';\n if (ch !== '\\\\') {\n for (const b of Buffer.from(ch, 'utf8')) bytes.push(b);\n continue;\n }\n\n const esc = inner[i + 1] ?? '';\n const simple: Record<string, number> = { n: 0x0a, t: 0x09, r: 0x0d, a: 0x07, b: 0x08, f: 0x0c, v: 0x0b };\n if (esc in simple) {\n bytes.push(simple[esc] as number);\n i += 1;\n } else if (esc === '\"' || esc === '\\\\') {\n bytes.push(esc.charCodeAt(0));\n i += 1;\n } else if (/[0-7]/.test(esc)) {\n const octal = inner.slice(i + 1, i + 4);\n bytes.push(parseInt(octal, 8) & 0xff);\n i += 3;\n } else {\n bytes.push(0x5c); // lone backslash\n }\n }\n\n return Buffer.from(bytes).toString('utf8');\n}\n","import { GitError, gitStream } from './exec.js';\nimport { GIT_LOG_FORMAT, parseCommitRecord, splitRecords, type RawCommit } from './parse.js';\n\nexport interface ReadCommitsOptions {\n /** Revision to walk back from. Default `HEAD`. */\n rev?: string;\n /** Exclusive lower bound -- walks `<afterCommit>..<rev>`. Used for incremental sync. */\n afterCommit?: string | null;\n /** Any git date expression, e.g. `90.days.ago` or `2025-01-01`. */\n since?: string | null;\n maxCount?: number | null;\n /** Merge commits carry PR titles, so they are kept by default. */\n includeMerges?: boolean;\n /** Restrict to pathspecs. */\n paths?: string[];\n}\n\n/** Errors that just mean \"there is nothing to read\", not \"something broke\". */\nconst EMPTY_HISTORY = /does not have any commits yet|unknown revision|bad revision|ambiguous argument/i;\n\nexport function buildLogArgs(opts: ReadCommitsOptions = {}): string[] {\n const { rev = 'HEAD', afterCommit, since, maxCount, includeMerges = true, paths } = opts;\n\n const args = ['log', `--format=${GIT_LOG_FORMAT}`, '--numstat', '--no-color'];\n\n if (!includeMerges) args.push('--no-merges');\n if (maxCount && maxCount > 0) args.push(`--max-count=${maxCount}`);\n if (since) args.push(`--since=${since}`);\n\n args.push(afterCommit ? `${afterCommit}..${rev}` : rev);\n\n if (paths?.length) args.push('--', ...paths);\n\n return args;\n}\n\n/**\n * Walk commit history, yielding one parsed commit at a time.\n *\n * The generator is lazy: breaking out of the loop kills the underlying `git`\n * process, so `--limit`-style callers never pay for the full history.\n */\nexport async function* readCommits(cwd: string, opts: ReadCommitsOptions = {}): AsyncGenerator<RawCommit> {\n const args = buildLogArgs(opts);\n let buffer = '';\n\n try {\n for await (const chunk of gitStream(cwd, args)) {\n buffer += chunk;\n const { records, rest } = splitRecords(buffer);\n buffer = rest;\n for (const record of records) {\n const commit = parseCommitRecord(record);\n if (commit) yield commit;\n }\n }\n } catch (err) {\n if (err instanceof GitError && EMPTY_HISTORY.test(err.stderr)) return;\n throw err;\n }\n\n for (const record of splitRecords(buffer, true).records) {\n const commit = parseCommitRecord(record);\n if (commit) yield commit;\n }\n}\n","import { makeNodeId } from '../core/ids.js';\nimport { truncate } from '../core/text.js';\nimport type { FileTouch, MemoryNode } from '../core/types.js';\nimport { readCommits, type ReadCommitsOptions } from '../git/log.js';\nimport type { RawCommit } from '../git/parse.js';\n\n/**\n * Maps raw git commits onto MemoryNodes.\n *\n * This is the only place that knows a commit is a commit -- everything\n * downstream (storage, search, context packing) sees generic nodes.\n */\n\nexport interface GitCommitCollectorOptions extends ReadCommitsOptions {\n /** Keep at most this many files per node, biggest churn first. Default 40. */\n maxFilesPerNode?: number;\n /** Hard cap on node body size, to bound index and embedding cost. Default 4000. */\n maxBodyChars?: number;\n}\n\nconst DEFAULTS = { maxFilesPerNode: 40, maxBodyChars: 4000 } as const;\nconst MAX_TITLE_CHARS = 200;\n\nexport interface ConventionalHeader {\n type: string | null;\n scope: string | null;\n breaking: boolean;\n description: string;\n}\n\nconst CONVENTIONAL = /^([a-z]+)(?:\\(([^)]*)\\))?(!)?:\\s*(.+)$/i;\n\nexport function parseConventionalHeader(subject: string): ConventionalHeader {\n const m = CONVENTIONAL.exec(subject.trim());\n if (!m) return { type: null, scope: null, breaking: false, description: subject.trim() };\n return {\n type: (m[1] ?? '').toLowerCase(),\n scope: m[2] ?? null,\n breaking: Boolean(m[3]),\n description: (m[4] ?? '').trim(),\n };\n}\n\n/**\n * Prior importance by commit type.\n *\n * The intuition: when an agent asks \"why is this code like this?\", a `fix` or\n * `refactor` explains far more than a `chore` or `style`.\n */\nconst TYPE_WEIGHTS: Record<string, number> = {\n fix: 0.8,\n feat: 0.8,\n revert: 0.78,\n perf: 0.7,\n refactor: 0.68,\n security: 0.85,\n test: 0.45,\n docs: 0.35,\n build: 0.32,\n ci: 0.28,\n chore: 0.25,\n style: 0.2,\n};\n\nconst AUTOMATED = /^(merge (branch|pull request|remote)|bump |update dependenc|\\[bot\\]|revert \"merge)/i;\n\nexport function scoreCommit(commit: RawCommit): number {\n const header = parseConventionalHeader(commit.subject);\n\n let score = header.type ? (TYPE_WEIGHTS[header.type] ?? 0.5) : 0.5;\n\n if (header.breaking) score += 0.12;\n // A commit that bothered to explain *why* is worth more than a bare subject.\n if (commit.messageBody.length > 120) score += 0.1;\n if (commit.isMerge) score = Math.min(score, 0.3);\n if (AUTOMATED.test(commit.subject)) score -= 0.15;\n\n const churn = commit.files.reduce((n, f) => n + (f.insertions ?? 0) + (f.deletions ?? 0), 0);\n // Very large commits are usually vendoring / generated code, not intent.\n if (commit.files.length > 100 || churn > 5000) score *= 0.75;\n if (commit.files.length <= 1 && churn <= 3) score -= 0.05;\n\n return Number(Math.min(1, Math.max(0.05, score)).toFixed(3));\n}\n\nfunction byChurnDesc(a: FileTouch, b: FileTouch): number {\n const ca = (a.insertions ?? 0) + (a.deletions ?? 0);\n const cb = (b.insertions ?? 0) + (b.deletions ?? 0);\n return cb - ca;\n}\n\nfunction renderFileLine(f: FileTouch): string {\n const churn = f.binary ? 'binary' : `+${f.insertions ?? 0}/-${f.deletions ?? 0}`;\n return f.previousPath ? ` ${f.path} (${churn}, renamed from ${f.previousPath})` : ` ${f.path} (${churn})`;\n}\n\nexport function toMemoryNode(\n commit: RawCommit,\n projectId: string,\n opts: GitCommitCollectorOptions = {},\n): MemoryNode {\n const maxFiles = opts.maxFilesPerNode ?? DEFAULTS.maxFilesPerNode;\n const maxBody = opts.maxBodyChars ?? DEFAULTS.maxBodyChars;\n\n const header = parseConventionalHeader(commit.subject);\n const keptFiles = [...commit.files].sort(byChurnDesc).slice(0, maxFiles);\n\n const insertions = commit.files.reduce((n, f) => n + (f.insertions ?? 0), 0);\n const deletions = commit.files.reduce((n, f) => n + (f.deletions ?? 0), 0);\n\n const bodyParts = [commit.subject];\n if (commit.messageBody) bodyParts.push('', commit.messageBody);\n if (keptFiles.length) {\n bodyParts.push('', 'Files changed:', ...keptFiles.map(renderFileLine));\n if (commit.files.length > keptFiles.length) {\n bodyParts.push(` ...and ${commit.files.length - keptFiles.length} more file(s)`);\n }\n }\n\n return {\n id: makeNodeId(projectId, 'git_commit', commit.sha),\n kind: 'git_commit',\n projectId,\n ts: commit.authoredAt,\n source: 'git',\n title: truncate(commit.subject || `(no subject) ${commit.shortSha}`, MAX_TITLE_CHARS),\n body: truncate(bodyParts.join('\\n'), maxBody),\n files: keptFiles,\n signal: scoreCommit(commit),\n meta: {\n sha: commit.sha,\n shortSha: commit.shortSha,\n parents: commit.parents,\n authorName: commit.authorName,\n authorEmail: commit.authorEmail,\n committedAt: commit.committedAt,\n isMerge: commit.isMerge,\n filesChanged: commit.files.length,\n insertions,\n deletions,\n conventionalType: header.type,\n conventionalScope: header.scope,\n breaking: header.breaking,\n },\n };\n}\n\n/** Stream commits from `cwd`'s repository as MemoryNodes. */\nexport async function* collectGitCommits(\n cwd: string,\n projectId: string,\n opts: GitCommitCollectorOptions = {},\n): AsyncGenerator<MemoryNode> {\n for await (const commit of readCommits(cwd, opts)) {\n yield toMemoryNode(commit, projectId, opts);\n }\n}\n","import { makeNodeId } from '../core/ids.js';\nimport { truncate } from '../core/text.js';\nimport type { MemoryNode } from '../core/types.js';\nimport type { RawShellEntry } from '../shell/types.js';\n\nexport interface ShellCollectorOptions {\n /** Default 1000 -- commands are short; no need for the 4000-char body cap commits use. */\n maxBodyChars?: number;\n}\n\nconst DEFAULT_MAX_BODY_CHARS = 1000;\nconst MAX_TITLE_CHARS = 200;\n\nconst NOISE = /^(cd|ls|dir|pwd|clear|cls|exit|history|whoami|date|type|cat|more|less|ll|la)\\b/i;\nconst BUILD_TEST =\n /^(npm|pnpm|yarn)\\s+(run\\s+)?(test|build|lint|typecheck|tsc)\\b|^(pytest|go\\s+test|cargo\\s+(test|build)|mvn\\s+test|gradle\\s+test|dotnet\\s+(test|build))\\b/i;\nconst INSTALL = /^(npm|pnpm|yarn)\\s+(install|add|remove|uninstall|ci)\\b|^pip\\s+install\\b|^(cargo\\s+add|go\\s+get|composer\\s+require)\\b/i;\nconst GIT_CMD = /^git\\s+/i;\nconst GIT_DESTRUCTIVE = /^git\\s+(push\\s+.*--force|reset\\s+--hard|clean\\s+-[a-z]*f|branch\\s+-d)\\b/i;\nconst RISKY = /(rm\\s+-rf|remove-item\\s+.*-recurse|del\\s+\\/s|drop\\s+(table|database)|--force\\b|truncate\\s+table)/i;\n\n/**\n * Prior importance by command shape.\n *\n * Kept deliberately coarse: this is a much weaker signal than a commit's\n * conventional-commit type, so scores cluster closer to the middle. `git`\n * commands score low by default because the git collector already captures\n * that history with far richer detail (diff, files, message) -- the shell\n * trace of `git commit -m \"...\"` would just be redundant noise next to it.\n */\nexport function scoreShellCommand(entry: RawShellEntry): number {\n const cmd = entry.command.trim();\n\n let score: number;\n if (NOISE.test(cmd)) score = 0.1;\n else if (RISKY.test(cmd) || GIT_DESTRUCTIVE.test(cmd)) score = 0.75;\n else if (INSTALL.test(cmd)) score = 0.6;\n else if (BUILD_TEST.test(cmd)) score = 0.55;\n else if (GIT_CMD.test(cmd)) score = 0.2;\n else score = 0.35;\n\n // Exit code is only known from the hook; an unknown code leaves score untouched.\n if (entry.exitCode !== null) {\n score += entry.exitCode !== 0 ? 0.25 : 0.05;\n }\n\n if (cmd.length > 60) score += 0.05;\n else if (cmd.length <= 3) score -= 0.05;\n\n return Number(Math.min(1, Math.max(0.05, score)).toFixed(3));\n}\n\nfunction renderBody(entry: RawShellEntry, maxChars: number): string {\n const parts = [`$ ${entry.command}`];\n const metaLine: string[] = [];\n if (entry.cwd) metaLine.push(`cwd: ${entry.cwd}`);\n if (entry.exitCode !== null) metaLine.push(`exit: ${entry.exitCode}`);\n if (entry.durationMs !== null) metaLine.push(`duration: ${entry.durationMs}ms`);\n if (metaLine.length) parts.push('', metaLine.join(' '));\n return truncate(parts.join('\\n'), maxChars);\n}\n\nexport function toMemoryNode(entry: RawShellEntry, projectId: string, opts: ShellCollectorOptions = {}): MemoryNode {\n const maxBody = opts.maxBodyChars ?? DEFAULT_MAX_BODY_CHARS;\n const titleLine = entry.command.split(/\\r?\\n/)[0] ?? entry.command;\n\n return {\n id: makeNodeId(projectId, 'shell_command', entry.naturalKey),\n kind: 'shell_command',\n projectId,\n ts: entry.ts,\n source: `shell:${entry.shell}`,\n title: truncate(titleLine, MAX_TITLE_CHARS),\n body: renderBody(entry, maxBody),\n files: [],\n signal: scoreShellCommand(entry),\n meta: {\n command: entry.command,\n cwd: entry.cwd,\n exitCode: entry.exitCode,\n durationMs: entry.durationMs,\n tsApprox: entry.tsApprox,\n shell: entry.shell,\n },\n };\n}\n\nexport function collectShellHistory(\n entries: readonly RawShellEntry[],\n projectId: string,\n opts: ShellCollectorOptions = {},\n): MemoryNode[] {\n return entries.map((entry) => toMemoryNode(entry, projectId, opts));\n}\n","import { readFile } from 'node:fs/promises';\nimport { listTranscriptFiles } from './paths.js';\nimport type { RawConversationTurn } from './types.js';\n\n/**\n * Parses Claude Code's local session transcript format.\n *\n * This format is internal and undocumented -- it is not a published API,\n * just what was observed on disk at `~/.claude/projects/<slug>/*.jsonl`\n * (one JSON object per line; `type: 'user' | 'assistant' | ...` records\n * threaded by `parentUuid`/`uuid`, message content shaped like the\n * Anthropic Messages API). Treat every assumption here as liable to break\n * on a Claude Code update: parse defensively, skip what doesn't match\n * rather than throw, and never let a shape change break `sync` for\n * git/shell.\n */\n\ninterface ContentBlock {\n type?: string;\n text?: string;\n [key: string]: unknown;\n}\n\ninterface TranscriptLine {\n type?: string;\n uuid?: string;\n parentUuid?: string | null;\n isSidechain?: boolean;\n timestamp?: string;\n cwd?: string;\n message?: {\n role?: string;\n content?: string | ContentBlock[];\n };\n}\n\nfunction parseLine(raw: string): TranscriptLine | null {\n const trimmed = raw.trim();\n if (!trimmed) return null;\n try {\n return JSON.parse(trimmed) as TranscriptLine;\n } catch {\n return null; // a torn write or a record shape we don't recognise -- skip, don't fail sync\n }\n}\n\n/** A real human message, not a tool-result being fed back into the model. */\nfunction extractUserText(line: TranscriptLine): string | null {\n const content = line.message?.content;\n if (typeof content === 'string') return content.trim() || null;\n\n if (Array.isArray(content)) {\n if (content.some((b) => b.type === 'tool_result')) return null;\n const text = content\n .filter((b) => b.type === 'text' && typeof b.text === 'string')\n .map((b) => b.text)\n .join('\\n')\n .trim();\n return text || null;\n }\n\n return null;\n}\n\n/** The assistant's prose reply. Tool calls and internal `thinking` are deliberately excluded. */\nfunction extractAssistantText(line: TranscriptLine): string {\n const content = line.message?.content;\n if (!Array.isArray(content)) return '';\n return content\n .filter((b) => b.type === 'text' && typeof b.text === 'string')\n .map((b) => b.text)\n .join('\\n')\n .trim();\n}\n\nexport interface ParseTranscriptOptions {\n source?: string;\n}\n\n/**\n * Group a transcript into exchanges: one real human message plus every bit\n * of assistant prose that follows it, up to the next human message.\n *\n * Tool calls in between are not part of the exchange's *text* -- they are\n * execution detail, already captured with far more structure by the git and\n * shell collectors when they matter. Sidechain records (sub-agent work\n * spawned via a Task-like tool) are excluded; they are not the primary\n * human/assistant dialogue this collector is after.\n */\nexport function parseClaudeCodeTranscript(raw: string, opts: ParseTranscriptOptions = {}): RawConversationTurn[] {\n const source = opts.source ?? 'claude-code';\n const turns: RawConversationTurn[] = [];\n\n let current: { uuid: string; userText: string; ts: string; cwd: string | null; assistantParts: string[] } | null = null;\n\n const flush = () => {\n if (!current) return;\n const assistantText = current.assistantParts.join('\\n\\n').trim();\n turns.push({\n naturalKey: `claude-code:${current.uuid}`,\n userText: current.userText,\n assistantText,\n ts: current.ts,\n cwd: current.cwd,\n source,\n });\n current = null;\n };\n\n for (const rawLine of raw.split(/\\r?\\n/)) {\n const line = parseLine(rawLine);\n if (!line || line.isSidechain) continue;\n\n if (line.type === 'user') {\n const userText = extractUserText(line);\n if (userText === null) continue; // a tool-result record, not a human message\n\n flush();\n current = {\n uuid: line.uuid ?? `noid-${turns.length}`,\n userText,\n ts: line.timestamp ?? new Date(0).toISOString(),\n cwd: line.cwd ?? null,\n assistantParts: [],\n };\n continue;\n }\n\n if (line.type === 'assistant' && current) {\n const text = extractAssistantText(line);\n if (text) current.assistantParts.push(text);\n }\n }\n\n flush();\n return turns;\n}\n\n/**\n * Read every transcript recorded for this repo and parse them all.\n *\n * Re-reads full files rather than tracking a per-file cursor: exchange\n * grouping needs to see a user turn's eventual assistant reply to close it\n * out, and a naive line-offset cursor can't guarantee it lands between two\n * exchanges rather than inside one. Content-addressed ids (the source's own\n * `uuid`) make re-processing a no-op cost at the store layer, same trade\n * the shell scrape fallback makes -- simpler and always correct beats\n * incremental and occasionally wrong.\n */\nexport async function collectClaudeCodeTranscripts(repoRoot: string): Promise<RawConversationTurn[]> {\n const files = await listTranscriptFiles(repoRoot);\n const turns: RawConversationTurn[] = [];\n\n for (const file of files) {\n const raw = await readFile(file, 'utf8');\n turns.push(...parseClaudeCodeTranscript(raw));\n }\n\n return turns;\n}\n","import { existsSync } from 'node:fs';\nimport { readdir } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Claude Code's own slugging rule for a project path, reverse-engineered\n * from what is actually on disk (undocumented -- see claude-code-reader.ts).\n * `D:\\ai-projects\\NexusMem` -> `D--ai-projects-NexusMem`: every path\n * separator and drive-letter colon becomes `-`; everything else, including\n * hyphens already in folder names, passes through untouched.\n */\nexport function claudeProjectSlug(repoRoot: string): string {\n return repoRoot.replace(/[\\\\/:]/g, '-');\n}\n\nexport function claudeProjectTranscriptDir(repoRoot: string): string {\n return join(homedir(), '.claude', 'projects', claudeProjectSlug(repoRoot));\n}\n\n/** Every session transcript recorded for this repo, oldest-looking name first is not guaranteed -- callers should sort if order matters. */\nexport async function listTranscriptFiles(repoRoot: string): Promise<string[]> {\n const dir = claudeProjectTranscriptDir(repoRoot);\n if (!existsSync(dir)) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isFile() && e.name.endsWith('.jsonl')).map((e) => join(dir, e.name));\n}\n","import { readFile, stat } from 'node:fs/promises';\r\nimport { join } from 'node:path';\r\nimport { git } from '../git/exec.js';\r\nimport type { RawDocFile } from './types.js';\r\n\r\n/**\r\n * `.md` pathspecs to index by default.\r\n *\r\n * A bare `*.md` is not anchored to the repo root -- git's default (non-glob)\r\n * pathspec matching runs per path component, so it already reaches\r\n * `docs/phase-2-spec.md` as well as the top-level `README.md` (confirmed\r\n * against this repo). One pattern covers both.\r\n */\r\nconst DEFAULT_PATHSPECS = ['*.md'];\r\n\r\nexport interface ListDocFilesOptions {\r\n include?: string[];\r\n}\r\n\r\n/**\r\n * Tracked-only by design: `git ls-files` already excludes `node_modules`,\r\n * `.git` and the self-ignoring `.nexusmem/` workspace (see\r\n * config/workspace.ts) for free, the same way the git collector gets commit\r\n * history without hand-rolled ignore logic.\r\n */\r\nexport async function listDocFiles(repoRoot: string, opts: ListDocFilesOptions = {}): Promise<string[]> {\r\n const pathspecs = opts.include ?? DEFAULT_PATHSPECS;\r\n const out = await git(repoRoot, ['ls-files', '--', ...pathspecs]);\r\n return out\r\n .split('\\n')\r\n .map((line) => line.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nexport interface DocScan {\r\n files: RawDocFile[];\r\n /**\r\n * Tracked paths that could not be read this run.\r\n *\r\n * Reported rather than silently dropped because \"produced no sections\" and\r\n * \"was never looked at\" mean opposite things to a caller that prunes: nodes\r\n * from a file in here must be kept, or one unreadable file would wipe every\r\n * section it had ever contributed.\r\n */\r\n unreadable: string[];\r\n}\r\n\r\nexport async function readDocFiles(repoRoot: string, opts: ListDocFilesOptions = {}): Promise<DocScan> {\r\n const paths = await listDocFiles(repoRoot, opts);\r\n const files: RawDocFile[] = [];\r\n const unreadable: string[] = [];\r\n\r\n for (const relPath of paths) {\r\n const path = relPath.replace(/\\\\/g, '/');\r\n const absPath = join(repoRoot, relPath);\r\n let content: string;\r\n let mtime: Date;\r\n try {\r\n [content, { mtime }] = await Promise.all([readFile(absPath, 'utf8'), stat(absPath)]);\r\n } catch {\r\n // Tracked in git but missing on disk (deleted-but-not-staged, a\r\n // worktree quirk) -- skip rather than fail the whole sync over it.\r\n unreadable.push(path);\r\n continue;\r\n }\r\n\r\n files.push({ path, content, ts: mtime.toISOString() });\r\n }\r\n\r\n return { files, unreadable };\r\n}\r\n","import { existsSync } from 'node:fs';\nimport { readFile, stat } from 'node:fs/promises';\nimport { sha256Hex } from '../core/ids.js';\nimport { readHookLog, type HookLogEntry } from './hook-log.js';\nimport { parseBashHistory } from './parse-bash.js';\nimport { parsePsReadLineHistory } from './parse-psreadline.js';\nimport { parseZshHistory } from './parse-zsh.js';\nimport { bashHistoryPath, hookLogPath, psReadLineHistoryPath, zshHistoryPath } from './paths.js';\nimport type { RawShellEntry } from './types.js';\n\nexport interface ShellSourceResult {\n /** The `shell:<name>` suffix used for both MemoryNode.source and the sync_state key. */\n name: string;\n entries: RawShellEntry[];\n /** Hook source only: how far into the log this read reached, to persist as the next cursor. */\n cursorAfter?: string;\n}\n\nexport interface CollectShellHistoryOptions {\n /** How many lines to keep from scrape-based (non-hook) sources. Default 300. */\n tailLines?: number;\n /** Repo root, for scoping hook-log entries to this project by cwd. */\n repoRoot?: string;\n /** Previous cursor for the hook log (a line count), or null to read from the start. */\n hookCursor?: string | null;\n /**\n * Once the hook is installed, its PowerShell coverage is authoritative --\n * the raw PSReadLine file duplicates the same commands with worse data\n * (no cwd, no exit code) and is skipped. Bash/zsh scraping is unaffected;\n * the hook only covers PowerShell. Default true.\n */\n preferHook?: boolean;\n}\n\nfunction isUnderRoot(cwd: string, root: string): boolean {\n const norm = (p: string) => p.replace(/\\\\/g, '/').replace(/\\/+$/, '').toLowerCase();\n const c = norm(cwd);\n const r = norm(root);\n return c === r || c.startsWith(`${r}/`);\n}\n\nfunction hookEntryToRaw(e: HookLogEntry): RawShellEntry {\n return {\n naturalKey: `pwsh-hook:${e.ts}:${sha256Hex(e.command).slice(0, 12)}`,\n command: e.command,\n ts: e.ts,\n tsApprox: false,\n exitCode: e.exitCode,\n cwd: e.cwd,\n durationMs: e.durationMs,\n shell: 'pwsh-hook',\n };\n}\n\nasync function tryReadScrapeSource(\n path: string,\n parse: (raw: string, mtimeMs: number, opts: { tailLines?: number }) => RawShellEntry[],\n tailLines: number,\n): Promise<RawShellEntry[] | null> {\n if (!existsSync(path)) return null;\n const [raw, stats] = await Promise.all([readFile(path, 'utf8'), stat(path)]);\n return parse(raw, stats.mtimeMs, { tailLines });\n}\n\n/** Enumerate and read every shell-history source available on this machine. */\nexport async function collectAvailableShellHistory(opts: CollectShellHistoryOptions = {}): Promise<ShellSourceResult[]> {\n const results: ShellSourceResult[] = [];\n const tailLines = opts.tailLines ?? 300;\n const preferHook = opts.preferHook ?? true;\n\n const hookPath = hookLogPath();\n const hookExists = existsSync(hookPath);\n\n if (hookExists) {\n const fromLine = Number(opts.hookCursor ?? '0') || 0;\n const { entries, totalLines } = await readHookLog(hookPath, fromLine);\n const scoped = opts.repoRoot ? entries.filter((e) => isUnderRoot(e.cwd, opts.repoRoot!)) : entries;\n results.push({ name: 'pwsh-hook', entries: scoped.map(hookEntryToRaw), cursorAfter: String(totalLines) });\n }\n\n const skipPwshScrape = preferHook && hookExists;\n if (!skipPwshScrape && process.platform === 'win32') {\n const entries = await tryReadScrapeSource(psReadLineHistoryPath(), parsePsReadLineHistory, tailLines);\n if (entries) results.push({ name: 'pwsh', entries });\n }\n\n const bashEntries = await tryReadScrapeSource(bashHistoryPath(), parseBashHistory, tailLines);\n if (bashEntries) results.push({ name: 'bash', entries: bashEntries });\n\n const zshEntries = await tryReadScrapeSource(zshHistoryPath(), parseZshHistory, tailLines);\n if (zshEntries) results.push({ name: 'zsh', entries: zshEntries });\n\n return results;\n}\n","import { appendFile, mkdir, readFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\n/**\n * One line of the opt-in hook log: a JSONL file the installed shell hook\n * appends to on every command. This is the high-quality tier -- exact\n * timestamp, cwd and exit code, none of which the scrape-based fallbacks can\n * offer.\n */\nexport interface HookLogEntry {\n ts: string;\n cwd: string;\n exitCode: number | null;\n durationMs: number | null;\n command: string;\n}\n\n/** A malformed line (typically a torn write from a crash mid-append) is skipped, not fatal. */\nexport function parseHookLogLine(line: string): HookLogEntry | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n\n let obj: unknown;\n try {\n obj = JSON.parse(trimmed);\n } catch {\n return null;\n }\n if (typeof obj !== 'object' || obj === null) return null;\n\n const o = obj as Record<string, unknown>;\n if (typeof o.ts !== 'string' || typeof o.cwd !== 'string' || typeof o.command !== 'string') return null;\n\n return {\n ts: o.ts,\n cwd: o.cwd,\n exitCode: typeof o.exitCode === 'number' ? o.exitCode : null,\n durationMs: typeof o.durationMs === 'number' ? o.durationMs : null,\n command: o.command,\n };\n}\n\nexport interface ReadHookLogResult {\n entries: HookLogEntry[];\n /** Total lines currently in the file -- the caller's next cursor. */\n totalLines: number;\n}\n\n/**\n * Read lines appended since `fromLine`.\n *\n * `fromLine` beyond the file's current length means the file was rotated or\n * cleared out from under a stale cursor -- treated the same way a stale git\n * cursor is: fall back to reading everything, rather than silently skipping\n * history that is actually new.\n */\nexport async function readHookLog(path: string, fromLine: number): Promise<ReadHookLogResult> {\n let raw: string;\n try {\n raw = await readFile(path, 'utf8');\n } catch {\n return { entries: [], totalLines: fromLine };\n }\n\n const lines = raw.split(/\\r?\\n/).filter((l) => l.length > 0);\n const slice = fromLine > 0 && fromLine <= lines.length ? lines.slice(fromLine) : lines;\n const entries = slice.map(parseHookLogLine).filter((e): e is HookLogEntry => e !== null);\n\n return { entries, totalLines: lines.length };\n}\n\n/** Append one entry. Exposed for tests; the real writer is the installed PowerShell hook. */\nexport async function appendHookLogEntry(path: string, entry: HookLogEntry): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n await appendFile(path, `${JSON.stringify(entry)}\\n`, 'utf8');\n}\n","import { sha256Hex } from '../core/ids.js';\nimport type { RawShellEntry } from './types.js';\nimport type { ScrapeOptions } from './parse-psreadline.js';\n\n/** A `#<epoch>` comment line precedes the command when `HISTTIMEFORMAT` is set. */\nconst EPOCH_COMMENT = /^#(\\d{9,10})$/;\n\n/**\n * Parse `.bash_history`.\n *\n * Real timestamps are used when present (`HISTTIMEFORMAT` is set); otherwise\n * falls back to the same backward-from-mtime approximation as PSReadLine.\n */\nexport function parseBashHistory(raw: string, mtimeMs: number, opts: ScrapeOptions = {}): RawShellEntry[] {\n const lines = raw.split(/\\r?\\n/);\n\n const prelim: Array<{ command: string; ts: string | null }> = [];\n let pendingEpoch: number | null = null;\n\n for (const line of lines) {\n if (line.trim().length === 0) continue;\n\n const m = EPOCH_COMMENT.exec(line.trim());\n if (m) {\n pendingEpoch = Number(m[1]);\n continue;\n }\n\n prelim.push({ command: line, ts: pendingEpoch !== null ? new Date(pendingEpoch * 1000).toISOString() : null });\n pendingEpoch = null;\n }\n\n const tail = opts.tailLines ? prelim.slice(-opts.tailLines) : prelim;\n const startIndex = prelim.length - tail.length;\n\n return tail.map((p, i) => {\n const fromEnd = tail.length - 1 - i;\n const approx = p.ts === null;\n return {\n naturalKey: `bash:${startIndex + i}:${sha256Hex(p.command).slice(0, 12)}`,\n command: p.command,\n ts: p.ts ?? new Date(mtimeMs - fromEnd * 1000).toISOString(),\n tsApprox: approx,\n exitCode: null,\n cwd: null,\n durationMs: null,\n shell: 'bash',\n };\n });\n}\n","import { sha256Hex } from '../core/ids.js';\nimport type { RawShellEntry } from './types.js';\n\nexport interface ScrapeOptions {\n /** Keep only the last N lines. Unbounded by default. */\n tailLines?: number;\n}\n\n/**\n * Parse PowerShell's `ConsoleHost_history.txt`.\n *\n * One command per line, no metadata at all -- not a timestamp, not an exit\n * code, not a cwd. Multi-line entries (a function typed at the prompt) are\n * not reconstructed; each physical line is treated as its own command. Good\n * enough for the common one-liner case, which is nearly all shell history.\n */\nexport function parsePsReadLineHistory(raw: string, mtimeMs: number, opts: ScrapeOptions = {}): RawShellEntry[] {\n const allLines = raw.split(/\\r?\\n/).filter((l) => l.trim().length > 0);\n const tail = opts.tailLines ? allLines.slice(-opts.tailLines) : allLines;\n const startIndex = allLines.length - tail.length;\n\n return tail.map((command, i) => {\n // No per-line timestamp exists, so space entries backward from the\n // file's mtime, one synthetic second apart -- preserves order and keeps\n // recency ranking roughly sane without ever being presented as exact.\n const fromEnd = tail.length - 1 - i;\n return {\n naturalKey: `pwsh:${startIndex + i}:${sha256Hex(command).slice(0, 12)}`,\n command,\n ts: new Date(mtimeMs - fromEnd * 1000).toISOString(),\n tsApprox: true,\n exitCode: null,\n cwd: null,\n durationMs: null,\n shell: 'pwsh',\n };\n });\n}\n","import { sha256Hex } from '../core/ids.js';\nimport type { RawShellEntry } from './types.js';\nimport type { ScrapeOptions } from './parse-psreadline.js';\n\n/** `EXTENDED_HISTORY` format: `: <epoch>:<duration>;<command>`. */\nconst EXTENDED_PREFIX = /^: (\\d+):(\\d+);(.*)$/;\n\n/**\n * Parse `.zsh_history`.\n *\n * Handles the (very common, oh-my-zsh-default) extended-history format with\n * real epoch timestamps, and its backslash-continuation convention for\n * commands typed across multiple physical lines. Falls back to the same\n * backward-from-mtime approximation as PSReadLine when extended history is\n * off.\n */\nexport function parseZshHistory(raw: string, mtimeMs: number, opts: ScrapeOptions = {}): RawShellEntry[] {\n const rawLines = raw.split(/\\r?\\n/);\n const prelim: Array<{ command: string; ts: string | null; durationMs: number | null }> = [];\n\n let i = 0;\n while (i < rawLines.length) {\n const line = rawLines[i] ?? '';\n if (line.trim().length === 0) {\n i += 1;\n continue;\n }\n\n const m = EXTENDED_PREFIX.exec(line);\n let epoch: number | null = null;\n let duration: number | null = null;\n let cmd: string;\n\n if (m) {\n epoch = Number(m[1]);\n duration = Number(m[2]);\n cmd = m[3] ?? '';\n } else {\n cmd = line;\n }\n\n // A trailing backslash means the command continues on the next physical line.\n while (cmd.endsWith('\\\\') && i + 1 < rawLines.length) {\n i += 1;\n cmd = `${cmd.slice(0, -1)}\\n${rawLines[i]}`;\n }\n\n prelim.push({ command: cmd, ts: epoch !== null ? new Date(epoch * 1000).toISOString() : null, durationMs: duration });\n i += 1;\n }\n\n const tail = opts.tailLines ? prelim.slice(-opts.tailLines) : prelim;\n const startIndex = prelim.length - tail.length;\n\n return tail.map((p, idx) => {\n const fromEnd = tail.length - 1 - idx;\n const approx = p.ts === null;\n return {\n naturalKey: `zsh:${startIndex + idx}:${sha256Hex(p.command).slice(0, 12)}`,\n command: p.command,\n ts: p.ts ?? new Date(mtimeMs - fromEnd * 1000).toISOString(),\n tsApprox: approx,\n exitCode: null,\n cwd: null,\n durationMs: p.durationMs,\n shell: 'zsh',\n };\n });\n}\n","import type { MemoryStore } from '../store/store.js';\nimport type { EmbeddingProvider } from './embed.js';\n\nexport interface EmbedPendingResult {\n embedded: number;\n skipped: number;\n /** True if the provider never produced a single vector -- likely means Ollama isn't reachable at all. */\n providerUnavailable: boolean;\n}\n\n/**\n * Embed every node for a project that doesn't have a vector yet.\n *\n * A node with no embedding just doesn't participate in vector search --\n * BM25 still finds it. This is additive, not a gate: sync always succeeds\n * whether or not an embedding provider is available.\n */\nexport async function embedPendingNodes(\n store: MemoryStore,\n provider: EmbeddingProvider,\n projectId: string,\n opts: { batchLimit?: number } = {},\n): Promise<EmbedPendingResult> {\n const pending = store.findNodesNeedingEmbedding(projectId, opts.batchLimit ?? 200);\n\n let embedded = 0;\n let skipped = 0;\n\n for (const node of pending) {\n const vector = await provider.embed(`${node.title}\\n${node.body}`);\n if (vector && vector.length === provider.dimension) {\n store.upsertEmbedding(node.rowid, vector);\n embedded += 1;\n } else {\n skipped += 1;\n }\n }\n\n return { embedded, skipped, providerUnavailable: pending.length > 0 && embedded === 0 };\n}\n","import { makeProjectId } from '../core/project.js';\nimport { readRepoInfo, type RepoInfo } from '../git/repo.js';\nimport { readConfig, resolveWorkspace, type NexusConfig, type Workspace } from '../config/workspace.js';\n\nexport interface CliContext {\n repo: RepoInfo;\n ws: Workspace;\n projectId: string;\n config: NexusConfig;\n}\n\n/**\n * Resolve the repo, its workspace and its config.\n *\n * The project id is always recomputed from the repo rather than trusted from\n * config, so that moving or re-cloning a repo cannot silently split its memory\n * across two namespaces.\n */\nexport async function loadContext(cwd: string): Promise<CliContext> {\n const repo = await readRepoInfo(cwd);\n const ws = resolveWorkspace(repo.root);\n const config = await readConfig(ws);\n return { repo, ws, projectId: makeProjectId({ root: repo.root, originUrl: repo.originUrl }), config };\n}\n","import { makeProjectId } from '../core/project.js';\r\nimport { readRepoInfo } from '../git/repo.js';\r\nimport { renderContextBlock } from '../retrieval/pack.js';\r\nimport { runHybridQuery } from '../retrieval/query-pipeline.js';\r\nimport { resolveWorkspace } from '../config/workspace.js';\r\nimport { MemoryStore } from '../store/store.js';\r\nimport { OllamaEmbeddingProvider } from '../vector/embed.js';\r\nimport { runInit } from '../cli/commands/init.js';\r\nimport { runSync, type SyncOptions } from '../cli/commands/sync.js';\r\n\r\n/**\r\n * Thin MCP-facing wrappers over the same CLI logic (`runSync`, the hybrid\r\n * query pipeline, `MemoryStore.stats`) -- no new business logic here. Every\r\n * tool takes an explicit `projectRoot` because, unlike a terminal command,\r\n * an MCP tool call carries no implicit shell cwd.\r\n */\r\n\r\nexport interface SearchMemoryInput {\r\n projectRoot: string;\r\n query: string;\r\n budget?: number;\r\n candidates?: number;\r\n /** BM25 only -- skip embedding the query and vector search. Mainly for tests; real callers want hybrid retrieval. */\r\n noVector?: boolean;\r\n}\r\n\r\nexport interface SearchMemoryOutput {\r\n text: string;\r\n matched: number;\r\n bm25Matched: number;\r\n vectorMatched: number;\r\n tokensUsed: number;\r\n tokensBudget: number;\r\n}\r\n\r\nexport async function searchMemory(input: SearchMemoryInput): Promise<SearchMemoryOutput> {\r\n const repo = await readRepoInfo(input.projectRoot);\r\n const ws = resolveWorkspace(repo.root);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n const budget = input.budget ?? 2000;\r\n const candidates = input.candidates ?? 30;\r\n\r\n const store = MemoryStore.open(ws.dbPath);\r\n try {\r\n const { bm25Count, vectorCount, hits, packed } = await runHybridQuery(store, projectId, input.query, {\r\n budget,\r\n candidates,\r\n embeddingProvider: input.noVector ? null : new OllamaEmbeddingProvider(),\r\n });\r\n\r\n return {\r\n text: renderContextBlock(input.query, packed),\r\n matched: hits.length,\r\n bm25Matched: bm25Count,\r\n vectorMatched: vectorCount,\r\n tokensUsed: packed.tokensUsed,\r\n tokensBudget: packed.tokensBudget,\r\n };\r\n } finally {\r\n store.close();\r\n }\r\n}\r\n\r\nexport interface SyncProjectInput {\r\n projectRoot: string;\r\n /** Skip the embedding pass for this sync. Mainly for tests; real callers want vectors kept fresh. */\r\n noEmbed?: boolean;\r\n}\r\n\r\nexport interface SyncProjectOutput {\r\n summary: string;\r\n}\r\n\r\n/**\r\n * Collects `runInit`/`runSync`'s summary through an explicit sink rather than\r\n * by reassigning `process.stdout.write`, which is what this used to do.\r\n *\r\n * That mattered more than it looked: under the stdio transport, `stdout` *is*\r\n * the JSON-RPC channel. `StdioServerTransport` holds the stream object and\r\n * resolves `.write` at send time, so a patched `write` also intercepts\r\n * protocol traffic -- a response emitted while a sync was running would land\r\n * in the capture buffer, and the patch's unconditional `return true` would\r\n * report it as delivered. Overlapping syncs compounded it: the second call\r\n * saved the first call's patch as \"the original\" and restored that instead.\r\n *\r\n * Passing a sink keeps the single shared implementation (the reason for the\r\n * original trade) without borrowing a global that something else owns.\r\n *\r\n * Always runs `init` first: an MCP client has no reason to know this tool\r\n * needs a separate init step, and `runInit` is already a safe no-op (just a\r\n * printed notice) when the project is initialized already.\r\n */\r\nexport async function syncProject(input: SyncProjectInput): Promise<SyncProjectOutput> {\r\n const chunks: string[] = [];\r\n const out = (chunk: string) => {\r\n chunks.push(chunk);\r\n };\r\n\r\n await runInit({ cwd: input.projectRoot, force: false, hook: false, enableConversation: false, out });\r\n\r\n const opts: SyncOptions = {\r\n cwd: input.projectRoot,\r\n full: false,\r\n rebuild: false,\r\n quiet: true,\r\n noEmbed: input.noEmbed,\r\n out,\r\n };\r\n await runSync(opts);\r\n\r\n return { summary: chunks.join('').trim() };\r\n}\r\n\r\nexport interface GetStatusInput {\r\n projectRoot: string;\r\n}\r\n\r\nexport interface GetStatusOutput {\r\n total: number;\r\n byKind: Record<string, number>;\r\n sources: Array<{ source: string; cursor: string | null; lastRunAt: number | null }>;\r\n}\r\n\r\nexport async function getStatus(input: GetStatusInput): Promise<GetStatusOutput> {\r\n const repo = await readRepoInfo(input.projectRoot);\r\n const ws = resolveWorkspace(repo.root);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n\r\n const store = MemoryStore.open(ws.dbPath);\r\n try {\r\n const stats = store.stats(projectId);\r\n return { total: stats.total, byKind: stats.byKind, sources: store.listSyncState(projectId) };\r\n } finally {\r\n store.close();\r\n }\r\n}\r\n","import pc from 'picocolors';\nimport { approxTokens } from '../../core/text.js';\nimport { renderContextBlock } from '../../retrieval/pack.js';\nimport { runHybridQuery } from '../../retrieval/query-pipeline.js';\nimport { MemoryStore } from '../../store/store.js';\nimport { OllamaEmbeddingProvider } from '../../vector/embed.js';\nimport { loadContext } from '../context.js';\n\nexport interface QueryOptions {\n cwd: string;\n query: string;\n /** Token budget for the packed context that gets printed to stdout. */\n budget: number;\n /** How many FTS/vector candidates to rank/pack from, before the budget is applied. */\n candidates: number;\n halfLifeDays?: number;\n /** Skip embedding the query and vector search entirely -- BM25-only, same as before hybrid retrieval existed. */\n noVector?: boolean;\n json: boolean;\n}\n\nexport async function runQuery(opts: QueryOptions): Promise<number> {\n const { ws, projectId } = await loadContext(opts.cwd);\n const store = MemoryStore.open(ws.dbPath);\n\n try {\n const { bm25Count, vectorCount, hits, packed } = await runHybridQuery(store, projectId, opts.query, {\n budget: opts.budget,\n candidates: opts.candidates,\n halfLifeDays: opts.halfLifeDays,\n embeddingProvider: opts.noVector ? null : new OllamaEmbeddingProvider(),\n });\n\n const matched = hits.length;\n\n // Packer efficiency: the packed context against the summed raw bodies of\n // *the same candidate set*. It measures ranking + budgeted packing\n // against its own input, which is what makes it useful for tuning.\n //\n // Deliberately NOT called \"token saved\": the baseline is hypothetical --\n // without NexusMem you'd never have sent these candidate bodies at all,\n // so this says nothing about a session's actual token bill. End-to-end\n // saving is measured against what the agent would otherwise have read,\n // and is a separate number entirely (README § Benchmarks).\n //\n // Can go negative: for a handful of small matches, fixed per-node\n // formatting overhead can outweigh what little there was to trim. The\n // efficiency comes from dropping low-score matches entirely once\n // candidates exceed the budget, and from truncating large bodies --\n // neither has much to work with on a tiny, already-terse result set.\n const rawTokens = hits.reduce((n, h) => n + approxTokens(h.body), 0);\n const packerEfficiency = rawTokens > 0 ? 1 - packed.tokensUsed / rawTokens : 0;\n\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n query: opts.query,\n matched,\n bm25Matched: bm25Count,\n vectorMatched: vectorCount,\n packed: packed.nodes,\n tokensUsed: packed.tokensUsed,\n tokensBudget: packed.tokensBudget,\n droppedForBudget: packed.droppedForBudget,\n },\n null,\n 2,\n )}\\n`,\n );\n return 0;\n }\n\n if (matched === 0) {\n process.stderr.write(`${pc.yellow('no matches')} for \"${opts.query}\"\\n`);\n return 0;\n }\n\n process.stderr.write(\n [\n `${pc.dim('matched')} ${bm25Count} bm25${vectorCount > 0 ? ` + ${vectorCount} vector` : ''}, packed ${pc.bold(String(packed.nodes.length))} into budget`,\n `${pc.dim('tokens ')} ${packed.tokensUsed}/${packed.tokensBudget}` +\n (packed.droppedForBudget ? pc.dim(` (${packed.droppedForBudget} dropped for budget)`) : ''),\n rawTokens > 0\n ? `${pc.dim('vs raw ')} ${rawTokens} tokens if these same matches were sent unpacked ${packerEfficiency >= 0 ? pc.green(`(${(packerEfficiency * 100).toFixed(0)}% packer efficiency)`) : pc.yellow(`(${(-packerEfficiency * 100).toFixed(0)}% larger -- overhead dominates on small result sets)`)}`\n : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n'),\n );\n\n process.stdout.write(`${renderContextBlock(opts.query, packed)}\\n`);\n return 0;\n } finally {\n store.close();\n }\n}\n","import pc from 'picocolors';\r\nimport { collectConversationTurns } from '../../collectors/conversation.js';\r\nimport { collectClaudeCodeTranscripts } from '../../conversation/claude-code-reader.js';\r\nimport { claudeProjectTranscriptDir, listTranscriptFiles } from '../../conversation/paths.js';\r\nimport { makeProjectId } from '../../core/project.js';\r\nimport { approxTokens } from '../../core/text.js';\r\nimport type { MemoryNode } from '../../core/types.js';\r\nimport { readRepoInfo } from '../../git/repo.js';\r\nimport { CONVERSATION_SIGNAL_BANDS, formatSignal } from '../format.js';\r\n\r\nexport interface ScanConversationOptions {\r\n cwd: string;\r\n minSignal: number;\r\n json: boolean;\r\n}\r\n\r\nexport async function runScanConversation(opts: ScanConversationOptions): Promise<number> {\r\n const repo = await readRepoInfo(opts.cwd);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n\r\n const files = await listTranscriptFiles(repo.root);\r\n\r\n if (!opts.json) {\r\n process.stderr.write(\r\n files.length\r\n ? `${pc.dim('transcripts')} ${files.length} file(s) in ${claudeProjectTranscriptDir(repo.root)}\\n\\n`\r\n : `${pc.yellow('no transcripts found')} at ${claudeProjectTranscriptDir(repo.root)}\\n`,\r\n );\r\n }\r\n\r\n const turns = await collectClaudeCodeTranscripts(repo.root);\r\n const nodes = collectConversationTurns(turns, projectId).filter((n) => n.signal >= opts.minSignal);\r\n\r\n if (opts.json) {\r\n process.stdout.write(`${JSON.stringify(nodes, null, 2)}\\n`);\r\n return 0;\r\n }\r\n\r\n for (const node of nodes) process.stdout.write(`${formatNode(node)}\\n`);\r\n\r\n const redactedTotal = nodes.reduce((n, x) => n + (Number(x.meta.redactedCount) || 0), 0);\r\n const approxTotal = nodes.reduce((n, x) => n + approxTokens(x.body), 0);\r\n process.stderr.write(\r\n `\\n${pc.bold(String(nodes.length))} of ${turns.length} exchange(s) above threshold ${pc.dim(`~${approxTotal.toLocaleString()} tokens if sent raw`)}` +\r\n (redactedTotal > 0 ? ` ${pc.yellow(`${redactedTotal} secret-like value(s) redacted`)}` : '') +\r\n '\\n',\r\n );\r\n\r\n return 0;\r\n}\r\n\r\nfunction formatNode(node: MemoryNode): string {\r\n return [formatSignal(node.signal, CONVERSATION_SIGNAL_BANDS), node.ts.slice(0, 16).replace('T', ' '), node.title].join(' ');\r\n}\r\n","import pc from 'picocolors';\r\n\r\n/**\r\n * Shared rendering for the `scan-*` commands.\r\n *\r\n * These commands all print one line per candidate node, led by its signal, so\r\n * a user can eyeball what a collector would ingest before committing to a\r\n * sync. The signal column previously existed as four near-identical private\r\n * copies, which had already drifted: three graded high signal green while\r\n * `scan-shell` graded it red, so the same column meant opposite things\r\n * depending on which command produced it.\r\n */\r\n\r\n/**\r\n * Cutoffs for the three signal bands, per source.\r\n *\r\n * These stay per-source on purpose. Collectors do not score on a shared\r\n * scale -- a commit's conventional-commit type is much stronger evidence than\r\n * a command's shape, so `scoreShellCommand` clusters near the middle while\r\n * git commits use the full range. One global cutoff would paint every shell\r\n * entry the same color and say nothing.\r\n */\r\nexport interface SignalBands {\r\n /** At or above this, the signal is high for this source. */\r\n high: number;\r\n /** At or above this (but below `high`), middling. */\r\n medium: number;\r\n}\r\n\r\nexport const GIT_SIGNAL_BANDS: SignalBands = { high: 0.7, medium: 0.45 };\r\nexport const SHELL_SIGNAL_BANDS: SignalBands = { high: 0.6, medium: 0.4 };\r\nexport const CONVERSATION_SIGNAL_BANDS: SignalBands = { high: 0.55, medium: 0.35 };\r\nexport const DOCS_SIGNAL_BANDS: SignalBands = { high: 0.55, medium: 0.35 };\r\n\r\nexport type SignalBand = 'high' | 'medium' | 'low';\r\n\r\n/**\r\n * Which band a signal falls in, given its source's cutoffs.\r\n *\r\n * Split out from the coloring so the threshold logic is assertable: under a\r\n * non-TTY test runner picocolors emits no escape codes, which would make a\r\n * test of the rendered string blind to exactly the kind of divergence this\r\n * module exists to prevent.\r\n */\r\nexport function signalBand(signal: number, bands: SignalBands): SignalBand {\r\n if (signal >= bands.high) return 'high';\r\n if (signal >= bands.medium) return 'medium';\r\n return 'low';\r\n}\r\n\r\n/**\r\n * The one place a band becomes a color.\r\n *\r\n * Being a single map is the actual fix for the drift: a per-source palette is\r\n * now unrepresentable rather than merely discouraged, so \"high is green\" holds\r\n * for every command by construction. Thresholds vary by source; the color\r\n * language does not.\r\n */\r\nconst BAND_COLOR: Record<SignalBand, (s: string) => string> = {\r\n high: pc.green,\r\n medium: pc.yellow,\r\n low: pc.dim,\r\n};\r\n\r\n/** A node's signal as a fixed-width, color-graded figure. */\r\nexport function formatSignal(signal: number, bands: SignalBands): string {\r\n return BAND_COLOR[signalBand(signal, bands)](signal.toFixed(2));\r\n}\r\n","import pc from 'picocolors';\r\nimport { collectDocFiles } from '../../collectors/docs.js';\r\nimport { makeProjectId } from '../../core/project.js';\r\nimport { approxTokens } from '../../core/text.js';\r\nimport type { MemoryNode } from '../../core/types.js';\r\nimport { readDocFiles } from '../../docs/read.js';\r\nimport { readRepoInfo } from '../../git/repo.js';\r\nimport { DOCS_SIGNAL_BANDS, formatSignal } from '../format.js';\r\n\r\nexport interface ScanDocsOptions {\r\n cwd: string;\r\n minSignal: number;\r\n json: boolean;\r\n}\r\n\r\nexport async function runScanDocs(opts: ScanDocsOptions): Promise<number> {\r\n const repo = await readRepoInfo(opts.cwd);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n\r\n const { files, unreadable } = await readDocFiles(repo.root);\r\n\r\n if (!opts.json) {\r\n process.stderr.write(\r\n files.length\r\n ? `${pc.dim('tracked .md files')} ${files.map((f) => f.path).join(', ')}\\n\\n`\r\n : `${pc.yellow('no tracked .md files found')}\\n`,\r\n );\r\n if (unreadable.length > 0) {\r\n process.stderr.write(`${pc.yellow('unreadable')} ${unreadable.join(', ')}\\n\\n`);\r\n }\r\n }\r\n\r\n const nodes = collectDocFiles(files, projectId).filter((n) => n.signal >= opts.minSignal);\r\n\r\n if (opts.json) {\r\n process.stdout.write(`${JSON.stringify(nodes, null, 2)}\\n`);\r\n return 0;\r\n }\r\n\r\n for (const node of nodes) process.stdout.write(`${formatNode(node)}\\n`);\r\n\r\n const approxTotal = nodes.reduce((n, x) => n + approxTokens(x.body), 0);\r\n process.stderr.write(\r\n `\\n${pc.bold(String(nodes.length))} section(s) from ${files.length} file(s) above threshold ${pc.dim(`~${approxTotal.toLocaleString()} tokens if sent raw`)}\\n`,\r\n );\r\n\r\n return 0;\r\n}\r\n\r\nfunction formatNode(node: MemoryNode): string {\r\n return [formatSignal(node.signal, DOCS_SIGNAL_BANDS), node.title].join(' ');\r\n}\r\n","import pc from 'picocolors';\r\nimport { collectGitCommits } from '../../collectors/git-commits.js';\r\nimport { makeProjectId } from '../../core/project.js';\r\nimport { approxTokens } from '../../core/text.js';\r\nimport type { MemoryNode } from '../../core/types.js';\r\nimport { readRepoInfo } from '../../git/repo.js';\r\nimport { formatSignal, GIT_SIGNAL_BANDS } from '../format.js';\r\n\r\nexport interface ScanGitOptions {\r\n cwd: string;\r\n since?: string;\r\n limit?: number;\r\n merges: boolean;\r\n json: boolean;\r\n minSignal: number;\r\n}\r\n\r\nexport async function runScanGit(opts: ScanGitOptions): Promise<number> {\r\n const repo = await readRepoInfo(opts.cwd);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n\r\n if (!opts.json) {\r\n process.stderr.write(\r\n [\r\n `${pc.dim('repo ')} ${repo.root}`,\r\n `${pc.dim('branch ')} ${repo.branch ?? pc.yellow('(detached)')}`,\r\n `${pc.dim('origin ')} ${repo.originUrl ?? pc.dim('(none)')}`,\r\n `${pc.dim('project')} ${pc.cyan(projectId)}`,\r\n '',\r\n ].join('\\n'),\r\n );\r\n }\r\n\r\n const nodes: MemoryNode[] = [];\r\n const collectOpts = {\r\n since: opts.since ?? null,\r\n maxCount: opts.limit ?? null,\r\n includeMerges: opts.merges,\r\n };\r\n\r\n for await (const node of collectGitCommits(repo.root, projectId, collectOpts)) {\r\n if (node.signal < opts.minSignal) continue;\r\n nodes.push(node);\r\n if (!opts.json) process.stdout.write(`${formatNode(node)}\\n`);\r\n }\r\n\r\n if (opts.json) {\r\n process.stdout.write(`${JSON.stringify(nodes, null, 2)}\\n`);\r\n return 0;\r\n }\r\n\r\n process.stderr.write(`\\n${summarize(nodes)}\\n`);\r\n return 0;\r\n}\r\n\r\nfunction formatNode(node: MemoryNode): string {\r\n const sha = String(node.meta.shortSha ?? '').padEnd(9);\r\n const date = node.ts.slice(0, 10);\r\n const files = Number(node.meta.filesChanged ?? 0);\r\n const churn = `+${node.meta.insertions ?? 0}/-${node.meta.deletions ?? 0}`;\r\n return [\r\n formatSignal(node.signal, GIT_SIGNAL_BANDS),\r\n pc.dim(date),\r\n pc.magenta(sha),\r\n node.title,\r\n pc.dim(`(${files} file${files === 1 ? '' : 's'}, ${churn})`),\r\n ].join(' ');\r\n}\r\n\r\n/** Exported for tests: the token total it reports must match every other scan command's. */\r\nexport function summarize(nodes: MemoryNode[]): string {\r\n if (nodes.length === 0) return pc.yellow('no commits matched');\r\n\r\n const timestamps = nodes.map((n) => n.ts).sort();\r\n const avgSignal = nodes.reduce((n, x) => n + x.signal, 0) / nodes.length;\r\n // The shared helper, not a local re-derivation: every scan command prints\r\n // this same \"tokens if sent raw\" figure, so they must all count it alike.\r\n const totalTokens = nodes.reduce((n, x) => n + approxTokens(x.body), 0);\r\n\r\n const fileHits = new Map<string, number>();\r\n for (const node of nodes) {\r\n for (const f of node.files) fileHits.set(f.path, (fileHits.get(f.path) ?? 0) + 1);\r\n }\r\n const hottest = [...fileHits.entries()]\r\n .sort((a, b) => b[1] - a[1])\r\n .slice(0, 5)\r\n .map(([path, count]) => ` ${String(count).padStart(3)}x ${path}`);\r\n\r\n return [\r\n `${pc.bold(String(nodes.length))} nodes ${pc.dim(`${timestamps[0]?.slice(0, 10)} .. ${timestamps.at(-1)?.slice(0, 10)}`)}`,\r\n ` avg signal ${avgSignal.toFixed(3)} ~${totalTokens.toLocaleString()} tokens if sent raw`,\r\n hottest.length ? ` hottest files:\\n${hottest.join('\\n')}` : '',\r\n ]\r\n .filter(Boolean)\r\n .join('\\n');\r\n}\r\n","import pc from 'picocolors';\r\nimport { collectShellHistory } from '../../collectors/shell-history.js';\r\nimport { makeProjectId } from '../../core/project.js';\r\nimport { approxTokens } from '../../core/text.js';\r\nimport type { MemoryNode } from '../../core/types.js';\r\nimport { readRepoInfo } from '../../git/repo.js';\r\nimport { collectAvailableShellHistory } from '../../shell/detect.js';\r\nimport { formatSignal, SHELL_SIGNAL_BANDS } from '../format.js';\r\n\r\nexport interface ScanShellOptions {\r\n cwd: string;\r\n tailLines: number;\r\n minSignal: number;\r\n json: boolean;\r\n}\r\n\r\nexport async function runScanShell(opts: ScanShellOptions): Promise<number> {\r\n const repo = await readRepoInfo(opts.cwd);\r\n const projectId = makeProjectId({ root: repo.root, originUrl: repo.originUrl });\r\n\r\n const results = await collectAvailableShellHistory({ tailLines: opts.tailLines, repoRoot: repo.root });\r\n\r\n if (!opts.json) {\r\n process.stderr.write(\r\n results.length\r\n ? `${pc.dim('sources found')} ${results.map((r) => r.name).join(', ')}\\n\\n`\r\n : `${pc.yellow('no shell history source found on this machine')}\\n`,\r\n );\r\n }\r\n\r\n const allNodes: MemoryNode[] = [];\r\n for (const result of results) {\r\n const nodes = collectShellHistory(result.entries, projectId).filter((n) => n.signal >= opts.minSignal);\r\n allNodes.push(...nodes);\r\n\r\n if (!opts.json) {\r\n process.stdout.write(`${pc.bold(`shell:${result.name}`)} ${pc.dim(`(${nodes.length} of ${result.entries.length} above threshold)`)}\\n`);\r\n for (const node of nodes) process.stdout.write(`${formatNode(node)}\\n`);\r\n process.stdout.write('\\n');\r\n }\r\n }\r\n\r\n if (opts.json) {\r\n process.stdout.write(`${JSON.stringify(allNodes, null, 2)}\\n`);\r\n return 0;\r\n }\r\n\r\n const approxTotal = allNodes.reduce((n, x) => n + approxTokens(x.body), 0);\r\n process.stderr.write(`${pc.bold(String(allNodes.length))} node(s) total ${pc.dim(`~${approxTotal.toLocaleString()} tokens if sent raw`)}\\n`);\r\n\r\n return 0;\r\n}\r\n\r\nfunction formatNode(node: MemoryNode): string {\r\n const approx = node.meta.tsApprox ? pc.dim('~') : ' ';\r\n const exit = node.meta.exitCode;\r\n // Red is reserved for the failure itself. The signal column grades\r\n // importance, not danger, and reads green-for-high like every other\r\n // `scan-*` command -- see cli/format.ts.\r\n const exitLabel = typeof exit === 'number' && exit !== 0 ? pc.red(`exit ${exit}`) : '';\r\n return [formatSignal(node.signal, SHELL_SIGNAL_BANDS), approx + node.ts.slice(0, 16).replace('T', ' '), node.title, exitLabel]\r\n .filter(Boolean)\r\n .join(' ');\r\n}\r\n","import { statSync } from 'node:fs';\nimport pc from 'picocolors';\nimport { MemoryStore } from '../../store/store.js';\nimport { currentSchemaVersion, LATEST_SCHEMA_VERSION } from '../../store/schema.js';\nimport { loadContext } from '../context.js';\n\nexport interface StatusOptions {\n cwd: string;\n}\n\nfunction humanBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`;\n}\n\nfunction fileSize(path: string): number {\n try {\n return statSync(path).size;\n } catch {\n return 0;\n }\n}\n\nexport async function runStatus(opts: StatusOptions): Promise<number> {\n const { repo, ws, projectId } = await loadContext(opts.cwd);\n const store = MemoryStore.open(ws.dbPath);\n\n try {\n const stats = store.stats(projectId);\n const sources = store.listSyncState(projectId);\n const gitCursor = sources.find((s) => s.source === 'git')?.cursor ?? null;\n const schema = currentSchemaVersion(store.raw);\n\n // WAL content counts towards what is actually on disk.\n const dbBytes = fileSize(ws.dbPath) + fileSize(`${ws.dbPath}-wal`);\n\n const kinds = Object.entries(stats.byKind)\n .sort((a, b) => b[1] - a[1])\n .map(([kind, n]) => ` ${String(n).padStart(6)} ${kind}`);\n\n process.stdout.write(\n [\n `${pc.dim('repo ')} ${repo.root}`,\n `${pc.dim('branch ')} ${repo.branch ?? pc.yellow('(detached)')}`,\n `${pc.dim('project ')} ${pc.cyan(projectId)}`,\n `${pc.dim('schema ')} v${schema}${schema === LATEST_SCHEMA_VERSION ? '' : pc.yellow(` (latest is v${LATEST_SCHEMA_VERSION})`)}`,\n `${pc.dim('database')} ${ws.dbPath} ${pc.dim(`(${humanBytes(dbBytes)})`)}`,\n '',\n `${pc.bold(String(stats.total))} node(s)${stats.total ? ` ${pc.dim(`${stats.oldest?.slice(0, 10)} .. ${stats.newest?.slice(0, 10)}`)}` : ''}`,\n ...kinds,\n stats.total ? ` ${pc.dim(`${stats.distinctFiles} distinct file path(s)`)}` : '',\n '',\n sources.length ? pc.dim('sources') : pc.yellow('no sources synced yet'),\n ...sources.map((s) => {\n const when = s.lastRunAt ? new Date(s.lastRunAt).toISOString().slice(0, 16).replace('T', ' ') : 'never';\n const cursorLabel = s.source === 'git' ? (s.cursor?.slice(0, 7) ?? '-') : (s.cursor ?? '-');\n return ` ${s.source.padEnd(14)} ${pc.dim(`last run ${when}`)} ${pc.dim(`cursor ${cursorLabel}`)}`;\n }),\n gitCursor && gitCursor !== repo.head ? `${pc.yellow('git behind HEAD')} — run ${pc.bold('nexusmem sync')}` : '',\n '',\n ]\n .filter((line) => line !== '')\n .join('\\n')\n .concat('\\n'),\n );\n\n return 0;\n } finally {\n store.close();\n }\n}\n"],"mappings":"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","names":["pc","resolve","code","signal","mkdir","readFile","writeFile","join","readFile","mkdir","writeFile","pc","dirname","dirname","pc","z","pc","DEFAULT_MAX_BODY_CHARS","DEFAULT_MAX_CHUNK_CHARS","MAX_TITLE_CHARS","EXPLANATION_MARKERS","toMemoryNodes","MAX_TITLE_CHARS","DEFAULT_MAX_BODY_CHARS","MAX_TITLE_CHARS","toMemoryNode","readFile","existsSync","homedir","join","readFile","readFile","join","join","readFile","existsSync","readFile","stat","mkdir","readFile","dirname","existsSync","readFile","stat","pc","git","z","pc","pc","pc","pc","pc","pc","pc","formatNode","pc","pc","formatNode","summarize","pc","pc","formatNode","pc","pc","pc"]}
|