Skip to content

Your First Index and Search

Rafael Fragoso edited this page Jun 8, 2026 · 2 revisions

Your First Index & Search

A guided tour using a tiny sample project, with the real output Columbus produces and what each result means. The sample is three TypeScript files plus a README:

src/config.ts    # type Config + parseConfig()
src/server.ts    # class Server + newServer(), imports ./config
src/index.ts     # imports ./server, starts it
README.md

1. Install

$ columbus install
Installed columbus project proj_2fd171eebe922fad
  config:   /tmp/cbsample/.columbus.json
  data dir: /tmp/cbdata
  indexed:  4 files, 7 symbols, 7 embedded

Outcome: a stable project id, a git-excluded .columbus.json, a data dir for the database outside your repo, and a first index where every symbol/file is embedded on-device for semantic search.

2. Keep it fresh

After edits, reindex updates only what changed:

$ columbus reindex --status
  index now: 4 files (unchanged: 4)

3. Search

$ columbus search "how do we read configuration"
1. parseConfig  [function]  (score 0.71, semantic match)
   src/config.ts:4
   function parseConfig(env: Record<string, string>): Config {
2. Config  [type]  (score 0.66, semantic match)
   src/config.ts:1
   type Config = { port: number; host: string };
3. config.ts  [file]  (score 0.48, semantic match)
   src/config.ts
...

Outcome: the query never says "parseConfig", yet it ranks first — search embedded the question and matched by meaning, then re-ranked with heuristics. Each hit has a score and a why (semantic match, or a heuristic reason like exact name match). Run it twice — same model, same order.

4. See the dependency graph

$ columbus graphs --in src
{ nodes: [src/config.ts, src/server.ts, src/index.ts, …],
  edges: [src/index.ts → src/server.ts (import), src/server.ts → src/config.ts (import)] }

Outcome: the file dependency graph as nodes + edges — here, that index.ts imports server.ts, which imports config.ts. Great for "what depends on this?" reasoning. See graphs.

5. Look closer with show

$ columbus show symbol parseConfig
1 definition(s) of "parseConfig"

1. parseConfig [function]  src/config.ts:4-6
   | export type Config = { port: number; host: string };
   |
   | // parseConfig reads raw env values into a typed Config.
   | export function parseConfig(env: Record<string, string>): Config {
   |   return { port: Number(env.PORT ?? 8080), host: env.HOST ?? "localhost" };
   | }
$ columbus show file src/server.ts
src/server.ts  [typescript, package , impl]

outline (4 symbols):
     3  class     Server
     4  method    Server.constructor
     5  method    Server.start
     8  function  newServer
imports: src/config.ts
imported by: src/index.ts

Outcome: full bodies with exact line ranges, file outlines, and import edges — without you ever opening the file. See Navigating Code.

6. See "never stale" for yourself

Insert two blank lines at the top of src/config.ts and do not reindex:

# before edit
1. parseConfig [function]  src/config.ts:4-6
# after inserting 2 blank lines, with NO reindex
1. parseConfig [function]  src/config.ts:6-8

Outcome: the line range tracked the live file (4-66-8) because the snippet is reconstructed from the working tree at query time. The index didn't even need updating. This is the core invariant — see Never Stale: Live Reconstruction.

7. Record a decision

$ columbus memory add context --type decision \
    --title "Default port is 8080" \
    --body "parseConfig falls back to 8080 when PORT is unset" \
    --evidence src/config.ts:4-6 --ref symbol:parseConfig --tag config
mem_001 [decision] Default port is 8080
parseConfig falls back to 8080 when PORT is unset
tags: config
evidence: src/config.ts:4-6
ref: symbol:parseConfig

Outcome: durable, queryable project knowledge tied to evidence in the code. See Project Memory.

Where to go next

Clone this wiki locally