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.
- seam/__init__.py +3 -0
- seam/_data/schema.sql +225 -0
- seam/_web/assets/index-BL_tqprR.js +216 -0
- seam/_web/assets/index-GTKUhVyD.css +1 -0
- seam/_web/index.html +13 -0
- seam/analysis/__init__.py +14 -0
- seam/analysis/affected.py +254 -0
- seam/analysis/builtins.py +966 -0
- seam/analysis/byte_budget.py +217 -0
- seam/analysis/changes.py +709 -0
- seam/analysis/cluster_naming.py +260 -0
- seam/analysis/clustering.py +216 -0
- seam/analysis/confidence.py +699 -0
- seam/analysis/embeddings.py +195 -0
- seam/analysis/flows.py +708 -0
- seam/analysis/impact.py +444 -0
- seam/analysis/imports.py +994 -0
- seam/analysis/imports_ext.py +780 -0
- seam/analysis/imports_resolve.py +176 -0
- seam/analysis/processes.py +453 -0
- seam/analysis/relevance.py +155 -0
- seam/analysis/rwr.py +129 -0
- seam/analysis/staleness.py +328 -0
- seam/analysis/steer.py +282 -0
- seam/analysis/synthesis.py +253 -0
- seam/analysis/synthesis_channels.py +433 -0
- seam/analysis/testpaths.py +103 -0
- seam/analysis/traversal.py +470 -0
- seam/cli/__init__.py +0 -0
- seam/cli/install.py +232 -0
- seam/cli/main.py +2602 -0
- seam/cli/output.py +137 -0
- seam/cli/read.py +244 -0
- seam/cli/serve.py +145 -0
- seam/config.py +551 -0
- seam/indexer/__init__.py +0 -0
- seam/indexer/cluster_index.py +425 -0
- seam/indexer/db.py +496 -0
- seam/indexer/embedding_index.py +183 -0
- seam/indexer/field_access.py +536 -0
- seam/indexer/field_access_c_cpp.py +643 -0
- seam/indexer/field_access_ext.py +708 -0
- seam/indexer/field_access_ext2.py +408 -0
- seam/indexer/field_access_go_rust.py +737 -0
- seam/indexer/field_access_php_swift.py +888 -0
- seam/indexer/field_access_ts.py +626 -0
- seam/indexer/graph.py +321 -0
- seam/indexer/graph_c.py +562 -0
- seam/indexer/graph_c_cpp.py +39 -0
- seam/indexer/graph_common.py +644 -0
- seam/indexer/graph_cpp.py +615 -0
- seam/indexer/graph_csharp.py +651 -0
- seam/indexer/graph_go.py +723 -0
- seam/indexer/graph_go_rust.py +39 -0
- seam/indexer/graph_java.py +689 -0
- seam/indexer/graph_java_csharp.py +38 -0
- seam/indexer/graph_php.py +914 -0
- seam/indexer/graph_python.py +628 -0
- seam/indexer/graph_ruby.py +748 -0
- seam/indexer/graph_rust.py +653 -0
- seam/indexer/graph_scope_infer.py +902 -0
- seam/indexer/graph_scope_infer_ext.py +723 -0
- seam/indexer/graph_scope_infer_ext2.py +992 -0
- seam/indexer/graph_swift.py +1014 -0
- seam/indexer/graph_swift_infer.py +515 -0
- seam/indexer/graph_typescript.py +663 -0
- seam/indexer/migrations.py +816 -0
- seam/indexer/parser.py +204 -0
- seam/indexer/pipeline.py +197 -0
- seam/indexer/signatures.py +634 -0
- seam/indexer/signatures_ext.py +780 -0
- seam/indexer/sync.py +287 -0
- seam/indexer/synthesis_index.py +291 -0
- seam/indexer/tokenize.py +79 -0
- seam/installer/__init__.py +67 -0
- seam/installer/claude.py +97 -0
- seam/installer/codex.py +94 -0
- seam/installer/core.py +127 -0
- seam/installer/cursor.py +61 -0
- seam/installer/guide.py +110 -0
- seam/installer/jsonfile.py +85 -0
- seam/installer/markdownfile.py +146 -0
- seam/installer/tomlfile.py +72 -0
- seam/query/__init__.py +0 -0
- seam/query/clusters.py +206 -0
- seam/query/comments.py +217 -0
- seam/query/context.py +293 -0
- seam/query/engine.py +940 -0
- seam/query/fts.py +328 -0
- seam/query/names.py +470 -0
- seam/query/pack.py +433 -0
- seam/query/semantic.py +339 -0
- seam/query/structure.py +727 -0
- seam/server/__init__.py +0 -0
- seam/server/graph_api.py +437 -0
- seam/server/handler_common.py +323 -0
- seam/server/impact_handler.py +615 -0
- seam/server/mcp.py +556 -0
- seam/server/tools.py +697 -0
- seam/server/trace_handler.py +184 -0
- seam/server/web.py +922 -0
- seam/watcher/__init__.py +0 -0
- seam/watcher/__main__.py +56 -0
- seam/watcher/daemon.py +237 -0
- seam_code-0.3.0.dist-info/METADATA +318 -0
- seam_code-0.3.0.dist-info/RECORD +109 -0
- seam_code-0.3.0.dist-info/WHEEL +4 -0
- seam_code-0.3.0.dist-info/entry_points.txt +2 -0
- 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)
|