descry-codegraph 0.1.1__tar.gz → 0.2.0__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 (94) hide show
  1. descry_codegraph-0.2.0/.github/assets/01-landing.png +0 -0
  2. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/.github/workflows/ci.yml +6 -1
  3. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/.github/workflows/release.yml +6 -1
  4. descry_codegraph-0.2.0/CHANGELOG.md +369 -0
  5. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/CLAUDE.md +3 -3
  6. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/PKG-INFO +60 -26
  7. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/README.md +59 -25
  8. descry_codegraph-0.2.0/claude-skill/README.md +112 -0
  9. descry_codegraph-0.2.0/claude-skill/descry/SKILL.md +251 -0
  10. descry_codegraph-0.2.0/claude-skill/descry/references/cli-reference.md +154 -0
  11. descry_codegraph-0.2.0/claude-skill/descry/references/mcp-tools.md +75 -0
  12. descry_codegraph-0.2.0/claude-skill/descry/references/recipes.md +154 -0
  13. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/pyproject.toml +1 -1
  14. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/__init__.py +1 -1
  15. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/_graph.py +1 -1
  16. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/ast_grep.py +34 -2
  17. descry_codegraph-0.2.0/src/descry/clang_parser.py +443 -0
  18. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/cli.py +2 -2
  19. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/cross_lang.py +3 -84
  20. descry_codegraph-0.2.0/src/descry/dart_parser.py +451 -0
  21. descry_codegraph-0.2.0/src/descry/dotnet_parser.py +418 -0
  22. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/embeddings.py +8 -6
  23. descry_codegraph-0.2.0/src/descry/generate.py +8417 -0
  24. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/git_history.py +9 -9
  25. descry_codegraph-0.2.0/src/descry/go_parser.py +382 -0
  26. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/handlers.py +158 -32
  27. descry_codegraph-0.2.0/src/descry/java_parser.py +336 -0
  28. descry_codegraph-0.2.0/src/descry/php_parser.py +367 -0
  29. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/query.py +24 -37
  30. descry_codegraph-0.2.0/src/descry/ruby_parser.py +328 -0
  31. descry_codegraph-0.2.0/src/descry/scip/adapter.py +209 -0
  32. descry_codegraph-0.2.0/src/descry/scip/adapters/__init__.py +23 -0
  33. descry_codegraph-0.2.0/src/descry/scip/adapters/_java_compat.init.gradle +21 -0
  34. descry_codegraph-0.2.0/src/descry/scip/adapters/clang.py +190 -0
  35. descry_codegraph-0.2.0/src/descry/scip/adapters/dart.py +143 -0
  36. descry_codegraph-0.2.0/src/descry/scip/adapters/dotnet.py +279 -0
  37. descry_codegraph-0.2.0/src/descry/scip/adapters/go.py +152 -0
  38. descry_codegraph-0.2.0/src/descry/scip/adapters/java.py +343 -0
  39. descry_codegraph-0.2.0/src/descry/scip/adapters/php.py +272 -0
  40. descry_codegraph-0.2.0/src/descry/scip/adapters/python.py +137 -0
  41. descry_codegraph-0.2.0/src/descry/scip/adapters/ruby.py +140 -0
  42. descry_codegraph-0.2.0/src/descry/scip/adapters/rust.py +216 -0
  43. descry_codegraph-0.2.0/src/descry/scip/adapters/typescript.py +295 -0
  44. descry_codegraph-0.2.0/src/descry/scip/cache.py +753 -0
  45. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/scip/parser.py +58 -131
  46. descry_codegraph-0.2.0/src/descry/scip/support.py +70 -0
  47. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/web/server.py +48 -37
  48. descry_codegraph-0.2.0/tests/test_clang_parser.py +160 -0
  49. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_config.py +0 -2
  50. descry_codegraph-0.2.0/tests/test_dart_parser.py +216 -0
  51. descry_codegraph-0.2.0/tests/test_dotnet_parser.py +241 -0
  52. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_generate.py +1 -1
  53. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_git_history.py +61 -0
  54. descry_codegraph-0.2.0/tests/test_go_parser.py +261 -0
  55. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_handlers.py +1 -1
  56. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_hardening.py +1 -1
  57. descry_codegraph-0.2.0/tests/test_java_parser.py +242 -0
  58. descry_codegraph-0.2.0/tests/test_php_parser.py +227 -0
  59. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_query.py +3 -3
  60. descry_codegraph-0.2.0/tests/test_ruby_parser.py +228 -0
  61. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_scip.py +121 -23
  62. descry_codegraph-0.2.0/tests/test_scip_clang.py +106 -0
  63. descry_codegraph-0.2.0/tests/test_scip_dart.py +96 -0
  64. descry_codegraph-0.2.0/tests/test_scip_dotnet.py +83 -0
  65. descry_codegraph-0.2.0/tests/test_scip_go.py +128 -0
  66. descry_codegraph-0.2.0/tests/test_scip_java.py +306 -0
  67. descry_codegraph-0.2.0/tests/test_scip_php.py +72 -0
  68. descry_codegraph-0.2.0/tests/test_scip_ruby.py +81 -0
  69. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_web_server.py +1 -1
  70. descry_codegraph-0.1.1/CHANGELOG.md +0 -68
  71. descry_codegraph-0.1.1/src/descry/generate.py +0 -3578
  72. descry_codegraph-0.1.1/src/descry/scip/cache.py +0 -1047
  73. descry_codegraph-0.1.1/src/descry/scip/support.py +0 -174
  74. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/.gitignore +0 -0
  75. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/LICENSE +0 -0
  76. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/SECURITY.md +0 -0
  77. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/justfile +0 -0
  78. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/pi-extension/README.md +0 -0
  79. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/pi-extension/descry-cli.py +0 -0
  80. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/pi-extension/descry-tools.ts +0 -0
  81. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/_env.py +0 -0
  82. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/mcp_server.py +0 -0
  83. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/scip/__init__.py +0 -0
  84. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/scip/pb2.py +0 -0
  85. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/tree_sitter_parser.py +0 -0
  86. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/web/__init__.py +0 -0
  87. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/web/web/index.html +0 -0
  88. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/src/descry/web/web/style.css +0 -0
  89. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/__init__.py +0 -0
  90. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/conftest.py +0 -0
  91. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/fixtures/small_graph.json +0 -0
  92. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_cli.py +0 -0
  93. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_mcp.py +0 -0
  94. {descry_codegraph-0.1.1 → descry_codegraph-0.2.0}/tests/test_tree_sitter_parser.py +0 -0
@@ -73,4 +73,9 @@ jobs:
73
73
  - name: Audit resolved environment
74
74
  run: |
75
75
  . .venv/bin/activate
76
- pip-audit --skip-editable
76
+ # CVE-2026-3219 (GHSA-58qw-9mgm-455v): concatenated tar/ZIP
77
+ # interpretation conflict in pip itself. Affects pip <= 26.0.1
78
+ # with no patched version upstream. pip is a build-time
79
+ # installer in the venv, not a runtime dep we ship.
80
+ # Re-evaluate when pip ships a fix.
81
+ pip-audit --skip-editable --ignore-vuln CVE-2026-3219
@@ -70,7 +70,12 @@ jobs:
70
70
  - name: Audit resolved environment
71
71
  run: |
72
72
  . .venv/bin/activate
73
- pip-audit --skip-editable
73
+ # CVE-2026-3219 (GHSA-58qw-9mgm-455v): concatenated tar/ZIP
74
+ # interpretation conflict in pip itself. Affects pip <= 26.0.1
75
+ # with no patched version upstream. pip is a build-time
76
+ # installer in the venv, not a runtime dep we ship.
77
+ # Re-evaluate when pip ships a fix.
78
+ pip-audit --skip-editable --ignore-vuln CVE-2026-3219
74
79
 
75
80
  publish-testpypi:
76
81
  name: Publish to TestPyPI
@@ -0,0 +1,369 @@
1
+ # Changelog
2
+
3
+ All notable changes to Descry will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to pre-1.0 semver (minor-version bumps may include breaking changes; see README versioning stance).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.2.0] — 2026-04-20
10
+
11
+ A large feature + resolution-quality release. Seven new SCIP language
12
+ adapters land (Java/Kotlin/Scala, Go, Ruby, PHP, C#/VB, C/C++, Dart)
13
+ alongside a language-general STDLIB_FILTER sweep that brought CALLS
14
+ resolution above 90% on 20 of 26 measured corpora. The release also
15
+ folds in a full session of convergence-audit hardening: user-input
16
+ parsing no longer 500s on garbage query strings, subprocess output
17
+ tolerates non-UTF-8 bytes, and the documented security invariants match
18
+ what the code actually does.
19
+
20
+ Graph schema stays at v1 — existing `.descry_cache/codebase_graph.json`
21
+ files remain readable. CLI subcommands and MCP tool names are unchanged
22
+ (19 of each, 1:1 parity).
23
+
24
+ ### Added
25
+
26
+ - **Dart / Flutter support** via `scip-dart` (Milestone D). DartAdapter
27
+ discovers projects by `pubspec.yaml` at the root or under
28
+ `packages/*` (melos / workspace layout); runs `scip-dart ./` from the
29
+ package root and uses `output_mode="rename"` to move the emitted
30
+ `index.scip` into the cache. DartParser handles imports
31
+ (`package:` / `dart:` / relative), classes (including `abstract`,
32
+ `base`, `sealed`, `final`, `interface class`, `mixin class`), mixins,
33
+ extensions, enums, typedefs, top-level constants, class inheritance
34
+ (`extends` → INHERITS edge), and call sites with null-safe receiver
35
+ chains (`?.method()`). Dart has heavy top-level code (top-level
36
+ functions, top-level variable initializers), so the parser emits
37
+ calls at file scope too — unlike Java/C# parsers which gate calls on
38
+ non-file context. pub smoke test hit **71%** resolution
39
+ (10,279 / 14,490 CALLS edges) — below the 91.2% Rust bar, accepted
40
+ as the Dart ceiling for test-heavy codebases where pub's internal
41
+ test-helper DSL (`servePackages`, `d.file`, `runPub`, etc.) contributes
42
+ the bulk of unresolved calls.
43
+ - **Java / Kotlin / Scala support** via `scip-java` (Milestone J of Wave 2).
44
+ JavaParser extracts classes, interfaces, enums, records, methods,
45
+ constructors, fields, imports, and call sites. JavaAdapter ships a
46
+ Gradle init-script that strips `-Werror` so scip-java works out of the
47
+ box on Apache projects (Kafka, etc.) whose builds treat warnings as
48
+ fatal. Kafka smoke test hit **92.7%** CALLS resolution.
49
+ - **Go support** via `scip-go` (Milestone G). GoParser covers packages,
50
+ grouped imports, type declarations, free functions, methods with
51
+ receivers, const/var blocks, and call sites. Kubernetes smoke test
52
+ hit **98.3%** resolution.
53
+ - **Ruby support** via `scip-ruby` (Milestone R). RubyParser uses
54
+ indent-based context tracking (Ruby uses `end` not `}`); extracts
55
+ classes with INHERITS edges, modules, methods (including `self.foo`,
56
+ `foo?`, `foo!`), `attr_reader/writer/accessor`, `require` /
57
+ `require_relative`, and top-level constants. Rails smoke test hit
58
+ **87.8%** — below the 91.2% Rust bar, accepted as the Ruby-without-
59
+ Sorbet ceiling (scip-ruby falls back to `# typed: false` heuristics
60
+ when Sorbet annotations are absent).
61
+ - **PHP support** via `scip-php` (Milestone P, third-party indexer by
62
+ davidrjenni). PhpParser handles namespaces, classes / interfaces /
63
+ traits / enums, `public/protected/private function`, properties,
64
+ constants, and method / static / instance calls; Allman-brace lookahead
65
+ (scan up to 10 lines for the opening `{`) was needed for Laravel's
66
+ style. Laravel smoke test hit **88.6%**.
67
+ - **C# / VB.NET support** via `scip-dotnet` (Milestone N). DotnetAdapter
68
+ sets `DOTNET_ROLL_FORWARD=LatestMajor` so scip-dotnet's net9 target
69
+ runs on systems with only net10 installed. Serilog smoke test hit
70
+ **83.7%**.
71
+ - **C / C++ support** via `scip-clang` (Milestone C). ClangAdapter emits
72
+ scheme `cxx` (verified from real indexes; not `scip-clang` as a name
73
+ might suggest). Discovery gives top priority to root-level
74
+ `compile_commands.json` so Bear-backed Makefile builds and top-level
75
+ CMake projects work as a single unit. ClangParser avoids regex
76
+ catastrophic backtracking (which hit >20s on Redis `src/dict.c` in
77
+ an early draft) via a hand-rolled `_extract_function_name` that scans
78
+ right-to-left through the argument list. Redis smoke test hit
79
+ **79.2%**; headers lag .c files (63.3% vs 81.2%) due to scip-clang
80
+ compdb-coverage limits on transitively-included headers.
81
+
82
+ ### Changed
83
+
84
+ - **CALLS resolution rose above the 90% bar on 20 of 26 measured
85
+ corpora** (up from ~10 pre-sweep) via a focused language-general
86
+ filter pass — zero codebase-specific entries. The sweep expanded
87
+ `STDLIB_FILTER` / `STDLIB_PREFIXES` across every supported language:
88
+ - **TypeScript / JavaScript**: `_JSTS_CONTROL_KEYWORDS` filter in
89
+ the `TSParser` regex fallback (rejects `if(`, `while(`, `for(`,
90
+ `switch(` captures — removes ~37k spurious unresolved on the
91
+ TypeScript compiler alone), tslib emit helpers (`__awaiter`,
92
+ `__generator`, `__rest`, `__spreadArray`, `__classPrivateFieldGet`,
93
+ `__addDisposableResource`, …), DOM APIs (`textContent`,
94
+ `getAttribute`, `classList`, `appendChild`, …), Array/Object
95
+ statics (`isArray`, `fromEntries`, `defineProperty`, …),
96
+ Playwright (`page.*`), Vitest/Jest todo / only / each / snapshot
97
+ modifiers, React core hooks + TanStack Query + Node `assert`
98
+ strict variants + Lingui / nanoid / uuid.* / date-fns / RN-Expo
99
+ surface. ast-grep now rejects `$FUNC` captures that include parens
100
+ or non-identifier characters (stops curried-call shape like
101
+ `test.runIf(isBuild)(…)` from polluting CALLS targets).
102
+ - **C#**: `var` added to `_DOTNET_CONTROL_KEYWORDS` (fixes ~1k/file
103
+ spurious `var (` captures on modern C# codebases) along with the
104
+ full modern declaration vocabulary (`record`, `partial`, `sealed`,
105
+ `override`, `static`, `params`, `required`, `scoped`, `file`,
106
+ `global`) and every primitive alias (`object`, `string`, `bool`,
107
+ `int`, `long`, `byte`, `char`, `float`, `double`, `decimal`,
108
+ `nint`, `nuint`). Moq (`It.IsAny`, `Mock.Of`, `Setup`, `Verify`,
109
+ `ReturnsAsync`), FakeItEasy (`A.CallTo`, `A.Fake`, `A.Dummy`),
110
+ ASP.NET Core DI (`Add*`, `Use*`, `Map*`, `GetRequiredService`),
111
+ .NET Reflection + Span/Memory, `System.String` statics, Selenium
112
+ WebDriver.
113
+ - **Java**: AssertJ (full `assertThat*` surface, `hasCauseInstanceOf`,
114
+ `isThrownBy`, `satisfies`), BDDMockito (`given`, `willReturn`,
115
+ `willThrow`), Project Reactor (`StepVerifier`, `expectNext`,
116
+ `flatMap`, `switchIfEmpty`, `subscribeOn`), Mockito static
117
+ (`mockStatic`, `mockConstruction`, `invocation.getArgument`),
118
+ Hamcrest matcher vocabulary, Apache Lucene types, ANTLR-generated
119
+ runtime, `java.time` / `java.util.Properties` / `java.nio.Buffer`
120
+ / `java.io.File`, `java.sql.*` + `java.util.Locale.*`,
121
+ `jakarta.servlet` + `javax.xml.sax`, reflection extras.
122
+ - **Go**: `encoding/binary` (`BigEndian/LittleEndian.Uint*/PutUint*`),
123
+ `sync` lock methods (`Lock`, `Unlock`, `RLock`, `RUnlock`,
124
+ `Add`/`Done`/`Wait`), `bytes.Buffer` + `strings.Builder` common
125
+ methods, `golang.org/x/sync/errgroup`, `go.uber.org/goleak`,
126
+ AWS SDK v2 helpers (`aws.Int32`, `aws.String`, …), ULID,
127
+ `klog.`/`ginkgo.`/`Gomega.`/`testify.` prefixes.
128
+ - **Rust**: clap CLI (`value_name`, `help_heading`, `get_one`,
129
+ `get_flag`, `long_about`, subcommand builders), snapbox test
130
+ (`stdout_eq`, `stderr_eq`, `subset_matches`), integer arithmetic
131
+ (`wrapping_*`, `saturating_*`, `checked_*`, `overflowing_*`,
132
+ `rotate_*`, `count_ones`/`_zeros`), atomic compare-exchange /
133
+ fetch variants, pointer `as_ptr`/`as_mut_ptr`/`copy_nonoverlapping`/
134
+ `read_volatile`, slice `is_ascii_*`/`split_whitespace`/
135
+ `char_indices`, fd `from_raw_fd`/`as_raw_fd`/`borrow_raw`.
136
+ - **C / C++**: TCL C API (`Tcl_*` prefix — sqlite bindings),
137
+ LLVM C API (`LLVM*` — postgres JIT), CPython C API (`Py_*`,
138
+ `PyObject_*`, `PyDict_*`, …), Win32 C API (`CreateFile`,
139
+ `GetLastError`, `LoadLibrary`, registry, `HeapAlloc`, …), POSIX
140
+ pthread full surface (`pthread_mutex_*`, `pthread_cond_*`,
141
+ `pthread_rwlock_*`, `pthread_spin_*`, `pthread_barrier_*`),
142
+ stdarg (`va_start`, `va_end`, `va_arg`, `va_copy`), C11
143
+ stdatomic, BSD-specific strings (`strlcpy`, `strlcat`, `arc4random_*`,
144
+ `reallocarray`), BSD socket byte-order (`ntohs`, `htonl`, …) +
145
+ full socket API, OpenSSL (`BN_`, `HMAC_`, `EC_KEY_`, `ENGINE_`),
146
+ jemalloc (public `mallocx`/`xallocx`/`rallocx`/`sallocx`/
147
+ `dallocx`/`nallocx` + internals `edata_`, `emap_`, `sz_`, `pa_`,
148
+ `hpa_`, `prof_`, `tsd_`, `tsdn_`, `atomic_*_zu`).
149
+ - **PHP**: PHPUnit `assertIs*` full surface + PHPUnit:: qualified
150
+ variants, Mockery extras (`makePartial`, `withArgs`), Carbon +
151
+ PHP `DateTime` full method set, `version_compare`, binary /
152
+ encoding / output stdlib (`pack`, `unpack`, `bin2hex`,
153
+ `curl_setopt_array`, `ob_start`/`ob_end_flush`, `strspn`, `strtr`,
154
+ `addslashes`), PSR-7 HTTP helpers (`getStatusCode`, `withHeader`,
155
+ `withBody`, `getUri`).
156
+ - **Ruby**: Full `String` / `Enumerable` / `Hash` / `IO` method
157
+ surface (`gsub`, `scan`, `end_with?`, `is_a?`, `key?`, `sysread`,
158
+ `each_with_object`, `tally`, …), `SecureRandom`/`Addrinfo`/
159
+ `Socket`/`TCP*`/`UDP*`/`UNIX*`/`IPAddr`/`Resolv`/`Zlib`/`CGI`/
160
+ `ERB`/`Liquid::` prefixes, JRuby Java-side API (`RubyString.`,
161
+ `RubyArray.`, `RubyHash.`, `ByteList.`, `ThreadContext.`, …) +
162
+ bare `newString`, `newFixnum`, `callMethod`, `getRuntime`.
163
+ - **Dart**: `package:test_descriptor` (`d.file`, `d.dir`,
164
+ `d.nothing`), `package:test_process` (`shouldExit`), `dart:typed_data`
165
+ fromList constructors, `dart:convert` (`JsonEncoder`, `Utf8Decoder`,
166
+ `LineSplitter`), `package:shelf` Response helpers, `package:args`
167
+ CLI builders (`addFlag`, `addOption`, `addSubcommand`,
168
+ `wasParsed`), `package:yaml` loaders, Dart SDK error types
169
+ (`StateError`, `UnsupportedError`, `FormatException`, …),
170
+ pub_semver (`Version`, `VersionConstraint`).
171
+ - **`is_non_project_call` now splits on `->` in addition to `.` and
172
+ `::`** so PHP `$obj->method()` and C `struct_ptr->method()` resolve
173
+ their bare method name against `STDLIB_FILTER` — previously
174
+ `headers->set`, `response->getStatusCode` never matched. Laravel /
175
+ Symfony resolution climbed 88% → 91%.
176
+
177
+ ### Fixed
178
+
179
+ - **SCIP incremental re-indexing** no longer silently degrades to zero
180
+ for adapters outside rust / typescript / python. `_hash_project` now
181
+ falls back to `_hash_generic_adapter` (walks the adapter's declared
182
+ extensions + hashes paths + bytes) for java / go / ruby / php /
183
+ dotnet / clang / dart, instead of raising `ValueError: Unknown project
184
+ type`. Before this fix, every second+ index produced a `.scip`-less
185
+ graph that looked successful in logs but had no SCIP-resolved CALLS
186
+ edges.
187
+ - **SCIP Strategy 1 name match.** `ScipIndex.resolve`'s exact
188
+ (file, line) lookup used to fall back to the first candidate in the
189
+ definitions map when no name matched. Downstream, the cross-crate
190
+ name check in `generate.py` would reject those mismatched targets,
191
+ but by then Strategy 2 (fuzzy resolve by name) had already been
192
+ skipped. Tighten Strategy 1 to require a name match; non-matching
193
+ line lookups now fall through to fuzzy. Measured: pub Dart 52% →
194
+ 71%, Redis C 79.2% → 80.4%.
195
+ - **`changes` no longer crashes on non-UTF-8 git diff output.**
196
+ `GitHistoryAnalyzer._run_git` previously used `subprocess.run(text=True)`
197
+ which raises `UnicodeDecodeError` the moment `git diff` emits a byte
198
+ that isn't valid UTF-8 (observed on c-postgres — byte 0x92 — and
199
+ rust-coreutils — byte 0xfd, both carrying Latin-1 content in older
200
+ source files). All subprocess callers that capture stdout/stderr
201
+ (`git_history.py`, `scip/cache.py`, `scip/adapter.py`, `ast_grep.py`)
202
+ now decode with `encoding="utf-8", errors="replace"` so a stray
203
+ non-UTF-8 byte becomes `U+FFFD` instead of aborting the tool.
204
+ Regression: `tests/test_git_history.py::TestGitHistoryNonUtf8Output`
205
+ seeds a throwaway repo with 0x92 / 0xfd bytes and asserts
206
+ `_run_git` + `get_changes` stay clean.
207
+ - **User-input numeric parsing is crash-safe.** The web API's
208
+ `?limit=foo` / `?depth=bar` inputs and the `DESCRY_SCIP_WORKERS` /
209
+ `DESCRY_PRIME_THREADS` / `DESCRY_SCIP_TIMEOUT` env-var overrides
210
+ previously called `int(raw)` directly; a non-integer value raised
211
+ `ValueError` and surfaced as a 500 (web) or a hard crash during
212
+ SCIP cache init (env var). Web handlers now go through the new
213
+ `_int_param(request, name, default)` helper that returns the
214
+ default on parse failure; env-var paths catch `ValueError`, log a
215
+ warning, and fall through to the auto-tuned default.
216
+ - **Exception chains preserved on re-raise.** Three sites in
217
+ `git_history.py` and one in `embeddings.py` re-raised exceptions
218
+ without `from err` / `from None`, which hid the underlying cause in
219
+ tracebacks. Now `FileNotFoundError` → `GitError("git not found")` /
220
+ `TimeoutExpired` → `GitError("timed out")` / `FileExistsError` →
221
+ `TimeoutError("embeddings lock")` all use `from None` (cause
222
+ already captured in the message), and `_verify_git`'s internal
223
+ re-raise uses `from err` to preserve the inner git failure.
224
+ - **`descry.__version__` matches `pyproject.toml`.** Since the 0.1.1
225
+ tag, the package version string was `"0.1.0"` in `__init__.py` but
226
+ `"0.1.1"` in `pyproject.toml`, so `descry health` reported the
227
+ wrong version. Bumped `__init__.py` and added this to the release
228
+ gate so a future mismatch is caught before tagging.
229
+ - **Documented security invariants now match the code.** `CLAUDE.md`
230
+ said `descry-web` "allows CORS `*`"; in reality the code omits
231
+ `CORSMiddleware` entirely so browsers enforce same-origin by
232
+ default (with `TrustedHostMiddleware` defeating DNS-rebinding).
233
+ The subprocess-sanitization list also named only `scip-typescript`
234
+ as the representative SCIP indexer — now enumerates all ten.
235
+ - **Implicit-`Optional` annotations in `query.py` made explicit.**
236
+ 11 signatures across `flow` / `trace_flow_structured` /
237
+ `get_context_prompt` / `find_trait_impls` used `x: int = None`
238
+ (which PEP 484 prohibits); now `int | None = None`.
239
+ - **Pure-JavaScript repos no longer drop every CALLS edge.** ast-grep
240
+ has separate `typescript` and `javascript` grammars; the
241
+ `extract_calls_typescript` / `extract_imports_typescript` helpers
242
+ hardcoded `-l typescript` for the subprocess, which returns no
243
+ matches when fed `.js` / `.jsx` / `.mjs` / `.cjs`. Caught by the
244
+ pre-publish corpus sweep against express (100% .js): scip-typescript
245
+ emitted 9,702 references but the graph had **0 CALLS edges**. New
246
+ `_ast_grep_lang_for(file_path)` routes to `javascript` for JS-family
247
+ extensions; express now resolves 58% of CALLS (graph went 1,353 →
248
+ 6,117 edges). 0.1.x users on pure-JS repos were silently affected.
249
+
250
+ ### Changed
251
+
252
+ - **Configuration docs now cover every parsed field.** README's
253
+ `[cross_lang]` TOML section (with `openapi_path`,
254
+ `backend_handler_patterns`, `frontend_api_patterns`, `api_prefixes`)
255
+ and `[timeouts] index_minutes` were parsed at runtime but undocumented;
256
+ added. Four `DESCRY_*` env vars (`SCIP_WORKERS`, `SCIP_TIMEOUT`,
257
+ `PRIME_THREADS`, `AST_GREP_MAX_FILES`) were honoured by the code but
258
+ missing from the environment-variables table; added.
259
+
260
+ ### Known limitations (surfaced by the pre-publish corpus sweep)
261
+
262
+ These aren't regressions in 0.2.0 — they're scenarios the new corpus
263
+ coverage made visible. Documenting so users know what to expect.
264
+
265
+ - **Kotlin codebases need scip-java to be useful.** Descry has no
266
+ `.kt` / `.scala` parser baseline; Kotlin and Scala source files are
267
+ walked by the file walker but produce no nodes when scip-java
268
+ doesn't run. scip-java in turn fails on Kotlin-DSL Gradle projects
269
+ that use precompiled-script-plugins (observed on both `ktorio/ktor`
270
+ and `Kotlin/kotlinx.coroutines` — same upstream task-ordering
271
+ error). Practical effect: pure-Kotlin repos with that build
272
+ pattern will produce a near-empty graph. Mixed Java+Kotlin repos
273
+ index the Java side only. The `JavaAdapter` now pre-flights for
274
+ this scenario and skips scip-java with a clear log message
275
+ ("scip-java incompatibility: kotlin-dsl precompiled-script-plugins
276
+ in <path>") instead of a confusing 1100-byte stderr dump from the
277
+ failed Gradle invocation. Detection covers `buildSrc/` and any
278
+ `includeBuild()` participants in `settings.gradle{,.kts}`.
279
+ - **C++ resolution ceiling without scip-clang.** scip-clang requires
280
+ a working `compile_commands.json` with all transitive deps
281
+ available (Boost, gflags, glog, fmt, etc. for `facebook/folly`).
282
+ When deps are missing scip-clang is skipped and the regex
283
+ `ClangParser` baseline reaches ~46–54% on modern template-heavy
284
+ C++ (folly 46%, abseil-cpp 54%). With a working compdb scip-clang
285
+ contributes ~40% of resolved CALLS; the rest is regex fallback.
286
+ Plain C codebases (redis, sqlite, postgres, curl) sit at 72–80%
287
+ on regex baseline alone.
288
+ - **scip-java does not support Ant.** Maven, Gradle, sbt, and Mill
289
+ are supported; Ant projects (e.g. `apache/cassandra`) fall back
290
+ to the regex `JavaParser`, which still reaches ~86% on its own.
291
+ - **scip-dart needs `dart pub get` first.** scip-dart aborts with
292
+ "Unable to locate packageConfig" when the project hasn't been
293
+ bootstrapped. Run `dart pub get` (or `flutter pub get` for Flutter
294
+ packages) before `descry index` to get type-aware Dart resolution.
295
+ - **scip-dotnet honours `global.json` SDK pins.** When a project
296
+ pins an SDK not installed on the host (e.g. `PowerShell/PowerShell`
297
+ pins .NET 11 preview), scip-dotnet's adapter detects the
298
+ incompatibility and falls back to the regex `DotnetParser`. The
299
+ regex baseline alone reached 90% on PowerShell, so the fallback
300
+ is usually fine.
301
+ - **Pinned Rust toolchains need `rust-analyzer` installed.** A
302
+ `rust-toolchain.toml` pin (e.g. `helix` pins `1.90.0`) means
303
+ rust-analyzer must be installed for that specific toolchain
304
+ via `rustup component add rust-analyzer --toolchain <version>`.
305
+ Otherwise scip-rust emits per-crate warnings and the regex
306
+ `RustParser` baseline carries the project (~80%).
307
+
308
+ ## [0.1.1] — 2026-04-17
309
+
310
+ Patch release focused on making cross-language tracing actually configurable,
311
+ plus a round of dead-code cleanup. No breaking changes.
312
+
313
+ ### Fixed
314
+
315
+ - **Cross-language tracing is now configurable from `.descry.toml`.** The
316
+ `DescryConfig.openapi_path` field existed in 0.1.0 but had no TOML loader
317
+ and the web `/api/cross-lang` handler hardcoded `public/api/latest.json`,
318
+ so custom spec locations silently did nothing. Added a `[cross_lang]`
319
+ section with `openapi_path`, `backend_handler_patterns`,
320
+ `frontend_api_patterns`, and `api_prefixes` keys. Web and CLI/MCP now
321
+ both honour the config and pass all four through to `CrossLangTracer`.
322
+ - `[cross_lang] openapi_path` is containment-checked against the project
323
+ root; a crafted `.descry.toml` cannot point the indexer at files outside
324
+ the configured project.
325
+
326
+ ### Removed
327
+
328
+ Dead code with no call sites anywhere in the tree was pruned:
329
+
330
+ - `DescryConfig.project_markers` (auto-detect used a module constant).
331
+ - `DescryConfig.use_tree_sitter_ts` (scaffolding field with no consumer).
332
+ - `descry.query.MAX_INLINE_THRESHOLD` constant.
333
+ - `CrossLangTracer.endpoint_to_node_id()` and module-level
334
+ `_create_cross_lang_edges()` helper.
335
+ - `SemanticSearcher._find_similar()` (distinct from
336
+ `GraphQuerier._find_similar_nodes`, which remains).
337
+ - `descry.ast_grep.extract_imports_typescript_batch()`.
338
+ - `ScipIndex.get_definition_location()` and `get_symbol_info()`.
339
+ - `TypeScriptSymbolTable.file_dir` attribute.
340
+ - `DescryService._clear_dedup_cache()` (superseded by `reset_caches()` in
341
+ the 0.1.0 hardening sweep).
342
+
343
+ A `vulture --min-confidence 80` sweep on `src/descry/` now reports zero
344
+ findings outside generated protobuf code.
345
+
346
+ ## [0.1.0] — 2026-04-16
347
+
348
+ Initial public PyPI release. Descry is a polyglot codebase knowledge graph toolkit with three interfaces: CLI (`descry`), MCP server (`descry-mcp`), and local web UI (`descry-web`).
349
+
350
+ ### Features
351
+
352
+ - **Indexer**: Parses Rust, Python, TypeScript, JavaScript, and Svelte into a cached knowledge graph of symbols (functions, classes, constants) and edges (calls, imports, defines).
353
+ - **SCIP integration**: Optional type-aware call resolution via `rust-analyzer` and `scip-typescript`; regex fallback otherwise.
354
+ - **Semantic search**: Optional embeddings via sentence-transformers (Jina code embeddings by default; model pinned by revision).
355
+ - **19 MCP tools**: search, callers, callees, context, flow, path, impls, structure, flatten, cross-lang (preview), churn, evolution, changes, semantic, quick, index, status, ensure, health.
356
+ - **Web UI**: Starlette + Alpine.js; 20+ UI panels for browsing the graph visually.
357
+ - **Configuration**: `.descry.toml` + env-var overrides for cache dir, timeouts, embedding model, SCIP toolchain, excluded dirs.
358
+
359
+ ### Security
360
+
361
+ - Git argument injection hardening on all user-controlled inputs (commit ranges, symbol names, file paths, pathspecs).
362
+ - Safe embedding-cache storage (JSON sidecar + safe-mode numpy load) with atomic writes and content-addressed cache keys.
363
+ - Default embedding model revision pinned for supply-chain integrity; `trust_remote_code` defaults to `False` for user-supplied models.
364
+ - TOML-sourced subprocess args (scip toolchain, extra args, embedding model path) are validated.
365
+ - Subprocess env sanitized against known credential patterns.
366
+ - Web UI path traversal containment on `/api/source` + regular-file / size / text-file checks.
367
+ - MCP `descry_index(path=...)` restricted to project root.
368
+ - Graph JSON carries `schema_version`; mismatched graphs are rejected with an actionable error.
369
+
@@ -23,7 +23,7 @@ just test tests/test_handlers.py::test_index # one test
23
23
  just test -k "cross_lang and not slow" # by expression
24
24
  ```
25
25
 
26
- Python 3.11+ required (enforced by `pyproject.toml`). Pre-commit gate: `just lint && just fmt && just test` all pass (195 tests currently — a drop is a regression signal).
26
+ Python 3.11+ required (enforced by `pyproject.toml`). Pre-commit gate: `just lint && just fmt && just test` all pass (497 tests currently — a drop is a regression signal).
27
27
 
28
28
  Invoke the tool while developing:
29
29
 
@@ -58,9 +58,9 @@ Packaging: `pyproject.toml` defines four console scripts — `descry`, `descry-m
58
58
 
59
59
  These are load-bearing — see `SECURITY.md` and the `## Security` section of `CHANGELOG.md`. Do not weaken without a review:
60
60
 
61
- - **Subprocess env sanitization.** Every subprocess call (git, rust-analyzer, scip-typescript, ast-grep, `descry-generate`) must pass `env=safe_env()` from `src/descry/_env.py`, which filters credential-shaped env vars by regex.
61
+ - **Subprocess env sanitization.** Every subprocess call — git, every SCIP indexer (`rust-analyzer`, `scip-typescript`, `scip-python`, `scip-java`, `scip-go`, `scip-ruby`, `scip-php`, `scip-dotnet`, `scip-clang`, `scip-dart`), ast-grep, and the `descry-generate` child process — must pass `env=safe_env()` from `src/descry/_env.py`, which filters credential-shaped env vars by regex.
62
62
  - **TOML-sourced subprocess args are validated.** `_validate_toolchain`, `_validate_scip_extra_arg`, `_validate_embedding_model` in `handlers.py` reject short flags, shell metacharacters, and out-of-root local paths. New TOML → subprocess plumbing must validate.
63
- - **Web UI is local-only by design.** `descry-web` binds `127.0.0.1`, has no auth, and allows CORS `*`. `/api/source` enforces project-root containment, rejects non-regular files / non-text content / >10 MiB, and uses `O_NOFOLLOW` on the final open. `/api/index` (and `/api/index/stream`) take no path parameter (always indexes the configured root). Don't add a `--host 0.0.0.0` flag or expose network listeners.
63
+ - **Web UI is local-only by design.** `descry-web` binds `127.0.0.1`, has no auth, and deliberately omits `CORSMiddleware` so browsers enforce same-origin — a tab on `evil.com` cannot read `/api/source` or trigger `/api/index`. `TrustedHostMiddleware` rejects non-loopback `Host` headers to defeat DNS-rebinding that would otherwise bypass the 127.0.0.1 bind. `/api/source` enforces project-root containment, rejects non-regular files / non-text content / >10 MiB, and uses `O_NOFOLLOW` on the final open. `/api/index` (and `/api/index/stream`) take no path parameter (always indexes the configured root). Don't add a `--host 0.0.0.0` flag, don't add `CORSMiddleware`, and don't expose network listeners.
64
64
  - **MCP `index(path=...)`** is restricted to the configured project root.
65
65
  - **Embedding cache** uses numpy safe-mode load + JSON sidecar + atomic writes + content-addressed keys. Default embedding model revision is pinned for supply-chain integrity; `trust_remote_code` defaults to `False` for user-supplied models.
66
66
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: descry-codegraph
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Codebase knowledge graph: polyglot indexer, semantic search, call-graph analysis
5
5
  Project-URL: Homepage, https://github.com/06chaynes/descry
6
6
  Project-URL: Repository, https://github.com/06chaynes/descry
@@ -67,7 +67,7 @@ Description-Content-Type: text/markdown
67
67
 
68
68
  Polyglot codebase knowledge graph with call-graph analysis, semantic search, and SCIP integration. Built for AI coding agents (MCP), with CLI and Web UI interfaces.
69
69
 
70
- Descry indexes your codebase into a knowledge graph of symbols (functions, classes, constants) and their relationships (calls, imports, defines). It supports Rust, Python, TypeScript, JavaScript, Svelte, Go, Java, and more — with type-aware resolution via SCIP for Rust and TypeScript.
70
+ Descry indexes your codebase into a knowledge graph of symbols (functions, classes, constants) and their relationships (calls, imports, defines). SCIP-backed type-aware resolution is available for Rust, TypeScript, JavaScript, Svelte, Python, Java (+ Kotlin / Scala), Go, Ruby, PHP, C# (+ VB.NET), C / C++, and Dart. The `.js` / `.jsx` / `.svelte` paths run through `scip-typescript`; pure Kotlin / Scala source needs `scip-java` (which doesn't always work on Kotlin-DSL Gradle projects — see [CHANGELOG](CHANGELOG.md#020--2026-04-20) known limitations).
71
71
 
72
72
  > ### ⚠️ Disclaimer — please read
73
73
  >
@@ -124,6 +124,7 @@ descry quick handle_request
124
124
  | **MCP Server** | `descry-mcp` | AI coding agents (Claude Code, etc.) |
125
125
  | **Web UI** | `descry-web` | Visual exploration at `http://127.0.0.1:8787` |
126
126
  | **Pi Extension** | See `pi-extension/` | Pi coding agent integration |
127
+ | **Claude Code Skill** | See `claude-skill/` | Teaches Claude when/how to reach for descry |
127
128
 
128
129
  ## MCP Setup
129
130
 
@@ -161,7 +162,7 @@ Descry uses the standard MCP stdio transport. Any MCP-compatible host can spawn
161
162
 
162
163
  ## Tools
163
164
 
164
- Descry provides 18 tools, available through all interfaces:
165
+ Descry provides 19 tools, available through all interfaces:
165
166
 
166
167
  | Tool | Description |
167
168
  |------|-------------|
@@ -187,35 +188,49 @@ Descry provides 18 tools, available through all interfaces:
187
188
 
188
189
  ## Configuration
189
190
 
190
- Descry works zero-config by auto-detecting your project root (looks for `.git`, `Cargo.toml`, `package.json`, `pyproject.toml`). For customization, add a `.descry.toml` to your project root:
191
+ Descry works zero-config by auto-detecting your project root. The walker looks for any of: `.git`, `.descry.toml`, `Cargo.toml`, `package.json`, `pyproject.toml`, `setup.py`, `go.mod`, `Gemfile`, `composer.json`, `build.gradle{,.kts}`, `settings.gradle{,.kts}`, `pom.xml`, `build.sbt`, `global.json`, `pubspec.yaml`, `CMakeLists.txt`, or `compile_commands.json`.
192
+
193
+ For customization, add a `.descry.toml` to your project root.
194
+
195
+ > **Heads-up on list-typed fields.** Several settings below are **lists that REPLACE descry's defaults** when set, not merge. These include `[project] excluded_dirs`, `[code_files] extensions`, `[test_detection] path_patterns` / `file_suffixes`, and `[git] churn_exclusions`. Leave them out unless you have a specific reason — descry's defaults already cover a wide language matrix. If you do set them, copy the full default list (see `_DEFAULT_*` constants in `src/descry/handlers.py`) and modify from there. `[syntax.lang_map]` is the one list-shaped field that *merges* with defaults.
191
196
 
192
197
  ```toml
193
198
  [project]
194
- excluded_dirs = ["target", "node_modules", "dist", ".git", "__pycache__", "build", "vendor"]
195
- max_stale_hours = 48
199
+ # OPTIONAL — REPLACES the 25-element default. Leave unset to keep defaults
200
+ # (.git, .gradle, .next, .svelte-kit, .venv, node_modules, target, build, …).
201
+ # excluded_dirs = ["target", "node_modules", "dist", "build", "vendor"]
202
+ max_stale_hours = 24
196
203
 
197
204
  [features]
198
- enable_scip = true # Type-aware resolution (requires rust-analyzer or scip-typescript)
205
+ enable_scip = true # Type-aware resolution (auto-detects which indexers are on PATH)
199
206
  enable_embeddings = true # Semantic search (requires sentence-transformers)
200
207
 
201
208
  [embeddings]
202
209
  model = "jinaai/jina-code-embeddings-0.5b"
203
210
 
204
211
  [test_detection]
205
- path_patterns = ["/tests/", "/test/", "/__tests__/"]
206
- file_suffixes = ["_test.rs", ".test.ts", ".spec.ts", "_test.py"]
212
+ # OPTIONAL — REPLACES defaults. Defaults cover Rust/Python/TS/Go/Ruby/Java/Kotlin/
213
+ # Scala/PHP/C#/Dart/C/C++ test conventions.
214
+ # path_patterns = ["/tests/", "/test/", "/__tests__/"]
215
+ # file_suffixes = ["_test.rs", ".test.ts", ".spec.ts", "_test.py"]
207
216
 
208
217
  [code_files]
209
- extensions = [".rs", ".py", ".ts", ".tsx", ".js", ".jsx", ".svelte", ".go", ".java"]
218
+ # OPTIONAL — REPLACES the 30-element default (.rs/.py/.ts/.tsx/.js/.jsx/.svelte/
219
+ # .go/.java/.kt/.scala/.rb/.rake/.gemspec/.php/.cs/.vb/.c/.cc/.cpp/.cxx/.cu/.h/
220
+ # .hh/.hpp/.hxx/.dart/.css/.scss/.html). Leave unset to keep defaults.
221
+ # extensions = [".rs", ".py", ".ts", ".tsx", ".go", ".java"]
210
222
 
211
223
  [git]
212
- churn_exclusions = [".descry_cache/", "Cargo.lock", "package-lock.json"]
224
+ # OPTIONAL — REPLACES defaults (.descry_cache/, .beads/, Cargo.lock,
225
+ # package-lock.json, yarn.lock, pnpm-lock.yaml).
226
+ # churn_exclusions = [".descry_cache/", "Cargo.lock", "package-lock.json"]
213
227
  timeout = 30
214
228
 
215
229
  [timeouts]
216
230
  scip_minutes = 0 # 0 = unlimited
217
231
  embedding_seconds = 60
218
232
  query_ms = 4000
233
+ index_minutes = 30 # Timeout for `descry index` subprocess
219
234
 
220
235
  [query]
221
236
  max_depth = 3
@@ -231,8 +246,16 @@ skip_crates = [] # Crate names to skip during SCIP indexing
231
246
  toolchain = "1.92.0" # Pin rust-analyzer version via rustup
232
247
 
233
248
  [syntax.lang_map]
249
+ # MERGES with defaults (additive). Maps file extensions to syntax-highlight names.
234
250
  ".svelte" = "svelte"
235
251
  ".proto" = "protobuf"
252
+
253
+ [cross_lang]
254
+ # Frontend -> backend handler tracing via OpenAPI spec.
255
+ openapi_path = "public/api/openapi.json" # must resolve inside project_root
256
+ backend_handler_patterns = ["backend/src/routes"] # path substrings; empty = no filter
257
+ frontend_api_patterns = ["webapp/src/lib/api"] # same
258
+ api_prefixes = ["/api/v1", "/api/v2", "/api"] # stripped when matching spec paths
236
259
  ```
237
260
 
238
261
  ### Environment Variables
@@ -243,6 +266,10 @@ toolchain = "1.92.0" # Pin rust-analyzer version via rustup
243
266
  | `DESCRY_CACHE_DIR` | `.descry_cache/` | Override cache directory location |
244
267
  | `DESCRY_NO_SCIP` | `false` | Disable SCIP indexing |
245
268
  | `DESCRY_NO_EMBEDDINGS` | `false` | Disable semantic search |
269
+ | `DESCRY_SCIP_WORKERS` | auto | Max parallel SCIP indexer workers (overrides the memory-based default) |
270
+ | `DESCRY_SCIP_TIMEOUT` | auto | Per-project SCIP indexer timeout in minutes (`0` / `none` disables) |
271
+ | `DESCRY_PRIME_THREADS` | auto | Threads used when pre-warming `rust-analyzer` cache |
272
+ | `DESCRY_AST_GREP_MAX_FILES` | `5000` | Skip ast-grep per-file invocation on repos larger than this many target-language files |
246
273
 
247
274
  Configuration precedence: defaults < `.descry.toml` < environment variables.
248
275
 
@@ -250,15 +277,20 @@ Configuration precedence: defaults < `.descry.toml` < environment variables.
250
277
 
251
278
  | Language | Parsing | SCIP (Type-Aware) | Requirements |
252
279
  |----------|---------|-------------------|--------------|
253
- | Rust | Regex + AST | Yes | `rust-analyzer` via rustup |
254
- | TypeScript | Regex (+ Tree-sitter opt-in) | Yes | `scip-typescript` via npm; `descry-codegraph[ast]` for tree-sitter |
255
- | Python | Regex + AST | Yes | `scip-python` via npm |
256
- | JavaScript | Regex (+ Tree-sitter opt-in) | — | `descry-codegraph[ast]` for tree-sitter |
257
- | Svelte | Regex | — | — |
258
- | Go | Regex | — | — |
259
- | Java | Regex | — | — |
260
-
261
- The tree-sitter TS/TSX/JS parser is currently extractor-only (symbol discovery) and ships behind the `ast` extra + `[features] use_tree_sitter_ts = true` in `.descry.toml`. It runs alongside the regex parser and is a stepping stone toward full AST-driven extraction in a future release.
280
+ | Rust | Regex (+ ast-grep when `sg` on PATH) | Yes | `rust-analyzer` via rustup |
281
+ | TypeScript | Regex (+ ast-grep when `sg` on PATH) | Yes | `scip-typescript` via npm |
282
+ | JavaScript | Regex (+ ast-grep when `sg` on PATH) | Yes (via `scip-typescript`) | `scip-typescript` via npm |
283
+ | Svelte | Regex | Yes (via `scip-typescript`) | `scip-typescript` via npm |
284
+ | Python | Regex + Python `ast` module | Yes | `scip-python` via npm |
285
+ | Java / Kotlin / Scala | Regex (Java only — `.kt` / `.scala` produce no nodes when scip-java doesn't run) | Yes | `scip-java` (`coursier install scip-java`); see [CHANGELOG](CHANGELOG.md#020--2026-04-20) for the Kotlin-DSL Gradle limitation |
286
+ | Go | Regex | Yes | `scip-go` (`go install github.com/sourcegraph/scip-go/cmd/scip-go@latest`) |
287
+ | Ruby | Regex | Yes | `scip-ruby` gem or direct binary |
288
+ | PHP | Regex | Yes | `scip-php` via `composer require --dev davidrjenni/scip-php` (third-party indexer) |
289
+ | C# / VB.NET | Regex (C# only) | Yes | `scip-dotnet` via `dotnet tool install --global scip-dotnet` |
290
+ | C / C++ | Regex | Yes | `scip-clang` binary + `compile_commands.json` (CMake `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`, Meson+Ninja, or `bear -- make`) |
291
+ | Dart / Flutter | Regex | Yes | `scip-dart` via `dart pub global activate scip_dart`; requires `dart pub get` at project root |
292
+
293
+ `ast-grep` is the `sg` Rust binary (install via Homebrew or `cargo install ast-grep`) — when present on `PATH`, descry uses it for higher-fidelity call extraction in Rust, TypeScript, and JavaScript files. It's auto-disabled on TS/JS corpora over `DESCRY_AST_GREP_MAX_FILES` (default 5,000) where per-file subprocess overhead would dominate. The `descry-codegraph[ast]` Python extra installs `tree-sitter*` packages that are scaffolding for a future AST-driven parser at `src/descry/tree_sitter_parser.py` — currently not wired into the active pipeline.
262
294
 
263
295
  SCIP provides precise call-graph resolution (resolving which specific function is called through traits, generics, etc.). Without SCIP, Descry falls back to regex-based name matching which handles most cases but may produce false positives on overloaded names.
264
296
 
@@ -315,17 +347,19 @@ Requires [uv](https://github.com/astral-sh/uv) and [just](https://github.com/cas
315
347
 
316
348
  3. **Query** — All tools query the cached graph. Keyword search uses TF-IDF scoring. Semantic search uses sentence-transformer embeddings. Call-graph traversal follows edges in the graph.
317
349
 
318
- 4. **Freshness** — `ensure` checks graph age against `max_stale_hours` and regenerates if needed. The MCP server pre-warms the graph on startup.
350
+ 4. **Freshness** — `ensure` regenerates the graph when its age exceeds the `--max-age-hours` flag (CLI) / `max_age_hours` argument (MCP), defaulting to 24 hours. The `[project] max_stale_hours` config drives the "STALE" badge in `descry status` output but does not by itself trigger regeneration. The MCP server pre-warms the graph on startup.
319
351
 
320
352
  ## Design Notes
321
353
 
322
- ### Web UI is local-only (CORS + auth)
354
+ ### Web UI is local-only — same-origin + DNS-rebind defenses
323
355
 
324
- `descry-web` is designed as a single-user local development tool. It binds to `127.0.0.1` by default, allows cross-origin requests (`allow_origins=["*"]`), and does not require authentication. This is deliberate:
356
+ `descry-web` is designed as a single-user local development tool. It binds to `127.0.0.1` by default, has no authentication, and **deliberately omits `CORSMiddleware`** so browsers enforce same-origin by default — a tab on `evil.com` cannot read `/api/source` or trigger `/api/index`. This is deliberate:
325
357
 
326
358
  - The UI is served by the same process that reads your repository; any authentication layer would be a shared-secret between your browser and your own terminal.
327
- - Path traversal and file-serving endpoints are hardened independently of CORS: `/api/source` enforces project-root containment, rejects non-regular files, caps size at 10 MiB, and refuses non-text content (with `O_NOFOLLOW` on the final open to defeat symlink swaps).
328
- - The reindex endpoints accept no path parameter; they always index the configured project root.
359
+ - `TrustedHostMiddleware` rejects requests whose `Host` header isn't loopback (`127.0.0.1`, `localhost`, `::1`), defeating DNS-rebinding attacks that would otherwise bypass the 127.0.0.1 bind.
360
+ - The `--host` flag is validated by `_loopback_host` and rejects non-loopback values with a clear error pointing at the reverse-proxy guidance.
361
+ - Path-traversal hardening on `/api/source` is independent of any CORS posture: it enforces project-root containment, rejects non-regular files, caps size at 10 MiB, refuses non-text content, and uses `O_NOFOLLOW` on the final open to defeat symlink swaps.
362
+ - The reindex endpoints (`/api/index`, `/api/index/stream`) accept no path parameter; they always index the configured project root.
329
363
 
330
364
  **Do not expose `descry-web` to an untrusted network.** If you need remote access, put it behind your own authenticated reverse proxy.
331
365
 
@@ -333,7 +367,7 @@ Requires [uv](https://github.com/astral-sh/uv) and [just](https://github.com/cas
333
367
 
334
368
  Descry is pre-1.0. Minor version bumps (`0.1.x` → `0.2.x`) may include breaking changes to the library API, graph schema, CLI, or MCP tool signatures. Patch releases (`0.1.0` → `0.1.1`) will not introduce breaking changes. Once we reach `1.0.0`, the project will follow [semver](https://semver.org/) strictly.
335
369
 
336
- The public library API in v0.1 is limited to `descry.__version__`. Submodules (`descry.handlers`, `descry.query`, etc.) are not considered stable API yet.
370
+ The only stable public API in pre-1.0 releases is `descry.__version__`. Submodules (`descry.handlers`, `descry.query`, etc.) are not considered stable API yet — refactor freely.
337
371
 
338
372
  ## Further Reading
339
373