botholomew 0.16.4 → 0.17.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.
Files changed (95) hide show
  1. package/README.md +46 -41
  2. package/package.json +3 -8
  3. package/src/chat/agent.ts +37 -40
  4. package/src/chat/session.ts +8 -8
  5. package/src/cli.ts +0 -2
  6. package/src/commands/capabilities.ts +32 -32
  7. package/src/commands/context.ts +124 -223
  8. package/src/commands/mcpx.ts +1 -1
  9. package/src/commands/nuke.ts +44 -15
  10. package/src/commands/prepare.ts +17 -13
  11. package/src/config/loader.ts +1 -8
  12. package/src/constants.ts +16 -32
  13. package/src/init/index.ts +11 -14
  14. package/src/mem/client.ts +17 -0
  15. package/src/{context → prompts}/capabilities.ts +11 -7
  16. package/src/schedules/store.ts +1 -1
  17. package/src/tasks/store.ts +1 -1
  18. package/src/threads/store.ts +1 -1
  19. package/src/tools/capabilities/refresh.ts +1 -1
  20. package/src/tools/membot/adapter.ts +111 -0
  21. package/src/tools/membot/copy.ts +59 -0
  22. package/src/tools/membot/count_lines.ts +53 -0
  23. package/src/tools/membot/edit.ts +72 -0
  24. package/src/tools/membot/exists.ts +54 -0
  25. package/src/tools/membot/index.ts +26 -0
  26. package/src/tools/{context → membot}/pipe.ts +34 -32
  27. package/src/tools/registry.ts +6 -37
  28. package/src/tools/tool.ts +6 -8
  29. package/src/tui/App.tsx +3 -4
  30. package/src/tui/components/ContextPanel.tsx +109 -226
  31. package/src/tui/components/HelpPanel.tsx +2 -2
  32. package/src/tui/components/StatusBar.tsx +0 -6
  33. package/src/tui/components/ThreadPanel.tsx +8 -7
  34. package/src/tui/wrapDetail.ts +11 -0
  35. package/src/worker/heartbeat.ts +0 -20
  36. package/src/worker/index.ts +11 -11
  37. package/src/worker/llm.ts +7 -9
  38. package/src/worker/prompt.ts +25 -13
  39. package/src/worker/spawn.ts +1 -1
  40. package/src/worker/tick.ts +10 -9
  41. package/src/commands/db.ts +0 -119
  42. package/src/commands/with-db.ts +0 -22
  43. package/src/context/chunker.ts +0 -275
  44. package/src/context/embedder-impl.ts +0 -100
  45. package/src/context/embedder.ts +0 -9
  46. package/src/context/fetcher-errors.ts +0 -8
  47. package/src/context/fetcher.ts +0 -515
  48. package/src/context/locks.ts +0 -146
  49. package/src/context/markdown-converter.ts +0 -186
  50. package/src/context/reindex.ts +0 -198
  51. package/src/context/store.ts +0 -841
  52. package/src/context/url-utils.ts +0 -25
  53. package/src/db/connection.ts +0 -255
  54. package/src/db/doctor.ts +0 -235
  55. package/src/db/embeddings.ts +0 -317
  56. package/src/db/query.ts +0 -56
  57. package/src/db/schema.ts +0 -93
  58. package/src/db/sql/1-core_tables.sql +0 -53
  59. package/src/db/sql/10-dedupe_context_items.sql +0 -26
  60. package/src/db/sql/11-rebuild_hnsw.sql +0 -8
  61. package/src/db/sql/12-workers.sql +0 -66
  62. package/src/db/sql/13-drive-paths.sql +0 -47
  63. package/src/db/sql/14-drop_hnsw_index.sql +0 -8
  64. package/src/db/sql/15-fts_index.sql +0 -8
  65. package/src/db/sql/16-source_url.sql +0 -7
  66. package/src/db/sql/17-worker_log_path.sql +0 -3
  67. package/src/db/sql/18-reset_embeddings_for_local.sql +0 -39
  68. package/src/db/sql/19-disk_backed_index.sql +0 -36
  69. package/src/db/sql/2-logging_tables.sql +0 -24
  70. package/src/db/sql/20-drop_db_tables_for_files.sql +0 -19
  71. package/src/db/sql/3-daemon_state.sql +0 -5
  72. package/src/db/sql/4-unique_context_path.sql +0 -1
  73. package/src/db/sql/5-reset_embeddings_for_openai.sql +0 -1
  74. package/src/db/sql/6-vss_index.sql +0 -7
  75. package/src/db/sql/7-drop_embeddings_fk.sql +0 -23
  76. package/src/db/sql/8-task_output.sql +0 -1
  77. package/src/db/sql/9-source-type.sql +0 -1
  78. package/src/tools/context/read-large-result.ts +0 -33
  79. package/src/tools/dir/create.ts +0 -47
  80. package/src/tools/dir/size.ts +0 -77
  81. package/src/tools/dir/tree.ts +0 -124
  82. package/src/tools/file/copy.ts +0 -73
  83. package/src/tools/file/count-lines.ts +0 -54
  84. package/src/tools/file/delete.ts +0 -83
  85. package/src/tools/file/edit.ts +0 -76
  86. package/src/tools/file/exists.ts +0 -33
  87. package/src/tools/file/info.ts +0 -66
  88. package/src/tools/file/move.ts +0 -66
  89. package/src/tools/file/read.ts +0 -67
  90. package/src/tools/file/write.ts +0 -58
  91. package/src/tools/search/fuse.ts +0 -96
  92. package/src/tools/search/index.ts +0 -127
  93. package/src/tools/search/regexp.ts +0 -82
  94. package/src/tools/search/semantic.ts +0 -167
  95. /package/src/{db → utils}/uuid.ts +0 -0
