@comfanion/usethis_search 3.0.0-dev.4 → 3.0.0-dev.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.ts CHANGED
@@ -2,7 +2,6 @@ import type { Plugin } from "@opencode-ai/plugin"
2
2
 
3
3
  import search from "./tools/search"
4
4
  import codeindex from "./tools/codeindex"
5
- import readInterceptor from "./tools/read-interceptor"
6
5
  import FileIndexerPlugin from "./file-indexer"
7
6
 
8
7
  const UsethisSearchPlugin: Plugin = async ({ directory, client }) => {
@@ -19,7 +18,6 @@ const UsethisSearchPlugin: Plugin = async ({ directory, client }) => {
19
18
  tool: {
20
19
  search,
21
20
  codeindex,
22
- read: readInterceptor,
23
21
  },
24
22
 
25
23
  event: async (args: any) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/usethis_search",
3
- "version": "3.0.0-dev.4",
3
+ "version": "3.0.0-dev.6",
4
4
  "description": "OpenCode plugin: semantic search with graph-based context (v3: graph relations, 1-hop context, LSP + regex analyzers)",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -16,7 +16,6 @@
16
16
  "file-indexer.ts",
17
17
  "tools/search.ts",
18
18
  "tools/codeindex.ts",
19
- "tools/read-interceptor.ts",
20
19
  "vectorizer/index.js",
21
20
  "vectorizer/content-cleaner.ts",
22
21
  "vectorizer/metadata-extractor.ts",
@@ -603,7 +603,13 @@ class CodebaseIndexer {
603
603
  if (!tables.includes(tableName)) return null;
604
604
 
605
605
  const table = await this.db.openTable(tableName);
606
- const allRows = await table.search([0]).limit(100000).execute();
606
+ let allRows;
607
+ try {
608
+ allRows = await table.search([0]).limit(100000).execute();
609
+ } catch (e) {
610
+ if (DEBUG) console.log("[vectorizer] BM25 index build failed (corrupted table?):", e.message);
611
+ return null;
612
+ }
607
613
 
608
614
  if (allRows.length === 0) return null;
609
615
 
@@ -643,7 +649,14 @@ class CodebaseIndexer {
643
649
  (options.tags && options.tags.length > 0);
644
650
  const isHybrid = HYBRID_CONFIG.enabled || options.hybrid;
645
651
  const fetchLimit = (hasFilters || isHybrid) ? Math.max(limit * 3, 50) : limit;
646
- let results = await table.search(queryEmbedding).limit(fetchLimit).execute();
652
+ let results;
653
+ try {
654
+ results = await table.search(queryEmbedding).limit(fetchLimit).execute();
655
+ } catch (e) {
656
+ // LanceDB schema error (e.g. missing vector column) — index is corrupted
657
+ if (DEBUG) console.log("[vectorizer] Vector search failed (corrupted index?):", e.message);
658
+ return [];
659
+ }
647
660
 
648
661
  // ── Hybrid search ───────────────────────────────────────────────────────
649
662
  if (HYBRID_CONFIG.enabled || options.hybrid) {
@@ -833,7 +846,13 @@ class CodebaseIndexer {
833
846
  if (!tables.includes(tableName)) return null;
834
847
 
835
848
  const table = await this.db.openTable(tableName);
836
- const rows = await table.search([0]).limit(100000).execute();
849
+ let rows;
850
+ try {
851
+ rows = await table.search([0]).limit(100000).execute();
852
+ } catch (e) {
853
+ if (DEBUG) console.log("[vectorizer] Chunk cache build failed (corrupted table?):", e.message);
854
+ return null;
855
+ }
837
856
  this._chunkCache = new Map();
838
857
  for (const row of rows) {
839
858
  if (row.chunk_id) {
@@ -1,127 +0,0 @@
1
- import { tool } from "@opencode-ai/plugin"
2
- import path from "path"
3
- import fs from "fs/promises"
4
-
5
- import { CodebaseIndexer } from "../vectorizer/index.js"
6
-
7
- // FR-043: Logging for intercepted Read() calls
8
- const DEBUG = process.env.DEBUG?.includes("vectorizer") || process.env.DEBUG === "*"
9
-
10
- interface ReadLogEntry {
11
- timestamp: number
12
- filePath: string
13
- relPath: string
14
- chunksFound: number
15
- relatedContextCount: number
16
- durationMs: number
17
- fallback: boolean
18
- }
19
-
20
- const LOG_MAX_ENTRIES = 500
21
-
22
- /**
23
- * Append a log entry to the Read() interception log file.
24
- * Non-blocking, non-fatal — errors are silently ignored.
25
- */
26
- async function logReadInterception(projectRoot: string, entry: ReadLogEntry): Promise<void> {
27
- try {
28
- const logPath = path.join(projectRoot, ".opencode", "vectors", "read-intercept.log.json")
29
- await fs.mkdir(path.dirname(logPath), { recursive: true })
30
-
31
- let entries: ReadLogEntry[] = []
32
- try {
33
- const raw = await fs.readFile(logPath, "utf-8")
34
- entries = JSON.parse(raw)
35
- } catch {
36
- // file doesn't exist or is invalid — start fresh
37
- }
38
-
39
- entries.push(entry)
40
- // Cap log size to avoid unbounded growth
41
- if (entries.length > LOG_MAX_ENTRIES) {
42
- entries = entries.slice(-LOG_MAX_ENTRIES)
43
- }
44
-
45
- await fs.writeFile(logPath, JSON.stringify(entries, null, 2), "utf-8")
46
- } catch {
47
- // non-fatal — logging must never break Read
48
- }
49
- }
50
-
51
- export default tool({
52
- description: `Read file with graph-aware context attachment. When available, this tool searches the file in the index and returns content + related context from the graph (imports, links, etc.).
53
-
54
- Use this instead of the standard Read tool for better context awareness.`,
55
-
56
- args: {
57
- filePath: tool.schema.string().describe("Path to the file to read"),
58
- },
59
-
60
- async execute(args) {
61
- const startTime = Date.now()
62
- const projectRoot = process.cwd()
63
- const filePath = path.isAbsolute(args.filePath) ? args.filePath : path.join(projectRoot, args.filePath)
64
-
65
- const relPath = path.relative(projectRoot, filePath)
66
-
67
- if (DEBUG) {
68
- console.log(`[read-interceptor] Intercepted Read("${relPath}")`)
69
- }
70
-
71
- const indexer = await new CodebaseIndexer(projectRoot, "code").init()
72
- const results = await indexer.search(relPath, 20, false, {})
73
- const fileChunks = results.filter(r => r.file === relPath)
74
- await indexer.unloadModel()
75
-
76
- const allRelated = fileChunks
77
- .flatMap(c => c.relatedContext || [])
78
- .filter((r, i, arr) => arr.findIndex(x => x.chunk_id === r.chunk_id) === i)
79
-
80
- const durationMs = Date.now() - startTime
81
- const fallback = fileChunks.length === 0
82
-
83
- // FR-043: Log the interception asynchronously (non-blocking)
84
- logReadInterception(projectRoot, {
85
- timestamp: startTime,
86
- filePath: args.filePath,
87
- relPath,
88
- chunksFound: fileChunks.length,
89
- relatedContextCount: allRelated.length,
90
- durationMs,
91
- fallback,
92
- }).catch(() => {})
93
-
94
- if (DEBUG) {
95
- console.log(
96
- `[read-interceptor] ${relPath}: ${fileChunks.length} chunks, ${allRelated.length} related, ${durationMs}ms${fallback ? " (fallback)" : ""}`
97
- )
98
- }
99
-
100
- if (fallback) {
101
- return `File "${relPath}" not indexed. Use original Read tool or run codeindex({ action: "reindex", index: "code" })`
102
- }
103
-
104
- let output = `## ${relPath}\n\n`
105
-
106
- output += `### Content\n\n`
107
- for (const chunk of fileChunks) {
108
- output += chunk.content + "\n\n"
109
- }
110
-
111
- if (allRelated.length > 0) {
112
- output += `### Related Context\n\n`
113
- for (const rel of allRelated) {
114
- const snippet = rel.content.length > 300
115
- ? rel.content.substring(0, 300) + "..."
116
- : rel.content
117
- output += `**${rel.file}** (${rel.relation})\n`
118
- output += `\`\`\`\n${snippet}\n\`\`\`\n\n`
119
- }
120
- }
121
-
122
- return output
123
- },
124
- })
125
-
126
- // Export for testing
127
- export { logReadInterception, ReadLogEntry }