botholomew 0.16.4 → 0.18.0
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/README.md +46 -41
- package/package.json +4 -9
- package/src/chat/agent.ts +37 -40
- package/src/chat/session.ts +10 -10
- package/src/cli.ts +0 -2
- package/src/commands/capabilities.ts +35 -33
- package/src/commands/context.ts +133 -221
- package/src/commands/init.ts +22 -1
- package/src/commands/mcpx.ts +21 -8
- package/src/commands/nuke.ts +52 -15
- package/src/commands/prepare.ts +16 -13
- package/src/config/loader.ts +1 -8
- package/src/config/schemas.ts +6 -0
- package/src/constants.ts +16 -32
- package/src/init/index.ts +52 -27
- package/src/mcpx/client.ts +21 -5
- package/src/mem/client.ts +33 -0
- package/src/{context → prompts}/capabilities.ts +11 -7
- package/src/schedules/store.ts +1 -1
- package/src/tasks/store.ts +1 -1
- package/src/threads/store.ts +1 -1
- package/src/tools/capabilities/refresh.ts +1 -1
- package/src/tools/membot/adapter.ts +111 -0
- package/src/tools/membot/copy.ts +59 -0
- package/src/tools/membot/count_lines.ts +53 -0
- package/src/tools/membot/edit.ts +72 -0
- package/src/tools/membot/exists.ts +54 -0
- package/src/tools/membot/index.ts +26 -0
- package/src/tools/{context → membot}/pipe.ts +34 -32
- package/src/tools/registry.ts +6 -37
- package/src/tools/tool.ts +6 -8
- package/src/tui/App.tsx +3 -4
- package/src/tui/components/ContextPanel.tsx +109 -226
- package/src/tui/components/HelpPanel.tsx +2 -2
- package/src/tui/components/StatusBar.tsx +0 -6
- package/src/tui/components/ThreadPanel.tsx +8 -7
- package/src/tui/wrapDetail.ts +11 -0
- package/src/worker/heartbeat.ts +0 -20
- package/src/worker/index.ts +13 -13
- package/src/worker/llm.ts +7 -9
- package/src/worker/prompt.ts +25 -13
- package/src/worker/spawn.ts +1 -1
- package/src/worker/tick.ts +10 -9
- package/src/commands/db.ts +0 -119
- package/src/commands/with-db.ts +0 -22
- package/src/context/chunker.ts +0 -275
- package/src/context/embedder-impl.ts +0 -100
- package/src/context/embedder.ts +0 -9
- package/src/context/fetcher-errors.ts +0 -8
- package/src/context/fetcher.ts +0 -515
- package/src/context/locks.ts +0 -146
- package/src/context/markdown-converter.ts +0 -186
- package/src/context/reindex.ts +0 -198
- package/src/context/store.ts +0 -841
- package/src/context/url-utils.ts +0 -25
- package/src/db/connection.ts +0 -255
- package/src/db/doctor.ts +0 -235
- package/src/db/embeddings.ts +0 -317
- package/src/db/query.ts +0 -56
- package/src/db/schema.ts +0 -93
- package/src/db/sql/1-core_tables.sql +0 -53
- package/src/db/sql/10-dedupe_context_items.sql +0 -26
- package/src/db/sql/11-rebuild_hnsw.sql +0 -8
- package/src/db/sql/12-workers.sql +0 -66
- package/src/db/sql/13-drive-paths.sql +0 -47
- package/src/db/sql/14-drop_hnsw_index.sql +0 -8
- package/src/db/sql/15-fts_index.sql +0 -8
- package/src/db/sql/16-source_url.sql +0 -7
- package/src/db/sql/17-worker_log_path.sql +0 -3
- package/src/db/sql/18-reset_embeddings_for_local.sql +0 -39
- package/src/db/sql/19-disk_backed_index.sql +0 -36
- package/src/db/sql/2-logging_tables.sql +0 -24
- package/src/db/sql/20-drop_db_tables_for_files.sql +0 -19
- package/src/db/sql/3-daemon_state.sql +0 -5
- package/src/db/sql/4-unique_context_path.sql +0 -1
- package/src/db/sql/5-reset_embeddings_for_openai.sql +0 -1
- package/src/db/sql/6-vss_index.sql +0 -7
- package/src/db/sql/7-drop_embeddings_fk.sql +0 -23
- package/src/db/sql/8-task_output.sql +0 -1
- package/src/db/sql/9-source-type.sql +0 -1
- package/src/tools/context/read-large-result.ts +0 -33
- package/src/tools/dir/create.ts +0 -47
- package/src/tools/dir/size.ts +0 -77
- package/src/tools/dir/tree.ts +0 -124
- package/src/tools/file/copy.ts +0 -73
- package/src/tools/file/count-lines.ts +0 -54
- package/src/tools/file/delete.ts +0 -83
- package/src/tools/file/edit.ts +0 -76
- package/src/tools/file/exists.ts +0 -33
- package/src/tools/file/info.ts +0 -66
- package/src/tools/file/move.ts +0 -66
- package/src/tools/file/read.ts +0 -67
- package/src/tools/file/write.ts +0 -58
- package/src/tools/search/fuse.ts +0 -96
- package/src/tools/search/index.ts +0 -127
- package/src/tools/search/regexp.ts +0 -82
- package/src/tools/search/semantic.ts +0 -167
- /package/src/{db → utils}/uuid.ts +0 -0
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import type { BotholomewConfig } from "../../config/schemas.ts";
|
|
2
|
-
import { embed, embedSingle } from "../../context/embedder.ts";
|
|
3
|
-
import { listContextDir, readContextFile } from "../../context/store.ts";
|
|
4
|
-
import { withDb } from "../../db/connection.ts";
|
|
5
|
-
import { indexStats, searchSemantic } from "../../db/embeddings.ts";
|
|
6
|
-
import { globToRegex } from "./regexp.ts";
|
|
7
|
-
|
|
8
|
-
export interface SemanticHit {
|
|
9
|
-
path: string;
|
|
10
|
-
chunk_index: number;
|
|
11
|
-
chunk_content: string;
|
|
12
|
-
score: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface SemanticOptions {
|
|
16
|
-
query: string;
|
|
17
|
-
scope?: string;
|
|
18
|
-
glob?: string;
|
|
19
|
-
limit?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// On-the-fly fallback (used when the index sidecar is empty / stale).
|
|
23
|
-
// One chunk per file truncated to MAX_CHARS; the indexed path is much faster
|
|
24
|
-
// and supports proper chunking via `botholomew context reindex`.
|
|
25
|
-
const MAX_CHARS = 4_000;
|
|
26
|
-
const MAX_FILES_TO_EMBED = 200;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Semantic search over `context/`. Prefers the persistent index sidecar
|
|
30
|
-
* (`context_index` table, populated by `botholomew context reindex`) when
|
|
31
|
-
* it has rows. Falls back to embedding files on the fly so a fresh project
|
|
32
|
-
* still gets useful results before the user has reindexed once.
|
|
33
|
-
*/
|
|
34
|
-
export async function runSemantic(
|
|
35
|
-
projectDir: string,
|
|
36
|
-
config: Required<BotholomewConfig>,
|
|
37
|
-
dbPath: string | null,
|
|
38
|
-
options: SemanticOptions,
|
|
39
|
-
): Promise<SemanticHit[]> {
|
|
40
|
-
if (dbPath) {
|
|
41
|
-
const indexed = await tryIndexedSearch(dbPath, config, options);
|
|
42
|
-
if (indexed) return indexed;
|
|
43
|
-
}
|
|
44
|
-
return runOnTheFly(projectDir, config, options);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function tryIndexedSearch(
|
|
48
|
-
dbPath: string,
|
|
49
|
-
config: Required<BotholomewConfig>,
|
|
50
|
-
options: SemanticOptions,
|
|
51
|
-
): Promise<SemanticHit[] | null> {
|
|
52
|
-
let stats: Awaited<ReturnType<typeof indexStats>>;
|
|
53
|
-
try {
|
|
54
|
-
stats = await withDb(dbPath, indexStats);
|
|
55
|
-
} catch {
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
if (stats.embedded === 0) return null;
|
|
59
|
-
|
|
60
|
-
const queryVec = await embedSingle(options.query, config);
|
|
61
|
-
const limit = options.limit ?? 100;
|
|
62
|
-
const rows = await withDb(dbPath, (conn) =>
|
|
63
|
-
searchSemantic(conn, queryVec, limit * 4),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
const globRegex = options.glob ? globToRegex(options.glob) : null;
|
|
67
|
-
const scope = options.scope
|
|
68
|
-
? options.scope.endsWith("/")
|
|
69
|
-
? options.scope
|
|
70
|
-
: `${options.scope}/`
|
|
71
|
-
: null;
|
|
72
|
-
|
|
73
|
-
const filtered: SemanticHit[] = [];
|
|
74
|
-
for (const r of rows) {
|
|
75
|
-
if (scope && !r.path.startsWith(scope) && r.path !== options.scope) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (globRegex) {
|
|
79
|
-
const filename = r.path.split("/").pop() ?? "";
|
|
80
|
-
if (!globRegex.test(filename)) continue;
|
|
81
|
-
}
|
|
82
|
-
filtered.push({
|
|
83
|
-
path: r.path,
|
|
84
|
-
chunk_index: r.chunk_index,
|
|
85
|
-
chunk_content: r.chunk_content,
|
|
86
|
-
score: r.score,
|
|
87
|
-
});
|
|
88
|
-
if (filtered.length >= limit) break;
|
|
89
|
-
}
|
|
90
|
-
return filtered;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function runOnTheFly(
|
|
94
|
-
projectDir: string,
|
|
95
|
-
config: Required<BotholomewConfig>,
|
|
96
|
-
options: SemanticOptions,
|
|
97
|
-
): Promise<SemanticHit[]> {
|
|
98
|
-
const entries = await listContextDir(projectDir, options.scope ?? "", {
|
|
99
|
-
recursive: true,
|
|
100
|
-
});
|
|
101
|
-
const globRegex = options.glob ? globToRegex(options.glob) : null;
|
|
102
|
-
|
|
103
|
-
const candidates: Array<{ path: string; content: string }> = [];
|
|
104
|
-
for (const entry of entries) {
|
|
105
|
-
if (entry.is_directory) continue;
|
|
106
|
-
if (!entry.is_textual) continue;
|
|
107
|
-
if (globRegex) {
|
|
108
|
-
const filename = entry.path.split("/").pop() ?? "";
|
|
109
|
-
if (!globRegex.test(filename)) continue;
|
|
110
|
-
}
|
|
111
|
-
let content: string;
|
|
112
|
-
try {
|
|
113
|
-
content = await readContextFile(projectDir, entry.path);
|
|
114
|
-
} catch {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (content.trim().length === 0) continue;
|
|
118
|
-
candidates.push({
|
|
119
|
-
path: entry.path,
|
|
120
|
-
content: content.slice(0, MAX_CHARS),
|
|
121
|
-
});
|
|
122
|
-
if (candidates.length >= MAX_FILES_TO_EMBED) break;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (candidates.length === 0) return [];
|
|
126
|
-
|
|
127
|
-
const [queryVec, fileVecs] = await Promise.all([
|
|
128
|
-
embedSingle(options.query, config),
|
|
129
|
-
embed(
|
|
130
|
-
candidates.map((c) => c.content),
|
|
131
|
-
config,
|
|
132
|
-
),
|
|
133
|
-
]);
|
|
134
|
-
|
|
135
|
-
const limit = options.limit ?? 100;
|
|
136
|
-
const scored: SemanticHit[] = [];
|
|
137
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
138
|
-
const c = candidates[i];
|
|
139
|
-
const v = fileVecs[i];
|
|
140
|
-
if (!c || !v) continue;
|
|
141
|
-
const score = cosine(queryVec, v);
|
|
142
|
-
scored.push({
|
|
143
|
-
path: c.path,
|
|
144
|
-
chunk_index: 0,
|
|
145
|
-
chunk_content: c.content,
|
|
146
|
-
score,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
scored.sort((a, b) => b.score - a.score);
|
|
150
|
-
return scored.slice(0, limit);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function cosine(a: number[], b: number[]): number {
|
|
154
|
-
let dot = 0;
|
|
155
|
-
let na = 0;
|
|
156
|
-
let nb = 0;
|
|
157
|
-
const len = Math.min(a.length, b.length);
|
|
158
|
-
for (let i = 0; i < len; i++) {
|
|
159
|
-
const ai = a[i] ?? 0;
|
|
160
|
-
const bi = b[i] ?? 0;
|
|
161
|
-
dot += ai * bi;
|
|
162
|
-
na += ai * ai;
|
|
163
|
-
nb += bi * bi;
|
|
164
|
-
}
|
|
165
|
-
const denom = Math.sqrt(na) * Math.sqrt(nb);
|
|
166
|
-
return denom === 0 ? 0 : dot / denom;
|
|
167
|
-
}
|
|
File without changes
|