wayfind 2.0.69 → 2.0.71

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/bin/mcp-server.js CHANGED
@@ -228,43 +228,32 @@ async function proxyGetEntry(id) {
228
228
  const TOOLS = [
229
229
  {
230
230
  name: 'search_context',
231
- description: 'Search the team\'s full decision history across all repos and all engineers. Returns ranked journal entries, decisions, and signals. Use this not file reads to answer any question about past work, architectural decisions, what was decided, or team activity. The content store covers history that state files cannot.',
231
+ description: 'Search the team\'s decision history across all repos and engineers. Returns ranked entries. Use mode=browse with since/until for time-range queries ("what happened this week"). Use mode=semantic with a query for topical searches. Pass dates, authors, and repos as explicit parameters.',
232
232
  inputSchema: {
233
233
  type: 'object',
234
234
  properties: {
235
- query: { type: 'string', description: 'Natural language search query' },
235
+ query: { type: 'string', description: 'Natural language search query (required for semantic mode, optional for browse)' },
236
236
  limit: { type: 'number', description: 'Max results (default: 10)' },
237
237
  repo: { type: 'string', description: 'Filter by repository name (e.g. "MyService", "MyOrg/my-repo")' },
238
238
  since: { type: 'string', description: 'Filter to entries on or after this date (YYYY-MM-DD)' },
239
- mode: { type: 'string', enum: ['semantic', 'text'], description: 'Search mode semantic uses embeddings, text uses keyword matching. Defaults to semantic if embeddings available.' },
239
+ until: { type: 'string', description: 'Filter to entries on or before this date (YYYY-MM-DD)' },
240
+ user: { type: 'string', description: 'Filter by author slug (lowercase first name, e.g. "nick")' },
241
+ source: { type: 'string', enum: ['journal', 'conversation', 'signal'], description: 'Filter by entry source type' },
242
+ mode: { type: 'string', enum: ['semantic', 'browse'], description: 'Search strategy. semantic (default) uses embeddings for relevance ranking. browse returns entries sorted by date (best for time-range queries).' },
240
243
  },
241
- required: ['query'],
242
244
  },
243
245
  },
244
246
  {
245
247
  name: 'get_entry',
246
- description: 'Retrieve the full content of a specific journal or signal entry by ID. Use the IDs returned by search_context or list_recent.',
248
+ description: 'Retrieve the full content of a specific journal or signal entry by ID. Use the IDs returned by search_context.',
247
249
  inputSchema: {
248
250
  type: 'object',
249
251
  properties: {
250
- id: { type: 'string', description: 'Entry ID from search_context or list_recent results' },
252
+ id: { type: 'string', description: 'Entry ID from search_context results' },
251
253
  },
252
254
  required: ['id'],
253
255
  },
254
256
  },
255
- {
256
- name: 'list_recent',
257
- description: 'List recent journal entries and decisions, optionally filtered by repo or date range. Returns metadata (no full content — use get_entry for that).',
258
- inputSchema: {
259
- type: 'object',
260
- properties: {
261
- limit: { type: 'number', description: 'Max entries to return (default: 20)' },
262
- repo: { type: 'string', description: 'Filter by repository name' },
263
- since: { type: 'string', description: 'Filter to entries on or after this date (YYYY-MM-DD)' },
264
- source: { type: 'string', enum: ['journal', 'conversation', 'signal'], description: 'Filter by entry source type' },
265
- },
266
- },
267
- },
268
257
  {
269
258
  name: 'get_signals',
270
259
  description: 'Retrieve recent signal entries (GitHub activity, Slack summaries, Intercom updates, Notion pages) for a specific channel or all channels.',
@@ -327,26 +316,53 @@ const TOOLS = [
327
316
  // ── Tool handlers ────────────────────────────────────────────────────────────
328
317
 
329
318
  async function handleSearchContext(args) {
330
- const { query, limit = 10, repo, since, mode } = args;
319
+ const { query, limit = 10, repo, since, until, user, source, mode: rawMode } = args;
320
+
321
+ // Auto-switch to browse if no query provided
322
+ const mode = (!query && rawMode !== 'browse') ? 'browse' : (rawMode || 'semantic');
331
323
 
332
- // Try container first for semantic search (has embeddings for full team)
333
- if (mode !== 'text') {
334
- const containerResult = await proxySearch({ query, limit, repo, since, mode });
324
+ // Browse mode return entries sorted by date (no embeddings needed)
325
+ if (mode === 'browse') {
326
+ const opts = { limit, repo, since, until, user, source };
327
+
328
+ // Try container first
329
+ const containerResult = await proxySearch({ query, limit, repo, since, until, user, source, mode: 'browse' });
335
330
  if (containerResult && containerResult.found > 0) {
336
331
  containerResult.source = 'container';
337
332
  return containerResult;
338
333
  }
334
+
335
+ // Fall back to local
336
+ const results = contentStore.queryMetadata(opts);
337
+ const top = results.slice(0, limit);
338
+ return {
339
+ found: results.length,
340
+ showing: top.length,
341
+ source: 'local',
342
+ results: top.map(r => ({
343
+ id: r.id,
344
+ date: r.entry.date,
345
+ repo: r.entry.repo,
346
+ title: r.entry.title,
347
+ source: r.entry.source,
348
+ user: r.entry.user || null,
349
+ tags: r.entry.tags || [],
350
+ summary: r.entry.summary || null,
351
+ })),
352
+ };
339
353
  }
340
354
 
341
- // Fall back to local search
342
- const opts = { limit, repo, since };
343
- let results;
344
- if (mode === 'text') {
345
- results = contentStore.searchText(query, opts);
346
- } else {
347
- results = await contentStore.searchJournals(query, opts);
355
+ // Semantic mode try container first (has embeddings for full team)
356
+ const containerResult = await proxySearch({ query, limit, repo, since, until, user, source, mode });
357
+ if (containerResult && containerResult.found > 0) {
358
+ containerResult.source = 'container';
359
+ return containerResult;
348
360
  }
349
361
 
362
+ // Fall back to local semantic search
363
+ const opts = { limit, repo, since, until, user, source };
364
+ const results = await contentStore.searchJournals(query, opts);
365
+
350
366
  if (!results || results.length === 0) {
351
367
  return { found: 0, results: [], source: 'local', hint: 'No matches. Try a broader query or check wayfind reindex.' };
352
368
  }
@@ -361,6 +377,7 @@ async function handleSearchContext(args) {
361
377
  repo: r.entry.repo,
362
378
  title: r.entry.title,
363
379
  source: r.entry.source,
380
+ user: r.entry.user || null,
364
381
  tags: r.entry.tags || [],
365
382
  summary: r.entry.summary || null,
366
383
  })),
@@ -397,25 +414,6 @@ async function handleGetEntry(args) {
397
414
  return { error: `Entry not found: ${id}` };
398
415
  }
399
416
 
400
- function handleListRecent(args) {
401
- const { limit = 20, repo, since, source } = args;
402
- const opts = { limit, repo, since, source };
403
- const results = contentStore.queryMetadata(opts);
404
- const top = results.slice(0, limit);
405
-
406
- return {
407
- total: results.length,
408
- showing: top.length,
409
- entries: top.map(r => ({
410
- id: r.id,
411
- date: r.entry.date,
412
- repo: r.entry.repo,
413
- title: r.entry.title,
414
- source: r.entry.source,
415
- tags: r.entry.tags || [],
416
- })),
417
- };
418
- }
419
417
 
420
418
  function handleGetSignals(args) {
421
419
  const { channel, limit = 20 } = args;
@@ -590,7 +588,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
590
588
  switch (name) {
591
589
  case 'search_context': result = await handleSearchContext(args); break;
592
590
  case 'get_entry': result = await handleGetEntry(args); break;
593
- case 'list_recent': result = handleListRecent(args); break;
594
591
  case 'get_signals': result = handleGetSignals(args); break;
595
592
  case 'get_team_status': result = handleGetTeamStatus(args); break;
596
593
  case 'get_personas': result = handleGetPersonas(); break;