synap-git 2.1.2__tar.gz → 2.1.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.
- {synap_git-2.1.2 → synap_git-2.1.3}/CHANGELOG.md +6 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/PKG-INFO +1 -1
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/__init__.py +1 -1
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/cli/main.py +43 -26
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/indexer/engine.py +65 -43
- {synap_git-2.1.2 → synap_git-2.1.3}/.gitignore +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/.synap.example/README.md +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/LICENSE.md +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/README.md +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/pyproject.toml +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/api/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/api/app.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/api/static/index.html +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/cli/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/cli/__main__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/config.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/diagnostics/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/diagnostics/logger.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/diagnostics/tracing.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/embeddings/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/git/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/git/state.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/indexer/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/indexer/daemon.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/indexer/scanner.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/indexer/wiki.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/mcp/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/mcp/server.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/parser/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/parser/registry.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/anthropic.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/base.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/factory.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/gemini.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/ollama.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/openai.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/provider/openrouter.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/py.typed +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/retrieval/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/retrieval/engine.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/retrieval/memory.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/storage/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/storage/sqlite.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/utils/__init__.py +0 -0
- {synap_git-2.1.2 → synap_git-2.1.3}/src/synap_git/utils/serialization.py +0 -0
|
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.1.3] - 2026-06-11
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **CLI Interactivity Enhancement**: Wrapped all long-running `synap` CLI commands (`init`, `start`, `stop`, `rollback`, `repair`) with interactive rich status spinners to ensure users never stare at an empty screen.
|
|
12
|
+
- **Incremental Indexing Progress**: Added a rich progress status to the `_incremental_index` background loop to provide visibility into parsing tasks during cache hits.
|
|
13
|
+
|
|
8
14
|
## [2.1.2] - 2026-06-11
|
|
9
15
|
|
|
10
16
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synap-git
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
4
4
|
Summary: Persistent structural context infrastructure for AI coding agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/saahilpal/synap-git
|
|
6
6
|
Project-URL: Repository, https://github.com/saahilpal/synap-git
|
|
@@ -524,7 +524,13 @@ def init(
|
|
|
524
524
|
_auto_protect_synap(settings.repository_path)
|
|
525
525
|
|
|
526
526
|
runtime = SynapRuntime(settings)
|
|
527
|
-
|
|
527
|
+
if not quiet and not json_output:
|
|
528
|
+
with console.status(
|
|
529
|
+
"[yellow]Initializing Synap runtime and scanning repository...[/yellow]", spinner="dots"
|
|
530
|
+
):
|
|
531
|
+
commit = runtime.bootstrap(force=force)
|
|
532
|
+
else:
|
|
533
|
+
commit = runtime.bootstrap(force=force)
|
|
528
534
|
|
|
529
535
|
if json_output:
|
|
530
536
|
_emit({"active_commit": commit, "state": "initialized"}, json_output=True)
|
|
@@ -670,20 +676,22 @@ def start(
|
|
|
670
676
|
console.print(f"[bold red]✗ Failed to start daemon:[/bold red] {e}")
|
|
671
677
|
raise typer.Exit(1)
|
|
672
678
|
|
|
673
|
-
console.print("[yellow]Starting Synap daemon...[/yellow]")
|
|
674
679
|
success = False
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
680
|
+
with console.status(
|
|
681
|
+
"[yellow]Starting Synap daemon and awaiting health check...[/yellow]", spinner="dots"
|
|
682
|
+
):
|
|
683
|
+
for _ in range(15): # wait up to 3 seconds
|
|
684
|
+
time.sleep(0.2)
|
|
685
|
+
if pid_file.exists():
|
|
686
|
+
hb = _read_daemon_heartbeat(abs_path)
|
|
687
|
+
if hb and hb.get("status") in ("healthy", "degraded"):
|
|
688
|
+
success = True
|
|
689
|
+
port = hb.get("port", 9876)
|
|
690
|
+
pid = int(hb.get("pid", 0))
|
|
691
|
+
console.print(f"[green]✓ Synap daemon started (PID {pid})[/green]")
|
|
692
|
+
console.print("[green]✓ Runtime healthy[/green]")
|
|
693
|
+
console.print(f"[green]✓ UI available at http://127.0.0.1:{port}[/green]")
|
|
694
|
+
break
|
|
687
695
|
|
|
688
696
|
if not success:
|
|
689
697
|
console.print(
|
|
@@ -766,11 +774,15 @@ def stop(
|
|
|
766
774
|
|
|
767
775
|
# Wait for graceful shutdown (up to 10 seconds)
|
|
768
776
|
success = False
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
777
|
+
with console.status(
|
|
778
|
+
f"[yellow]Waiting for daemon (PID {pid}) to shut down gracefully...[/yellow]",
|
|
779
|
+
spinner="dots",
|
|
780
|
+
):
|
|
781
|
+
for _ in range(50):
|
|
782
|
+
time.sleep(0.2)
|
|
783
|
+
if not _is_process_running(pid):
|
|
784
|
+
success = True
|
|
785
|
+
break
|
|
774
786
|
|
|
775
787
|
# If graceful failed, force kill
|
|
776
788
|
if not success:
|
|
@@ -1234,7 +1246,10 @@ def rollback(
|
|
|
1234
1246
|
|
|
1235
1247
|
structlog.get_logger().error("suppressed_error_caught", error=str(e), exc_info=True)
|
|
1236
1248
|
|
|
1237
|
-
|
|
1249
|
+
with console.status(
|
|
1250
|
+
f"[yellow]Reindexing repository to match {selected_commit}...[/yellow]", spinner="dots"
|
|
1251
|
+
):
|
|
1252
|
+
runtime.bootstrap(force=True)
|
|
1238
1253
|
console.print(f"[green]✓ Rolled back successfully to {selected_commit}[/green]")
|
|
1239
1254
|
|
|
1240
1255
|
|
|
@@ -1294,15 +1309,17 @@ def repair(
|
|
|
1294
1309
|
|
|
1295
1310
|
runtime.initialize_storage()
|
|
1296
1311
|
console.print("[2/3] Reindexing all symbols")
|
|
1297
|
-
|
|
1312
|
+
with console.status("[yellow]Reindexing repository...[/yellow]", spinner="dots"):
|
|
1313
|
+
runtime.bootstrap(force=True)
|
|
1298
1314
|
|
|
1299
1315
|
console.print("[3/3] Regenerating wiki from last known state")
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1316
|
+
with console.status("[yellow]Regenerating wiki documentation...[/yellow]", spinner="dots"):
|
|
1317
|
+
try:
|
|
1318
|
+
runtime.wiki.generate_project_wiki()
|
|
1319
|
+
except Exception as e:
|
|
1320
|
+
import structlog
|
|
1304
1321
|
|
|
1305
|
-
|
|
1322
|
+
structlog.get_logger().error("suppressed_error_caught", error=str(e), exc_info=True)
|
|
1306
1323
|
|
|
1307
1324
|
status_info = runtime.status()
|
|
1308
1325
|
console.print(f"\n[green]✓ Recovery complete. {status_info.files} files restored.[/green]")
|
|
@@ -601,55 +601,77 @@ class SynapRuntime:
|
|
|
601
601
|
# Process additions and modifications
|
|
602
602
|
registry = CodeParserRegistry()
|
|
603
603
|
parsed_results = []
|
|
604
|
-
for rel_path in filtered_added_or_modified:
|
|
605
|
-
self.logger.info("reindexing_file", path=rel_path)
|
|
606
|
-
p = self.settings.repository_path / rel_path
|
|
607
604
|
|
|
608
|
-
|
|
609
|
-
content = p.read_text(encoding="utf-8", errors="replace")
|
|
610
|
-
except Exception as e:
|
|
611
|
-
self.logger.warning("failed_to_read_file", path=rel_path, error=str(e))
|
|
612
|
-
continue
|
|
605
|
+
import contextlib
|
|
613
606
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
617
|
-
file_id_hash = hashlib.sha256((rel_path + content_hash).encode("utf-8")).hexdigest()
|
|
618
|
-
|
|
619
|
-
parse_result = registry.parse(p, relative_path=rel_path, text=content)
|
|
620
|
-
symbols_list = [
|
|
621
|
-
{
|
|
622
|
-
"symbol_id": sym.stable_id,
|
|
623
|
-
"name": sym.name,
|
|
624
|
-
"kind": sym.kind,
|
|
625
|
-
"start_line": sym.start_line,
|
|
626
|
-
"end_line": sym.end_line,
|
|
627
|
-
"ast_hash": sym.ast_hash,
|
|
628
|
-
"metadata": sym.metadata,
|
|
629
|
-
}
|
|
630
|
-
for sym in parse_result.symbols
|
|
631
|
-
]
|
|
632
|
-
|
|
633
|
-
self.store.upsert_file_and_symbols(
|
|
634
|
-
file_id=file_id_hash,
|
|
635
|
-
path=rel_path,
|
|
636
|
-
git_oid=git_oids.get(rel_path, ""),
|
|
637
|
-
content_hash=content_hash,
|
|
638
|
-
language=parse_result.language,
|
|
639
|
-
symbols=symbols_list,
|
|
640
|
-
)
|
|
641
|
-
self._schedule_embedding(file_id_hash, symbols_list, content_hash)
|
|
607
|
+
from rich.console import Console
|
|
642
608
|
|
|
643
|
-
|
|
644
|
-
|
|
609
|
+
console = Console(stderr=True)
|
|
610
|
+
show_progress = (
|
|
611
|
+
self.settings.logging_mode.name == "HUMAN" and len(filtered_added_or_modified) > 0
|
|
612
|
+
)
|
|
645
613
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
614
|
+
with (
|
|
615
|
+
console.status(
|
|
616
|
+
"[yellow]Parsing incrementally changed files...[/yellow]", spinner="dots"
|
|
617
|
+
)
|
|
618
|
+
if show_progress
|
|
619
|
+
else contextlib.nullcontext()
|
|
620
|
+
) as status_ctx:
|
|
621
|
+
for idx, rel_path in enumerate(filtered_added_or_modified, 1):
|
|
622
|
+
if show_progress and hasattr(status_ctx, "update"):
|
|
623
|
+
status_ctx.update( # type: ignore[union-attr]
|
|
624
|
+
f"[yellow]Parsing incrementally changed files ([white]{idx}/{len(filtered_added_or_modified)}[/white])...[/yellow]"
|
|
625
|
+
)
|
|
626
|
+
|
|
627
|
+
self.logger.info("reindexing_file", path=rel_path)
|
|
628
|
+
p = self.settings.repository_path / rel_path
|
|
629
|
+
|
|
630
|
+
try:
|
|
631
|
+
content = p.read_text(encoding="utf-8", errors="replace")
|
|
632
|
+
except Exception as e:
|
|
633
|
+
self.logger.warning("failed_to_read_file", path=rel_path, error=str(e))
|
|
634
|
+
continue
|
|
635
|
+
|
|
636
|
+
import hashlib
|
|
637
|
+
|
|
638
|
+
content_hash = hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
639
|
+
file_id_hash = hashlib.sha256((rel_path + content_hash).encode("utf-8")).hexdigest()
|
|
640
|
+
|
|
641
|
+
parse_result = registry.parse(p, relative_path=rel_path, text=content)
|
|
642
|
+
symbols_list = [
|
|
643
|
+
{
|
|
644
|
+
"symbol_id": sym.stable_id,
|
|
645
|
+
"name": sym.name,
|
|
646
|
+
"kind": sym.kind,
|
|
647
|
+
"start_line": sym.start_line,
|
|
648
|
+
"end_line": sym.end_line,
|
|
649
|
+
"ast_hash": sym.ast_hash,
|
|
650
|
+
"metadata": sym.metadata,
|
|
651
|
+
}
|
|
652
|
+
for sym in parse_result.symbols
|
|
653
|
+
]
|
|
654
|
+
|
|
655
|
+
self.store.upsert_file_and_symbols(
|
|
656
|
+
file_id=file_id_hash,
|
|
657
|
+
path=rel_path,
|
|
658
|
+
git_oid=git_oids.get(rel_path, ""),
|
|
659
|
+
content_hash=content_hash,
|
|
660
|
+
language=parse_result.language,
|
|
661
|
+
symbols=symbols_list,
|
|
650
662
|
)
|
|
663
|
+
self._schedule_embedding(file_id_hash, symbols_list, content_hash)
|
|
664
|
+
|
|
665
|
+
self.store.set_wiki_status(rel_path, None, "stale")
|
|
666
|
+
self.store.enqueue_wiki(rel_path)
|
|
667
|
+
|
|
668
|
+
with self.store.connect() as conn:
|
|
669
|
+
conn.execute(
|
|
670
|
+
"DELETE FROM edges WHERE source_symbol IN (SELECT symbol_id FROM symbols WHERE file_id = ?)",
|
|
671
|
+
(file_id_hash,),
|
|
672
|
+
)
|
|
651
673
|
|
|
652
|
-
|
|
674
|
+
parsed_results.append((file_id_hash, rel_path, list(parse_result.imports)))
|
|
653
675
|
|
|
654
676
|
# Re-resolve edges for changed files
|
|
655
677
|
if parsed_results:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|