memory-journal-mcp 7.2.0 → 7.4.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.
@@ -1,5 +1,5 @@
1
1
  import { transformAutoReturn } from './chunk-OKOVZ5QE.js';
2
- import { GitHubIntegration, resolveAuthor, logger, ValidationError, ResourceNotFoundError, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-IWKLHSPU.js';
2
+ import { GitHubIntegration, resolveAuthor, logger, ValidationError, ResourceNotFoundError, assertSafeDirectoryPath, MemoryJournalMcpError, matchSuggestion, ConfigurationError } from './chunk-WXDEVIFL.js';
3
3
  import { z, ZodError } from 'zod';
4
4
  import { open, stat, mkdir, rename, appendFile, readdir, readFile } from 'fs/promises';
5
5
  import { tmpdir } from 'os';
@@ -59,7 +59,7 @@ async function resolveIssueUrl(context, projectNumber, issueNumber, existingUrl)
59
59
  ([_, v]) => v.project_number === projectNumber
60
60
  );
61
61
  if (entry) {
62
- const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-2TFMXHIJ.js');
62
+ const { GitHubIntegration: GitHubIntegration2 } = await import('./github-integration-YODGZH3K.js');
63
63
  const targetGithub = new GitHubIntegration2(entry[1].path);
64
64
  const repoInfo = await targetGithub.getRepoInfo();
65
65
  if (repoInfo.owner && repoInfo.repo) {
@@ -114,7 +114,7 @@ var SIGNIFICANCE_TYPES = [
114
114
  "release"
115
115
  ];
116
116
  var MAX_CONTENT_LENGTH = 5e4;
117
- var MAX_QUERY_LIMIT = 500;
117
+ var MAX_QUERY_LIMIT = Number("500");
118
118
  var DATE_MIN_SENTINEL = "1970-01-01";
119
119
  var DATE_MAX_SENTINEL = "2999-12-31";
120
120
  var DATE_FORMAT_REGEX = /^\d{4}-\d{2}-\d{2}$/;
@@ -140,7 +140,8 @@ var EntryOutputSchema = z.object({
140
140
  workflowRunId: z.number().nullable().optional(),
141
141
  workflowName: z.string().nullable().optional(),
142
142
  workflowStatus: z.string().nullable().optional(),
143
- source: z.enum(["personal", "team"]).optional()
143
+ source: z.enum(["personal", "team"]).optional(),
144
+ importanceScore: z.number().optional().describe("Importance score (0.0-1.0), present when sort_by=importance")
144
145
  }).extend(ErrorFieldsMixin.shape);
145
146
  var EntriesListOutputSchema = z.object({
146
147
  entries: z.array(EntryOutputSchema).optional(),
@@ -208,7 +209,7 @@ var CreateEntrySchemaMcp = z.object({
208
209
  share_with_team: z.boolean().optional().default(false)
209
210
  });
210
211
  var GetEntryByIdSchema = z.object({
211
- entry_id: z.number(),
212
+ entry_id: z.number().int(),
212
213
  include_relationships: z.boolean().optional().default(true)
213
214
  });
214
215
  var GetEntryByIdSchemaMcp = z.object({
@@ -217,11 +218,13 @@ var GetEntryByIdSchemaMcp = z.object({
217
218
  });
218
219
  var GetRecentEntriesSchema = z.object({
219
220
  limit: z.number().min(1).max(MAX_QUERY_LIMIT).optional().default(5),
220
- is_personal: z.boolean().optional()
221
+ is_personal: z.boolean().optional(),
222
+ sort_by: z.enum(["timestamp", "importance"]).optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
221
223
  });
222
224
  var GetRecentEntriesSchemaMcp = z.object({
223
225
  limit: relaxedNumber().optional().default(5),
224
- is_personal: z.boolean().optional()
226
+ is_personal: z.boolean().optional(),
227
+ sort_by: z.string().optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
225
228
  });
226
229
  var CreateEntryMinimalSchema = z.object({
227
230
  content: z.string().min(1).max(MAX_CONTENT_LENGTH)
@@ -247,6 +250,7 @@ var EntryByIdOutputSchema = z.object({
247
250
  error: z.string().optional()
248
251
  }).extend(ErrorFieldsMixin.shape);
249
252
  var TestSimpleOutputSchema = z.object({
253
+ success: z.boolean().optional(),
250
254
  message: z.string()
251
255
  }).extend(ErrorFieldsMixin.shape);
252
256
  var TagsListOutputSchema = z.object({
@@ -364,6 +368,7 @@ function getCoreTools(context) {
364
368
  }
365
369
  const { score: importance, breakdown: importanceBreakdown } = db.calculateImportance(entry_id);
366
370
  const result = {
371
+ success: true,
367
372
  entry,
368
373
  importance,
369
374
  importanceBreakdown
@@ -387,9 +392,9 @@ function getCoreTools(context) {
387
392
  annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
388
393
  handler: (params) => {
389
394
  try {
390
- const { limit, is_personal } = GetRecentEntriesSchema.parse(params);
391
- const entries = db.getRecentEntries(limit, is_personal);
392
- return { entries, count: entries.length };
395
+ const { limit, is_personal, sort_by } = GetRecentEntriesSchema.parse(params);
396
+ const entries = db.getRecentEntries(limit, is_personal, sort_by);
397
+ return { success: true, entries, count: entries.length };
393
398
  } catch (err) {
394
399
  return formatHandlerError(err);
395
400
  }
@@ -425,7 +430,7 @@ function getCoreTools(context) {
425
430
  handler: (params) => {
426
431
  try {
427
432
  const { message } = TestSimpleSchema.parse(params);
428
- return { message: `Test response: ${message}` };
433
+ return { success: true, message: `Test response: ${message}` };
429
434
  } catch (err) {
430
435
  return formatHandlerError(err);
431
436
  }
@@ -443,7 +448,7 @@ function getCoreTools(context) {
443
448
  try {
444
449
  const rawTags = db.listTags();
445
450
  const tags = rawTags.map((t) => ({ name: t.name, count: t.usageCount }));
446
- return { tags, count: tags.length };
451
+ return { success: true, tags, count: tags.length };
447
452
  } catch (err) {
448
453
  return formatHandlerError(err);
449
454
  }
@@ -457,10 +462,19 @@ var DEDUP_KEY_LENGTH = 200;
457
462
  function calcPerDbLimit(limit, hasTeamDb) {
458
463
  return hasTeamDb ? Math.min(limit * 2, MAX_QUERY_LIMIT) : limit;
459
464
  }
460
- function mergeAndDedup(personal, team, limit) {
465
+ function mergeAndDedup(personal, team, limit, sortBy = "timestamp") {
461
466
  const seen = /* @__PURE__ */ new Set();
462
467
  const merged = [];
463
- const all = [...personal, ...team].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
468
+ const all = [...personal, ...team].sort((a, b) => {
469
+ if (sortBy === "importance") {
470
+ const scoreA = Number(a["importanceScore"]) || 0;
471
+ const scoreB = Number(b["importanceScore"]) || 0;
472
+ if (scoreA !== scoreB) {
473
+ return scoreB - scoreA;
474
+ }
475
+ }
476
+ return b.timestamp.localeCompare(a.timestamp);
477
+ });
464
478
  for (const entry of all) {
465
479
  const key = entry.content.slice(0, DEDUP_KEY_LENGTH);
466
480
  if (!seen.has(key)) {
@@ -470,6 +484,30 @@ function mergeAndDedup(personal, team, limit) {
470
484
  }
471
485
  return limit !== void 0 ? merged.slice(0, limit) : merged;
472
486
  }
487
+ function passMetadataFilters(entry, options, db) {
488
+ if (options.isPersonal !== void 0 && entry.isPersonal !== options.isPersonal) return false;
489
+ if (options.projectNumber !== void 0 && entry.projectNumber !== options.projectNumber)
490
+ return false;
491
+ if (options.issueNumber !== void 0 && entry.issueNumber !== options.issueNumber) return false;
492
+ if (options.prNumber !== void 0 && entry.prNumber !== options.prNumber) return false;
493
+ if (options.prStatus !== void 0 && entry.prStatus !== options.prStatus) return false;
494
+ if (options.workflowRunId !== void 0 && entry.workflowRunId !== options.workflowRunId)
495
+ return false;
496
+ if (options.entryType && entry.entryType !== options.entryType) return false;
497
+ if (options.startDate) {
498
+ const entryDate = entry.timestamp.split("T")[0] ?? "";
499
+ if (entryDate < options.startDate) return false;
500
+ }
501
+ if (options.endDate) {
502
+ const entryDate = entry.timestamp.split("T")[0] ?? "";
503
+ if (entryDate > options.endDate) return false;
504
+ }
505
+ if (options.tags && options.tags.length > 0) {
506
+ const entryTags = Array.isArray(entry.tags) ? entry.tags : db.getTagsForEntry(entry.id);
507
+ if (!options.tags.some((t) => entryTags.includes(t))) return false;
508
+ }
509
+ return true;
510
+ }
473
511
 
474
512
  // src/handlers/tools/search/auto.ts
475
513
  var QUESTION_PATTERNS = [
@@ -510,7 +548,7 @@ function ftsSearch(query, db, teamDb, options) {
510
548
  const perDbLimit = calcPerDbLimit(options.limit, !!teamDb);
511
549
  let personalEntries;
512
550
  if (!query && !hasFilters) {
513
- personalEntries = db.getRecentEntries(perDbLimit, options.isPersonal);
551
+ personalEntries = db.getRecentEntries(perDbLimit, options.isPersonal, options.sortBy);
514
552
  } else {
515
553
  personalEntries = db.searchEntries(query || "", {
516
554
  limit: perDbLimit,
@@ -523,13 +561,14 @@ function ftsSearch(query, db, teamDb, options) {
523
561
  tags: options.tags,
524
562
  entryType: options.entryType,
525
563
  startDate: options.startDate,
526
- endDate: options.endDate
564
+ endDate: options.endDate,
565
+ sortBy: options.sortBy
527
566
  });
528
567
  }
529
568
  if (teamDb && options.isPersonal !== true) {
530
569
  let teamEntries;
531
570
  if (!query && !hasFilters) {
532
- teamEntries = teamDb.getRecentEntries(perDbLimit);
571
+ teamEntries = teamDb.getRecentEntries(perDbLimit, void 0, options.sortBy);
533
572
  } else {
534
573
  teamEntries = teamDb.searchEntries(query || "", {
535
574
  limit: perDbLimit,
@@ -541,13 +580,15 @@ function ftsSearch(query, db, teamDb, options) {
541
580
  tags: options.tags,
542
581
  entryType: options.entryType,
543
582
  startDate: options.startDate,
544
- endDate: options.endDate
583
+ endDate: options.endDate,
584
+ sortBy: options.sortBy
545
585
  });
546
586
  }
547
587
  const merged = mergeAndDedup(
548
588
  personalEntries.map((e) => ({ ...e, source: "personal" })),
549
589
  teamEntries.map((e) => ({ ...e, source: "team" })),
550
- options.limit
590
+ options.limit,
591
+ options.sortBy
551
592
  );
552
593
  return { entries: merged, count: merged.length };
553
594
  }
@@ -603,7 +644,7 @@ async function hybridSearch(query, db, vectorManager, options) {
603
644
  for (const id of sortedIds) {
604
645
  const entry = entriesMap.get(id);
605
646
  if (!entry) continue;
606
- if (options.isPersonal !== void 0 && entry.isPersonal !== options.isPersonal) continue;
647
+ if (!passMetadataFilters(entry, options, db)) continue;
607
648
  entries.push({ ...entry, source: "personal" });
608
649
  }
609
650
  return { entries, fusionScores };
@@ -625,7 +666,8 @@ var SearchEntriesSchema = z.object({
625
666
  tags: z.array(z.string()).optional(),
626
667
  entry_type: z.enum(ENTRY_TYPES).optional(),
627
668
  start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
628
- end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional()
669
+ end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
670
+ sort_by: z.enum(["timestamp", "importance"]).optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
629
671
  });
630
672
  var SearchEntriesSchemaMcp = z.object({
631
673
  query: z.string().optional(),
@@ -642,7 +684,8 @@ var SearchEntriesSchemaMcp = z.object({
642
684
  tags: z.array(z.string()).optional(),
643
685
  entry_type: z.string().optional(),
644
686
  start_date: z.string().optional(),
645
- end_date: z.string().optional()
687
+ end_date: z.string().optional(),
688
+ sort_by: z.string().optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
646
689
  });
647
690
  var SearchByDateRangeSchema = z.object({
648
691
  start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE),
@@ -654,7 +697,8 @@ var SearchByDateRangeSchema = z.object({
654
697
  issue_number: z.number().optional(),
655
698
  pr_number: z.number().optional(),
656
699
  workflow_run_id: z.number().optional(),
657
- limit: z.number().max(MAX_QUERY_LIMIT).optional().default(500)
700
+ limit: z.number().max(MAX_QUERY_LIMIT).optional().default(500),
701
+ sort_by: z.enum(["timestamp", "importance"]).optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
658
702
  });
659
703
  var SearchByDateRangeSchemaMcp = z.object({
660
704
  start_date: z.string(),
@@ -666,7 +710,8 @@ var SearchByDateRangeSchemaMcp = z.object({
666
710
  issue_number: relaxedNumber().optional(),
667
711
  pr_number: relaxedNumber().optional(),
668
712
  workflow_run_id: relaxedNumber().optional(),
669
- limit: relaxedNumber().optional().default(500)
713
+ limit: relaxedNumber().optional().default(500),
714
+ sort_by: z.string().optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
670
715
  });
671
716
  var SemanticSearchSchema = z.object({
672
717
  query: z.string().optional(),
@@ -738,7 +783,9 @@ function getSearchTools(context) {
738
783
  ...formatHandlerError(
739
784
  new ValidationError(
740
785
  "Search requires either a query string or at least one filter",
741
- { suggestion: "Provide a search query or use get_recent_entries instead" }
786
+ {
787
+ suggestion: "Provide a search query or use get_recent_entries instead"
788
+ }
742
789
  )
743
790
  ),
744
791
  entries: [],
@@ -758,7 +805,8 @@ function getSearchTools(context) {
758
805
  tags: input.tags,
759
806
  entryType: input.entry_type,
760
807
  startDate: input.start_date,
761
- endDate: input.end_date
808
+ endDate: input.end_date,
809
+ sortBy: input.sort_by
762
810
  };
763
811
  switch (effectiveMode) {
764
812
  case "semantic": {
@@ -766,9 +814,10 @@ function getSearchTools(context) {
766
814
  const result = ftsSearch(input.query, db, teamDb, searchOptions);
767
815
  return { ...result, searchMode: "fts (fallback)" };
768
816
  }
817
+ const internalLimit = hasFilters ? Math.min(Math.max(input.limit * 10, 100), 1e3) : input.limit * 2;
769
818
  const semanticResults = await vectorManager.search(
770
819
  query,
771
- input.limit,
820
+ internalLimit,
772
821
  0.25
773
822
  );
774
823
  const entryIds = semanticResults.map((r) => r.entryId);
@@ -776,11 +825,31 @@ function getSearchTools(context) {
776
825
  const entries = semanticResults.map((r) => {
777
826
  const entry = entriesMap.get(r.entryId);
778
827
  if (!entry) return null;
779
- if (input.is_personal !== void 0 && entry.isPersonal !== input.is_personal)
828
+ if (!passMetadataFilters(
829
+ entry,
830
+ searchOptions,
831
+ db
832
+ ))
780
833
  return null;
781
834
  return { ...entry, source: "personal" };
782
- }).filter((e) => e !== null);
835
+ }).filter((e) => e !== null).slice(0, input.limit);
836
+ if (input.sort_by === "importance") {
837
+ const scored = entries.map((e) => {
838
+ const { score } = db.calculateImportance(e.id);
839
+ return { ...e, importanceScore: Math.round(score * 100) / 100 };
840
+ });
841
+ scored.sort(
842
+ (a, b) => (b.importanceScore ?? 0) - (a.importanceScore ?? 0)
843
+ );
844
+ return {
845
+ success: true,
846
+ entries: scored,
847
+ count: scored.length,
848
+ searchMode: isAuto ? "semantic (auto)" : "semantic"
849
+ };
850
+ }
783
851
  return {
852
+ success: true,
784
853
  entries,
785
854
  count: entries.length,
786
855
  searchMode: isAuto ? "semantic (auto)" : "semantic"
@@ -797,7 +866,24 @@ function getSearchTools(context) {
797
866
  vectorManager,
798
867
  searchOptions
799
868
  );
869
+ if (input.sort_by === "importance") {
870
+ const scored = entries.map((e) => {
871
+ const entryId = e["id"];
872
+ const { score } = db.calculateImportance(entryId);
873
+ return { ...e, importanceScore: Math.round(score * 100) / 100 };
874
+ });
875
+ scored.sort(
876
+ (a, b) => (b.importanceScore ?? 0) - (a.importanceScore ?? 0)
877
+ );
878
+ return {
879
+ success: true,
880
+ entries: scored,
881
+ count: scored.length,
882
+ searchMode: isAuto ? "hybrid (auto)" : "hybrid"
883
+ };
884
+ }
800
885
  return {
886
+ success: true,
801
887
  entries,
802
888
  count: entries.length,
803
889
  searchMode: isAuto ? "hybrid (auto)" : "hybrid"
@@ -808,6 +894,7 @@ function getSearchTools(context) {
808
894
  const result = ftsSearch(input.query, db, teamDb, searchOptions);
809
895
  return {
810
896
  ...result,
897
+ success: true,
811
898
  searchMode: isAuto ? "fts (auto)" : "fts"
812
899
  };
813
900
  }
@@ -847,7 +934,8 @@ function getSearchTools(context) {
847
934
  issueNumber: input.issue_number,
848
935
  prNumber: input.pr_number,
849
936
  workflowRunId: input.workflow_run_id,
850
- limit: perDbLimit
937
+ limit: perDbLimit,
938
+ sortBy: input.sort_by
851
939
  });
852
940
  if (teamDb && input.is_personal !== true) {
853
941
  const teamEntries = teamDb.searchByDateRange(
@@ -860,17 +948,23 @@ function getSearchTools(context) {
860
948
  issueNumber: input.issue_number,
861
949
  prNumber: input.pr_number,
862
950
  workflowRunId: input.workflow_run_id,
863
- limit: perDbLimit
951
+ limit: perDbLimit,
952
+ sortBy: input.sort_by
864
953
  }
865
954
  );
866
955
  const merged = mergeAndDedup(
867
956
  personalEntries.map((e) => ({ ...e, source: "personal" })),
868
957
  teamEntries.map((e) => ({ ...e, source: "team" })),
869
- input.limit
958
+ input.limit,
959
+ input.sort_by
870
960
  );
871
- return { entries: merged, count: merged.length };
961
+ return { success: true, entries: merged, count: merged.length };
872
962
  }
873
- return { entries: personalEntries, count: personalEntries.length };
963
+ return {
964
+ success: true,
965
+ entries: personalEntries,
966
+ count: personalEntries.length
967
+ };
874
968
  } catch (err) {
875
969
  return formatHandlerError(err);
876
970
  }
@@ -912,17 +1006,19 @@ function getSearchTools(context) {
912
1006
  count: 0
913
1007
  };
914
1008
  }
1009
+ const hasFilters = input.is_personal !== void 0 || input.tags !== void 0 || input.entry_type !== void 0 || input.start_date !== void 0 || input.end_date !== void 0;
1010
+ const internalLimit = hasFilters ? Math.min(Math.max(input.limit * 10, 100), 1e3) : input.limit * 2;
915
1011
  let results;
916
1012
  if (input.entry_id !== void 0) {
917
1013
  results = await vectorManager.searchByEntryId(
918
1014
  input.entry_id,
919
- input.limit ?? 10,
1015
+ internalLimit,
920
1016
  input.similarity_threshold ?? 0.25
921
1017
  );
922
1018
  } else {
923
1019
  results = await vectorManager.search(
924
1020
  input.query ?? "",
925
- input.limit ?? 10,
1021
+ internalLimit,
926
1022
  input.similarity_threshold ?? 0.25
927
1023
  );
928
1024
  }
@@ -931,29 +1027,22 @@ function getSearchTools(context) {
931
1027
  const entries = results.map((r) => {
932
1028
  const entry = entriesMap.get(r.entryId);
933
1029
  if (!entry) return null;
934
- if (input.is_personal !== void 0 && entry.isPersonal !== input.is_personal)
935
- return null;
936
- if (input.tags && input.tags.length > 0) {
937
- const entryTags = db.getTagsForEntry(entry.id);
938
- if (!input.tags.some((t) => entryTags.includes(t))) return null;
939
- }
940
- if (input.entry_type && entry.entryType !== input.entry_type)
1030
+ const filterOptions = {
1031
+ isPersonal: input.is_personal,
1032
+ tags: input.tags,
1033
+ entryType: input.entry_type,
1034
+ startDate: input.start_date,
1035
+ endDate: input.end_date
1036
+ };
1037
+ if (!passMetadataFilters(entry, filterOptions, db))
941
1038
  return null;
942
- if (input.start_date) {
943
- const entryDate = entry.timestamp.split("T")[0] ?? "";
944
- if (entryDate < input.start_date) return null;
945
- }
946
- if (input.end_date) {
947
- const entryDate = entry.timestamp.split("T")[0] ?? "";
948
- if (entryDate > input.end_date) return null;
949
- }
950
1039
  if (input.entry_id !== void 0 && entry.id === input.entry_id)
951
1040
  return null;
952
1041
  return {
953
1042
  ...entry,
954
1043
  similarity: Math.round(r.score * 100) / 100
955
1044
  };
956
- }).filter((e) => e !== null);
1045
+ }).filter((e) => e !== null).slice(0, input.limit);
957
1046
  const stats = vectorManager.getStats();
958
1047
  const isIndexEmpty = stats.itemCount === 0;
959
1048
  const includeHint = input.hint_on_empty ?? true;
@@ -962,6 +1051,7 @@ function getSearchTools(context) {
962
1051
  const allNoise = entries.length > 0 && bestSimilarity < QUALITY_FLOOR2;
963
1052
  const hint = isIndexEmpty && includeHint ? "No entries in vector index. Use rebuild_vector_index to index existing entries." : entries.length === 0 && includeHint ? `No entries matched your query above the similarity threshold (${String(input.similarity_threshold ?? 0.25)}). Try lowering similarity_threshold (e.g., 0.15) for broader matches.` : allNoise ? `Results may be noise \u2014 best similarity (${String(bestSimilarity)}) is below quality floor (${String(QUALITY_FLOOR2)}). Try a more specific query or raise similarity_threshold to filter weak matches.` : void 0;
964
1053
  return {
1054
+ success: true,
965
1055
  query: input.query,
966
1056
  ...input.entry_id !== void 0 ? { entryId: input.entry_id } : {},
967
1057
  entries,
@@ -1120,7 +1210,7 @@ function getAnalyticsTools(context) {
1120
1210
  end_date,
1121
1211
  project_breakdown
1122
1212
  );
1123
- return { ...stats, groupBy: group_by };
1213
+ return { success: true, ...stats, groupBy: group_by };
1124
1214
  } catch (err) {
1125
1215
  return formatHandlerError(err);
1126
1216
  }
@@ -1162,6 +1252,7 @@ function getAnalyticsTools(context) {
1162
1252
  );
1163
1253
  if (!projectsResult[0] || projectsResult[0].values.length === 0) {
1164
1254
  return {
1255
+ success: true,
1165
1256
  project_count: 0,
1166
1257
  total_entries: 0,
1167
1258
  projects: [],
@@ -1233,6 +1324,7 @@ function getAnalyticsTools(context) {
1233
1324
  )
1234
1325
  }));
1235
1326
  return {
1327
+ success: true,
1236
1328
  project_count: projects.length,
1237
1329
  total_entries: totalEntries,
1238
1330
  projects: projects.map((p) => ({
@@ -1338,6 +1430,24 @@ function getRelationshipTools(context) {
1338
1430
  success: false
1339
1431
  };
1340
1432
  }
1433
+ const fromEntry = db.getEntryById(input.from_entry_id);
1434
+ if (!fromEntry || fromEntry.deletedAt) {
1435
+ return {
1436
+ ...formatHandlerError(
1437
+ new ResourceNotFoundError("Entry", String(input.from_entry_id))
1438
+ ),
1439
+ success: false
1440
+ };
1441
+ }
1442
+ const toEntry = db.getEntryById(input.to_entry_id);
1443
+ if (!toEntry || toEntry.deletedAt) {
1444
+ return {
1445
+ ...formatHandlerError(
1446
+ new ResourceNotFoundError("Entry", String(input.to_entry_id))
1447
+ ),
1448
+ success: false
1449
+ };
1450
+ }
1341
1451
  const existingRelationships = db.getRelationships(input.from_entry_id);
1342
1452
  const existing = existingRelationships.find(
1343
1453
  (r) => r.fromEntryId === input.from_entry_id && r.toEntryId === input.to_entry_id && r.relationshipType === input.relationship_type
@@ -1400,6 +1510,9 @@ function getRelationshipTools(context) {
1400
1510
  const entry = db.getEntryById(input.entry_id);
1401
1511
  if (!entry) {
1402
1512
  return {
1513
+ ...formatHandlerError(
1514
+ new ResourceNotFoundError("Entry", String(input.entry_id))
1515
+ ),
1403
1516
  success: false,
1404
1517
  entry_count: 0,
1405
1518
  relationship_count: 0,
@@ -2612,7 +2725,8 @@ var CreateGitHubIssueWithEntryOutputSchema = z.object({
2612
2725
  project: z.object({
2613
2726
  projectNumber: z.number(),
2614
2727
  added: z.boolean(),
2615
- message: z.string(),
2728
+ message: z.string().optional(),
2729
+ error: z.string().optional(),
2616
2730
  initialStatus: z.object({
2617
2731
  status: z.string(),
2618
2732
  set: z.boolean()
@@ -2643,7 +2757,8 @@ var CloseGitHubIssueWithEntryOutputSchema = z.object({
2643
2757
  }).optional(),
2644
2758
  kanban: z.object({
2645
2759
  moved: z.boolean(),
2646
- projectNumber: z.number(),
2760
+ projectNumber: z.number().optional(),
2761
+ error: z.string().optional(),
2647
2762
  message: z.string().optional()
2648
2763
  }).optional(),
2649
2764
  message: z.string().optional(),
@@ -2711,6 +2826,7 @@ var DeleteMilestoneOutputSchema = z.object({
2711
2826
  instruction: z.string().optional()
2712
2827
  }).extend(ErrorFieldsMixin.shape);
2713
2828
  var RepoInsightsOutputSchema = z.object({
2829
+ success: z.boolean().optional(),
2714
2830
  owner: z.string().optional(),
2715
2831
  repo: z.string().optional(),
2716
2832
  section: z.string().optional(),
@@ -4304,6 +4420,7 @@ function getGitHubInsightsTools(context) {
4304
4420
  const repo = resolved.repo;
4305
4421
  const section = input.sections;
4306
4422
  const result = {
4423
+ success: true,
4307
4424
  owner,
4308
4425
  repo,
4309
4426
  section
@@ -4664,20 +4781,24 @@ var TeamCreateEntrySchemaMcp = z.object({
4664
4781
  author: z.string().optional()
4665
4782
  });
4666
4783
  var TeamGetRecentSchema = z.object({
4667
- limit: z.number().min(1).max(500).optional().default(10)
4784
+ limit: z.number().min(1).max(500).optional().default(10),
4785
+ sort_by: z.enum(["timestamp", "importance"]).optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
4668
4786
  });
4669
4787
  var TeamGetRecentSchemaMcp = z.object({
4670
- limit: relaxedNumber().optional().default(10)
4788
+ limit: relaxedNumber().optional().default(10),
4789
+ sort_by: z.string().optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
4671
4790
  });
4672
4791
  var TeamSearchSchema = z.object({
4673
4792
  query: z.string().optional(),
4674
4793
  tags: z.array(z.string()).optional(),
4675
- limit: z.number().max(500).optional().default(10)
4794
+ limit: z.number().max(500).optional().default(10),
4795
+ sort_by: z.enum(["timestamp", "importance"]).optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
4676
4796
  });
4677
4797
  var TeamSearchSchemaMcp = z.object({
4678
4798
  query: z.string().optional(),
4679
4799
  tags: z.array(z.string()).optional(),
4680
- limit: relaxedNumber().optional().default(10)
4800
+ limit: relaxedNumber().optional().default(10),
4801
+ sort_by: z.string().optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
4681
4802
  });
4682
4803
  var TeamGetEntryByIdSchema = z.object({
4683
4804
  entry_id: z.number(),
@@ -4692,14 +4813,16 @@ var TeamSearchByDateRangeSchema = z.object({
4692
4813
  end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE),
4693
4814
  entry_type: z.enum(ENTRY_TYPES).optional(),
4694
4815
  tags: z.array(z.string()).optional(),
4695
- limit: z.number().max(500).optional().default(50)
4816
+ limit: z.number().max(500).optional().default(50),
4817
+ sort_by: z.enum(["timestamp", "importance"]).optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
4696
4818
  });
4697
4819
  var TeamSearchByDateRangeSchemaMcp = z.object({
4698
4820
  start_date: z.string().optional().describe("Start date (YYYY-MM-DD)"),
4699
4821
  end_date: z.string().optional().describe("End date (YYYY-MM-DD)"),
4700
4822
  entry_type: z.string().optional(),
4701
4823
  tags: z.array(z.string()).optional(),
4702
- limit: relaxedNumber().optional().default(50)
4824
+ limit: relaxedNumber().optional().default(50),
4825
+ sort_by: z.string().optional().default("timestamp").describe("Sort results by timestamp (default) or importance score")
4703
4826
  });
4704
4827
  var TeamUpdateEntrySchema = z.object({
4705
4828
  entry_id: z.number(),
@@ -4891,6 +5014,10 @@ var TeamSemanticSearchSchema = z.object({
4891
5014
  entry_id: z.number().optional().describe("Find entries related to this entry ID"),
4892
5015
  limit: z.number().max(500).optional().default(10),
4893
5016
  similarity_threshold: z.number().optional().default(0.25),
5017
+ tags: z.array(z.string()).optional(),
5018
+ entry_type: z.enum(ENTRY_TYPES).optional(),
5019
+ start_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
5020
+ end_date: z.string().regex(DATE_FORMAT_REGEX, DATE_FORMAT_MESSAGE).optional(),
4894
5021
  hint_on_empty: z.boolean().optional().default(true).describe("Include hint when no results found (default: true)")
4895
5022
  });
4896
5023
  var TeamSemanticSearchSchemaMcp = z.object({
@@ -4898,6 +5025,10 @@ var TeamSemanticSearchSchemaMcp = z.object({
4898
5025
  entry_id: relaxedNumber().optional().describe("Find entries related to this entry ID"),
4899
5026
  limit: relaxedNumber().optional().default(10),
4900
5027
  similarity_threshold: relaxedNumber().optional().default(0.25),
5028
+ tags: z.array(z.string()).optional(),
5029
+ entry_type: z.string().optional(),
5030
+ start_date: z.string().optional(),
5031
+ end_date: z.string().optional(),
4901
5032
  hint_on_empty: z.boolean().optional().default(true).describe("Include hint when no results found (default: true)")
4902
5033
  });
4903
5034
  var TeamAddToVectorIndexSchema = z.object({
@@ -4978,6 +5109,40 @@ var TeamCrossProjectInsightsOutputSchema = z.object({
4978
5109
  success: z.boolean().optional(),
4979
5110
  error: z.string().optional()
4980
5111
  }).extend(ErrorFieldsMixin.shape);
5112
+ var TeamCollaborationMatrixSchema = z.object({
5113
+ period: z.enum(["week", "month", "quarter"]).optional().default("month").describe("Time granularity for the activity heatmap"),
5114
+ limit: z.number().max(100).optional().default(20).describe("Max authors to include")
5115
+ });
5116
+ var TeamCollaborationMatrixSchemaMcp = z.object({
5117
+ period: z.string().optional().default("month"),
5118
+ limit: relaxedNumber().optional().default(20)
5119
+ });
5120
+ var TeamCollaborationMatrixOutputSchema = z.object({
5121
+ success: z.boolean().optional(),
5122
+ totalAuthors: z.number().optional(),
5123
+ totalEntries: z.number().optional(),
5124
+ authorActivity: z.array(
5125
+ z.object({
5126
+ author: z.string(),
5127
+ period: z.string(),
5128
+ entryCount: z.number()
5129
+ })
5130
+ ).optional(),
5131
+ crossAuthorLinks: z.array(
5132
+ z.object({
5133
+ fromAuthor: z.string(),
5134
+ toAuthor: z.string(),
5135
+ linkCount: z.number()
5136
+ })
5137
+ ).optional(),
5138
+ impactFactor: z.array(
5139
+ z.object({
5140
+ author: z.string(),
5141
+ inboundLinks: z.number()
5142
+ })
5143
+ ).optional(),
5144
+ error: z.string().optional()
5145
+ }).extend(ErrorFieldsMixin.shape);
4981
5146
 
4982
5147
  // src/handlers/tools/team/core-tools.ts
4983
5148
  function getTeamCoreTools(context) {
@@ -5090,8 +5255,8 @@ function getTeamCoreTools(context) {
5090
5255
  if (!teamDb) {
5091
5256
  return { ...TEAM_DB_ERROR_RESPONSE };
5092
5257
  }
5093
- const { limit } = TeamGetRecentSchema.parse(params);
5094
- const entries = teamDb.getRecentEntries(limit);
5258
+ const { limit, sort_by } = TeamGetRecentSchema.parse(params);
5259
+ const entries = teamDb.getRecentEntries(limit, void 0, sort_by);
5095
5260
  const authorMap = batchFetchAuthors(
5096
5261
  teamDb,
5097
5262
  entries.map((e) => e.id)
@@ -5147,12 +5312,16 @@ function getTeamSearchTools(context) {
5147
5312
  if (!teamDb) {
5148
5313
  return { ...TEAM_DB_ERROR_RESPONSE };
5149
5314
  }
5150
- const { query, tags, limit } = TeamSearchSchema.parse(params);
5315
+ const { query, tags, limit, sort_by } = TeamSearchSchema.parse(params);
5316
+ const searchLimit = tags && tags.length > 0 ? Math.min(Math.max(limit * 5, 50), 1e3) : limit;
5151
5317
  let entries;
5152
5318
  if (query) {
5153
- entries = teamDb.searchEntries(query, { limit });
5319
+ entries = teamDb.searchEntries(query, {
5320
+ limit: searchLimit,
5321
+ sortBy: sort_by
5322
+ });
5154
5323
  } else {
5155
- entries = teamDb.getRecentEntries(limit);
5324
+ entries = teamDb.getRecentEntries(searchLimit, void 0, sort_by);
5156
5325
  }
5157
5326
  if (tags && tags.length > 0) {
5158
5327
  const entryIds = entries.map((e) => e.id);
@@ -5178,6 +5347,7 @@ function getTeamSearchTools(context) {
5178
5347
  });
5179
5348
  }
5180
5349
  }
5350
+ entries = entries.slice(0, limit);
5181
5351
  const authorMap = batchFetchAuthors(
5182
5352
  teamDb,
5183
5353
  entries.map((e) => e.id)
@@ -5186,7 +5356,7 @@ function getTeamSearchTools(context) {
5186
5356
  ...e,
5187
5357
  author: authorMap.get(e.id) ?? null
5188
5358
  }));
5189
- return { entries: enriched, count: enriched.length };
5359
+ return { success: true, entries: enriched, count: enriched.length };
5190
5360
  } catch (err) {
5191
5361
  return formatHandlerError(err);
5192
5362
  }
@@ -5205,7 +5375,7 @@ function getTeamSearchTools(context) {
5205
5375
  if (!teamDb) {
5206
5376
  return { ...TEAM_DB_ERROR_RESPONSE };
5207
5377
  }
5208
- const { start_date, end_date, entry_type, tags, limit } = TeamSearchByDateRangeSchema.parse(params);
5378
+ const { start_date, end_date, entry_type, tags, limit, sort_by } = TeamSearchByDateRangeSchema.parse(params);
5209
5379
  if (start_date > end_date) {
5210
5380
  return {
5211
5381
  success: false,
@@ -5219,7 +5389,8 @@ function getTeamSearchTools(context) {
5219
5389
  const entries = teamDb.searchByDateRange(start_date, end_date, {
5220
5390
  entryType: entry_type,
5221
5391
  tags,
5222
- limit
5392
+ limit,
5393
+ sortBy: sort_by
5223
5394
  });
5224
5395
  const authorMap = batchFetchAuthors(
5225
5396
  teamDb,
@@ -5229,7 +5400,7 @@ function getTeamSearchTools(context) {
5229
5400
  ...e,
5230
5401
  author: authorMap.get(e.id) ?? null
5231
5402
  }));
5232
- return { entries: enriched, count: enriched.length };
5403
+ return { success: true, entries: enriched, count: enriched.length };
5233
5404
  } catch (err) {
5234
5405
  return formatHandlerError(err);
5235
5406
  }
@@ -5450,6 +5621,7 @@ function getTeamAnalyticsTools(context) {
5450
5621
  );
5451
5622
  if (!projectsResult[0] || projectsResult[0].values.length === 0) {
5452
5623
  return {
5624
+ success: true,
5453
5625
  project_count: 0,
5454
5626
  total_entries: 0,
5455
5627
  projects: [],
@@ -5526,6 +5698,7 @@ function getTeamAnalyticsTools(context) {
5526
5698
  )
5527
5699
  }));
5528
5700
  return {
5701
+ success: true,
5529
5702
  project_count: projects.length,
5530
5703
  total_entries: totalEntries,
5531
5704
  projects: projects.map((p) => ({
@@ -5540,6 +5713,97 @@ function getTeamAnalyticsTools(context) {
5540
5713
  return formatHandlerError(err);
5541
5714
  }
5542
5715
  }
5716
+ },
5717
+ {
5718
+ name: "team_get_collaboration_matrix",
5719
+ title: "Team Collaboration Matrix",
5720
+ description: "Analyze cross-author collaboration: activity heatmap per period, cross-linking patterns between authors, and impact factor (inbound links). Requires TEAM_DB_PATH.",
5721
+ group: "team",
5722
+ inputSchema: TeamCollaborationMatrixSchemaMcp,
5723
+ outputSchema: TeamCollaborationMatrixOutputSchema,
5724
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
5725
+ handler: (params) => {
5726
+ try {
5727
+ if (!teamDb) {
5728
+ return { ...TEAM_DB_ERROR_RESPONSE };
5729
+ }
5730
+ const { period, limit } = TeamCollaborationMatrixSchema.parse(params);
5731
+ const dateFormat = period === "week" ? "%Y-W%W" : period === "quarter" ? "%Y-Q" : "%Y-%m";
5732
+ const activityResult = teamDb.executeRawQuery(
5733
+ `SELECT
5734
+ COALESCE(author, 'unknown') AS author,
5735
+ strftime('${dateFormat}', timestamp) AS period,
5736
+ COUNT(*) AS entry_count
5737
+ FROM memory_journal
5738
+ WHERE deleted_at IS NULL
5739
+ GROUP BY author, period
5740
+ ORDER BY period DESC, entry_count DESC
5741
+ LIMIT ?`,
5742
+ [limit * 10]
5743
+ // Up to 10 periods per author
5744
+ );
5745
+ const authorActivity = activityResult[0]?.values.map((row) => ({
5746
+ author: row[0],
5747
+ period: row[1],
5748
+ entryCount: row[2]
5749
+ })) ?? [];
5750
+ const crossLinkResult = teamDb.executeRawQuery(
5751
+ `SELECT
5752
+ COALESCE(m1.author, 'unknown') AS from_author,
5753
+ COALESCE(m2.author, 'unknown') AS to_author,
5754
+ COUNT(*) AS link_count
5755
+ FROM relationships r
5756
+ JOIN memory_journal m1 ON r.from_entry_id = m1.id
5757
+ JOIN memory_journal m2 ON r.to_entry_id = m2.id
5758
+ WHERE m1.deleted_at IS NULL AND m2.deleted_at IS NULL
5759
+ AND COALESCE(m1.author, 'unknown') != COALESCE(m2.author, 'unknown')
5760
+ GROUP BY from_author, to_author
5761
+ ORDER BY link_count DESC
5762
+ LIMIT ?`,
5763
+ [limit]
5764
+ );
5765
+ const crossAuthorLinks = crossLinkResult[0]?.values.map((row) => ({
5766
+ fromAuthor: row[0],
5767
+ toAuthor: row[1],
5768
+ linkCount: row[2]
5769
+ })) ?? [];
5770
+ const impactResult = teamDb.executeRawQuery(
5771
+ `SELECT
5772
+ COALESCE(m2.author, 'unknown') AS author,
5773
+ COUNT(*) AS inbound_links
5774
+ FROM relationships r
5775
+ JOIN memory_journal m2 ON r.to_entry_id = m2.id
5776
+ WHERE m2.deleted_at IS NULL
5777
+ GROUP BY author
5778
+ ORDER BY inbound_links DESC
5779
+ LIMIT ?`,
5780
+ [limit]
5781
+ );
5782
+ const impactFactor = impactResult[0]?.values.map((row) => ({
5783
+ author: row[0],
5784
+ inboundLinks: row[1]
5785
+ })) ?? [];
5786
+ const totalsResult = teamDb.executeRawQuery(
5787
+ `SELECT
5788
+ COUNT(DISTINCT COALESCE(author, 'unknown')) AS total_authors,
5789
+ COUNT(*) AS total_entries
5790
+ FROM memory_journal
5791
+ WHERE deleted_at IS NULL`
5792
+ );
5793
+ const totalAuthors = totalsResult[0]?.values[0]?.[0] ?? 0;
5794
+ const totalEntries = totalsResult[0]?.values[0]?.[1] ?? 0;
5795
+ return {
5796
+ success: true,
5797
+ totalAuthors,
5798
+ totalEntries,
5799
+ authorActivity,
5800
+ crossAuthorLinks,
5801
+ impactFactor
5802
+ };
5803
+ } catch (err) {
5804
+ return formatHandlerError(err);
5805
+ }
5806
+ }
5543
5807
  }
5544
5808
  ];
5545
5809
  }
@@ -5638,6 +5902,17 @@ function getTeamRelationshipTools(context) {
5638
5902
  const { entry_id, tag, depth } = TeamVisualizeRelationshipsSchema.parse(params);
5639
5903
  let entryIds = [];
5640
5904
  if (entry_id !== void 0) {
5905
+ const fromEntry = teamDb.getEntryById(entry_id);
5906
+ if (!fromEntry) {
5907
+ return {
5908
+ success: false,
5909
+ error: `Team entry ${String(entry_id)} not found`,
5910
+ code: "RESOURCE_NOT_FOUND",
5911
+ category: "resource",
5912
+ suggestion: "Verify the team entry ID and try again",
5913
+ recoverable: true
5914
+ };
5915
+ }
5641
5916
  entryIds = [entry_id];
5642
5917
  const visited = new Set(entryIds);
5643
5918
  let frontier = [...entryIds];
@@ -6083,17 +6358,19 @@ function getTeamVectorTools(context) {
6083
6358
  count: 0
6084
6359
  };
6085
6360
  }
6361
+ const hasFilters = input.tags !== void 0 || input.entry_type !== void 0 || input.start_date !== void 0 || input.end_date !== void 0;
6362
+ const internalLimit = hasFilters ? Math.min(Math.max(input.limit * 10, 100), 1e3) : input.limit * 2;
6086
6363
  let results;
6087
6364
  if (input.entry_id !== void 0) {
6088
6365
  results = await teamVectorManager.searchByEntryId(
6089
6366
  input.entry_id,
6090
- input.limit ?? 10,
6367
+ internalLimit,
6091
6368
  input.similarity_threshold ?? 0.25
6092
6369
  );
6093
6370
  } else {
6094
6371
  results = await teamVectorManager.search(
6095
6372
  input.query ?? "",
6096
- input.limit ?? 10,
6373
+ internalLimit,
6097
6374
  input.similarity_threshold ?? 0.25
6098
6375
  );
6099
6376
  }
@@ -6105,12 +6382,24 @@ function getTeamVectorTools(context) {
6105
6382
  if (!entry) return null;
6106
6383
  if (input.entry_id !== void 0 && entry.id === input.entry_id)
6107
6384
  return null;
6385
+ if (!passMetadataFilters(
6386
+ entry,
6387
+ {
6388
+ tags: input.tags,
6389
+ entryType: input.entry_type,
6390
+ startDate: input.start_date,
6391
+ endDate: input.end_date
6392
+ },
6393
+ teamDb
6394
+ )) {
6395
+ return null;
6396
+ }
6108
6397
  return {
6109
6398
  ...entry,
6110
6399
  author: authorMap.get(r.entryId) ?? null,
6111
6400
  similarity: Math.round(r.score * 100) / 100
6112
6401
  };
6113
- }).filter((e) => e !== null);
6402
+ }).filter((e) => e !== null).slice(0, input.limit);
6114
6403
  const stats = teamVectorManager.getStats();
6115
6404
  const isIndexEmpty = stats.itemCount === 0;
6116
6405
  const includeHint = input.hint_on_empty ?? true;
@@ -6118,6 +6407,7 @@ function getTeamVectorTools(context) {
6118
6407
  const allNoise = entries.length > 0 && bestSimilarity < QUALITY_FLOOR;
6119
6408
  const hint = isIndexEmpty && includeHint ? "No entries in team vector index. Use team_rebuild_vector_index to index existing entries." : entries.length === 0 && includeHint ? `No entries matched your query above the similarity threshold (${String(input.similarity_threshold ?? 0.25)}). Try lowering similarity_threshold (e.g., 0.15) for broader matches.` : allNoise ? `Results may be noise \u2014 best similarity (${String(bestSimilarity)}) is below quality floor (${String(QUALITY_FLOOR)}). Try a more specific query or raise similarity_threshold to filter weak matches.` : void 0;
6120
6409
  return {
6410
+ success: true,
6121
6411
  query: input.query,
6122
6412
  ...input.entry_id !== void 0 ? { entryId: input.entry_id } : {},
6123
6413
  entries,
@@ -7682,7 +7972,8 @@ var TOOL_GROUPS = {
7682
7972
  "team_get_vector_index_stats",
7683
7973
  "team_rebuild_vector_index",
7684
7974
  "team_add_to_vector_index",
7685
- "team_get_cross_project_insights"
7975
+ "team_get_cross_project_insights",
7976
+ "team_get_collaboration_matrix"
7686
7977
  ],
7687
7978
  codemode: ["mj_execute_code"]
7688
7979
  };