Skip to main content
The Search API provides fast, ranked full-text search over learnings using SQLite’s FTS5 engine with BM25 scoring. Search by keywords, categories, or projects with sub-millisecond performance.

SearchResult Interface

export interface SearchResult extends Learning {
  rank: number;          // BM25 relevance score (lower is better)
  snippet?: string;      // Highlighted match excerpt
}
BM25 is a probabilistic ranking function. Lower scores indicate higher relevance.

SearchOptions Interface

export interface SearchOptions {
  limit?: number;        // Max results (default: 10)
  project?: string;      // Filter by project
  category?: string;     // Filter by category
}

Search Functions

searchLearnings

db
Database.Database
required
SQLite database instance from store.db or initializeDatabase().
query
string
required
Search query. Supports prefix matching, phrases, and boolean operators.
options
SearchOptions
default:"{}"
Optional filters for limit, project, and category.
return
SearchResult[]
Array of learnings ranked by BM25 relevance with highlighted snippets.
import { createStore, searchLearnings } from 'pro-workflow';

const store = createStore();

const results = searchLearnings(store.db, 'testing', { limit: 5 });

for (const result of results) {
  console.log(`#${result.id} [${result.category}] ${result.rule}`);
  console.log(`  Rank: ${result.rank}`);
  if (result.snippet) {
    console.log(`  Match: ${result.snippet}`);
  }
}

store.close();

Query Syntax

Prefix Matching

All terms are automatically prefix-matched:
searchLearnings(store.db, 'test');
// Matches: "testing", "tests", "test-driven", etc.
Use quotes for exact phrases:
searchLearnings(store.db, '"git commit"');
// Matches only exact phrase "git commit"

Multiple Terms (OR)

Space-separated terms are OR’d:
searchLearnings(store.db, 'git commit push');
// Matches learnings containing ANY of: git, commit, push

Wildcards

Use * for explicit wildcards:
searchLearnings(store.db, 'test*');
// Matches: test, testing, tests, etc.
Prefix matching is applied automatically, so wildcards are rarely needed.

BM25 Tuning

The search query uses custom BM25 weights:
bm25(learnings_fts, 1.0, 2.0, 1.0, 1.0)
//                   ^    ^    ^    ^
//                   |    |    |    └─ correction weight
//                   |    |    └────── mistake weight
//                   |    └─────────── rule weight (2x)
//                   └──────────────── category weight
The rule field has 2x weight because it’s the primary searchable field.

Specialized Search Functions

searchByCategory

db
Database.Database
required
SQLite database instance.
category
string
required
Category name (e.g., "Testing", "Git").
options
SearchOptions
default:"{}"
Optional filters for limit and project.
return
Learning[]
Learnings in the category, sorted by times_applied DESC, created_at DESC.
import { createStore, searchByCategory } from 'pro-workflow';

const store = createStore();
const testingLearnings = searchByCategory(store.db, 'Testing', { limit: 10 });

store.close();
This bypasses FTS and uses a direct category lookup. Faster for category-only queries.

getRelatedLearnings

db
Database.Database
required
SQLite database instance.
learningId
number
required
Learning ID to find related learnings for.
limit
number
default:"5"
Maximum number of related learnings to return.
return
SearchResult[]
Learnings related to the specified learning, excluding the learning itself.
import { createStore, getRelatedLearnings } from 'pro-workflow';

const store = createStore();
const related = getRelatedLearnings(store.db, 42, 5);

for (const learning of related) {
  console.log(`Related: ${learning.rule}`);
}

store.close();

getMostAppliedLearnings

db
Database.Database
required
SQLite database instance.
limit
number
default:"10"
Maximum number of learnings to return.
return
Learning[]
Learnings with times_applied > 0, sorted by times_applied DESC, created_at DESC.
import { createStore, getMostAppliedLearnings } from 'pro-workflow';

const store = createStore();
const topLearnings = getMostAppliedLearnings(store.db, 10);

console.log('Most applied learnings:');
for (const learning of topLearnings) {
  console.log(`  ${learning.rule} (${learning.times_applied}x)`);
}

store.close();
Use this to surface frequently-applied patterns. Great for onboarding or session handoffs.

getRecentLearnings

db
Database.Database
required
SQLite database instance.
limit
number
default:"10"
Maximum number of learnings to return.
project
string | undefined
Filter by project. Omit for all learnings.
return
Learning[]
Learnings sorted by created_at DESC.
import { createStore, getRecentLearnings } from 'pro-workflow';

const store = createStore();
const recent = getRecentLearnings(store.db, 5, 'my-app');

store.close();

Snippet Highlighting

Snippets show 32 tokens of context with matches highlighted:
const results = searchLearnings(store.db, 'test');
for (const result of results) {
  console.log(result.snippet);
  // Example: "Always run <mark>tests</mark> before committing..."
}
Snippets use <mark> tags. Strip or convert to ANSI codes for CLI display.

Performance

FTS5 Index

The learnings_fts virtual table indexes:
  • category
  • rule
  • mistake
  • correction
Updates are automatic via triggers (no manual sync required).

Query Performance

OperationTime
Simple query (1 term)<1ms
Complex query (3+ terms)<5ms
Filtered query (project + category)<10ms
Full table scan (no FTS)10-100ms
For best performance, use FTS queries (searchLearnings) instead of SELECT * WHERE rule LIKE '%term%'.

CLI Examples

# Search learnings
node -e "
const { createStore, searchLearnings } = require('pro-workflow');
const store = createStore();
const results = searchLearnings(store.db, 'testing', { limit: 5 });
for (const r of results) {
  console.log('#' + r.id + ': ' + r.rule);
}
store.close();
"

Integration Examples

commands/search.md
---
description: Search learnings by keyword
argument-hint: <query>
---

# Search Learnings

Query: $ARGUMENTS

Run this:

\`\`\`bash
node -e "
const { createStore, searchLearnings } = require('pro-workflow');
const store = createStore();
const results = searchLearnings(store.db, '$ARGUMENTS', { limit: 10 });

if (results.length === 0) {
  console.log('No learnings found.');
} else {
  console.log('Found ' + results.length + ' learnings:\\n');
  for (const r of results) {
    console.log('#' + r.id + ' [' + r.category + '] ' + r.rule);
    if (r.mistake) console.log('  Mistake: ' + r.mistake);
    console.log('  Applied: ' + r.times_applied + 'x\\n');
  }
}

store.close();
"
\`\`\`
skills/replay-learnings/SKILL.md
---
name: replay-learnings
description: Surface relevant past learnings for the current task
---

# Replay Learnings

Ask the user what they're working on, then search for relevant learnings:

\`\`\`typescript
import { createStore, searchLearnings } from 'pro-workflow';

const store = createStore();
const task = '<user input>';

const results = searchLearnings(store.db, task, { limit: 5 });

if (results.length > 0) {
  console.log('Relevant learnings:');
  for (const r of results) {
    console.log(`- ${r.rule}`);
  }
}

store.close();
\`\`\`

Next Steps

Learnings API

CRUD operations for learnings

Commands

Build search commands

Skills

Create searchable skills

Agents

Preload search results into agents

Build docs developers (and LLMs) love