seam-code 0.3.0__py3-none-any.whl

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.
Files changed (109) hide show
  1. seam/__init__.py +3 -0
  2. seam/_data/schema.sql +225 -0
  3. seam/_web/assets/index-BL_tqprR.js +216 -0
  4. seam/_web/assets/index-GTKUhVyD.css +1 -0
  5. seam/_web/index.html +13 -0
  6. seam/analysis/__init__.py +14 -0
  7. seam/analysis/affected.py +254 -0
  8. seam/analysis/builtins.py +966 -0
  9. seam/analysis/byte_budget.py +217 -0
  10. seam/analysis/changes.py +709 -0
  11. seam/analysis/cluster_naming.py +260 -0
  12. seam/analysis/clustering.py +216 -0
  13. seam/analysis/confidence.py +699 -0
  14. seam/analysis/embeddings.py +195 -0
  15. seam/analysis/flows.py +708 -0
  16. seam/analysis/impact.py +444 -0
  17. seam/analysis/imports.py +994 -0
  18. seam/analysis/imports_ext.py +780 -0
  19. seam/analysis/imports_resolve.py +176 -0
  20. seam/analysis/processes.py +453 -0
  21. seam/analysis/relevance.py +155 -0
  22. seam/analysis/rwr.py +129 -0
  23. seam/analysis/staleness.py +328 -0
  24. seam/analysis/steer.py +282 -0
  25. seam/analysis/synthesis.py +253 -0
  26. seam/analysis/synthesis_channels.py +433 -0
  27. seam/analysis/testpaths.py +103 -0
  28. seam/analysis/traversal.py +470 -0
  29. seam/cli/__init__.py +0 -0
  30. seam/cli/install.py +232 -0
  31. seam/cli/main.py +2602 -0
  32. seam/cli/output.py +137 -0
  33. seam/cli/read.py +244 -0
  34. seam/cli/serve.py +145 -0
  35. seam/config.py +551 -0
  36. seam/indexer/__init__.py +0 -0
  37. seam/indexer/cluster_index.py +425 -0
  38. seam/indexer/db.py +496 -0
  39. seam/indexer/embedding_index.py +183 -0
  40. seam/indexer/field_access.py +536 -0
  41. seam/indexer/field_access_c_cpp.py +643 -0
  42. seam/indexer/field_access_ext.py +708 -0
  43. seam/indexer/field_access_ext2.py +408 -0
  44. seam/indexer/field_access_go_rust.py +737 -0
  45. seam/indexer/field_access_php_swift.py +888 -0
  46. seam/indexer/field_access_ts.py +626 -0
  47. seam/indexer/graph.py +321 -0
  48. seam/indexer/graph_c.py +562 -0
  49. seam/indexer/graph_c_cpp.py +39 -0
  50. seam/indexer/graph_common.py +644 -0
  51. seam/indexer/graph_cpp.py +615 -0
  52. seam/indexer/graph_csharp.py +651 -0
  53. seam/indexer/graph_go.py +723 -0
  54. seam/indexer/graph_go_rust.py +39 -0
  55. seam/indexer/graph_java.py +689 -0
  56. seam/indexer/graph_java_csharp.py +38 -0
  57. seam/indexer/graph_php.py +914 -0
  58. seam/indexer/graph_python.py +628 -0
  59. seam/indexer/graph_ruby.py +748 -0
  60. seam/indexer/graph_rust.py +653 -0
  61. seam/indexer/graph_scope_infer.py +902 -0
  62. seam/indexer/graph_scope_infer_ext.py +723 -0
  63. seam/indexer/graph_scope_infer_ext2.py +992 -0
  64. seam/indexer/graph_swift.py +1014 -0
  65. seam/indexer/graph_swift_infer.py +515 -0
  66. seam/indexer/graph_typescript.py +663 -0
  67. seam/indexer/migrations.py +816 -0
  68. seam/indexer/parser.py +204 -0
  69. seam/indexer/pipeline.py +197 -0
  70. seam/indexer/signatures.py +634 -0
  71. seam/indexer/signatures_ext.py +780 -0
  72. seam/indexer/sync.py +287 -0
  73. seam/indexer/synthesis_index.py +291 -0
  74. seam/indexer/tokenize.py +79 -0
  75. seam/installer/__init__.py +67 -0
  76. seam/installer/claude.py +97 -0
  77. seam/installer/codex.py +94 -0
  78. seam/installer/core.py +127 -0
  79. seam/installer/cursor.py +61 -0
  80. seam/installer/guide.py +110 -0
  81. seam/installer/jsonfile.py +85 -0
  82. seam/installer/markdownfile.py +146 -0
  83. seam/installer/tomlfile.py +72 -0
  84. seam/query/__init__.py +0 -0
  85. seam/query/clusters.py +206 -0
  86. seam/query/comments.py +217 -0
  87. seam/query/context.py +293 -0
  88. seam/query/engine.py +940 -0
  89. seam/query/fts.py +328 -0
  90. seam/query/names.py +470 -0
  91. seam/query/pack.py +433 -0
  92. seam/query/semantic.py +339 -0
  93. seam/query/structure.py +727 -0
  94. seam/server/__init__.py +0 -0
  95. seam/server/graph_api.py +437 -0
  96. seam/server/handler_common.py +323 -0
  97. seam/server/impact_handler.py +615 -0
  98. seam/server/mcp.py +556 -0
  99. seam/server/tools.py +697 -0
  100. seam/server/trace_handler.py +184 -0
  101. seam/server/web.py +922 -0
  102. seam/watcher/__init__.py +0 -0
  103. seam/watcher/__main__.py +56 -0
  104. seam/watcher/daemon.py +237 -0
  105. seam_code-0.3.0.dist-info/METADATA +318 -0
  106. seam_code-0.3.0.dist-info/RECORD +109 -0
  107. seam_code-0.3.0.dist-info/WHEEL +4 -0
  108. seam_code-0.3.0.dist-info/entry_points.txt +2 -0
  109. seam_code-0.3.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,184 @@
1
+ """handle_seam_trace handler — trace the shortest call/dependency path.
2
+
3
+ Extracted from seam/server/tools.py (Slice 2, P2 #103) as a pure mechanical split.
4
+ No logic change — byte-identical output before and after the extraction.
5
+
6
+ Import dependency: trace_handler → handler_common (one direction only, no cycle).
7
+ """
8
+
9
+ import logging
10
+ import sqlite3
11
+ from pathlib import Path
12
+ from typing import Any
13
+
14
+ from seam.analysis import flows as flows_module
15
+ from seam.server.handler_common import (
16
+ _TRACE_DEPTH_DEFAULT,
17
+ _TRACE_DEPTH_MAX,
18
+ _TRACE_DEPTH_MIN,
19
+ _apply_verbosity,
20
+ _clamp,
21
+ _invalid_input,
22
+ _maybe_attach_staleness,
23
+ _qualified_trace_candidates,
24
+ _resolve_uid,
25
+ _serialize_edge_hop,
26
+ _serialize_hop,
27
+ _trace_not_found,
28
+ )
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def handle_seam_trace(
34
+ conn: sqlite3.Connection,
35
+ source: str,
36
+ target: str,
37
+ root: Path,
38
+ max_depth: int = _TRACE_DEPTH_DEFAULT,
39
+ verbose: bool = True,
40
+ *,
41
+ uid: str | None = None,
42
+ target_uid: str | None = None,
43
+ ) -> dict[str, Any]:
44
+ """Handler for the seam_trace MCP tool.
45
+
46
+ Finds the shortest call/dependency path from source to target, and also
47
+ returns one-hop callers and callees for both symbols so the agent can see
48
+ the immediate neighborhood alongside the path.
49
+
50
+ Args:
51
+ conn: Open SQLite connection.
52
+ source: Starting symbol name (must not be blank/whitespace).
53
+ target: Destination symbol name (must not be blank/whitespace).
54
+ root: Project root for path relativization (not currently used —
55
+ paths in flows are symbol names, not file paths; kept for
56
+ API consistency with other handlers).
57
+ max_depth: Max hops for path finding. Clamped to [1, 10]. Default: 10.
58
+
59
+ Returns:
60
+ A JSON-able dict with:
61
+ found (bool) — True if a path was found.
62
+ source (str) — the queried source name (echoed back).
63
+ target (str) — the queried target name (echoed back).
64
+ paths (list) — list of paths; each path is a list of Hop dicts.
65
+ Empty list when source and target are not connected.
66
+ callers_source (list) — one-hop callers of source (EdgeHop dicts).
67
+ callees_source (list) — one-hop callees of source (EdgeHop dicts).
68
+ callers_target (list) — one-hop callers of target (EdgeHop dicts).
69
+ callees_target (list) — one-hop callees of target (EdgeHop dicts).
70
+
71
+ Each Hop dict:
72
+ from_name (str) — source of this edge
73
+ to_name (str) — target of this edge
74
+ kind (str) — full closed vocabulary: call | import | extends |
75
+ implements | instantiates | holds | reads | writes | uses
76
+ confidence (str) — EXTRACTED | INFERRED | AMBIGUOUS
77
+ synthesized_by (str|null) — E4: channel name for heuristic edges, null for static
78
+ (present when SEAM_EDGE_PROVENANCE="on"; stripped in lean)
79
+
80
+ Each EdgeHop dict:
81
+ name (str) — neighboring symbol
82
+ kind (str) — full closed vocabulary (same as Hop.kind above)
83
+ confidence (str) — EXTRACTED | INFERRED | AMBIGUOUS
84
+ synthesized_by (str|null) — E4: same semantics as Hop.synthesized_by above
85
+
86
+ Error shapes:
87
+ {"error": "INVALID_INPUT", "message": "..."} — blank source or target.
88
+ """
89
+ # uid / target_uid (P6c): stable handles pin the exact source/target symbols.
90
+ # The path graph is name-keyed, so each uid resolves to its symbol NAME — the
91
+ # handle just removes the disambiguation round-trip. An unknown uid yields the
92
+ # standard "not connected" result (found=False), not an error.
93
+ if uid is not None:
94
+ resolved_src = _resolve_uid(conn, uid)
95
+ if resolved_src is None:
96
+ return _trace_not_found(uid, target_uid or target)
97
+ source = resolved_src[0]
98
+ if target_uid is not None:
99
+ resolved_tgt = _resolve_uid(conn, target_uid)
100
+ if resolved_tgt is None:
101
+ return _trace_not_found(source, target_uid)
102
+ target = resolved_tgt[0]
103
+
104
+ # Validate: source and target must not be empty or whitespace-only.
105
+ if not source or not source.strip():
106
+ return _invalid_input("source must not be empty or whitespace-only")
107
+ if not target or not target.strip():
108
+ return _invalid_input("target must not be empty or whitespace-only")
109
+
110
+ # Clamp depth to valid range.
111
+ safe_depth = _clamp(max_depth, _TRACE_DEPTH_MIN, _TRACE_DEPTH_MAX)
112
+
113
+ clean_source = source.strip()
114
+ clean_target = target.strip()
115
+
116
+ # Find the shortest path from source to target.
117
+ # Thread root as repo_root for Phase 5 import-promotion (root is the project root).
118
+ paths = flows_module.trace(
119
+ conn, clean_source, clean_target, max_depth=safe_depth, repo_root=root
120
+ )
121
+ resolved_source, resolved_target = clean_source, clean_target
122
+
123
+ # Bare->qualified fallback (Tier D11): with Tier B receiver inference, method call
124
+ # edges are stored QUALIFIED ('Class.method'), so a bare source/target matches no
125
+ # edge `from_name`/`to_name` and trace returns nothing — an agent typing the natural
126
+ # bare identifier gets found:false and must retry fully-qualified. ONLY on a genuine
127
+ # miss (paths == []; self-trace returns [[]] and is left alone), resolve the bare
128
+ # endpoints to their qualified symbol forms and retry the candidate pairs. Zero
129
+ # regression: endpoints that already connect (top-level functions, qualified names)
130
+ # never enter this branch.
131
+ if not paths:
132
+ src_cands = [clean_source, *_qualified_trace_candidates(conn, clean_source)]
133
+ tgt_cands = [clean_target, *_qualified_trace_candidates(conn, clean_target)]
134
+ for s in src_cands:
135
+ for t in tgt_cands:
136
+ if s == clean_source and t == clean_target:
137
+ continue # the exact pair was already tried above
138
+ cand = flows_module.trace(conn, s, t, max_depth=safe_depth, repo_root=root)
139
+ if cand:
140
+ paths, resolved_source, resolved_target = cand, s, t
141
+ break
142
+ if paths:
143
+ break
144
+
145
+ # Gather one-hop neighborhood for both symbols — useful context for the agent.
146
+ # Use the RESOLVED endpoints so the neighborhood reflects the symbols the path
147
+ # actually connected (identical to the inputs when no resolution was needed).
148
+ # Thread repo_root so callers/callees also surface import-promotion provenance.
149
+ callers_source = flows_module.callers(conn, resolved_source, repo_root=root)
150
+ callees_source = flows_module.callees(conn, resolved_source, repo_root=root)
151
+ callers_target = flows_module.callers(conn, resolved_target, repo_root=root)
152
+ callees_target = flows_module.callees(conn, resolved_target, repo_root=root)
153
+
154
+ # Serialize paths: each Path is list[Hop]; Hop is already a plain TypedDict.
155
+ # Convert to plain dicts for JSON-safety.
156
+ # Phase 5: include resolved_by for provenance (null when fast-path / not available).
157
+ # Include best_candidate (relativized) for AMBIGUOUS hops.
158
+ # Apply _apply_verbosity to each hop so lean mode strips resolved_by/best_candidate.
159
+ serialized_paths = [
160
+ [_apply_verbosity(_serialize_hop(hop, root), verbose) for hop in path] for path in paths
161
+ ]
162
+
163
+ trace_result: dict[str, Any] = {
164
+ "found": len(paths) > 0,
165
+ # Echo the RESOLVED endpoints so the agent sees what the path connected (e.g. a
166
+ # bare 'bar' that resolved to 'Foo.bar'); identical to the input when unchanged.
167
+ "source": resolved_source,
168
+ "target": resolved_target,
169
+ "paths": serialized_paths,
170
+ "callers_source": [
171
+ _apply_verbosity(_serialize_edge_hop(h, root), verbose) for h in callers_source
172
+ ],
173
+ "callees_source": [
174
+ _apply_verbosity(_serialize_edge_hop(h, root), verbose) for h in callees_source
175
+ ],
176
+ "callers_target": [
177
+ _apply_verbosity(_serialize_edge_hop(h, root), verbose) for h in callers_target
178
+ ],
179
+ "callees_target": [
180
+ _apply_verbosity(_serialize_edge_hop(h, root), verbose) for h in callees_target
181
+ ],
182
+ }
183
+ # P2: attach staleness banner LAST — purely additive, byte-identical when fresh.
184
+ return _maybe_attach_staleness(trace_result, conn, root)