Agent skill

Benchmarking Search

by TriliumNext in TriliumNext/Trilium

A skill your agent uses when measuring or optimising Trilium's backend search — "why is autocomplete slow on a big database?", "where does a search spend its time?", "did this change actually make…

AGPL-3.0Auto-check passedKnowledge Management

Install Benchmarking Search

skills CLI
$ npx skills add TriliumNext/Trilium --skill benchmarking-search -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install TriliumNext/Trilium benchmarking-search --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/TriliumNext/Trilium.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/benchmarking-search .claude/skills/benchmarking-search && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
benchmarking-search
GitHub stars
38k
Token cost
~2.3k tokens
SKILL.md length
991 words
Files
3
Skills in repo
23
Repo updated
First seen
Licence
AGPL-3.0

At a glance

A skill your agent uses when measuring or optimising Trilium's backend search — "why is autocomplete slow on a big database?", "where does a search spend its time?", "did this change actually make…

  • Works in 6 steps: Snapshot a real database, read-only → Run the benchmark → Profile and attribute → …
  • Optimising Triliums backend search — why is autocomplete slow on a big database?
  • SKILL.md covers 1. Snapshot a real database,…, 2. Run the benchmark, 3. Profile and attribute and 4. Measurement discipline, plus 2 more sections
  • Runs TypeScript scripts from its folder; calls node and sqlite3

What it does

Benchmarking Search is an agent skill from TriliumNext/Trilium. Use when measuring or optimising Trilium's backend search — "why is autocomplete slow on a big database?", "where does a search spend its time?", "did this change actually make search faster?", or any before/after on packages/trilium-core/src/services/search. Boots core against a read-only snapshot of a real database, times a query, and attributes a CPU profile by caller or by callee. Includes the measurement discipline that separates a real win from machine noise — several plausible "wins" in this area have…

Its SKILL.md is about 2.3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files.

It sits in Knowledge Management. The repository describes itself as: Build your personal knowledge base with Trilium Notes. The licence is AGPL-3.0.

When your agent uses it

  • Optimising Triliums backend search — why is autocomplete slow on a big database?
  • Where does a search spend its time?
  • Did this change actually make search faster?
  • Any before/after on packages/trilium-core/src/services/search

Example prompts

  • “s backend search —”
  • “where does a search spend its time?”
  • “did this change actually make search faster?”
  • “/benchmarking-search”

Workflow steps

6 steps, taken from the step headings in SKILL.md.

  1. Snapshot a real database, read-only
  2. Run the benchmark
  3. Profile and attribute
  4. Measurement discipline
  5. Where the time goes
  6. What has already been done

What it can do on your machine

Read from SKILL.md and the folder at commit 80be026. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Ships script files (TypeScript), which the agent can run.

    Shell commands in SKILL.md call:

    • node
    • sqlite3

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Benchmarking Search loads about 2.3k tokens when it runs. Until then it costs about 157 tokens; SKILL.md has 991 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~157
When it runs · the whole SKILL.md, loaded when a task matches
~2.3k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from TriliumNext/Trilium at commit 80be026, republished under its AGPL-3.0 licence (© TriliumNext). 991 words, ~2,283 tokens.

Download SKILL.mdSave it as .claude/skills/benchmarking-search/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.
name
benchmarking-search
description
Use when measuring or optimising Trilium's backend search — "why is autocomplete slow on a big database?", "where does a search spend its time?", "did this change actually make search faster?", or any before/after on `packages/trilium-core/src/services/search`. Boots core against a read-only snapshot of a real database, times a query, and attributes a CPU profile by caller or by callee. Includes the measurement discipline that separates a real win from machine noise — several plausible "wins" in this area have turned out to be drift. Don't write a new timing harness or profile parser; both live here.

Search performance only reproduces on a real database. A synthetic fixture has uniform titles, no clones, no inherited attributes and a shallow tree, and none of the costs that dominate in practice show up. Always measure against a snapshot of a real document.

readsanswers
bench-search.mtsa database snapshothow long does this query take, and how does that change
bench-profile.mtsa .cpuprofile it writeswhich function, called by whom, and what does it contain

1. Snapshot a real database, read-only

Never benchmark against a live document — and never let the harness write to one.

bash
# macOS: ~/Library/Application Support/trilium-data ; Linux: ~/.local/share/trilium-data
cd ~/Library/Application\ Support/trilium-data
sqlite3 "file:document.db?mode=ro" ".backup '/path/to/bench/document.db'"

.backup uses SQLite's backup API, so it is safe while the app is running and handles the WAL. The source is opened mode=ro and never written. A 1.9 GB document copies in about seven seconds.

