@mercury-fw/core 0.31.0 → 0.32.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/CHANGELOG.md +27 -0
- package/README.md +0 -1
- package/dist/src/config/define-config.d.ts +2 -2
- package/dist/src/plugins/plugin-loader.d.ts +6 -8
- package/dist/src/wiki/wiki-note.d.ts +3 -0
- package/dist/src/wiki/wiki-read.d.ts +2 -1
- package/package.json +5 -5
- package/src/admin/wiki-routes.ts +2 -1
- package/src/compose.ts +2 -9
- package/src/config/define-config.ts +2 -2
- package/src/plugins/plugin-loader.ts +10 -21
- package/src/wiki/self-review-tools.ts +4 -3
- package/src/wiki/vault-cli.ts +1 -1
- package/src/wiki/wiki-note.ts +6 -0
- package/src/wiki/wiki-read.ts +3 -2
- package/src/wiki/wiki-tools.ts +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# @mercury-fw/core
|
|
2
2
|
|
|
3
|
+
## 0.32.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1fa3a97: - Every tool plugin declared in `mercury.config.ts` loads: `MERCURY_CLIS` is no longer read. An app that used it to keep a declared plugin off now gets that plugin on; remove the plugin from the config instead.
|
|
8
|
+
- A new app's env example has no `MERCURY_CLIS`, and its config's comment says every declared plugin and channel is active.
|
|
9
|
+
- A plugin caught in a dependency cycle is always reported at startup.
|
|
10
|
+
- The plugins' READMEs no longer ask to list them in `MERCURY_CLIS`.
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- @mercury-fw/plugin-types@0.32.0
|
|
15
|
+
- @mercury-fw/channel-types@0.32.0
|
|
16
|
+
- @mercury-fw/cli-engine@0.32.0
|
|
17
|
+
- @mercury-fw/confirm-engine@0.32.0
|
|
18
|
+
|
|
19
|
+
## 0.31.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- 7926aa2: - Wiki grep ignores case, for the agent, the nightly review, `mfw vault grep` and the HTTP `/wiki/grep` route.
|
|
24
|
+
- The agent's `write_file` and the nightly review's `write_curated` take a path starting with `curated/`, as listing, reading and grepping give it, instead of writing under `curated/curated/`.
|
|
25
|
+
- @mercury-fw/plugin-types@0.31.1
|
|
26
|
+
- @mercury-fw/channel-types@0.31.1
|
|
27
|
+
- @mercury-fw/cli-engine@0.31.1
|
|
28
|
+
- @mercury-fw/confirm-engine@0.31.1
|
|
29
|
+
|
|
3
30
|
## 0.31.0
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -27,7 +27,6 @@ A scaffolded app's `src/index.ts` (the service) and `src/repl.ts` (the REPL) are
|
|
|
27
27
|
| `OLLAMA_THINK` | `false` for a model that doesn't support thinking. |
|
|
28
28
|
| `QDRANT_URL` | Qdrant, for the episodic memory (default `http://qdrant:6333`). |
|
|
29
29
|
| `WIKI_VAULT_PATH` | Where the wiki lives. Required. |
|
|
30
|
-
| `MERCURY_CLIS` | The tool plugins this deployment turns on, by name, comma-separated. |
|
|
31
30
|
|
|
32
31
|
Qdrant being unreachable degrades memory, it doesn't stop the agent.
|
|
33
32
|
|
|
@@ -15,8 +15,8 @@ import type { Plugin } from "@mercury-fw/plugin-types";
|
|
|
15
15
|
import type { ChannelPlugin } from "@mercury-fw/channel-types";
|
|
16
16
|
import type { Persona } from "../session/system-prompt.ts";
|
|
17
17
|
/** The shape of a Mercury instance's composition config: the tool plugins
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* and the channel plugins (each enabled by being declared here: declared =
|
|
19
|
+
* active), each loaded by its own loader, plus the
|
|
20
20
|
* assistant's persona (its identity and tone; the defaults when left out). */
|
|
21
21
|
export type MercuryConfig = {
|
|
22
22
|
plugins: Plugin[];
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
* nothing about any specific plugin — or about CLIs — the composition root
|
|
7
7
|
* names them, this loop processes them identically.
|
|
8
8
|
*
|
|
9
|
-
* Two properties it guarantees
|
|
10
|
-
* -
|
|
11
|
-
* in MERCURY_CLIS);
|
|
9
|
+
* Two properties it guarantees:
|
|
10
|
+
* - every plugin the app declares is loaded: declaring it is what enables it;
|
|
12
11
|
* - a plugin that fails — its `build()` throws — degrades as a single unit:
|
|
13
12
|
* none of its contributions land, the failure is
|
|
14
13
|
* logged with detail, and every other plugin and the process itself carry
|
|
@@ -50,10 +49,9 @@ export interface LoadedPlugins {
|
|
|
50
49
|
* config map to infer activation from anymore. */
|
|
51
50
|
activated: string[];
|
|
52
51
|
}
|
|
53
|
-
/** Everything the loader needs from the composition root:
|
|
54
|
-
*
|
|
52
|
+
/** Everything the loader needs from the composition root: the runtime
|
|
53
|
+
* context every `build()` gets. */
|
|
55
54
|
export interface PluginLoadContext {
|
|
56
|
-
enabledClis: string[];
|
|
57
55
|
model: LanguageModel;
|
|
58
56
|
env: Record<string, string | undefined>;
|
|
59
57
|
log: (msg: string) => void;
|
|
@@ -79,7 +77,7 @@ export declare function orderByDependencies(plugins: Plugin[]): {
|
|
|
79
77
|
* dependency between them. Never throws — a plugin that fails is logged and
|
|
80
78
|
* skipped, its contributions staged and merged only once the whole plugin
|
|
81
79
|
* succeeds so a later failure can't leave it half-wired. A plugin whose
|
|
82
|
-
* declared `dependsOn` isn't fully activated (a dependency
|
|
83
|
-
*
|
|
80
|
+
* declared `dependsOn` isn't fully activated (a dependency failed, unknown,
|
|
81
|
+
* or itself skipped) is skipped fail-soft too, transitively.
|
|
84
82
|
*/
|
|
85
83
|
export declare function loadPlugins(plugins: Plugin[], ctx: PluginLoadContext): Promise<LoadedPlugins>;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/** `path` relative to curated/: a vault-relative one (`curated/x.md`, as
|
|
2
|
+
* listing, reading and grepping give it) loses its leading `curated/`. */
|
|
3
|
+
export declare function relativeToCurated(path: string): string;
|
|
1
4
|
/** Writes a curated doc at `curated/<relativePath>` (e.g. "standards/jira-fields.md"). */
|
|
2
5
|
export declare function writeCuratedNote(vaultPath: string, relativePath: string, fields: {
|
|
3
6
|
author?: string;
|
|
@@ -19,7 +19,8 @@ export type WikiGrepMatch = {
|
|
|
19
19
|
line: number;
|
|
20
20
|
text: string;
|
|
21
21
|
};
|
|
22
|
-
/** Searches every file under `roots` for `pattern` (a regular expression
|
|
22
|
+
/** Searches every file under `roots` for `pattern` (a regular expression,
|
|
23
|
+
* case-insensitive: a note's wording isn't the question's), line by line. */
|
|
23
24
|
export declare function grepWikiInRoots(vaultPath: string, roots: string[], pattern: string): Promise<WikiGrepMatch[]>;
|
|
24
25
|
/** Searches every file visible to `userId` for `pattern` (a regular expression), line by line. */
|
|
25
26
|
export declare function grepWiki(vaultPath: string, userId: string, pattern: string): Promise<WikiGrepMatch[]>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mercury-fw/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.32.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -31,10 +31,10 @@
|
|
|
31
31
|
"test": "bun test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@mercury-fw/channel-types": "0.
|
|
35
|
-
"@mercury-fw/cli-engine": "0.
|
|
36
|
-
"@mercury-fw/confirm-engine": "0.
|
|
37
|
-
"@mercury-fw/plugin-types": "0.
|
|
34
|
+
"@mercury-fw/channel-types": "0.32.0",
|
|
35
|
+
"@mercury-fw/cli-engine": "0.32.0",
|
|
36
|
+
"@mercury-fw/confirm-engine": "0.32.0",
|
|
37
|
+
"@mercury-fw/plugin-types": "0.32.0",
|
|
38
38
|
"@qdrant/js-client-rest": "^1.19.0",
|
|
39
39
|
"ai": "^7.0.126",
|
|
40
40
|
"ai-sdk-ollama": "^4.4.0",
|
package/src/admin/wiki-routes.ts
CHANGED
|
@@ -31,7 +31,8 @@ export async function readWikiVaultFile(vaultPath: string, relativePath: string)
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export async function grepWikiVault(vaultPath: string, pattern: string): Promise<WikiGrepMatch[]> {
|
|
34
|
-
|
|
34
|
+
// Case-insensitive, like every other wiki grep.
|
|
35
|
+
const regex = new RegExp(pattern, "i");
|
|
35
36
|
const files = await listWikiVault(vaultPath);
|
|
36
37
|
const matches: WikiGrepMatch[] = [];
|
|
37
38
|
for (const file of files) {
|
package/src/compose.ts
CHANGED
|
@@ -123,11 +123,6 @@ function requireEnv(name: string): string {
|
|
|
123
123
|
* app) reads its own `mercury.config.ts` and passes it in. See {@link ComposedApp}.
|
|
124
124
|
*/
|
|
125
125
|
export async function composeMercury(config: MercuryConfig): Promise<ComposedApp> {
|
|
126
|
-
const enabledClis = (process.env.MERCURY_CLIS ?? "")
|
|
127
|
-
.split(",")
|
|
128
|
-
.map((s) => s.trim())
|
|
129
|
-
.filter(Boolean);
|
|
130
|
-
|
|
131
126
|
// The model is constructed up front, before the plugins load and the system
|
|
132
127
|
// prompt is built: a plugin's post-turn guard can be model-backed (Jira's
|
|
133
128
|
// issue-list corrector is), and the system prompt is assembled from the
|
|
@@ -149,13 +144,11 @@ export async function composeMercury(config: MercuryConfig): Promise<ComposedApp
|
|
|
149
144
|
// handed in — this file no longer names them. The loader processes an opaque
|
|
150
145
|
// list (see plugins/plugin-loader.ts): each supplies its allowlist as data, a
|
|
151
146
|
// system-prompt fragment, and a `build()` that turns the runtime context into
|
|
152
|
-
// post-processors and post-turn guards.
|
|
153
|
-
//
|
|
154
|
-
// degrades only itself.
|
|
147
|
+
// post-processors and post-turn guards. Every declared plugin loads; one
|
|
148
|
+
// that fails (an invalid allowlist, missing configuration) degrades only itself.
|
|
155
149
|
const plugins = config.plugins;
|
|
156
150
|
|
|
157
151
|
const loadedPlugins = await loadPlugins(plugins, {
|
|
158
|
-
enabledClis,
|
|
159
152
|
model,
|
|
160
153
|
env: process.env,
|
|
161
154
|
log: (msg) => console.error(msg),
|
|
@@ -16,8 +16,8 @@ import type { ChannelPlugin } from "@mercury-fw/channel-types";
|
|
|
16
16
|
import type { Persona } from "../session/system-prompt.ts";
|
|
17
17
|
|
|
18
18
|
/** The shape of a Mercury instance's composition config: the tool plugins
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* and the channel plugins (each enabled by being declared here: declared =
|
|
20
|
+
* active), each loaded by its own loader, plus the
|
|
21
21
|
* assistant's persona (its identity and tone; the defaults when left out). */
|
|
22
22
|
export type MercuryConfig = {
|
|
23
23
|
plugins: Plugin[];
|
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
* nothing about any specific plugin — or about CLIs — the composition root
|
|
7
7
|
* names them, this loop processes them identically.
|
|
8
8
|
*
|
|
9
|
-
* Two properties it guarantees
|
|
10
|
-
* -
|
|
11
|
-
* in MERCURY_CLIS);
|
|
9
|
+
* Two properties it guarantees:
|
|
10
|
+
* - every plugin the app declares is loaded: declaring it is what enables it;
|
|
12
11
|
* - a plugin that fails — its `build()` throws — degrades as a single unit:
|
|
13
12
|
* none of its contributions land, the failure is
|
|
14
13
|
* logged with detail, and every other plugin and the process itself carry
|
|
@@ -54,10 +53,9 @@ export interface LoadedPlugins {
|
|
|
54
53
|
activated: string[];
|
|
55
54
|
}
|
|
56
55
|
|
|
57
|
-
/** Everything the loader needs from the composition root:
|
|
58
|
-
*
|
|
56
|
+
/** Everything the loader needs from the composition root: the runtime
|
|
57
|
+
* context every `build()` gets. */
|
|
59
58
|
export interface PluginLoadContext {
|
|
60
|
-
enabledClis: string[];
|
|
61
59
|
model: LanguageModel;
|
|
62
60
|
env: Record<string, string | undefined>;
|
|
63
61
|
log: (msg: string) => void;
|
|
@@ -116,8 +114,8 @@ export function orderByDependencies(plugins: Plugin[]): { ordered: Plugin[]; cyc
|
|
|
116
114
|
* dependency between them. Never throws — a plugin that fails is logged and
|
|
117
115
|
* skipped, its contributions staged and merged only once the whole plugin
|
|
118
116
|
* succeeds so a later failure can't leave it half-wired. A plugin whose
|
|
119
|
-
* declared `dependsOn` isn't fully activated (a dependency
|
|
120
|
-
*
|
|
117
|
+
* declared `dependsOn` isn't fully activated (a dependency failed, unknown,
|
|
118
|
+
* or itself skipped) is skipped fail-soft too, transitively.
|
|
121
119
|
*/
|
|
122
120
|
export async function loadPlugins(plugins: Plugin[], ctx: PluginLoadContext): Promise<LoadedPlugins> {
|
|
123
121
|
const promptFragments: string[] = [];
|
|
@@ -127,13 +125,9 @@ export async function loadPlugins(plugins: Plugin[], ctx: PluginLoadContext): Pr
|
|
|
127
125
|
const postTurnGuards: PostTurnGuard[] = [];
|
|
128
126
|
|
|
129
127
|
const { ordered, cyclic } = orderByDependencies(plugins);
|
|
130
|
-
// A plugin caught in a cycle can't be ordered, so it can't load.
|
|
131
|
-
// only when it's enabled — a disabled plugin contributes nothing regardless,
|
|
132
|
-
// same silence as any other disabled plugin.
|
|
128
|
+
// A plugin caught in a cycle can't be ordered, so it can't load.
|
|
133
129
|
for (const plugin of cyclic) {
|
|
134
|
-
|
|
135
|
-
ctx.log(`plugin "${plugin.name}" not activated: part of or depends on a dependency cycle`);
|
|
136
|
-
}
|
|
130
|
+
ctx.log(`plugin "${plugin.name}" not activated: part of or depends on a dependency cycle`);
|
|
137
131
|
}
|
|
138
132
|
|
|
139
133
|
// Names that fully activated — a dependency must be in here for a dependent
|
|
@@ -142,11 +136,6 @@ export async function loadPlugins(plugins: Plugin[], ctx: PluginLoadContext): Pr
|
|
|
142
136
|
const activated = new Set<string>();
|
|
143
137
|
|
|
144
138
|
for (const plugin of ordered) {
|
|
145
|
-
// Not enabled on this instance: contribute nothing, and don't even
|
|
146
|
-
// validate the config — same as a CLI left out of MERCURY_CLIS.
|
|
147
|
-
if (!ctx.enabledClis.includes(plugin.name)) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
139
|
// Contract-version skew: core and plugin are versioned and installed
|
|
151
140
|
// separately, so a plugin built against a different contract than this core
|
|
152
141
|
// supports is possible. Refuse it fail-soft rather than run it against a
|
|
@@ -160,8 +149,8 @@ export async function loadPlugins(plugins: Plugin[], ctx: PluginLoadContext): Pr
|
|
|
160
149
|
}
|
|
161
150
|
// Every declared dependency must have fully activated first. Because we
|
|
162
151
|
// process in dependency-first order, a dependency that was going to load
|
|
163
|
-
// already has; anything still missing
|
|
164
|
-
//
|
|
152
|
+
// already has; anything still missing failed, is unknown, or was itself
|
|
153
|
+
// skipped — so this dependent degrades fail-soft too. Checked before
|
|
165
154
|
// touching this plugin's own build, so a doomed plugin does no work.
|
|
166
155
|
const missingDeps = (plugin.dependsOn ?? []).filter((dep) => !activated.has(dep));
|
|
167
156
|
if (missingDeps.length > 0) {
|
|
@@ -19,7 +19,7 @@ import type { ExecutableTool } from "@mercury-fw/plugin-types";
|
|
|
19
19
|
import { tool } from "ai";
|
|
20
20
|
import { z } from "zod";
|
|
21
21
|
import { listWikiFilesInRoots, readWikiFileInRoots, grepWikiInRoots, selfReviewRoots, readIndexFile } from "./wiki-read.ts";
|
|
22
|
-
import { writeCuratedNote, writeIndexFile, deleteRawEntry, deleteCuratedEntry } from "./wiki-note.ts";
|
|
22
|
+
import { writeCuratedNote, writeIndexFile, deleteRawEntry, deleteCuratedEntry, relativeToCurated } from "./wiki-note.ts";
|
|
23
23
|
import { normalizeIndexKey, upsertIndexEntry, removeIndexEntry } from "./index-entry.ts";
|
|
24
24
|
|
|
25
25
|
export type SelfReviewToolsDeps = { vaultPath: string };
|
|
@@ -76,11 +76,12 @@ export function createSelfReviewTools(
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
const write_curated = tool({
|
|
79
|
-
description:
|
|
79
|
+
description:
|
|
80
|
+
'Create or overwrite a curated doc. "path" is relative to curated/, e.g. "standards/jira-fields.md"; the path list_files and grep give ("curated/standards/jira-fields.md") works too.',
|
|
80
81
|
inputSchema: z.object({ path: z.string().min(1), content: z.string() }),
|
|
81
82
|
execute: async ({ path, content }) => {
|
|
82
83
|
try {
|
|
83
|
-
await writeCuratedNote(vaultPath, path, {}, content);
|
|
84
|
+
await writeCuratedNote(vaultPath, relativeToCurated(path), {}, content);
|
|
84
85
|
return { ok: true as const };
|
|
85
86
|
} catch (err) {
|
|
86
87
|
return { ok: false as const, error: String(err) };
|
package/src/wiki/vault-cli.ts
CHANGED
|
@@ -127,7 +127,7 @@ async function main(): Promise<void> {
|
|
|
127
127
|
case "grep": {
|
|
128
128
|
const pattern = args[0];
|
|
129
129
|
if (!pattern) usage();
|
|
130
|
-
const regex = new RegExp(pattern);
|
|
130
|
+
const regex = new RegExp(pattern, "i");
|
|
131
131
|
const glob = new Bun.Glob("**/*.md");
|
|
132
132
|
for await (const file of glob.scan({ cwd: vaultPath })) {
|
|
133
133
|
const content = await Bun.file(`${vaultPath}/${file}`).text();
|
package/src/wiki/wiki-note.ts
CHANGED
|
@@ -201,6 +201,12 @@ async function deleteVaultFile(vaultPath: string, fullPath: string, commitMessag
|
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/** `path` relative to curated/: a vault-relative one (`curated/x.md`, as
|
|
205
|
+
* listing, reading and grepping give it) loses its leading `curated/`. */
|
|
206
|
+
export function relativeToCurated(path: string): string {
|
|
207
|
+
return path.replace(/^curated\//, "");
|
|
208
|
+
}
|
|
209
|
+
|
|
204
210
|
/** Writes a curated doc at `curated/<relativePath>` (e.g. "standards/jira-fields.md"). */
|
|
205
211
|
export async function writeCuratedNote(
|
|
206
212
|
vaultPath: string,
|
package/src/wiki/wiki-read.ts
CHANGED
|
@@ -88,9 +88,10 @@ export async function readWikiFile(vaultPath: string, userId: string, relativePa
|
|
|
88
88
|
|
|
89
89
|
export type WikiGrepMatch = { path: string; line: number; text: string };
|
|
90
90
|
|
|
91
|
-
/** Searches every file under `roots` for `pattern` (a regular expression
|
|
91
|
+
/** Searches every file under `roots` for `pattern` (a regular expression,
|
|
92
|
+
* case-insensitive: a note's wording isn't the question's), line by line. */
|
|
92
93
|
export async function grepWikiInRoots(vaultPath: string, roots: string[], pattern: string): Promise<WikiGrepMatch[]> {
|
|
93
|
-
const regex = new RegExp(pattern);
|
|
94
|
+
const regex = new RegExp(pattern, "i");
|
|
94
95
|
const files = await listWikiFilesInRoots(vaultPath, roots);
|
|
95
96
|
const matches: WikiGrepMatch[] = [];
|
|
96
97
|
|
package/src/wiki/wiki-tools.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { ExecutableTool } from "@mercury-fw/plugin-types";
|
|
|
15
15
|
import { tool } from "ai";
|
|
16
16
|
import { z } from "zod";
|
|
17
17
|
import { listWikiFiles, readWikiFile, grepWiki, readWikiFileInRoots } from "./wiki-read.ts";
|
|
18
|
-
import { writeCuratedNote } from "./wiki-note.ts";
|
|
18
|
+
import { writeCuratedNote, relativeToCurated } from "./wiki-note.ts";
|
|
19
19
|
|
|
20
20
|
export type WikiToolsDeps = { vaultPath: string; userId: string };
|
|
21
21
|
|
|
@@ -55,12 +55,13 @@ export function createWikiTools(
|
|
|
55
55
|
const write_file = tool({
|
|
56
56
|
description:
|
|
57
57
|
'Write or update a curated wiki document (team knowledge — conventions, standards, decisions). "path" ' +
|
|
58
|
-
'is relative to curated/, e.g. "standards/jira-fields.md"
|
|
58
|
+
'is relative to curated/, e.g. "standards/jira-fields.md"; the path grep and read_file give ' +
|
|
59
|
+
'("curated/standards/jira-fields.md") works too. This can only write under curated/ — your ' +
|
|
59
60
|
"own semantic notes are managed automatically by the memory consolidation process, not through this tool.",
|
|
60
61
|
inputSchema: z.object({ path: z.string().min(1), content: z.string() }),
|
|
61
62
|
execute: async ({ path, content }) => {
|
|
62
63
|
try {
|
|
63
|
-
await writeCuratedNote(vaultPath, path, { last_updated: new Date().toISOString().slice(0, 10) }, content);
|
|
64
|
+
await writeCuratedNote(vaultPath, relativeToCurated(path), { last_updated: new Date().toISOString().slice(0, 10) }, content);
|
|
64
65
|
return { ok: true as const };
|
|
65
66
|
} catch (err) {
|
|
66
67
|
return { ok: false as const, error: String(err) };
|
|
@@ -70,7 +71,7 @@ export function createWikiTools(
|
|
|
70
71
|
|
|
71
72
|
const grep = tool({
|
|
72
73
|
description:
|
|
73
|
-
"Search wiki documents (curated/ plus your own inferred/ notes) for a regular expression pattern. " +
|
|
74
|
+
"Search wiki documents (curated/ plus your own inferred/ notes) for a regular expression pattern, ignoring case. " +
|
|
74
75
|
"Returns matching lines with their file path and line number.",
|
|
75
76
|
inputSchema: z.object({ pattern: z.string().min(1) }),
|
|
76
77
|
execute: async ({ pattern }) => {
|