Skip to content

feat(workspace): folder indexing + cross-file hybrid search - #38

Open
Peefy wants to merge 8 commits into
mainfrom
feat/workspace-folder-indexing
Open

feat(workspace): folder indexing + cross-file hybrid search#38
Peefy wants to merge 8 commits into
mainfrom
feat/workspace-folder-indexing

Conversation

@Peefy

@Peefy Peefy commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds @melandlabs/workspace — a CLI for indexing an OKF / Markdown folder into SQLite and querying it with lexical / semantic / hybrid / cross-file strategies. Built on the existing memory-store SQLite infra and shared via the opencontext facade.

What's in the box

  • New package: packages/workspace (sqlite store, embedding queue, 4 search strategies, OKF backend, multi-format parsers).
  • CLI surface: opencontext workspace update|search|list wired through the main bin.
  • Local embeddings by default: workspace CLI defaults EMBEDDING_PROVIDER=local (Xenova/all-MiniLM-L6-v2, 384 dims). Demos and OKF review workflows run offline — no OPENROUTER_API_KEY needed.
  • Multi-format parsing: .md / .markdown / .txt pass-through, .pdf / .docx / .pages go through packages/rag parsers.
  • Cross-file BFS: hybrid search → walk workspace_reference_edges extracted from Markdown [text](./other.md) links.
  • Re-exports from @melandlabs/opencontext for downstream consumers.
  • Tutorial: docs/tutorials/use-cases/09-workspace-folder-indexing.md (Quick Verification recipe + walkthrough).
  • Example: examples/src/simple/22-workspace.ts (sha256 dedup / lexical / semantic / cross-file end-to-end).

Storage

Reuses ~/.opencontext/memory/store.db. New tables:

  • workspace_resources (project-scoped)
  • workspace_resource_versions (sha256-keyed version chain)
  • workspace_chunks + workspace_chunks_fts (FTS5 mirror w/ AI/AD/AU triggers)
  • workspace_reference_edges (cites edges from Markdown links)
  • workspace_jobs (indexing job log)
  • workspace_chunks_vec_d{384,1536} (per-dim vec0 child table)

Test results

package result
@melandlabs/workspace 16/16
@melandlabs/opencontext 109/109
@melandlabs/memory-store 208/208
@melandlabs/okf 140/140
examples 282 OK / 3 SKIP / 0 FAIL

pnpm -w build clean.

Usage

# Local embeddings — no API key needed
pnpm opencontext workspace update \
  --workspace-id demo --path ./wiki --json

pnpm opencontext workspace search \
  --workspace-id demo --query "limitation" --strategy cross-file --hops 1 --json

pnpm opencontext workspace list --workspace-id demo --json

Co-Authored-By

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

Peefy and others added 8 commits September 11, 2026 20:37
Adds the @melandlabs/workspace package — a CLI for indexing an
OKF/Markdown folder into SQLite and querying it with lexical,
semantic, hybrid, and cross-file strategies.

What's in the box
- packages/workspace: new package with sqlite store, embedding
  queue (class-internal serial Promise queue), 4 search strategies
  (lexical / semantic / hybrid RRF / cross-file BFS over cites
  edges), OKF backend, multi-format parsers adapter
  (.md / .markdown / .txt / .pdf / .docx / .pages).
- Local embeddings default: workspace CLI defaults
  EMBEDDING_PROVIDER=local (Xenova/all-MiniLM-L6-v2, 384 dims)
  so demos run offline without OPENROUTER_API_KEY.
- opencontext CLI surface: `opencontext workspace update|search|list`
  wired through the main bin.
- Re-exports from @melandlabs/opencontext for downstream consumers.
- Tutorial: docs/tutorials/use-cases/09-workspace-folder-indexing.md
  covering the Quick Verification recipe, multi-format walkthrough,
  and the local/cloud provider switch.
- Example: examples/src/simple/22-workspace.ts runs an end-to-end
  sha256-dedup / lexical / semantic / cross-file demo against a
  scratch store.

Storage
- Reuses ~/.opencontext/memory/store.db (same DB as memory-store).
- New tables: workspace_resources, workspace_resource_versions,
  workspace_chunks + workspace_chunks_fts, workspace_reference_edges,
  workspace_jobs, plus a per-dim vec0 child table
  workspace_chunks_vec_d{384,1536}.
- FTS5 mirror tables and AI/AD/AU triggers mirror the existing
  memory-store schema style.

Verified
- workspace 16/16, opencontext 109/109, memory-store 208/208,
  okf 140/140, examples 282 OK / 3 SKIP / 0 FAIL.
- pnpm -w build clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI lint + format + changeset were failing on the workspace package:

- biome: removed unused row→resource helper functions
  (toWorkspaceResourceVersion / toWorkspaceChunk /
  toWorkspaceReferenceEdge) and their backing interfaces
  (WorkspaceResourceVersionRow / WorkspaceReferenceEdgeRow) —
  they were dead code from when the file was first scaffolded.
- biome: dropped the unused pickFrontMatterType helper in
  okf-backend.ts and the unused scheduleBackgroundClose stub in
  cli.ts.
- biome: applied safe + unsafe fixes for useTemplate /
  noUnusedTemplateLiteral / noUnsafeDeclarationMerging across the
  workspace package. Renamed the SqliteWorkspaceStore interface to
  ISqliteWorkspaceStore to clear the unsafe-declaration-merging
  error (the class now `implements ISqliteWorkspaceStore`).
