Skip to content

feat: file content source for search — bounded passive-scope file index - #17

Merged
wefio merged 9 commits into
mainfrom
feat/file-content-source
Sep 1, 2026
Merged

feat: file content source for search — bounded passive-scope file index#17
wefio merged 9 commits into
mainfrom
feat/file-content-source

Conversation

@wefio

@wefio wefio commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Design proposal + core implementation for adding a file content source to
mg search.

Design (docs/design/file-content-source-design.md)

Bounded passive-scan file index with a learned scope:

  1. *.nmg-search-scope* — path-list file opposite to .gitignore\ (includes hot zones); editable, auto-grown.
  2. Bounded passive scan — crawler indexes files under the scope (FTS, lexical-first), incremental via content hash, never outside scope.
  3. Scope evolution — Agent grep/read behaviour teaches the scope (the Agent is the first crawler).
  4. Search fusion — memory + file results, separate partition (not mixed into memory ranking).

Implementation (committed)

  • \src/core/file-index.ts: FileIndex — scope parse, bounded scan, trigram FTS (external-content table), incremental crawl, search with excerpts, addScopePath.
  • \ ypes.ts: \FileHit\ + \MemoryContext.files.
  • \service.ts: #searchProjectFiles\ attaches file hits when projectDir supplied; #fileIndexes\ lifecycle.
  • \stg.ts: merge passes \ iles\ through.
  • \search-projection.ts\ + \�gent-surface.ts\ + \main.ts: file hits render as a separate \ ile=\ partition.
  • \ ests/core/file-index.test.ts: 7 cases (parse, resolve, collect, crawl/search/incremental, change/removal, scope growth, excludes).
  • .nmg-search-scope: initial scope (src/core, src/integration).

Verified: tsc clean, file-index 7/7, agent-surface + session-AG 20/20, end-to-end crawl+search on the real repo works (62 files indexed; \hybridScore\ hits search-ranking/retrieval/qpp).

Pending

Proposes a bounded passive-scan file index as a second nmg search content source: .nmg-search-scope hot-zone file (semantically opposite to .gitignore), scope learned from Agent grep/read behaviour, lexical-first FTS index, git-status incremental updates, and memory+file result fusion. Also anchors .nmg/ to the repo root in .gitignore so nested .nmg dirs are not ignored. Mirrors Zoekt/CocoIndex patterns at small scale; lexical-first per the grep-vs-vector finding.
Adds a second content source to nmg search: a bounded passive-scan FTS index over the project's own files, so an Agent does not have to manually re-discover a changing project.

- src/core/file-index.ts: FileIndex with .nmg-search-scope (path-list, semantically opposite to .gitignore), incremental crawl via content hash, trigram FTS (external-content table), search with excerpts, and addScopePath for scope growth (the Agent is the first crawler).
- types.ts: FileHit + MemoryContext.files (separate from memory results).
- service.ts: #searchProjectFiles attaches file hits when projectDir is supplied; #fileIndexes lifecycle.
- stg.ts: mergeStgLtgContexts passes files through.
- search-projection.ts + agent-surface.ts + main.ts: file hits render as a separate file= partition (not mixed into memory ranking).
- tests: file-index.test.ts (7 cases: scope parse, resolve, collect, crawl/search/incremental, change/removal, scope growth, excludes).

Files are a search index, not memory: content never enters STG/LTG.
The DSH adapter observes grep/read tool results (tools/result event) and feeds non-empty hit paths into the file content source scope via a provided FileIndex service. The Agent's own search behaviour grows .nmg-search-scope, closing the scope-evolution loop of the file content source design. The listener observes only (never intercepts), parses defensively, and degrades to a no-op when the FileIndex service is absent. FileIndex is created per-plugin and closed on dispose.
Accept exec.toolName and exec.input as defensive aliases for exec.name/exec.arguments (different host integrations may use either), with comments noting the confirmed dsh-tools field names (grep/read from dsh-tool-fs-search/dsh-tool-fs).
@wefio wefio changed the title docs: file content source for search — design proposal feat: file content source for search — bounded passive-scope file index Sep 1, 2026
Old stores created before the opaque-id migration still carry UNIQUE(task_id, sequence), but new writes store sequence=0 (the column is legacy), so the second entry of any channel failed with a UNIQUE constraint error. ensureTaskBoardColumns now rebuilds the table without that constraint when the legacy autoindex is detected, preserving all data and columns. Verified on the real user DB: two entries on the same task now insert cleanly.
collectScopePaths → guard clauses + composed helpers (grepHitPaths/readHitPaths), applying the guard-clause and composed-function refactoring techniques to bring the observer below the complexity threshold.
@wefio
wefio merged commit b9e2d3f into main Sep 1, 2026
7 checks passed
wefio added a commit that referenced this pull request Sep 1, 2026
Guides reducing cyclomatic complexity (CodeFactor Complex Method / npm run complexity:gate) using guard clauses, composed functions, lookup tables, and modern TS features, with the worked example of the dsh-nmg scope observer (37 → below threshold). Validated on PR #17.
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