The snapshot must be named document.db and sit in a directory of its own — that directory becomes TRILIUM_DATA_DIR.

2. Run the benchmark

bench-search.mts has to live under apps/server/ to run: @triliumnext/core is a workspace package that pnpm links only into apps/server/node_modules, and ESM resolves bare specifiers from the importing file's own location.

bash
cp .claude/skills/benchmarking-search/bench-search.mts apps/server/

TRILIUM_RESOURCE_DIR=$PWD/apps/server/src \
TRILIUM_DATA_DIR=/path/to/bench \
TRILIUM_GENERAL_READONLY=true \
TRILIUM_GENERAL_NOBACKUP=true \
BENCH_ITERATIONS=40 \
  node --import tsx apps/server/bench-search.mts a t 'the quick brown fox'

rm apps/server/bench-search.mts   # when finished
variablewhy
TRILIUM_RESOURCE_DIRassets live under src/assets in dev; without it resource_dir.ts calls process.exit(1) at import time
TRILIUM_GENERAL_READONLYthe harness refuses to start without it, so a snapshot is never written to
TRILIUM_GENERAL_NOBACKUPstops the backup service touching the snapshot directory
BENCH_ITERATIONSdefault 5. Use 40 for anything you intend to act on
BENCH_PROFILEwrite a .cpuprofile covering only the search calls
BENCH_AUTOCOMPLETE=1 sets autocomplete: true, taking NoteFlatTextExp's single-token path
BENCH_ONE_PASS=1 disables two-pass ranking, i.e. the pre-ab6dc34c15 behaviour

It boots core exactly as apps/server/src/main.ts does, minus the HTTP server, so the code under test is the real thing.

3. Profile and attribute

bash
BENCH_PROFILE=/tmp/a.cpuprofile ... node --import tsx apps/server/bench-search.mts a

# top functions by self time
node --import tsx .claude/skills/benchmarking-search/bench-profile.mts /tmp/a.cpuprofile 15

# who calls removeDiacritic, walking past thin wrappers
node --import tsx .claude/skills/benchmarking-search/bench-profile.mts /tmp/a.cpuprofile 8 \
  removeDiacritic "normalize,normalizeSearchText,tokenizeIntoWords"

# what NoteFlatTextExp.execute spends its time on, by callee
node --import tsx .claude/skills/benchmarking-search/bench-profile.mts /tmp/a.cpuprofile 10 \
  "children:execute@note_flat_text"

The skip list matters. removeDiacritic is called ~100% from normalize, which is called ~100% from normalizeSearchText — attributing one level up tells you nothing. Skipping those names lands the blame on code that chose to normalize.

The @file suffix on children: narrows to one implementation; execute is a method on every expression type.

4. Measurement discipline

This is the most important section. Search work in this repo has repeatedly produced plausible numbers that were wrong.

Calibrate before trusting anything. Run the same code three times. Observed on a developer machine running Trilium alongside: ±10% at 12 iterations, ±4-5% at 40. Anything smaller than the floor is unresolvable, and reporting it as a win is a mistake.

Never compare measurements taken minutes apart. The machine drifts. One change here was recorded at 189 ms, and the identical code measured 210 ms later in the same session. Always measure both arms back to back, ideally in one command.

Prefer phase timers to wall clock. They are in-process, unsampled, and immune to profiler overhead. They are not in the codebase — add them temporarily around the stages of performSearch:

ts
const t0 = performance.now();
const noteSet = expression.execute(allNoteSet, executionContext, searchContext);
const executeMs = performance.now() - t0;
// ... same around result construction, the computeScore loop, and the sort
getLog().info(`phases: execute=${executeMs.toFixed(1)} build=${buildMs.toFixed(1)} ...`);

Then take the minimum of each phase independently across iterations — the least-contended estimate of each — rather than the phases of the best single iteration.

Phase timers catch what wall clock hides. A per-search cache keyed on `${noteId}-${parentNoteId}` showed a consistent 4-8% wall-clock improvement across every query, and the phase it targeted had gone 58.4 ms → 62.1 ms, i.e. slower. The tell: executeMs had also "improved", and the change could not possibly touch executeMs. If a phase your change cannot reach appears to move, you are reading noise.

Self time undercounts. Ranking leads by self time led to sizing one at ~23 ms when its subtree was 42.9 ms. Use children: for anything whose cost is in its callees.

Strings are the usual culprit. Almost every real win here was an allocation removed, not an algorithm improved: a template literal built per call, a Map key concatenated per lookup, String.normalize("NFD") re-run over a constant. Prefer nested maps over combined string keys — Map<parent, Map<child, V>> allocates nothing per lookup.

Show full SKILL.md (350 more words)Show less

5. Where the time goes

