@theronap/cortex-mcp 0.9.43 → 0.9.44

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.
@@ -115,7 +115,7 @@ if (cmd === 'setup') {
115
115
  const { closeFetch } = await import('../lib/diagnose.mjs')
116
116
  await closeFetch()
117
117
  } else if (cmd === 'grep') {
118
- // Literal substring search over the viewer's visible brain wiki (thin client of /api/grep).
118
+ // Ranked keyword (fts) search by default; --substring/--literal for exact lookups. Thin client of /api/grep.
119
119
  const { runGrep } = await import('../lib/grep_cli.mjs')
120
120
  process.exitCode = await runGrep(rest)
121
121
  const { closeFetch } = await import('../lib/diagnose.mjs')
package/lib/grep_cli.mjs CHANGED
@@ -5,16 +5,20 @@ import { fetchCortex, classify, resolveBase } from './diagnose.mjs'
5
5
  // Output is ASCII-only (outbound-message convention).
6
6
 
7
7
  // Pure: parse argv after `grep` → { query, mode, max }.
8
- // cortex grep <terms...> [--mode fts | --fts] [--max N]
8
+ // cortex grep <terms...> [--substring | --literal] [--max N]
9
+ // Default mode is 'fts' (ranked keyword — multi-word queries work); pass --substring/--literal for
10
+ // an exact literal match (identifiers, [[links]], code). (#220)
9
11
  export function parseGrepArgs(argv = []) {
10
- const out = { query: '', mode: 'substring', max: undefined }
12
+ const out = { query: '', mode: 'fts', max: undefined }
11
13
  const terms = []
12
14
  for (let i = 0; i < argv.length; i++) {
13
15
  const a = argv[i]
14
16
  if (a === '--mode') {
15
- out.mode = argv[++i] === 'fts' ? 'fts' : 'substring'
17
+ out.mode = argv[++i] === 'substring' ? 'substring' : 'fts'
16
18
  } else if (a === '--fts') {
17
19
  out.mode = 'fts'
20
+ } else if (a === '--substring' || a === '--literal') {
21
+ out.mode = 'substring'
18
22
  } else if (a === '--max') {
19
23
  const n = Number(argv[++i])
20
24
  if (Number.isFinite(n)) out.max = Math.trunc(n)
@@ -50,7 +54,7 @@ export async function runGrep(rest = []) {
50
54
  }
51
55
  const { query, mode, max } = parseGrepArgs(rest)
52
56
  if (!query) {
53
- process.stderr.write('usage: cortex grep <query> [--mode fts] [--max N]\n')
57
+ process.stderr.write('usage: cortex grep <query> [--substring|--literal] [--max N]\n')
54
58
  return 1
55
59
  }
56
60
  const qs = new URLSearchParams({ q: query, mode })
@@ -2,13 +2,17 @@ import { describe, it, expect } from 'bun:test'
2
2
  import { parseGrepArgs, formatGrepHits } from './grep_cli.mjs'
3
3
 
4
4
  describe('parseGrepArgs', () => {
5
- it('joins free terms into the query, defaults substring', () => {
6
- expect(parseGrepArgs(['hello', 'world'])).toEqual({ query: 'hello world', mode: 'substring', max: undefined })
5
+ // #220: default is 'fts' (ranked keyword); --substring/--literal opts back into exact matching.
6
+ it('joins free terms into the query, defaults fts', () => {
7
+ expect(parseGrepArgs(['hello', 'world'])).toEqual({ query: 'hello world', mode: 'fts', max: undefined })
7
8
  })
8
- it('honors --mode fts and --fts', () => {
9
+ it('honors --substring/--literal to force exact matching, else fts', () => {
10
+ expect(parseGrepArgs(['q', '--substring']).mode).toBe('substring')
11
+ expect(parseGrepArgs(['q', '--literal']).mode).toBe('substring')
12
+ expect(parseGrepArgs(['q', '--mode', 'substring']).mode).toBe('substring')
9
13
  expect(parseGrepArgs(['q', '--mode', 'fts']).mode).toBe('fts')
10
14
  expect(parseGrepArgs(['--fts', 'q']).mode).toBe('fts')
11
- expect(parseGrepArgs(['q', '--mode', 'bogus']).mode).toBe('substring')
15
+ expect(parseGrepArgs(['q', '--mode', 'bogus']).mode).toBe('fts')
12
16
  })
13
17
  it('parses --max as an integer, ignores non-numeric', () => {
14
18
  expect(parseGrepArgs(['q', '--max', '25']).max).toBe(25)
package/lib/server.mjs CHANGED
@@ -266,14 +266,14 @@ export async function runServer(version) {
266
266
  {
267
267
  title: 'Grep the brain wiki',
268
268
  description:
269
- 'Literal substring search across your visible brain wiki pages (matches symbols, identifiers, [[links]]). Returns matching sections with a context snippet and their outbound [[links]].',
269
+ 'Ranked keyword search across your visible brain wiki pages. Multi-word natural-language queries work (results are ranked by relevance). Pass mode:"substring" for an exact literal match of symbols, identifiers, or [[links]]. Returns matching sections with a context snippet and their outbound [[links]].',
270
270
  inputSchema: {
271
- query: z.string().describe('literal substring to find'),
272
- mode: z.enum(['substring', 'fts']).optional().describe("'substring' (default, grep-like) or 'fts' (ranked keyword)"),
271
+ query: z.string().describe('search terms (natural language is fine)'),
272
+ mode: z.enum(['substring', 'fts']).optional().describe("'fts' (default, ranked keyword) or 'substring' (exact literal — for identifiers / [[links]] / code)"),
273
273
  },
274
274
  },
275
275
  async ({ query, mode }) => {
276
- const qs = new URLSearchParams({ q: query, mode: mode === 'fts' ? 'fts' : 'substring' })
276
+ const qs = new URLSearchParams({ q: query, mode: mode === 'substring' ? 'substring' : 'fts' })
277
277
  const res = await fetchCortex(`${BASE}/api/grep?${qs.toString()}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
278
278
  if (!res.ok) {
279
279
  const body = await res.text()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.43",
3
+ "version": "0.9.44",
4
4
  "description": "Connect your AI assistant to Cortex — your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {