symgrep-codesearch 0.6.2__tar.gz → 0.6.3__tar.gz

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 (59) hide show
  1. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/PKG-INFO +1 -1
  2. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/pyproject.toml +4 -1
  3. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/cli.py +60 -0
  4. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/mcp_server.py +29 -1
  5. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep_codesearch.egg-info/PKG-INFO +1 -1
  6. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/LICENSE +0 -0
  7. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/README.md +0 -0
  8. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/setup.cfg +0 -0
  9. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/__init__.py +0 -0
  10. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/blocks.py +0 -0
  11. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/classfile.py +0 -0
  12. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/data_symbols.py +0 -0
  13. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/docsymbols.py +0 -0
  14. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/enclosing.py +0 -0
  15. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/mcp_structured.py +0 -0
  16. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/report.py +0 -0
  17. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/resolve.py +0 -0
  18. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/resolve_js.py +0 -0
  19. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/resolve_python.py +0 -0
  20. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/ripgrep.py +0 -0
  21. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/semantic.py +0 -0
  22. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/structural.py +0 -0
  23. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep/structure.py +0 -0
  24. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep_codesearch.egg-info/SOURCES.txt +0 -0
  25. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep_codesearch.egg-info/dependency_links.txt +0 -0
  26. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep_codesearch.egg-info/entry_points.txt +0 -0
  27. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep_codesearch.egg-info/requires.txt +0 -0
  28. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/src/symgrep_codesearch.egg-info/top_level.txt +0 -0
  29. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_blocks_extraction.py +0 -0
  30. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_blocks_retrieval.py +0 -0
  31. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_bulk_describe.py +0 -0
  32. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_classfile.py +0 -0
  33. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_data_symbols.py +0 -0
  34. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_describe_lane_confinement.py +0 -0
  35. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_doc_symbols.py +0 -0
  36. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_file_structure.py +0 -0
  37. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_freshness.py +0 -0
  38. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_honest_recall.py +0 -0
  39. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_http_ensure.py +0 -0
  40. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_index_reconstruct.py +0 -0
  41. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_index_scope.py +0 -0
  42. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_index_worker.py +0 -0
  43. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_mcp_structured.py +0 -0
  44. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_name_uniqueness.py +0 -0
  45. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_nudge_runaway.py +0 -0
  46. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_report_filters.py +0 -0
  47. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_resolve.py +0 -0
  48. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_resolve_js.py +0 -0
  49. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_resolve_python.py +0 -0
  50. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_resolve_r2.py +0 -0
  51. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_review_regressions.py +0 -0
  52. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_semantic_freshness_signal.py +0 -0
  53. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_sites_and_symbols.py +0 -0
  54. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_structural.py +0 -0
  55. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_symbol_vocabulary.py +0 -0
  56. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_ts_fallback_loud.py +0 -0
  57. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_ts_symbol_coverage.py +0 -0
  58. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_usage.py +0 -0
  59. {symgrep_codesearch-0.6.2 → symgrep_codesearch-0.6.3}/tests/test_veccore_adoption.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: symgrep-codesearch
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Code search with honest recall — structure-aware grep that tells you what it does not know, plus search by meaning
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/ashdamle/symgrep
@@ -7,6 +7,9 @@ build-backend = "setuptools.build_meta"
7
7
  # Semgrep team. The IMPORT name is unchanged — `import symgrep` — so consumers (honestreview, spendguard,
8
8
  # 7thsense, warden) are unaffected by the distribution rename. The package dir stays src/symgrep.
9
9
  name = "symgrep-codesearch"
10
+ # 0.6.3: MCP surface completeness — TOOL_DESCRIPTION now documents structural=/lang= (the last undescribed
11
+ # params), and a new `symgrep install-mcp` subcommand self-registers the server via `claude mcp add`. No API
12
+ # change; keeps PyPI current with the capability descriptions pip-installed MCP clients see.
10
13
  # 0.6.2: ripgrep is a system binary and cannot be a normal pip dependency, but the new `[rg]` extra pulls
11
14
  # `ripgrep-bin` (a wheel bundling the rg binary) for a self-contained install; rg_binary() now also finds an
12
15
  # rg next to the interpreter (venv bin), so the pip-only path works even under `.venv/bin/python -m …`.
@@ -17,7 +20,7 @@ name = "symgrep-codesearch"
17
20
  # semantic search. Minor bump (dependency surface changed; no breaking import/CLI/MCP change).
18
21
  # 0.5.0: added the public `file_structure` library API (+ its MCP tool and `symgrep structure` CLI), import
19
22
  # module-name normalization, and the FileStructure `connects` list (structure schema v2).
20
- version = "0.6.2"
23
+ version = "0.6.3"
21
24
  description = "Code search with honest recall — structure-aware grep that tells you what it does not know, plus search by meaning"
22
25
  readme = "README.md"
23
26
  # 3.11+ : the maintained grammar pack (tree-sitter-language-pack, a CORE dep below) ships abi3 wheels for
@@ -409,10 +409,70 @@ def _cmd_structure(rest) -> int:
409
409
  return 0
410
410
 
411
411
 
412
+ def _resolve_mcp_entrypoint() -> "Optional[str]":
413
+ """Absolute path to symgrep's MCP stdio entrypoint for THIS install, resolved — never hardcoded. The
414
+ `symgrep-mcp` console script sits next to the interpreter (venv bin) for a pip OR editable install; a bare
415
+ checkout with no install falls back to the repo's bin/symgrep-mcp wrapper. None if neither is found."""
416
+ import shutil
417
+ exe_dir = os.path.dirname(sys.executable or "")
418
+ cand = os.path.join(exe_dir, "symgrep-mcp")
419
+ if os.path.isfile(cand) and os.access(cand, os.X_OK):
420
+ return cand
421
+ on_path = shutil.which("symgrep-mcp")
422
+ if on_path:
423
+ return on_path
424
+ repo_bin = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
425
+ "bin", "symgrep-mcp")
426
+ if os.path.isfile(repo_bin) and os.access(repo_bin, os.X_OK):
427
+ return repo_bin
428
+ return None
429
+
430
+
431
+ def _cmd_install_mcp(rest) -> int:
432
+ """`symgrep install-mcp [--scope user|local|project] [--name NAME] [--dry-run]` — register symgrep's own
433
+ MCP server with Claude Code, so a fresh install advertises itself as a tool. It does NOT write
434
+ ~/.claude.json itself (one shared file must have ONE writer); it shells to `claude mcp add`, the safe
435
+ single writer that ships with the client — so this command carries no config-mutation logic to drift."""
436
+ import shutil
437
+ import subprocess
438
+ sp = argparse.ArgumentParser(prog="symgrep install-mcp")
439
+ sp.add_argument("--scope", default="user", choices=["local", "user", "project"],
440
+ help="Claude Code config scope to register in (default: user)")
441
+ sp.add_argument("--name", default="symgrep", help="server name to register under (default: symgrep)")
442
+ sp.add_argument("--dry-run", action="store_true", help="print the exact `claude mcp add` command, write nothing")
443
+ a = sp.parse_args(rest)
444
+ claude = shutil.which("claude")
445
+ if not claude:
446
+ print("install-mcp: the `claude` CLI is not on PATH. symgrep delegates registration to it rather than "
447
+ "writing ~/.claude.json itself — install Claude Code, then re-run.", file=sys.stderr)
448
+ return 2
449
+ entry = _resolve_mcp_entrypoint()
450
+ if not entry:
451
+ print("install-mcp: could not locate symgrep's MCP entrypoint (`symgrep-mcp` console script or "
452
+ "bin/symgrep-mcp). Install symgrep in this interpreter (`pip install symgrep-codesearch`) first.",
453
+ file=sys.stderr)
454
+ return 2
455
+ cmd = [claude, "mcp", "add", "--scope", a.scope, a.name, "--", entry]
456
+ if a.dry_run:
457
+ print("DRY RUN — would run:\n " + " ".join(cmd))
458
+ return 0
459
+ print("registering symgrep's MCP server:\n " + " ".join(cmd))
460
+ rc = subprocess.run(cmd).returncode
461
+ if rc == 0:
462
+ # MCP servers are read at client START: a just-registered server is callable in the NEXT session, not
463
+ # this one — say so rather than imply a live pickup this cannot deliver.
464
+ print(f"registered '{a.name}' at scope '{a.scope}'. It becomes callable in the NEXT Claude session "
465
+ "(MCP servers load at client start). `claude mcp add` leaves an existing entry intact, so this "
466
+ "is safe to re-run.")
467
+ return rc
468
+
469
+
412
470
  def main(argv=None) -> int:
413
471
  _argv = list(sys.argv[1:] if argv is None else argv)
414
472
  if _argv and _argv[0] == "structure": # `symgrep structure <path>` — a subcommand over the flag CLI
415
473
  return _cmd_structure(_argv[1:])
474
+ if _argv and _argv[0] == "install-mcp": # `symgrep install-mcp` — self-register as an MCP server
475
+ return _cmd_install_mcp(_argv[1:])
416
476
  ap = argparse.ArgumentParser(prog="symgrep", description=__doc__)