For a query matching most of the database (a single letter matches ~84% of a 22k-note document, since flat text includes noteId, type and mime):

phasewhat happenstypical share
executegetCandidateNotes scans every flat text; searchPathTowardsRoot resolves a note path per candidate~50%
builda SearchResult per match, each resolving its path segment titles~19%
scorecomputeScore per match: title, path and content contributions~27%
sortfull sort; the comparator's notePathTitle tiebreak fires constantly on equal scores~6%

Every phase is O(matches). That is the ceiling: no micro-optimisation halves a query that matches 18,000 notes. Reaching a large win means processing fewer results — which is what two-pass ranking does — or matching fewer notes, which is a product decision.

The slowest shape is not the biggest result set but zero matches with several tokens: a 5-token query with no matches costs ~650 ms, because fewer than five good results triggers the progressive search's second pass and the whole scan runs twice, with cost linear in token count. That is the shape behind issue #10712.

6. What has already been done

Eight commits on perf/autocomplete-debounce (PR #11542), each with its measurements in the commit body. Read those before re-treading:

  • Two-pass ranking — score without the path, shortlist, then fully score the shortlist. −43% on large result sets; the only structural win.
  • Result cap at 25 — detailing went 155-309 ms → 21-50 ms, response 212 KB → 16-30 KB.
  • Client debounce rewritten — was notesCount / 20 ms capped at a second, measured from the last search rather than the last keystroke.
  • Four commits removing per-result re-derivation of query-side and note-side strings.

Tried and rejected, with numbers, so they are not retried blindly:

why
Reordering the archived filter after the fulltext matchbroke 3 parse.spec assertions; execute +6 ms
Per-search getAllNotePaths cachecopy-on-extend allocation exactly cancelled the sharing
Lazy notePathTitle alonebuild −16%, sort +68% — the sort tiebreak reads it
Lazy notePathArraybroke 5 tests for ~2%
autocomplete: true (the single-token fast path)faster on 1 char, slower on 2+, and it changes results — it keeps candidates matching only via inherited attributes

© TriliumNext, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 2 other files in .claude/skills/benchmarking-search of TriliumNext/Trilium.

  • SKILL.md
  • bench-profile.mts
  • bench-search.mts

Open the folder on GitHubat commit 80be026

Compare with similar skills

Benchmarking Search next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Benchmarking Search compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Benchmarking Search this skillTriliumNext/Trilium38k—~2.3kAutomated safety check: PassAGPL-3.0
Logseq Review Workflow Evallogseq/logseq45k—~1kAutomated safety check: PassAGPL-3.0
Baoyu URL To Markdownsdyckjq-lab/llm-wiki-skill2.5k2 repos~3.2kAutomated safety check: PassNone
Obsidian CLIAtmosphere/atmosphere3.8k13 repos~795Automated safety check: PassApache-2.0
Esm Cjs Risk Scanlogseq/logseq45k—~3.3kAutomated safety check: PassAGPL-3.0
Knowledge Searchdataelement/bisheng12k—~1.1kAutomated safety check: PassApache-2.0

Similar skills

  • Compare two revisions of the Logseq logseq-review-workflow skill by running the same review prompt against isolated before and after skill snapshots, collecting both outputs, and producing a…

    45k GitHub stars~1k tokensUpdated today
    Knowledge ManagementAuto-check passed
  • Baoyu URL To Markdown

    sdyckjq-lab/llm-wiki-skill

    Fetch any URL and convert to markdown using Chrome CDP. An agent skill from sdyckjq-lab/llm-wiki-skill.

    2.5k GitHub starsUsed in 2 repos~3.2k tokens
    Knowledge ManagementAuto-check passed
  • Obsidian CLI

    Atmosphere/atmosphere

    Interact with Obsidian vaults using the Obsidian CLI to read, create, search, and manage notes, tasks, properties, and more.

    3.8k GitHub starsUsed in 13 repos~795 tokens
    Knowledge ManagementAuto-check passed
  • Esm Cjs Risk Scan

    logseq/logseq

    Scan Logseq ClojureScript Node/Electron targets for npm module loading risks, especially ESM-only packages that may fail when loaded through js/require or shadow-cljs require-based shims.

    45k GitHub stars~3.3k tokensUpdated today
    Knowledge ManagementAuto-check passed
  • Knowledge Search

    dataelement/bisheng

    Search the user's knowledge bases and knowledge spaces (企业知识库检索).

    12k GitHub stars~1.1k tokensUpdated today
    Knowledge ManagementAuto-check passed
  • Capture Conversation

    outline/outline

    Save the current conversation, a decision, or a set of notes as a document in an Outline collection; use when the user wants to keep what was discussed in their knowledge base.

    41k GitHub stars~474 tokensUpdated today
    Knowledge ManagementAuto-check passed

More from TriliumNext/Trilium

All 23 skills in this repo
  • Cutting A Release

    TriliumNext/Trilium

    A skill your agent uses when cutting, preparing, or debugging a Trilium release — bumping the monorepo version, tagging, or diagnosing a failed "Release" workflow run.

    38k GitHub stars~3.2k tokensUpdated today
    Auto-check passed
  • Developing Electron Desktop

    TriliumNext/Trilium

    A skill your agent uses when working on the Trilium Electron desktop app (apps/desktop) — adding or changing an electronApi method / IPC channel, touching preload.ts, main.ts, services/window.ts or…

    38k GitHub stars~5.7k tokensUpdated today
    Auto-check passed
  • Evolving The Data Model

    TriliumNext/Trilium

    A skill your agent uses when adding a DB migration or a new column/field to a Becca entity in Trilium ("add a migration", "new column on notes/attributes", "ALTER TABLE", "add a field to…

    38k GitHub stars~2.1k tokensUpdated today
    Auto-check passed
  • Adding Internal API Route

    TriliumNext/Trilium

    A skill your agent uses when adding, moving, or wiring an internal REST endpoint in Trilium (a new /api/ route) — choosing between a core-shared handler (packages/trilium-core/src/routes/index.ts…

    38k GitHub stars~3.3k tokensUpdated today
    Auto-check passed
  • Adding LLM MCP Tools

    TriliumNext/Trilium

    A skill your agent uses when adding, changing, or reviewing an LLM/MCP tool in Trilium (the defineTools definitions under packages/trilium-core/src/services/llm/tools/ —…

    38k GitHub stars~2.5k tokensUpdated today
    Auto-check passed
  • Ckeditor5 Plugin Development

    TriliumNext/Trilium

    Write, extend, and review CKEditor 5 plugins in the Trilium (TriliumNext Notes) monorepo — the rich-text-note editor under packages/ckeditor5, whose plugins live in src/plugins/.

    38k GitHub stars~4.9k tokensUpdated today
    Auto-check passed

Questions about Benchmarking Search

What does Benchmarking Search do?

A skill your agent uses when measuring or optimising Trilium's backend search — "why is autocomplete slow on a big database?", "where does a search spend its time?", "did this change actually make…. Benchmarking Search is an agent skill from TriliumNext/Trilium.", or any before/after on packages/trilium-core/src/services/search.

When should I use Benchmarking Search?

Benchmarking Search fits situations like: optimising Triliums backend search — why is autocomplete slow on a big database?; where does a search spend its time?; did this change actually make search faster?; any before/after on packages/trilium-core/src/services/search.

How do I install Benchmarking Search in Claude Code?

Run `npx skills add TriliumNext/Trilium --skill benchmarking-search -a claude-code`. Or copy the skill folder (.claude/skills/benchmarking-search in TriliumNext/Trilium) into .claude/skills/benchmarking-search in your project. Claude Code loads it when a task matches its description.

How do I install Benchmarking Search in Codex?

Run `npx skills add TriliumNext/Trilium --skill benchmarking-search -a codex`. Or copy the skill folder (.claude/skills/benchmarking-search in TriliumNext/Trilium) into .agents/skills/benchmarking-search in your project. Codex loads it when a task matches its description.

Can I use Benchmarking Search in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add TriliumNext/Trilium --skill benchmarking-search -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/benchmarking-search, .gemini/skills/benchmarking-search, .github/skills/benchmarking-search and .opencode/skills/benchmarking-search in your project.

What does Benchmarking Search need to run?

Going by SKILL.md and its folder, Benchmarking Search needs TypeScript for the scripts in its folder and the command-line tools its instructions call (node and sqlite3).

Does Benchmarking Search access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Benchmarking Search safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Benchmarking Search use?

Benchmarking Search is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Benchmarking Search use?

About 2.3k tokens (SKILL.md is roughly 9.1k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Benchmarking Search?

Skills that share tags, products or a category with Benchmarking Search: Logseq Review Workflow Eval (logseq/logseq, 45k stars), Baoyu URL To Markdown (sdyckjq-lab/llm-wiki-skill, 2.5k stars), Obsidian CLI (Atmosphere/atmosphere, 3.8k stars) and Esm Cjs Risk Scan (logseq/logseq, 45k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Benchmarking Search?

TriliumNext (a GitHub organization) maintains it in TriliumNext/Trilium, which has 38,265 GitHub stars. The repository holds 23 skills in this directory. The repository was last updated on October 10, 2026.

Source: TriliumNext/Trilium on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.