Skip to content

Vault Cortex AGENTS.md changelog

io.github.aliasunder/vault-cortex

5 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • AGENTS.md +42 −11

    https://raw.githubusercontent.com/aliasunder/vault-cortex/HEAD/AGENTS.md

    −     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tags, body-tags + obsidian-remark-parser + remark-ports/, tasks, recurrence, memory-entries, canvas, plaintext, pdf + pdf-engine, moment-format−     vault-operations/ # Vault read, write, and patch: vault-filesystem (base I/O), vault-patcher, note-mover, memory-store, daily-notes, vault-folder-config, task-mutations, task-format-config, trash-config, trash-sweeper, asset-operations−     oauth/ # OAuth 2.1: oauth-provider, oauth-routes, consent-page−   exports a single `links` namespace, _not_ for the service-grouping reason above−   but to wall off its `/g` grammar regexes (shared `lastIndex` footgun) behind−   position-safe methods.−   destructured `import { env } from "node:process"`, which `logger.ts` uses.−   failure modes or a no-match / empty-result contract worth clarifying−   ("returns an empty array, not an error"); omit it only for tools that−   cannot meaningfully fail. Include `Obsidian syntax:` on write tools.−    `PromptRegistrationContext`.+   http-error-middleware.ts # Express error handler shared by the full server and setup mode: short 4xx for bad request bodies, 500 with a stack otherwise+     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tags, body-tags + obsidian-remark-parser + remark-ports/, tasks, recurrence, memory-entries, canvas, file-kinds, html, plaintext, pdf + pdf-engine, moment-format+     vault-operations/ # Vault read, write, and patch: vault-filesystem (base I/O), vault-patcher, note-mover, memory-store, daily-notes, vault-folder-config, task-mutations, task-format-config, trash-config, trash-sweeper, asset-operations, asset-text+       pdf-extractor.ts, pdf-extraction-child.ts, pdf-extraction-protocol.ts # One shared child owns PDF text reads and deferred indexing; raw page rendering stays in the server+     oauth/ # OAuth 2.1: oauth-provider, oauth-routes, authorize-guard, consent-page, unknown-client-page+   exports a single `links` namespace to keep link recognition private behind+   position-safe methods. Its scanners own their cursors per call; the remaining+   `/g` inline-code regex is used through `matchAll` and `replace`.+ - Pass field values as they are: the logger caps every string it logs.+   destructured `import { env } from "node:process"`, which `logger.ts` uses+   and `pdf-extractor.ts` uses for the environment allowlist handed to the+   PDF child.+   failure modes; omit it only for tools that cannot meaningfully fail.+   An empty-result contract ("returns an empty array, not an error")+   goes under `Behavior:`, not `Errors:`. Include `Obsidian syntax:` on+   write tools.+ - **State a fact, never its absence, its implication or its mechanism.**+   The reader is an agent picking tools from text it pays for by the+   token. A definition never says that a limit, cap or restriction does+   not exist ("no upper cap"): a limit is named only where one applies,+   and an unnamed one is not assumed. It never spells out what a stated+   fact already implies ("in the server's timezone" already says the day+   may not be the caller's), and never explains a mechanism the agent+   cannot change ("until the index catches up"). The test for any clause:+   delete it, and if the agent would send the same call and read the+   result the same way, it stays deleted.+    `PromptRegistrationContext`. Wrap the handler in `trackedPromptHandler`+    (`prompt-helpers.ts`) so background embedding waits while the prompt+    runs. `tool-definitions.ts` wraps every tool handler centrally, but+    nothing wraps prompts for you.+    Add a foreground tracking test, like the existing prompts' ones, that+    spies on `trackForegroundWork` and fails when the handler skips the+    wrapper.+ - Vitest prints a test's console output, `src/logger.ts`'s lines included,+   only when that test fails (`silent: "passed-only"`, with the log lines+   routed to the console by `src/__tests__/helpers/route-log-lines-to-console.ts`).+   To see a passing test's output, run `npm test -- <file> --silent=false`.+ - New test support files go in subfolders of their `__tests__/` folder:+   a module that a test imports or runs (a harness, an input generator,+   an oracle, a script the test spawns) in `helpers/`, and data that the

  • AGENTS.md +200 −200

    https://raw.githubusercontent.com/aliasunder/vault-cortex/HEAD/AGENTS.md

    − .claude/ # Committed Claude Code hook setup only (the rest is gitignored): settings.json registers the hook below−   hooks/install-deps.sh # Runs at session start and worktree entry: loads nvm, installs dependencies when needed (npm ci, sst install), and at session start puts the checkout's Node (the .nvmrc version when nvm has it, else nvm's default alias) first on PATH for the session's later Bash commands−     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tags, tasks, recurrence, memory-entries, canvas, plaintext, pdf + pdf-engine, moment-format−     mcp-core/ # MCP protocol surface: mcp-router, tool-registry, tool-availability, tool-definitions, prompt-definitions−     search/ # SQLite FTS5, hybrid search, file watching, embedding: search-index, search-queries, fts-query, hybrid-search, search-helpers, rrf, embedder, reranker, chunker, file-watcher−     disk via `createRequire` and mutates `globalThis` (canvas polyfill−     injection). It lives here because it is PDF domain logic: the bootstrap−     that configures pdfjs so `pdf.ts`'s extraction pipeline works. The two PDF− <!-- distilled from vault Reference/code-standards-* on 2026-09-23; refresh: run the sync-code-standards skill -->− These rules are authoring guidance, not a review checklist — apply them− The rest are the author's responsibility at write time. The list below− covers both kinds, with reasons:− - Functional over OOP. Arrow functions over `function` declarations.− - Factory/closure pattern for stateful modules (see search-index.ts).− - `type` over `interface` unless `interface` is specifically required.− - TypeScript strict mode. `node:` prefix for built-ins.− - Explicit return types on exports. Zod for MCP tool schemas.− - No `any`, `as` casts, or `!` (`as const` is allowed); use runtime−   guards or schema validation instead. When a library method returns−   `T | null` but the null case is unreachable (e.g.−   `DateTime.now().toISO()`), throw on null — never fall back to `?? ""` or−   another sentinel, which passes the type checker and hides the bug−   downstream.− - Blank lines separate logical sections — a declaration never runs−   straight into the guard that consumes it.− - Model states in the type system — reach for a discriminated union, a−   user-defined [type predicate](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)−   (`value is Type`), or `never`-exhaustiveness before reshaping−   an API to route around the checker. Optional fields doc-commented−   "present only in mode X" are the cue for a discriminated union; a−   callback param with a closed set of instantiations becomes a−   discriminated field naming the domain choice (every caller passes one of−   two formatters → `format: "date" | "datetime"`). Reuse type predicates−   in filters over union members. Keep `x is T` guard bodies simple —−   predicates are compiler-trusted, not verified. A−   short `&&` chain is fine; when checks need a negated `in` or `||`−   branches, early returns read clearer.− - One discriminant represents one outcome. When two result shapes encode−   the same statuses and need a converter between them (a boolean `ok` result−   beside a `status` result for the same outcomes), use one+ .claude/ # Committed dependency hooks (the rest is gitignored); settings.json registers Claude Code setup+   hooks/install-deps.sh # Claude session/worktree setup; loads the checkout's Node and installs dependencies/SST types when needed+   hooks/prepare-command.sh # Optional command interface: environment selects Node; prepare also runs dependency setup+   obsidian-tag-oracle.ts, generate-body-tag-vectors.ts, compare-body-tags-random.ts # Check body tags against Obsidian's own parser (see "Upgrading remark-parse")+     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tags, body-tags + obsidian-remark-parser + remark-ports/, tasks, recurrence, memory-entries, canvas, plaintext, pdf + pdf-engine, moment-format+     mcp-core/ # MCP protocol surface: mcp-router, tool-registry, tool-availability, tool-definitions, prompt-definitions, coded-error-remedy+     search/ # SQLite FTS5, hybrid search, file watching, embedding: search-index, search-queries, fts-query, hybrid-search, search-helpers, rrf, embedder, reranker, search-models, chunker, file-watcher, body-tag-parser + body-tag-worker (body tags parsed in a worker thread)+     disk via `createRequire` and sets the pdfjs build unpdf uses for the whole+     process (`definePDFJSModule`). It lives here because it is PDF domain+     logic: the bootstrap that configures pdfjs so `pdf.ts`'s extraction+     pipeline works. The two PDF+ ## Dependency setup for agent commands+ Before integrating agent command setup, read [Agent command setup](./CONTRIBUTING.md#agent-command-setup).+ <!-- distilled from vault Reference/code-standards-* on 2026-11-03; refresh (merges, keeps hand edits): run the sync-code-standards skill -->+ These rules are authoring guidance, not a review checklist. Apply them+ The rest are the author's responsibility at write time.+ ### Tooling and project defaults+ - TypeScript strict mode. `node:` prefix for built-ins. Explicit return+   types on exports. Zod for MCP tool schemas.+ - Factory/closure pattern for stateful modules (see `search-index.ts`);+   export style is under "Module layering" above.+ - Luxon `DateTime` over the native `Date` API (`DateTime.now()`,+   `.toISO()`, `.toISODate()`, `.toUnixInteger()`); no manual millisecond+   arithmetic or `setDate()` mutation.+ - Standalone scripts are TypeScript (`.ts`, tsx-compatible), never+   `.mjs`/`.js`; `scripts/` meets the same bar as `src/`.+   (`SYNC_CONFIGS=none`), never empty/unset semantics: the sentinel is+   (`READONLY_MODE`, the term users search for), not internal symmetry+   (`WRITE_TOOLS_ENABLED`).+ - Pin what must match across machines: overrides to exact versions; a dev+   tool used locally and in CI to one version plus each platform's archive+   SHA-256, verified on first download, never a package manager's current+   version or an npm wrapper fetching an unverified binary.+ - Vet a new dependency (maintainers, releases, downloads) before adopting+   it; at negligible adoption with a small core, write it locally.+   Established high-adoption libraries are exempt.+ ### Naming+ - Values are named for what they are: `availableHeadings` not+   `available`, `searchText` not `needle`, `filesIndexed` not `count`.+   This holds everywhere, including callback params (`entry` not `e`),

    Showing the first 40 of each.

  • AGENTS.md +80 −49

    https://raw.githubusercontent.com/aliasunder/vault-cortex/HEAD/AGENTS.md

    − Gateway with a Lambda authorizer (path-aware: OAuth endpoints pass− through, /mcp validates static token or JWT). IaC via SST v4.−   functions/authorizer.ts # Lambda: path-aware auth (OAuth pass-through, JWT + static)−   **PDF engine exception:** `pdf-engine.ts` is the one module in this folder−   that performs side effects — it resolves `pdfjs-dist` package paths from disk−   via `createRequire` and mutates `globalThis` (canvas polyfill injection). It−   lives here because it is PDF domain logic: the bootstrap that configures pdfjs−   so `pdf.ts`'s extraction pipeline works. The two PDF modules are a unit and−   belong together in the parser layer.−   `pdf.ts` imports it as a sibling for the `extractPdfText` pipeline.−   **Dual-format task mutations:** `tasks.ts` reads **and writes** both emoji−   signifiers (`✅`, `📅`, `⏫`) and Dataview inline fields (`[completion:: date]`,−   `[priority:: high]`). Mutation functions must strip both formats when removing−   a field (a Dataview-formatted task must not get fields orphaned). `tasks.ts`−   takes the format as a parameter; `vault-operations/task-format-config.ts`−   decides it from the user's Tasks plugin settings (`taskFormat` in−   `.obsidian/plugins/obsidian-tasks-plugin/data.json`, emoji by default), and−   its `setDoneDate`/`setCancelledDate` switches control whether completion−   dates are stamped at all. When `.obsidian/` is not synced to the server,−   `task-format-config.ts` falls back to emoji.− - Background callers (file-watcher, startup) use the root logger−   directly — no request context available− - **`Errors:` gets one bullet per distinct remedy, and every bullet−   keeps its remedy.** Messages that share a remedy share one bullet,−   which lists only the messages that tool can raise: for example−   **Exception — shell scripts outside vitest's include paths.** The−   remote image's s6 init scripts under `rootfs/etc/s6-overlay/scripts/`−   are the `vault-mcp` server's boot chain for the `:remote` target, and−   the Claude Code hook sits in `.claude/hooks/`. Neither is a TypeScript−   module, and vitest's include paths (`src/`, `cli/src/`, `scripts/`)−   reach neither folder. The init-script tests live in−   `src/vault-mcp/__tests__/` (`init-check-auth.test.ts`,−   `init-obsidian-login.test.ts`, `init-first-sync.test.ts`,−   `init-setup-user.test.ts`, `init-setup-vault.test.ts`,−   `print-derived-env.test.ts`, which covers the derivation that−   `init-derive-env` publishes), and the hook's test in `src/__tests__/`−   (`install-deps.test.ts`). These script−   tests run the real script under its own shell (`sh` for the init−   scripts, `bash` for the hook) with stub binaries on `PATH`, and name−   the script they cover —+ Gateway: OAuth endpoints and `/healthz` pass straight through, and every+ other route goes through a Lambda authorizer that validates the static+ token or JWT.+ IaC via SST v4.+   functions/authorizer.ts # Lambda: validates the static token or a JWT on every protected route+   - **PDF engine exception:** `pdf-engine.ts` is the one module in this folder+     that performs side effects — it resolves `pdfjs-dist` package paths from+     disk via `createRequire` and mutates `globalThis` (canvas polyfill+     injection). It lives here because it is PDF domain logic: the bootstrap+     that configures pdfjs so `pdf.ts`'s extraction pipeline works. The two PDF+     modules are a unit and belong together in the parser layer.+     `pdf.ts` imports it as a sibling for the `extractPdfText` pipeline.+   - **Dual-format task mutations:** `tasks.ts` reads **and writes** both emoji+     signifiers (`✅`, `📅`, `⏫`) and Dataview inline fields+     (`[completion:: date]`, `[priority:: high]`). Mutation functions must strip+     both formats when removing a field (a Dataview-formatted task must not get+     fields orphaned). `tasks.ts` takes the format as a parameter;+     `vault-operations/task-format-config.ts` decides it from the user's Tasks+     plugin settings (`taskFormat` in+     `.obsidian/plugins/obsidian-tasks-plugin/data.json`, emoji by default), and+     its `setDoneDate`/`setCancelledDate` switches control whether completion+     dates are stamped at all. When `.obsidian/` is not synced to the server,+     `task-format-config.ts` falls back to emoji.+ - Background modules (`file-watcher`) take the logger the same way,+   through their entry point. Startup wiring in `server.ts` has no+   request context, so it passes the root logger+ - **`Errors:` gets one bullet per distinct remedy (errors of one kind+   with short remedies may share one bullet that names each remedy), and+   every bullet keeps its remedy.** Messages that share a remedy share one+   bullet, which lists only the messages that tool can raise: for example+ - **Every sentence changes what a calling agent does.** A sentence earns+   its place when it changes which tool or values the agent sends, what it+   does with the result or an error, or when it corrects a wrong assumption+   the rest of the definition leaves open. Judge each claim in a sentence on+   its own, and keep only the claims that meet this test. How the server+   carries out the call (which `.obsidian` file holds a setting, what the+   search index holds, how a save stages its write) goes in ARCHITECTURE.md,+   however accurate it is; the outcome the caller sees stays in the+   description.+   **Exception — shell scripts outside vitest's include paths.** These

    Showing the first 40 of each.

  • AGENTS.md +32 −9

    https://raw.githubusercontent.com/aliasunder/vault-cortex/HEAD/AGENTS.md

    − .claude/ # Committed Claude Code hook setup only (the rest is gitignored): settings.json registers hooks/install-deps.sh for session start and worktree entry, and hooks/require-nonroot-tests.sh before each shell command. install-deps.sh loads nvm, installs dependencies when needed (npm ci, sst install), and at session start puts nvm's Node first on PATH for later commands; require-nonroot-tests.sh stops a root session's test runs, makes the folders they write writable for the nobody user, and re−     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tasks, recurrence, memory-entries, canvas, plaintext, pdf + pdf-engine, moment-format−   builds them per server from the enabled tools.−   error responses to clients never include paths, stack traces, or−   implementation state.−   the Claude Code hooks sit in `.claude/hooks/`. Neither is a TypeScript−   `init-derive-env` publishes), and the hook tests in `src/__tests__/`−   (`install-deps.test.ts`, `require-nonroot-tests.test.ts`). These script−   scripts, `bash` for the hooks) with stub binaries on `PATH`, and name+ .claude/ # Committed Claude Code hook setup only (the rest is gitignored): settings.json registers the hook below+   hooks/install-deps.sh # Runs at session start and worktree entry: loads nvm, installs dependencies when needed (npm ci, sst install), and at session start puts the checkout's Node (the .nvmrc version when nvm has it, else nvm's default alias) first on PATH for the session's later Bash commands+     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tags, tasks, recurrence, memory-entries, canvas, plaintext, pdf + pdf-engine, moment-format+   builds them per server from the enabled tools and the vault path.+   error responses to clients never include the server's own paths,+   stack traces, or implementation state. A Node error's paths reach the+   client vault-relative, because the tool and prompt boundaries rewrite+   them (`describeErrorRelativeTo`), so a wrapper passes the Node error as+   `cause` and never copies its message into its own. Errors the server+   throws itself keep their text, since they can quote the caller's input+   (a refused absolute path comes back as sent).+ - **ShellCheck** (`npm run lint:shell`, lint-staged + CI) checks only the+   scripts `SHELL_SCRIPT_GLOBS` in `scripts/lint-shell.ts` lists, so add a+   new shell script's path there. ShellCheck cannot read+   `#!/command/with-contenv sh`, so s6 scripts that start with it carry+   `# shellcheck shell=sh`. A disable directive gets a comment saying why.+ - A test that denies file access with `chmod` and expects the access to+   fail can't pass as root, because root ignores file permissions. Mark it+   `it.skipIf(runsAsRoot)`, importing `runsAsRoot` from+   `src/__tests__/runs-as-root.ts`. CI runs it as a normal user. To run+   these tests from a root shell, such as a cloud session, use+   `npm run test:as-nobody`; it takes the same arguments as `vitest run`.+   the Claude Code hook sits in `.claude/hooks/`. Neither is a TypeScript+   `init-derive-env` publishes), and the hook's test in `src/__tests__/`+   (`install-deps.test.ts`). These script+   scripts, `bash` for the hook) with stub binaries on `PATH`, and name+ Pick a commit's conventional-commit type by what it changes in what this+ repo ships: the server's Docker images and the `vault-cortex` npm CLI.+ Claude Code hooks, dev scripts and other local tooling+ are never `feat` or `fix`: use `chore`, or `ci` or `build` where they fit,+ so they stay out of the changelog's Features and Bug Fixes. A squash merge+ lands the PR title, so the title takes the type of the PR's main purpose.

  • AGENTS.md +104 −200

    https://raw.githubusercontent.com/aliasunder/vault-cortex/HEAD/AGENTS.md

    − The tree lists the main folders and the files an agent most often needs:− entry points and core modules. Small self-explanatory helpers and routine− files (READMEs and other top-level docs, compose files, tool configs such as− `package.json`, `tsconfig.json`, and `eslint.config.ts`, and the `.github/`,− `.devin/`, and `.husky/` folders) are left out; run `ls` on a folder for the− full list.− render.yaml # Render Blueprint (repo root — Render reads it only from there); backs the Deploy to Render button− .claude/ # Committed Claude Code session hooks (rest of .claude/ is gitignored)−   settings.json # SessionStart + PostToolUse(EnterWorktree) → install-deps.sh−   hooks/−     install-deps.sh # nvm + npm ci guard for fresh clones and worktrees− obsidian-headless/ # Lockfile-pinned obsidian-headless for Docker remote target− rootfs/ # Container filesystem overlay (remote target)−   etc/s6-overlay/ # init chain + svc-obsidian-sync + svc-vault-mcp (run + finish: restart after setup mode's sign-in; see setup/ below)−   usr/local/bin/get-sync-token # interactive Obsidian Sync token helper (terminal alternative to setup mode's browser sign-in)− .env.example # template for Lightsail .env− templates/ # Bootstrap templates for new vaults−   memory/ # About Me/ memory file templates− deploy/ # End-user quickstart (no clone needed)−   local/ # vault-cortex:latest + bind-mounted vault−     .env.example # MCP_AUTH_TOKEN + VAULT_PATH−   remote/ # vault-cortex:remote + named volumes−     .env.example # + OBSIDIAN_AUTH_TOKEN, VAULT_NAME, PUBLIC_URL−   render/ # Render one-click guide (render.yaml lives at the repo root)−   railway/ # Railway one-click guide (template definition lives in CONTRIBUTING.md; template itself in Railway)−   fonts/−     DejaVuSans.ttf # Embedded in render script for deterministic text rendering− scripts/ # Dev/ops helpers (not shipped in Docker)−   dev.ts # Deployment helper (docker:build/push/publish, lightsail:up over SSH)−   run-sst.ts # Runs local SST commands with the external deployment env−   instance-env.ts # PUBLIC_URL for lightsail:up; must resolve like sst.config.ts and deploy.yml, or the authorizer rejects tokens−   sync-cli-env-blocks.ts # Syncs deploy/ .env.example optional blocks into cli/src/env.ts−   lobehub-manifest.ts # Builds lhm.plugin.json from the live MCP tool/prompt registry−   sync-lobehub-manifest.ts # Writes the gitignored lhm.plugin.json (npm run sync:lobehub-manifest)−   generate-dockerhub-readme.ts # Generates DOCKERHUB.md from README.md, stripping content Cloudflare's WAF in front of Docker Hub blocks−   render-social-preview.ts # Renders social-preview.svg → .png via Puppeteer−   search-eval.ts # Search ranking eval: scores hybrid search against a local judgment file of queries and expected results−   search-eval-plan.ts # Judgment-file schema, CLI validation, and whether to reuse the snapshot and index−   search-eval-snapshot.ts # Vault copy the eval runs against, skipping hidden paths and configured prefixes−   tool-surface-capture.ts # Boots an in-process server per config combo and reads its tools and prompts; feeds the snapshot test, the size report, and the LobeHub manifest+ The tree lists the main folders, a few root files, the loose files in `src/`+ and `src/vault-mcp/`, and single-file folders. Where the names help, a+ folder's comment names its main modules, without the `.ts` extension. Inside+ a folder, a file gets its own line only when its comment states a constraint+ the name does not show. Test folders appear only when their harness needs+ explaining. Routine files (READMEs and other top-level docs, compose files,+ tool configs such as `package.json`, `tsconfig.json`, and `eslint.config.ts`,+ and the `.github/`, `.devin/`, and `.husky/` folders) are left out; run `ls`+ on a folder for the full list.+ render.yaml # Render Blueprint; stays at the repo root, the only place Render reads it+ .claude/ # Committed Claude Code hook setup only (the rest is gitignored): settings.json registers hooks/install-deps.sh for session start and worktree entry, and hooks/require-nonroot-tests.sh before each shell command. install-deps.sh loads nvm, installs dependencies when needed (npm ci, sst install), and at session start puts nvm's Node first on PATH for later commands; require-nonroot-tests.sh stops a root session's test runs, makes the folders they write writable for the nobody user, and re+ obsidian-headless/ # Lockfile-pinned obsidian-headless Sync CLI for the :remote image+ rootfs/ # Container filesystem overlay for the :remote image: s6 init chain and services in etc/s6-overlay/, and usr/local/bin/get-sync-token, an in-container terminal sign-in to Obsidian that prints the Sync token for .env+ templates/memory/ # About Me/ memory file templates for new vaults+ deploy/ # End-user quickstarts (no clone needed): local/ and remote/ (guide, compose file, .env.example), render/ and railway/ (one-click guides; the Railway template definition lives in CONTRIBUTING.md)+ scripts/ # Dev and ops helpers in TypeScript (not shipped in Docker); most open with a header comment saying what they do+   instance-env.ts # Derives PUBLIC_URL for manual deploys (npm run lightsail:up) the same way sst.config.ts and deploy.yml do, or the Lambda authorizer rejects tokens+   tool-surface-capture.ts # Boots one server per settings combination for the tool-surface snapshot test, size report, and LobeHub manifest; an on/off setting that gates tools goes in its SURFACE_AXES+ cli/src/ # npx vault-cortex CLI, run on the host: bin (entry) → main (wiring) → program (Commander), one module per command (init, configure, upgrade, get-sync-token; lifecycle holds start, restart, logs, and down), and the shared modules they use (prompts, docker, env, scaffold, and others)+   __tests__/integration/ # Interactive flows through node-pty in a real PTY: pty-harness, the cli-pty tests, and a fake docker binary in fixtures/+   auth.ts # Auth helpers shared by the Lambda authorizer and vault-mcp/+   jwt.ts # Minimal HS256 JWT sign and verify for the Lambda authorizer and the OAuth server+   utils/ # Generic helpers with no domain logic (admission rules under Module layering)+   functions/authorizer.ts # Lambda: path-aware auth (OAuth pass-through, JWT + static)+   __tests__/integration/ # Server tests over real HTTP through the SDK Client; files listed under "Integration tests — when to add"+   __tests__/docker/ # :remote image boot tests (npm run test:remote-boot): docker-harness, and fixtures/ob, a stub of the obsidian-headless CLI+     server.ts, config.ts # Entry point and env-var loader, the only loose files here+     obsidian-markdown/ # Pure parsers and transforms with no I/O (pdf-engine excepted): lines, frontmatter, callouts, headings, links, tasks, recurrence, memory-entries, canvas, plaintext, pdf + pdf-engine, moment-format+     vault-operations/ # Vault read, write, and patch: vault-filesystem (base I/O), vault-patcher, note-mover, memory-store, daily-notes, vault-folder-config, task-mutations, task-format-config, trash-config, trash-sweeper, asset-operations+     mcp-core/ # MCP protocol surface: mcp-router, tool-registry, tool-availability, tool-definitions, prompt-definitions+       tools/ # One <domain>-tools module per data-layer domain (vault-crud, search, task, memory, daily-note, asset), plus tool-helpers+       prompts/ # One <name>-prompt module per prompt (vault-orientation, memory-review, daily-review), plus prompt-helpers+     search/ # SQLite FTS5, hybrid search, file watching, embedding: search-index, search-queries, fts-query, hybrid-search, search-helpers, rrf, embedder, reranker, chunker, file-watcher+     oauth/ # OAuth 2.1: oauth-provider, oauth-routes, consent-page+     setup/ # Setup mode (browser sign-in to Obsidian Sync while the :remote image has no working token): setup-server (entry), setup-routes, setup-page, obsidian-api, vault-key, sync-token-store+   fence state machine (`advanceFence`) — every function that walks lines+   and tracks code fences calls it, so they can't disagree about where a+   fence opens.+   (`createSafeHandlers`, `formatNoteMetadata`, `ToolRegistrationContext`+   type) live in `tool-helpers.ts`. Group modules take `safeHandler` and

    Showing the first 40 of each.