- opencontext/src/cli/opencontext.ts: reordered the workspace
  import so biome's organizeImports is happy.
- Added .changeset/workspace-folder-indexing.md bumping
  @melandlabs/workspace (minor) and @melandlabs/opencontext
  (minor).

Verified locally:
- pnpm -w build clean
- pnpm -w format:check clean
- pnpm -r lint clean (workspace + opencontext)
- workspace 16/16, opencontext 109/109, memory-store 208/208,
  okf 140/140

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… is absent

The smoke test pulls @melandlabs/* from npmjs.org via
`pnpm install --ignore-workspace`. @melandlabs/workspace isn't
published yet, so a static `import { ... } from "@melandlabs/opencontext"`
of the workspace re-exports throws ERR_MODULE_NOT_FOUND at module
load time and breaks the entire smoke suite.

Switch the demo to a top-level `import type { ... }` (erased at
compile time, no runtime resolution) plus an `await import(
"@melandlabs/workspace")` inside the demo body, wrapped in try/catch.
If the dynamic import fails we record a single [SKIP] and return,
so the rest of the suite keeps running.

This also makes the demo robust in the local monorepo before
workspace is symlinked into examples/node_modules — the demo
will start running for real once workspace is added as a
dependency in examples/package.json after the first publish.

Verified locally:
- examples pnpm test: exit 0, demo/workspace → [SKIP] with reason
- 307 OK lines, 0 FAIL
- pnpm -r lint clean

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previously the 22-workspace demo wrote its 3-file contract / statute
fixture inline via writeFile at the top of buildFixture(). That kept
the markdown content trapped inside the demo file with no way to
edit / reuse / diff it independently.

Move the fixture to examples/fixtures/workspace-wiki/:
- a.md (contract, Limitation of Liability, cites b.md)
- b.md (contract, indemnification, cites a.md)
- law-clause.md (statute, matches a.md's 12-month cap)
- README.md describing the role of each file

22-workspace.ts now resolves the fixture dir relative to its own
URL and copyFile's each markdown into a per-run tmp dir, so the
fixture is the source of truth and the demo always starts from a
clean slate.

Verified:
- workspace demo passes all 11 [OK] checks when @melandlabs/workspace
  is symlinked into examples/node_modules (lexical / semantic /
  cross-file / sha256-dedup re-run)
- pnpm format:check clean
- pnpm -r lint clean

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The fixture folder only contained .md files, but the workspace
parsers-adapter advertises support for .pdf / .docx / .pages and
the tutorial documents that. This commit makes the demo actually
exercise those formats end-to-end.

Changes:
- packages/workspace/package.json: declare pdf-parse ^2.0.0 and
  mammoth ^1.11.0 as runtime deps. These are dynamically
  imported by @langchain/community's PDFLoader / DocxLoader (via
  @melandlabs/rag); without them the multi-format parsing path
  ERR_MODULE_NOT_FOUNDs at runtime.
- examples/fixtures/workspace-wiki/law-brief.pdf (53 KB, pandoc
  output of law-clause.md).
- examples/fixtures/workspace-wiki/signed-addendum.docx (3.6 KB,
  textutil-generated).
- examples/fixtures/workspace-wiki.README.md: moved out of the
  indexed folder so the walker doesn't pick it up as a .md
  resource. Documents the role of every fixture file and the
  supported format matrix.
- examples/src/simple/22-workspace.ts: extend FIXTURE_FILES to
  include law-brief.pdf + signed-addendum.docx; bump the
  >=3 assertions to >=5 (files_scanned, files_added,
  listWorkspaceResources, sha256-dedup re-run).

Verified locally with the workspace package symlinked into
examples/node_modules:
- listOkfFolderResources → 5 resources (3 .md, 1 .pdf, 1 .docx),
  body extraction succeeds for all five.
- workspace demo 12/12 [OK]: scanned=5, added=5, lexical search
  for 'limitation' returns 3 hits (now includes the DOCX),
  cross-file expansion surfaces a + b + signed-addendum, re-run
  reports all 5 unchanged.
- workspace tests 16/16
- pnpm -r lint clean, pnpm format:check clean

Not included (not supported by the parsers-adapter yet):
.xlsx, .numbers, raster.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- parsers-adapter: dynamic-import SheetJS, convert every sheet to CSV
  with a `# Sheet: <name>` header so the chunker preserves boundaries;
  .numbers goes through macOS `textutil -convert xlsx` then SheetJS
- okf-backend: extend SUPPORTED_EXTENSIONS with .xlsx / .xls / .numbers
  and map them to `resource_type: spreadsheet`
- fixture: add contract-terms.xlsx (2 sheets — Liability Caps +
  Indemnification) plus a regen script; demo now asserts ≥ 6 resources
  and the cross-file search hits the spreadsheet alongside the .md/.pdf/.docx
- examples/package.json: add @melandlabs/workspace as a dev dep so the
  dynamic import in 22-workspace.ts resolves to the local symlink in dev
  (the npm-installed smoke test still falls through to skip cleanly)
…context transitively

The smoke test does `pnpm install --ignore-workspace` to verify the
published packages resolve cleanly. Listing `@melandlabs/workspace` as
a direct dep with `workspace:*` made that step fail before the demo
could even try its skip-on-missing-impot fallback.

In monorepo dev the symlink is created transitively through
`@melandlabs/opencontext`'s workspace dep, so the dynamic import in
22-workspace.ts still resolves. The npm-installed smoke test simply
hits the existing skip path the demo was already designed for.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant