@jefuriiij/synthra 0.1.21 → 0.1.22

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.
@@ -2780,15 +2780,33 @@ Reason: ${retrieval.reason}
2780
2780
  return textContent(`${header}
2781
2781
  ${packed.text}`);
2782
2782
  }
2783
+ function resolveFileTarget(graph, filePath) {
2784
+ const files = graph.nodes.filter((n) => n.kind === "file");
2785
+ const exact = files.find((n) => n.path === filePath);
2786
+ if (exact) return { node: exact };
2787
+ const suffix = "/" + filePath;
2788
+ const matches = files.filter((n) => n.path.endsWith(suffix));
2789
+ if (matches.length === 1) return { node: matches[0] };
2790
+ if (matches.length > 1) return { ambiguous: matches.map((n) => n.path) };
2791
+ return { none: true };
2792
+ }
2783
2793
  function graphRead(args, ctx) {
2784
2794
  const target = typeof args?.target === "string" ? args.target : "";
2785
2795
  if (!target) return errorContent("graph_read: 'target' (string) is required");
2786
2796
  const [rawFile, symbolName] = target.includes("::") ? target.split("::", 2) : [target, void 0];
2787
2797
  const filePath = (rawFile ?? "").trim();
2788
- const fileNode = ctx.graph.nodes.find(
2789
- (n) => n.kind === "file" && n.path === filePath
2790
- );
2791
- if (!fileNode) return errorContent(`graph_read: file not found in graph: ${filePath}`);
2798
+ const resolved = resolveFileTarget(ctx.graph, filePath);
2799
+ if ("ambiguous" in resolved) {
2800
+ const shown = resolved.ambiguous.slice(0, 5).join(", ");
2801
+ const more = resolved.ambiguous.length > 5 ? ", \u2026" : "";
2802
+ return errorContent(
2803
+ `graph_read: '${filePath}' matches multiple files (${shown}${more}). Pass a longer path.`
2804
+ );
2805
+ }
2806
+ if ("none" in resolved) {
2807
+ return errorContent(`graph_read: file not found in graph: ${filePath}`);
2808
+ }
2809
+ const fileNode = resolved.node;
2792
2810
  if (!symbolName) {
2793
2811
  return textContent(`# ${fileNode.path}
2794
2812
 
@@ -2796,10 +2814,10 @@ ${fileNode.content}`);
2796
2814
  }
2797
2815
  const cleanSym = symbolName.trim();
2798
2816
  const symbol = ctx.graph.nodes.find(
2799
- (n) => n.kind === "symbol" && n.file === filePath && n.name === cleanSym
2817
+ (n) => n.kind === "symbol" && n.file === fileNode.path && n.name === cleanSym
2800
2818
  );
2801
2819
  if (!symbol) {
2802
- return errorContent(`graph_read: symbol '${cleanSym}' not found in ${filePath}`);
2820
+ return errorContent(`graph_read: symbol '${cleanSym}' not found in ${fileNode.path}`);
2803
2821
  }
2804
2822
  const lines = fileNode.content.split(/\r?\n/);
2805
2823
  const body = lines.slice(symbol.start_line - 1, symbol.end_line).join("\n");
@@ -2984,16 +3002,32 @@ function looksLikeNonSymbolQuery(pattern) {
2984
3002
  }
2985
3003
  return false;
2986
3004
  }
2987
- function recentlyTouchedMatchesQuery(recentPaths, queryTokens) {
3005
+ function recentlyTouchedMatchesQuery(recentPaths, queryTokens, graph) {
3006
+ if (recentPaths.length === 0) return [];
3007
+ const recent = new Set(recentPaths);
3008
+ const keywordsByPath = /* @__PURE__ */ new Map();
3009
+ for (const n of graph.nodes) {
3010
+ if (n.kind === "file" && recent.has(n.path)) keywordsByPath.set(n.path, n.keywords);
3011
+ }
2988
3012
  const matches = [];
2989
3013
  for (const path of recentPaths) {
2990
3014
  const lower = path.toLowerCase();
3015
+ let matched = false;
2991
3016
  for (const t of queryTokens) {
2992
3017
  if (lower.includes(t)) {
2993
- matches.push(path);
3018
+ matched = true;
2994
3019
  break;
2995
3020
  }
2996
3021
  }
3022
+ if (!matched) {
3023
+ for (const kw of keywordsByPath.get(path) ?? []) {
3024
+ if (queryTokens.has(kw)) {
3025
+ matched = true;
3026
+ break;
3027
+ }
3028
+ }
3029
+ }
3030
+ if (matched) matches.push(path);
2997
3031
  }
2998
3032
  return matches;
2999
3033
  }
@@ -3044,7 +3078,7 @@ async function handleGate(req, ctx) {
3044
3078
  }
3045
3079
  const qTokens = new Set(tokenizeQuery(query));
3046
3080
  const recentPaths = ctx.activity.recentFilePaths(RECENT_ACTIVITY_WINDOW_MS);
3047
- const overlap = recentlyTouchedMatchesQuery(recentPaths, qTokens);
3081
+ const overlap = recentlyTouchedMatchesQuery(recentPaths, qTokens, ctx.graph);
3048
3082
  if (overlap.length > 0) {
3049
3083
  const res2 = {
3050
3084
  decision: "allow",