heimdall-code 0.3.0__tar.gz → 0.3.1__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 (33) hide show
  1. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/.claude-plugin/plugin.json +1 -1
  2. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/CHANGELOG.md +11 -0
  3. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/PKG-INFO +2 -2
  4. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/__init__.py +1 -1
  5. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/cli.py +5 -1
  6. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/registry.py +8 -3
  7. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/pyproject.toml +1 -1
  8. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/tests/test_heimdall.py +15 -0
  9. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/uv.lock +1 -1
  10. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/.claude-plugin/marketplace.json +0 -0
  11. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/.github/workflows/release.yml +0 -0
  12. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/.gitignore +0 -0
  13. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/.mcp.json +0 -0
  14. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/CLAUDE.md +0 -0
  15. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/CONTRIBUTING.md +0 -0
  16. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/LICENSE +0 -0
  17. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/README.md +0 -0
  18. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/.gitignore +0 -0
  19. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/QUESTIONS.md +0 -0
  20. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/README.md +0 -0
  21. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/RESULTS.md +0 -0
  22. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/compare.py +0 -0
  23. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/dump_lbug.mjs +0 -0
  24. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/kubernetes.py +0 -0
  25. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/questions.py +0 -0
  26. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/bench/terraform.py +0 -0
  27. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/__main__.py +0 -0
  28. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/graph.py +0 -0
  29. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/index.py +0 -0
  30. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/install.py +0 -0
  31. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/mcp_server.py +0 -0
  32. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/heimdall/parse.py +0 -0
  33. {heimdall_code-0.3.0 → heimdall_code-0.3.1}/skills/heimdall/SKILL.md +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "heimdall",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "A code knowledge graph that sees everything. Indexes any repo into a SQLite property graph and answers what calls this, what breaks if I change it, and how does A reach B.",
5
5
  "author": {
6
6
  "name": "Bogdan Sapovskyi",
@@ -3,6 +3,17 @@
3
3
  Notable changes, newest first. Versions follow [semver](https://semver.org); until 1.0
4
4
  the graph schema and the JSON payloads may still move.
5
5
 
6
+ ## 0.3.1 — 2026-08-12
7
+
8
+ ### Fixed
9
+
10
+ - A failed project lookup no longer takes the MCP server down with it. `search` with no
11
+ project, from a directory with no index, raised `SystemExit` — a `BaseException` the
12
+ tool handler never turned into an error reply, so it unwound the server's task group
13
+ and every later call found a dead pipe. `registry.resolve` raises `LookupError` now and
14
+ `cli.py` turns it back into `SystemExit` where a terminal is watching; the message and
15
+ exit code are unchanged there.
16
+
6
17
  ## 0.3.0 — 2026-08-01
7
18
 
8
19
  ### Added
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: heimdall-code
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: A code knowledge graph that sees everything -- indexes any repository into a SQLite property graph and serves it to AI agents over MCP.
5
5
  Project-URL: Homepage, https://github.com/stPhoenix/heimdall
6
6
  Project-URL: Source, https://github.com/stPhoenix/heimdall
@@ -9,5 +9,5 @@ inheritance) and serves it to agents over MCP.
9
9
  from .graph import Graph
10
10
  from .index import build
11
11
 
12
- __version__ = "0.3.0"
12
+ __version__ = "0.3.1"
13
13
  __all__ = ["Graph", "__version__", "build"]
@@ -181,7 +181,11 @@ def main(argv: list[str] | None = None) -> int:
181
181
  serve(a.project)
182
182
  return 0
183
183
 
184
- g = Graph.open(registry.resolve(a.project), readonly=True)
184
+ try:
185
+ db = registry.resolve(a.project)
186
+ except LookupError as exc:
187
+ raise SystemExit(str(exc)) from None
188
+ g = Graph.open(db, readonly=True)
185
189
  try:
186
190
  match a.cmd:
187
191
  case "search":
@@ -31,7 +31,12 @@ def register(name: str, db: Path) -> None:
31
31
 
32
32
 
33
33
  def resolve(project: str | None, cwd: Path | None = None) -> Path:
34
- """A project name, an explicit db path, or the repo in the current directory."""
34
+ """A project name, an explicit db path, or the repo in the current directory.
35
+
36
+ Failure is a LookupError, never SystemExit: this also runs inside the MCP server,
37
+ where a BaseException escapes the tool handler and kills the process mid-request --
38
+ the client then hangs on a reply that will never come.
39
+ """
35
40
  if project:
36
41
  p = Path(project)
37
42
  if p.suffix == ".db" and p.exists():
@@ -42,10 +47,10 @@ def resolve(project: str | None, cwd: Path | None = None) -> Path:
42
47
  matches = [v for k, v in entries.items() if project in k]
43
48
  if len(matches) == 1:
44
49
  return Path(matches[0])
45
- raise SystemExit(f"unknown project {project!r}; known: {', '.join(sorted(load())) or '(none)'}")
50
+ raise LookupError(f"unknown project {project!r}; known: {', '.join(sorted(load())) or '(none)'}")
46
51
  here = (cwd or Path.cwd()).resolve()
47
52
  for d in (here, *here.parents):
48
53
  db = d / ".heimdall" / "graph.db"
49
54
  if db.exists():
50
55
  return db
51
- raise SystemExit("no index found here -- run `heimdall index` first, or pass --project")
56
+ raise LookupError("no index found here -- run `heimdall index` first, or pass --project")
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  # `heimdall` on PyPI is an unrelated screenshot library; the command is still `heimdall`
3
3
  name = "heimdall-code"
4
- version = "0.3.0"
4
+ version = "0.3.1"
5
5
  description = "A code knowledge graph that sees everything -- indexes any repository into a SQLite property graph and serves it to AI agents over MCP."
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.12"
@@ -451,6 +451,20 @@ def test_install_writes_agent_config() -> None:
451
451
  assert r.returncode == 0, r.stdout + r.stderr # the assertion that failed, not just "exit 1"
452
452
 
453
453
 
454
+ def test_resolve_failures_are_ordinary_exceptions() -> None:
455
+ """SystemExit here killed the MCP server mid-request; the client hung, then reconnected."""
456
+ from heimdall import registry
457
+
458
+ with tempfile.TemporaryDirectory() as d:
459
+ for bad in (lambda: registry.resolve(None, Path(d)), lambda: registry.resolve("nope::missing", Path(d))):
460
+ try:
461
+ bad()
462
+ except Exception as exc: # LookupError is an Exception; SystemExit is not
463
+ assert isinstance(exc, LookupError), exc
464
+ else:
465
+ raise AssertionError("expected a LookupError")
466
+
467
+
454
468
  def test_every_declared_language_extracts() -> None:
455
469
  from heimdall.parse import EXT_LANG, parse_source
456
470
 
@@ -466,5 +480,6 @@ if __name__ == "__main__":
466
480
  test_end_to_end()
467
481
  test_submodules_are_indexed()
468
482
  test_install_writes_agent_config()
483
+ test_resolve_failures_are_ordinary_exceptions()
469
484
  test_every_declared_language_extracts()
470
485
  print("ok")
@@ -204,7 +204,7 @@ wheels = [
204
204
 
205
205
  [[package]]
206
206
  name = "heimdall-code"
207
- version = "0.3.0"
207
+ version = "0.3.1"
208
208
  source = { editable = "." }
209
209
  dependencies = [
210
210
  { name = "mcp" },
File without changes
File without changes
File without changes
File without changes
File without changes