@@ -1,10 +1,10 @@
1
+ import { isHelpfulError } from "membot";
1
2
  import { z } from "zod";
2
- import { PathConflictError, writeContextFile } from "../../context/store.ts";
3
3
  import { getTool, type ToolDefinition } from "../tool.ts";
4
4
 
5
5
  const PREVIEW_CHARS = 200;
6
6
  const ERROR_MESSAGE_CAP = 2000;
7
- const TOOL_NAME = "pipe_to_context";
7
+ const TOOL_NAME = "membot_pipe";
8
8
 
9
9
  function truncate(s: string, cap: number): string {
10
10
  if (s.length <= cap) return s;
@@ -15,29 +15,28 @@ const inputSchema = z.object({
15
15
  tool_name: z
16
16
  .string()
17
17
  .describe(
18
- "Name of the tool to dispatch. Its full output is piped into a file under context/; you (the LLM) will only see the storage acknowledgment, never the raw bytes.",
18
+ "Name of the tool to dispatch. Its full output is captured and written to membot under `logical_path`; you (the LLM) only see the storage acknowledgment, never the raw bytes.",
19
19
  ),
20
20
  tool_input: z
21
21
  .record(z.string(), z.unknown())
22
22
  .describe(
23
23
  "Arguments to pass to the inner tool (same shape as a normal call).",
24
24
  ),
25
- path: z
25
+ logical_path: z
26
26
  .string()
27
27
  .describe(
28
- "Project-relative path under context/ to write the captured output to (e.g. 'gdoc/quarterly-plan.md').",
28
+ "Destination logical_path under which to store the captured output (e.g. 'gdoc/quarterly-plan.md'). Creates a new version on every call.",
29
29
  ),
30
- on_conflict: z
31
- .enum(["error", "overwrite"])
30
+ change_note: z
31
+ .string()
32
32
  .optional()
33
- .describe(
34
- "What to do if a file already exists at this path. Defaults to 'error'. Pass 'overwrite' to replace.",
35
- ),
33
+ .describe("Free-text note attached to the new version."),
36
34
  });
37
35
 
38
36
  const outputSchema = z.object({
39
37
  is_error: z.boolean(),
40
- path: z.string().optional(),
38
+ logical_path: z.string().optional(),
39
+ version_id: z.string().optional(),
41
40
  bytes_written: z.number().optional(),
42
41
  preview: z
43
42
  .string()
@@ -52,18 +51,19 @@ const outputSchema = z.object({
52
51
  "forbidden_tool",
53
52
  "invalid_input",
54
53
  "inner_tool_error",
55
- "path_conflict",
54
+ "write_failed",
55
+ "internal_error",
56
56
  ])
57
57
  .optional(),
58
58
  message: z.string().optional(),
59
59
  next_action_hint: z.string().optional(),
60
60
  });
61
61
 
62
- export const pipeToContextTool = {
62
+ export const membotPipeTool = {
63
63
  name: TOOL_NAME,
64
64
  description:
65
- "[[ bash equivalent command: cmd > file ]] Run another tool and pipe its full output directly into a file under context/, without the result flowing through the conversation. Use this when you need a large tool output (Google Docs via mcp_exec, web fetches, search dumps) saved for later inspection but you do NOT need to read the bytes yourself. You'll only see the storage ack (path, size, short preview).",
66
- group: "context",
65
+ "[[ bash equivalent command: cmd > file ]] Run another tool and pipe its full output directly into a membot logical_path, without the result flowing through the conversation. Use this when you need a large tool output (Google Docs via mcp_exec, web fetches, search dumps) captured for later inspection but you do NOT need to read the bytes yourself. You'll only see the storage ack (logical_path, version_id, short preview).",
66
+ group: "membot",
67
67
  inputSchema,
68
68
  outputSchema,
69
69
  execute: async (input, ctx) => {
@@ -82,9 +82,9 @@ export const pipeToContextTool = {
82
82
  return {
83
83
  is_error: true,
84
84
  error_type: "forbidden_tool",
85
- message: `Tool "${inner.name}" cannot be piped (terminal tools and pipe_to_context itself are not allowed).`,
85
+ message: `Tool "${inner.name}" cannot be piped (terminal tools and ${TOOL_NAME} itself are not allowed).`,
86
86
  next_action_hint:
87
- "Pipe a non-terminal tool (search, mcp_exec, etc.) instead.",
87
+ "Pipe a non-terminal tool (mcp_exec, membot_read, etc.) instead.",
88
88
  };
89
89
  }
90
90
 
@@ -143,30 +143,32 @@ export const pipeToContextTool = {
143
143
  }
144
144
 
145
145
  try {
146
- const entry = await writeContextFile(
147
- ctx.projectDir,
148
- input.path,
149
- innerOutput,
150
- { onConflict: input.on_conflict ?? "error" },
151
- );
146
+ const written = await ctx.mem.write({
147
+ logical_path: input.logical_path,
148
+ content: innerOutput,
149
+ change_note: input.change_note,
150
+ });
152
151
  return {
153
152
  is_error: false,
154
- path: entry.path,
155
- bytes_written: innerOutput.length,
153
+ logical_path: written.logical_path,
154
+ version_id: written.version_id,
155
+ bytes_written: written.size_bytes,
156
156
  preview: innerOutput.slice(0, PREVIEW_CHARS),
157
157
  };
158
158
  } catch (err) {
159
- if (err instanceof PathConflictError) {
159
+ if (isHelpfulError(err)) {
160
160
  return {
161
161
  is_error: true,
162
- error_type: "path_conflict",
163
- path: err.path,
164
- message: `A file already exists at context/${err.path}. The inner tool ran but its output was discarded.`,
165
- next_action_hint:
166
- "Retry with on_conflict='overwrite' to replace, or pick a different path.",
162
+ error_type: "write_failed",
163
+ message: `Inner tool ran, but write to ${input.logical_path} failed: ${err.message}`,
164
+ next_action_hint: err.hint,
167
165
  };
168
166
  }
169
- throw err;
167
+ return {
168
+ is_error: true,
169
+ error_type: "internal_error",
170
+ message: err instanceof Error ? err.message : String(err),
171
+ };
170
172
  }
171
173
  },
172
174
  } satisfies ToolDefinition<typeof inputSchema, typeof outputSchema>;
@@ -1,27 +1,12 @@
1
1
  // Capabilities tools
2
2
  import { capabilitiesRefreshTool } from "./capabilities/refresh.ts";
3
- // Context tools
4
- import { pipeToContextTool } from "./context/pipe.ts";
5
- import { readLargeResultTool } from "./context/read-large-result.ts";
6
- // Context — directory operations
7
- import { contextCreateDirTool } from "./dir/create.ts";
8
- import { contextDirSizeTool } from "./dir/size.ts";
9
- import { contextTreeTool } from "./dir/tree.ts";
10
- // Context — file operations
11
- import { contextCopyTool } from "./file/copy.ts";
12
- import { contextCountLinesTool } from "./file/count-lines.ts";
13
- import { contextDeleteTool } from "./file/delete.ts";
14
- import { contextEditTool } from "./file/edit.ts";
15
- import { contextExistsTool } from "./file/exists.ts";
16
- import { contextInfoTool } from "./file/info.ts";
17
- import { contextMoveTool } from "./file/move.ts";
18
- import { contextReadTool } from "./file/read.ts";
19
- import { contextWriteTool } from "./file/write.ts";
20
3
  // MCP tools
21
4
  import { mcpExecTool } from "./mcp/exec.ts";
22
5
  import { mcpInfoTool } from "./mcp/info.ts";
23
6
  import { mcpListToolsTool } from "./mcp/list-tools.ts";
24
7
  import { mcpSearchTool } from "./mcp/search.ts";
8
+ // Membot tools (knowledge store)
9
+ import { registerMembotTools } from "./membot/index.ts";
25
10
  // Prompt tools
26
11
  import { promptCreateTool } from "./prompt/create.ts";
27
12
  import { promptDeleteTool } from "./prompt/delete.ts";
@@ -32,8 +17,6 @@ import { promptReadTool } from "./prompt/read.ts";
32
17
  import { createScheduleTool } from "./schedule/create.ts";
33
18
  import { scheduleEditTool } from "./schedule/edit.ts";
34
19
  import { listSchedulesTool } from "./schedule/list.ts";
35
- // Search tools
36
- import { searchTool } from "./search/index.ts";
37
20
  // Skill tools
38
21
  import { skillDeleteTool } from "./skill/delete.ts";
39
22
  import { skillEditTool } from "./skill/edit.ts";
@@ -73,26 +56,15 @@ export function registerAllTools(): void {
73
56
  registerTool(listTasksTool);
74
57
  registerTool(viewTaskTool);
75
58
 
76
- // Context (file/dir + self-reflection)
77
- registerTool(contextCreateDirTool);
78
- registerTool(contextTreeTool);
79
- registerTool(contextDirSizeTool);
80
- registerTool(contextReadTool);
81
- registerTool(contextWriteTool);
82
- registerTool(contextEditTool);
83
- registerTool(contextDeleteTool);
84
- registerTool(contextCopyTool);
85
- registerTool(contextMoveTool);
86
- registerTool(contextInfoTool);
87
- registerTool(contextExistsTool);
88
- registerTool(contextCountLinesTool);
59
+ // Knowledge store (membot) — add/read/write/edit/search/versions/refresh etc.
60
+ registerMembotTools();
61
+
62
+ // Prompts
89
63
  registerTool(promptListTool);
90
64
  registerTool(promptReadTool);
91
65
  registerTool(promptCreateTool);
92
66
  registerTool(promptEditTool);
93
67
  registerTool(promptDeleteTool);
94
- registerTool(readLargeResultTool);
95
- registerTool(pipeToContextTool);
96
68
 
97
69
  // Capabilities
98
70
  registerTool(capabilitiesRefreshTool);
@@ -102,9 +74,6 @@ export function registerAllTools(): void {
102
74
  registerTool(scheduleEditTool);
103
75
  registerTool(listSchedulesTool);
104
76
 
105
- // Search
106
- registerTool(searchTool);
107
-
108
77
  // Skill
109
78
  registerTool(skillListTool);
110
79
  registerTool(skillReadTool);
package/src/tools/tool.ts CHANGED
@@ -1,19 +1,17 @@
1
1
  import type { Tool as AnthropicTool } from "@anthropic-ai/sdk/resources/messages";
2
2
  import type { McpxClient } from "@evantahler/mcpx";
3
+ import type { MembotClient } from "membot";
3
4
  import { z } from "zod";
4
5
  import type { BotholomewConfig } from "../config/schemas.ts";
5
- import type { DbConnection } from "../db/connection.ts";
6
6
 
7
7
  export interface ToolContext {
8
8
  /**
9
- * Short-lived DB connection scoped to this tool call. Safe for single-query
10
- * tools. Do NOT hold it across slow work (network, embedding, long loops) —
11
- * the instance-level file lock stays held until every connection closes.
12
- * For long-running tools, use `dbPath` with `withDb` per logical operation.
9
+ * Per-process membot client. Backs every `membot_*` tool. Membot manages
10
+ * its own DuckDB connection lifecycle internally (lazy claim, release
11
+ * between operations), so tools just call `ctx.mem.<op>(...)` directly
12
+ * no per-call open/close needed.
13
13
  */
14
- conn: DbConnection;
15
- /** Path to the DuckDB file. Use with `withDb` for long-running tools. */
16
- dbPath: string;
14
+ mem: MembotClient;
17
15
  projectDir: string;
18
16
  config: Required<BotholomewConfig>;
19
17
  mcpxClient: McpxClient | null;
package/src/tui/App.tsx CHANGED
@@ -186,18 +186,17 @@ function AppInner({
186
186
  setUsage,
187
187
  });
188
188
 
189
- const sessionDbPath = sessionRef.current?.dbPath;
189
+ const sessionReady = sessionRef.current != null;
190
190
  const inputBarHeader = useMemo(
191
191
  () =>
192
- sessionDbPath ? (
192
+ sessionReady ? (
193
193
  <StatusBar
194
194
  projectDir={projectDir}
195
- dbPath={sessionDbPath}
196
195
  chatTitle={chatTitle}
197
196
  onWorkerStatusChange={setWorkerRunning}
198
197
  />
199
198
  ) : null,
200
- [projectDir, sessionDbPath, chatTitle],
199
+ [projectDir, sessionReady, chatTitle],
201
200
  );
202
201
 
203
202
  const allToolCalls = useMemo(