417
477
  ap.add_argument("pattern", nargs="?", default=None,
418
478
  help="regex to search (omit with --index: then every positional is a ROOT)")
@@ -55,7 +55,30 @@ TOOL_DESCRIPTION = (
55
55
  "nothing is CALLED that, never that nothing DOES it. That gap is how duplicate implementations get "
56
56
  "written — measured in one codebase: `share.scrub` beside `share._scrub_text`, `adapters.call_complete` "
57
57
  "beside `adapters.call`, `_save_state` in three files. Requires an index (symgrep --index <root>); if "
58
- "there is none it says so rather than returning an empty result."
58
+ "there is none it says so rather than returning an empty result.\n\n"
59
+ "THREE MORE MODES, each a different question:\n"
60
+ " usage=true — is this name DEFINED, REFERENCED, both, or neither? Returns a verdict in\n"
61
+ " {defined_referenced, defined_unused, undefined_referenced, absent}. LEXICAL,\n"
62
+ " by name: 'defined_unused' is a possibly-unused HINT, never proof of dead code.\n"
63
+ " resolve=true — cross-file candidate resolution: {resolved_unique, ambiguous, external, absent}.\n"
64
+ " When >1 definition shares a name it reports AMBIGUOUS with candidates rather\n"
65
+ " than picking a winner. Use this before trusting any single definition of a\n"
66
+ " name that occurs more than once — import order decides it at runtime.\n"
67
+ " structured=true — machine-readable JSON with a mechanical `status` (ok | no_index | unavailable)\n"
68
+ " instead of prose. Use whenever a program, not a human, reads the result.\n\n"
69
+ "unit= sets --semantic GRANULARITY: 'symbol' (default, a whole function) | 'block' (a sub-function\n"
70
+ "AST block — Type-4 clone retrieval, finds copy-pasted fragments whole-function search misses) |\n"
71
+ "'data' (a schema/config/proto/sql definition) | 'both'/'all'. block and data hits need\n"
72
+ "structured=true to read their span. kind= filters within a unit, e.g. 'data_schema', 'sql_table',\n"
73
+ "'proto_message'.\n\n"
74
+ "structural=true SWITCHES FROM REGEX TO AST-PATTERN MATCH — `pattern` becomes a code snippet with\n"
75
+ "metavariables ($NAME matches one node, $$$ is variadic), matched by syntactic SHAPE, so it finds forms a\n"
76
+ "regex cannot express (e.g. `$A == $A` for self-comparisons) without tripping on whitespace or naming. It\n"
77
+ "REQUIRES lang= to pick the grammar the pattern is parsed in (lang= one of the tree-sitter languages:\n"
78
+ "'python' | 'go' | 'javascript' | 'typescript' | 'tsx' | 'rust' | 'java' | 'c' | 'cpp' | 'ruby' | 'php' |\n"
79
+ "'c_sharp' | 'kotlin' | 'scala' | 'bash'); with no usable grammar it reports UNAVAILABLE rather than\n"
80
+ "silently falling back to text. Use it for structural queries a name/regex search cannot express; use\n"
81
+ "plain regex or semantic=true for everything else."
59
82
  )
60
83
 
61
84
 
@@ -131,6 +154,11 @@ def _run_fastmcp(http=None) -> bool:
131
154
  definitions, references, resolution_basis:"candidate-name"}. verdict ∈ resolved_unique | ambiguous |
132
155
  external | absent. When >1 definition shares the name it is AMBIGUOUS (candidates, not one winner).
133
156
 
157
+ structural=True: match by AST SHAPE, not regex — `pattern` is a code snippet with metavariables
158
+ ($NAME matches one node, $$$ is variadic). Requires lang= (the tree-sitter grammar the pattern parses
159
+ in, e.g. "python"/"go"/"rust"); reports UNAVAILABLE when no grammar is usable rather than falling back
160
+ to text. lang is used ONLY by structural search.
161
+
134
162
  unit: --semantic granularity — "symbol" (default; a whole function/method, the only pre-v6 behaviour,
135
163
  byte-stable) | "block" (a sub-function AST block, Type-4 clone retrieval) | "data" (a schema/data/config
136
164
  symbol — a data_schema file unit or a proto/sql definition) | "both"/"all". block/data hits need
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: symgrep-codesearch
3
- Version: 0.6.2
3
+ Version: 0.6.3
4
4
  Summary: Code search with honest recall — structure-aware grep that tells you what it does not know, plus search by meaning
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/ashdamle/symgrep