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/README.md +74 -137
- package/bin/connectors/llm.js +87 -0
- package/bin/content-store.js +12 -142
- package/bin/mcp-server.js +47 -50
- package/bin/slack-bot.js +136 -1060
- package/bin/team-context.js +41 -31
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/setup.sh +9 -27
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
//
|
|
333
|
-
if (mode
|
|
334
|
-
const
|
|
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
|
-
//
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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;
|