codevira 1.6.0__tar.gz → 1.6.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.
- {codevira-1.6.0 → codevira-1.6.1}/CHANGELOG.md +51 -22
- {codevira-1.6.0/codevira.egg-info → codevira-1.6.1}/PKG-INFO +2 -2
- {codevira-1.6.0 → codevira-1.6.1}/README.md +1 -1
- {codevira-1.6.0 → codevira-1.6.1/codevira.egg-info}/PKG-INFO +2 -2
- {codevira-1.6.0 → codevira-1.6.1}/indexer/index_codebase.py +75 -48
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/cli.py +225 -9
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/ide_inject.py +46 -1
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/server.py +6 -3
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/search.py +39 -10
- {codevira-1.6.0 → codevira-1.6.1}/pyproject.toml +1 -1
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_index_codebase.py +10 -11
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_search.py +29 -34
- {codevira-1.6.0 → codevira-1.6.1}/LICENSE +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/MANIFEST.in +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/builder.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/developer.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/documenter.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/orchestrator.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/planner.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/reviewer.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/agents/tester.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/codevira.egg-info/SOURCES.txt +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/codevira.egg-info/dependency_links.txt +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/codevira.egg-info/entry_points.txt +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/codevira.egg-info/requires.txt +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/codevira.egg-info/top_level.txt +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/config.example.yaml +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/docs/how-i-built-persistent-memory-for-ai-agents.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/docs/linkedin-article-ai-agent-memory.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/docs/linkedin-post-ai-agent-memory.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/docs/medium-your-ai-coding-agent-has-amnesia.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/docs/roadmap.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/graph/_schema.yaml +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/__init__.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/chunker.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/global_db.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/graph_generator.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/outcome_tracker.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/rule_learner.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/sqlite_graph.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/indexer/treesitter_parser.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/__init__.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/__main__.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/auto_init.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/crash_logger.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/__init__.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/builder.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/developer.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/documenter.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/orchestrator.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/planner.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/reviewer.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/agents/tester.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/config.example.yaml +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/coding-standards.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/engineering-excellence.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/git-cicd-governance.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/git_commits.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/incremental-updates.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/master_rule.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/multi-language.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/persistence.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/resilience-observability.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/smoke-testing.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/data/rules/testing-standards.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/detect.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/gitignore.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/global_sync.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/http_server.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/launchd.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/migrate.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/paths.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/prompts.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/__init__.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/changesets.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/code_reader.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/graph.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/learning.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/playbook.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/mcp_server/tools/roadmap.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/coding-standards.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/engineering-excellence.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/git-cicd-governance.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/git_commits.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/incremental-updates.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/master_rule.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/persistence.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/resilience-observability.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/smoke-testing.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/rules/testing-standards.md +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/setup.cfg +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_auto_init.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_chunker.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_cli.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_crash_logger.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_detect.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_gitignore.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_global_db.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_global_sync.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_graph_generator.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_http_server.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_ide_inject.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_launchd.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_migrate.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_outcome_tracker.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_paths.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_prompts.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_rule_learner.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_server.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_sqlite_graph.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_changesets.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_code_reader.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_graph.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_learning.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_playbook.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_tools_roadmap.py +0 -0
- {codevira-1.6.0 → codevira-1.6.1}/tests/test_treesitter_parser.py +0 -0
|
@@ -11,32 +11,61 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## [1.6.1] — 2026-04-16 — Stability, Graceful Degradation & Cleanup
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **`codevira clean` command**: One-shot removal of all Codevira data, IDE configs,
|
|
20
|
+
and services. Supports `--all` (per-project artifacts), `--dry-run` (preview),
|
|
21
|
+
and `-y` (skip confirmation).
|
|
22
|
+
- **Google Antigravity global mode**: `codevira register` now includes Antigravity
|
|
23
|
+
with a single global entry — no per-project hardcoded paths.
|
|
20
24
|
|
|
21
25
|
### Fixed
|
|
22
|
-
- **
|
|
23
|
-
`
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
26
|
+
- **Graceful degradation when chromadb not installed**: `refresh_index` and
|
|
27
|
+
`cmd_incremental` now work in graph-only mode instead of crashing with
|
|
28
|
+
ImportError. Background file watcher no longer generates noisy exceptions
|
|
29
|
+
on every file save.
|
|
30
|
+
- **`sys.exit()` crashes eliminated**: `server.py` module-level import failure
|
|
31
|
+
now uses stderr + raise instead of corrupting the MCP stdio protocol.
|
|
32
|
+
`cmd_incremental` no longer kills the MCP server process from background
|
|
33
|
+
watcher threads.
|
|
34
|
+
- **Binary resolution in user-facing output**: `codevira init` "For other AI
|
|
35
|
+
tools" section now shows the resolved `codevira` binary path instead of a
|
|
36
|
+
hardcoded Python interpreter path (e.g. `/opt/homebrew/...`).
|
|
37
|
+
- **Antigravity config path**: Fixed from `~/.gemini/settings/mcp_config.json`
|
|
38
|
+
(wrong) to `~/.gemini/antigravity/mcp_config.json` (correct).
|
|
39
|
+
- **Rich markup escaping**: `codevira[search]` install hints now display
|
|
40
|
+
correctly — Rich no longer strips `[search]` as a style tag.
|
|
41
|
+
- **`codevira status` without chromadb**: Shows "Semantic Search: not installed"
|
|
42
|
+
with install tip instead of crashing. Added graph node count to status.
|
|
43
|
+
- **Git hook uses full binary resolution**: Post-commit hook now uses
|
|
44
|
+
`_resolve_command()` instead of simple `shutil.which`.
|
|
45
|
+
|
|
46
|
+
### Performance
|
|
47
|
+
- **`get_data_dir()` caching**: Result cached per project root. First call runs
|
|
48
|
+
subprocess + metadata scan; subsequent calls are O(1) dict lookups.
|
|
49
|
+
- **`set_project_dir()` cache invalidation**: Changing the project root now
|
|
50
|
+
clears the data-dir cache automatically.
|
|
51
|
+
- **Unbounded join timeout**: Background semantic indexing thread capped at
|
|
52
|
+
5 minutes; server continues in graph-only mode if it hangs.
|
|
35
53
|
|
|
36
54
|
### Changed
|
|
37
|
-
- **
|
|
38
|
-
|
|
39
|
-
|
|
55
|
+
- **Package renamed**: `codevira-mcp` → `codevira`. Install with `pip install
|
|
56
|
+
codevira`. CLI command is now `codevira` (not `codevira-mcp`).
|
|
57
|
+
- **Removed unused `gitpython` dependency**: CodeVira uses `subprocess` for
|
|
58
|
+
all git operations. Saves ~20MB install weight.
|
|
59
|
+
- **Removed out-of-scope rule files**: REST API standards, SSE/UI events,
|
|
60
|
+
TUI layout/keybinding rules — none apply to an MCP server.
|
|
61
|
+
- **Removed vendor-specific secret patterns from crash logger**: Stripe, AWS,
|
|
62
|
+
GitHub token regexes were irrelevant to CodeVira's scope.
|
|
63
|
+
- **Test isolation hardened**: Autouse fixture clears `_data_dir_cache` and
|
|
64
|
+
resets `_project_dir_override` between every test.
|
|
65
|
+
- **`_init_done` renamed to `_init_started`**: Name matches semantics — the
|
|
66
|
+
flag signals thread launch, not completion.
|
|
67
|
+
- **`install_launchd()` accepts `project_dir`**: Adds `--project-dir` to
|
|
68
|
+
ProgramArguments and `WorkingDirectory` to the plist.
|
|
40
69
|
|
|
41
70
|
---
|
|
42
71
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: codevira
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.1
|
|
4
4
|
Summary: Persistent adaptive memory for AI coding agents — MCP server with context graph, semantic search, adaptive learning, roadmap tracking, and cross-tool continuity.
|
|
5
5
|
Author-email: Sachin Shelke <sachin.worldnet@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,7 +46,7 @@ Requires-Dist: sentence-transformers>=2.7.0; extra == "all"
|
|
|
46
46
|
[](https://www.python.org/)
|
|
47
47
|
[](LICENSE)
|
|
48
48
|
[](https://modelcontextprotocol.io)
|
|
49
|
-
[](CHANGELOG.md)
|
|
50
50
|
[](CONTRIBUTING.md)
|
|
51
51
|
|
|
52
52
|
**Works with:** Claude Code · Claude Desktop · Cursor · Windsurf · Google Antigravity · any MCP-compatible AI tool
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://www.python.org/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
[](https://modelcontextprotocol.io)
|
|
8
|
-
[](CHANGELOG.md)
|
|
9
9
|
[](CONTRIBUTING.md)
|
|
10
10
|
|
|
11
11
|
**Works with:** Claude Code · Claude Desktop · Cursor · Windsurf · Google Antigravity · any MCP-compatible AI tool
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: codevira
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.1
|
|
4
4
|
Summary: Persistent adaptive memory for AI coding agents — MCP server with context graph, semantic search, adaptive learning, roadmap tracking, and cross-tool continuity.
|
|
5
5
|
Author-email: Sachin Shelke <sachin.worldnet@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -46,7 +46,7 @@ Requires-Dist: sentence-transformers>=2.7.0; extra == "all"
|
|
|
46
46
|
[](https://www.python.org/)
|
|
47
47
|
[](LICENSE)
|
|
48
48
|
[](https://modelcontextprotocol.io)
|
|
49
|
-
[](CHANGELOG.md)
|
|
50
50
|
[](CONTRIBUTING.md)
|
|
51
51
|
|
|
52
52
|
**Works with:** Claude Code · Claude Desktop · Cursor · Windsurf · Google Antigravity · any MCP-compatible AI tool
|
|
@@ -61,9 +61,10 @@ def _get_chroma_client():
|
|
|
61
61
|
try:
|
|
62
62
|
import chromadb
|
|
63
63
|
except ImportError:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
raise ImportError(
|
|
65
|
+
"Semantic search requires chromadb. "
|
|
66
|
+
"Install it with: pip install 'codevira[search]'"
|
|
67
|
+
)
|
|
67
68
|
db_dir = str(_index_dir())
|
|
68
69
|
return chromadb.PersistentClient(path=db_dir)
|
|
69
70
|
|
|
@@ -72,9 +73,10 @@ def _get_embedding_fn():
|
|
|
72
73
|
from chromadb.utils import embedding_functions
|
|
73
74
|
return embedding_functions.SentenceTransformerEmbeddingFunction(model_name="all-MiniLM-L6-v2")
|
|
74
75
|
except ImportError:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
raise ImportError(
|
|
77
|
+
"Semantic search requires sentence-transformers. "
|
|
78
|
+
"Install it with: pip install 'codevira[search]'"
|
|
79
|
+
)
|
|
78
80
|
|
|
79
81
|
def _compute_hash(file_path: Path) -> str:
|
|
80
82
|
hasher = hashlib.sha256()
|
|
@@ -177,7 +179,7 @@ def cmd_full_rebuild():
|
|
|
177
179
|
db = SQLiteGraph(get_data_dir() / "graph" / "graph.db")
|
|
178
180
|
|
|
179
181
|
if not _check_search_deps():
|
|
180
|
-
console.print("[yellow]⚠[/yellow] Semantic search skipped — install with: [bold]pip install 'codevira\\[search
|
|
182
|
+
console.print("[yellow]⚠[/yellow] Semantic search skipped — install with: [bold]pip install 'codevira\\[search]'[/bold]")
|
|
181
183
|
# Still build the graph even without search deps
|
|
182
184
|
from indexer.graph_generator import generate_graph_sqlite
|
|
183
185
|
result = generate_graph_sqlite(str(_project_root()), str(get_data_dir() / "graph" / "graph.db"))
|
|
@@ -272,50 +274,62 @@ def cmd_incremental(quiet: bool = False, file_paths: list[str] | None = None):
|
|
|
272
274
|
file_label = "requested file(s)" if explicit_files else "changed file(s)"
|
|
273
275
|
console.print(f"[bold cyan]Incremental update:[/bold cyan] {len(changed_items)} {file_label}")
|
|
274
276
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
# Check if semantic search deps are available
|
|
278
|
+
has_search = _check_search_deps()
|
|
279
|
+
collection = None
|
|
280
|
+
|
|
281
|
+
if has_search:
|
|
282
|
+
try:
|
|
283
|
+
client = _get_chroma_client()
|
|
284
|
+
embed_fn = _get_embedding_fn()
|
|
285
|
+
collection = client.get_collection(COLLECTION_NAME, embedding_function=embed_fn)
|
|
286
|
+
except Exception:
|
|
287
|
+
# No existing collection — skip semantic indexing, still update graph
|
|
288
|
+
collection = None
|
|
289
|
+
if not quiet:
|
|
290
|
+
console.print("[yellow]⚠[/yellow] No semantic index found — updating graph only.")
|
|
283
291
|
|
|
284
292
|
indexed_any = False
|
|
285
|
-
# Acquire ChromaDB write lock to prevent concurrent writes with the
|
|
286
|
-
# background watcher or background full-index thread.
|
|
287
|
-
with _chroma_write_lock:
|
|
288
|
-
for fpath, fhash in changed_items:
|
|
289
|
-
try:
|
|
290
|
-
collection.delete(where={"file_path": fpath})
|
|
291
|
-
except Exception as e:
|
|
292
|
-
try:
|
|
293
|
-
from mcp_server.crash_logger import log_crash
|
|
294
|
-
log_crash(e, context=f"incremental index: delete old chunks for {fpath}")
|
|
295
|
-
except Exception: pass
|
|
296
293
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
db.update_file_hash(fpath, fhash)
|
|
309
|
-
indexed_any = True
|
|
310
|
-
console.print(f" [green]+[/green] Re-indexed {len(chunks)} chunks for {fpath}")
|
|
294
|
+
if collection is not None:
|
|
295
|
+
# Semantic search + graph update
|
|
296
|
+
with _chroma_write_lock:
|
|
297
|
+
for fpath, fhash in changed_items:
|
|
298
|
+
try:
|
|
299
|
+
collection.delete(where={"file_path": fpath})
|
|
300
|
+
except Exception as e:
|
|
301
|
+
try:
|
|
302
|
+
from mcp_server.crash_logger import log_crash
|
|
303
|
+
log_crash(e, context=f"incremental index: delete old chunks for {fpath}")
|
|
304
|
+
except Exception: pass
|
|
311
305
|
|
|
312
|
-
except Exception as e:
|
|
313
|
-
console.print(f"[red]Error indexing {fpath}: {e}[/red]")
|
|
314
306
|
try:
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
307
|
+
chunks = chunk_file(str(_project_root() / fpath), str(_project_root()))
|
|
308
|
+
if chunks:
|
|
309
|
+
ids, docs, metas = [], [], []
|
|
310
|
+
for chunk in chunks:
|
|
311
|
+
doc_id, doc, meta = _chunk_to_document(chunk)
|
|
312
|
+
ids.append(doc_id)
|
|
313
|
+
docs.append(doc)
|
|
314
|
+
metas.append(meta)
|
|
315
|
+
collection.add(ids=ids, documents=docs, metadatas=metas)
|
|
316
|
+
|
|
317
|
+
db.update_file_hash(fpath, fhash)
|
|
318
|
+
indexed_any = True
|
|
319
|
+
console.print(f" [green]+[/green] Re-indexed {len(chunks)} chunks for {fpath}")
|
|
320
|
+
|
|
321
|
+
except Exception as e:
|
|
322
|
+
console.print(f"[red]Error indexing {fpath}: {e}[/red]")
|
|
323
|
+
try:
|
|
324
|
+
from mcp_server.crash_logger import log_crash
|
|
325
|
+
log_crash(e, context=f"incremental index: indexing {fpath}")
|
|
326
|
+
except Exception: pass
|
|
327
|
+
continue
|
|
328
|
+
else:
|
|
329
|
+
# Graph-only mode: update file hashes without semantic indexing
|
|
330
|
+
for fpath, fhash in changed_items:
|
|
331
|
+
db.update_file_hash(fpath, fhash)
|
|
332
|
+
indexed_any = True
|
|
319
333
|
|
|
320
334
|
if indexed_any:
|
|
321
335
|
generate_graph_sqlite(str(_project_root()), str(db.db_path))
|
|
@@ -516,8 +530,18 @@ def cmd_status():
|
|
|
516
530
|
|
|
517
531
|
stale_files = _get_changed_files(db)
|
|
518
532
|
|
|
533
|
+
# Count graph nodes
|
|
534
|
+
try:
|
|
535
|
+
nodes = db.conn.execute("SELECT COUNT(*) FROM nodes").fetchone()[0]
|
|
536
|
+
except Exception:
|
|
537
|
+
nodes = 0
|
|
538
|
+
|
|
519
539
|
table = Table(show_header=False, box=None)
|
|
520
|
-
table.add_row("[cyan]
|
|
540
|
+
table.add_row("[cyan]Graph Nodes:[/cyan]", str(nodes))
|
|
541
|
+
if search_available:
|
|
542
|
+
table.add_row("[cyan]ChromaDB Chunks:[/cyan]", str(chunk_count))
|
|
543
|
+
else:
|
|
544
|
+
table.add_row("[cyan]Semantic Search:[/cyan]", "[dim]not installed[/dim]")
|
|
521
545
|
table.add_row("[cyan]Outdated Files:[/cyan]", str(len(stale_files)))
|
|
522
546
|
|
|
523
547
|
panel = Panel(
|
|
@@ -528,13 +552,16 @@ def cmd_status():
|
|
|
528
552
|
)
|
|
529
553
|
console.print(panel)
|
|
530
554
|
|
|
555
|
+
if not search_available:
|
|
556
|
+
console.print("\n[dim] Tip: pip install 'codevira\\[search]' to enable semantic code search[/dim]")
|
|
557
|
+
|
|
531
558
|
if stale_files:
|
|
532
559
|
console.print("\n[yellow]Files requiring re-indexing:[/yellow]")
|
|
533
560
|
for fp, _ in stale_files[:10]:
|
|
534
561
|
console.print(f" - {fp}")
|
|
535
562
|
if len(stale_files) > 10:
|
|
536
563
|
console.print(f" ... and {len(stale_files) - 10} more.")
|
|
537
|
-
|
|
564
|
+
|
|
538
565
|
db.close()
|
|
539
566
|
|
|
540
567
|
def cmd_generate_graph():
|
|
@@ -236,9 +236,12 @@ def cmd_init() -> None:
|
|
|
236
236
|
hooks_dir.mkdir(exist_ok=True)
|
|
237
237
|
hook_path = hooks_dir / "post-commit"
|
|
238
238
|
|
|
239
|
-
# Find codevira executable path
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
# Find codevira executable path using full resolution chain
|
|
240
|
+
from mcp_server.ide_inject import _resolve_command
|
|
241
|
+
resolved_cmd, _py = _resolve_command()
|
|
242
|
+
# For git hooks, use the resolved binary if found; otherwise bare name
|
|
243
|
+
# (git hooks inherit the user's shell PATH)
|
|
244
|
+
cmd_path = resolved_cmd if resolved_cmd != _py else "codevira"
|
|
242
245
|
|
|
243
246
|
hook_content = (
|
|
244
247
|
"#!/bin/sh\n"
|
|
@@ -311,20 +314,25 @@ def cmd_init() -> None:
|
|
|
311
314
|
log_crash(e, context="codevira init: global memory register", project_path=str(cwd))
|
|
312
315
|
except Exception: pass
|
|
313
316
|
|
|
314
|
-
# Print
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
python_exe =
|
|
317
|
+
# Print config for undetected tools — use the resolved binary path,
|
|
318
|
+
# not the Python interpreter, so users get a clean command.
|
|
319
|
+
from mcp_server.ide_inject import _resolve_command
|
|
320
|
+
cmd_path, python_exe = _resolve_command()
|
|
318
321
|
project_path = str(cwd)
|
|
319
322
|
|
|
323
|
+
is_python_fallback = (cmd_path == python_exe)
|
|
320
324
|
print()
|
|
321
325
|
print(" For other AI tools, add this to their MCP config:")
|
|
322
326
|
print()
|
|
323
327
|
print(' {')
|
|
324
328
|
print(' "mcpServers": {')
|
|
325
329
|
print(' "codevira": {')
|
|
326
|
-
|
|
327
|
-
|
|
330
|
+
if is_python_fallback:
|
|
331
|
+
print(f' "command": "{python_exe}",')
|
|
332
|
+
print(f' "args": ["-m", "mcp_server", "--project-dir", "{project_path}"]')
|
|
333
|
+
else:
|
|
334
|
+
print(f' "command": "{cmd_path}",')
|
|
335
|
+
print(f' "args": ["--project-dir", "{project_path}"]')
|
|
328
336
|
print(' }')
|
|
329
337
|
print(' }')
|
|
330
338
|
print(' }')
|
|
@@ -473,6 +481,11 @@ def cmd_register(
|
|
|
473
481
|
path = _inject_claude_desktop(project_root, cmd_path, python_exe)
|
|
474
482
|
if path:
|
|
475
483
|
results["Claude Desktop"] = path
|
|
484
|
+
elif ide == "antigravity":
|
|
485
|
+
from mcp_server.ide_inject import inject_global_antigravity
|
|
486
|
+
path = inject_global_antigravity(cmd_path, python_exe)
|
|
487
|
+
if path:
|
|
488
|
+
results["Antigravity (global)"] = path
|
|
476
489
|
except Exception as e:
|
|
477
490
|
print(f" Warning: could not configure {ide}: {e}")
|
|
478
491
|
|
|
@@ -575,6 +588,24 @@ def main() -> None:
|
|
|
575
588
|
help="Inject an HTTP URL config into Claude Code (e.g. https://localhost:7443/mcp)",
|
|
576
589
|
)
|
|
577
590
|
|
|
591
|
+
# clean
|
|
592
|
+
clean_parser = subparsers.add_parser(
|
|
593
|
+
"clean",
|
|
594
|
+
help="Remove all Codevira data, IDE configs, and services",
|
|
595
|
+
)
|
|
596
|
+
clean_parser.add_argument(
|
|
597
|
+
"--all", action="store_true",
|
|
598
|
+
help="Also clean per-project artifacts (legacy .codevira/, git hooks, per-project IDE configs)",
|
|
599
|
+
)
|
|
600
|
+
clean_parser.add_argument(
|
|
601
|
+
"--dry-run", action="store_true",
|
|
602
|
+
help="Show what would be removed without deleting anything",
|
|
603
|
+
)
|
|
604
|
+
clean_parser.add_argument(
|
|
605
|
+
"-y", "--yes", action="store_true",
|
|
606
|
+
help="Skip confirmation prompt",
|
|
607
|
+
)
|
|
608
|
+
|
|
578
609
|
args = parser.parse_args(raw_args)
|
|
579
610
|
|
|
580
611
|
if args.command == "init":
|
|
@@ -613,10 +644,195 @@ def main() -> None:
|
|
|
613
644
|
claude_desktop=getattr(args, "claude_desktop", False),
|
|
614
645
|
http_url=getattr(args, "http_url", None),
|
|
615
646
|
)
|
|
647
|
+
elif args.command == "clean":
|
|
648
|
+
cmd_clean(
|
|
649
|
+
clean_all=getattr(args, "all", False),
|
|
650
|
+
dry_run=getattr(args, "dry_run", False),
|
|
651
|
+
yes=getattr(args, "yes", False),
|
|
652
|
+
)
|
|
616
653
|
else:
|
|
617
654
|
# No subcommand → start MCP server (stdio transport)
|
|
618
655
|
cmd_server()
|
|
619
656
|
|
|
620
657
|
|
|
658
|
+
# ---------------------------------------------------------------------------
|
|
659
|
+
# cmd_clean
|
|
660
|
+
# ---------------------------------------------------------------------------
|
|
661
|
+
|
|
662
|
+
def cmd_clean(clean_all: bool = False, dry_run: bool = False, yes: bool = False) -> None:
|
|
663
|
+
"""Remove all Codevira data, IDE configs, and services."""
|
|
664
|
+
import shutil
|
|
665
|
+
|
|
666
|
+
from mcp_server.paths import get_global_home
|
|
667
|
+
from mcp_server.ide_inject import (
|
|
668
|
+
_claude_global_config_path,
|
|
669
|
+
_claude_desktop_config_path,
|
|
670
|
+
_cursor_global_config_path,
|
|
671
|
+
_windsurf_global_config_path,
|
|
672
|
+
_antigravity_config_path,
|
|
673
|
+
remove_codevira_from_config,
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
actions: list[tuple[str, callable]] = []
|
|
677
|
+
print()
|
|
678
|
+
print(" Codevira — Clean Setup")
|
|
679
|
+
print(" " + "─" * 40)
|
|
680
|
+
print()
|
|
681
|
+
|
|
682
|
+
# 1. Global data directory
|
|
683
|
+
global_home = get_global_home()
|
|
684
|
+
if global_home.exists():
|
|
685
|
+
# Count projects and size
|
|
686
|
+
projects_dir = global_home / "projects"
|
|
687
|
+
project_count = len(list(projects_dir.iterdir())) if projects_dir.exists() else 0
|
|
688
|
+
try:
|
|
689
|
+
total_size = sum(f.stat().st_size for f in global_home.rglob("*") if f.is_file())
|
|
690
|
+
size_str = f"{total_size / 1024 / 1024:.1f} MB"
|
|
691
|
+
except Exception:
|
|
692
|
+
size_str = "unknown size"
|
|
693
|
+
print(f" • ~/.codevira/ ({project_count} projects, {size_str})")
|
|
694
|
+
actions.append(("Removed ~/.codevira/", lambda: shutil.rmtree(global_home, ignore_errors=True)))
|
|
695
|
+
|
|
696
|
+
# 2. IDE configs
|
|
697
|
+
ide_configs = [
|
|
698
|
+
("Claude Code global", _claude_global_config_path()),
|
|
699
|
+
("Claude Desktop", _claude_desktop_config_path()),
|
|
700
|
+
("Cursor global", _cursor_global_config_path()),
|
|
701
|
+
("Windsurf global", _windsurf_global_config_path()),
|
|
702
|
+
("Antigravity", _antigravity_config_path()),
|
|
703
|
+
]
|
|
704
|
+
for ide_name, config_path in ide_configs:
|
|
705
|
+
if config_path.exists():
|
|
706
|
+
from mcp_server.ide_inject import _read_json_safe
|
|
707
|
+
data = _read_json_safe(config_path)
|
|
708
|
+
servers = data.get("mcpServers", {})
|
|
709
|
+
has_codevira = any(k == "codevira" or k.startswith("codevira-") for k in servers)
|
|
710
|
+
if has_codevira:
|
|
711
|
+
print(f" • {ide_name} config (mcpServers.codevira)")
|
|
712
|
+
actions.append(
|
|
713
|
+
(f"Removed codevira from {ide_name}",
|
|
714
|
+
lambda p=config_path: remove_codevira_from_config(p))
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
# 3. Launchd service
|
|
718
|
+
plist_path = Path.home() / "Library" / "LaunchAgents" / "com.codevira.mcp-serve.plist"
|
|
719
|
+
if plist_path.exists():
|
|
720
|
+
print(" • Launchd service (com.codevira.mcp-serve)")
|
|
721
|
+
def _unload_launchd():
|
|
722
|
+
try:
|
|
723
|
+
from mcp_server.launchd import uninstall_launchd
|
|
724
|
+
uninstall_launchd()
|
|
725
|
+
except Exception:
|
|
726
|
+
plist_path.unlink(missing_ok=True)
|
|
727
|
+
actions.append(("Unloaded launchd service", _unload_launchd))
|
|
728
|
+
|
|
729
|
+
# 4. Server log
|
|
730
|
+
log_path = Path.home() / "Library" / "Logs" / "codevira.log"
|
|
731
|
+
if log_path.exists():
|
|
732
|
+
print(f" • ~/Library/Logs/codevira.log")
|
|
733
|
+
actions.append(("Removed server log", lambda: log_path.unlink(missing_ok=True)))
|
|
734
|
+
|
|
735
|
+
# 5. Per-project artifacts (only with --all)
|
|
736
|
+
if clean_all and global_home.exists():
|
|
737
|
+
projects_dir = global_home / "projects"
|
|
738
|
+
if projects_dir.exists():
|
|
739
|
+
for meta_file in projects_dir.glob("*/metadata.json"):
|
|
740
|
+
try:
|
|
741
|
+
import json
|
|
742
|
+
meta = json.loads(meta_file.read_text())
|
|
743
|
+
project_path = Path(meta.get("original_path", ""))
|
|
744
|
+
if project_path.exists():
|
|
745
|
+
_collect_project_cleanup(project_path, actions)
|
|
746
|
+
except Exception:
|
|
747
|
+
continue
|
|
748
|
+
|
|
749
|
+
if not actions:
|
|
750
|
+
print(" Nothing to clean — Codevira is not installed.")
|
|
751
|
+
print()
|
|
752
|
+
return
|
|
753
|
+
|
|
754
|
+
print()
|
|
755
|
+
|
|
756
|
+
# Confirmation
|
|
757
|
+
if dry_run:
|
|
758
|
+
print(" [dry-run] No changes made.")
|
|
759
|
+
print()
|
|
760
|
+
return
|
|
761
|
+
|
|
762
|
+
if not yes:
|
|
763
|
+
answer = input(" Remove all of the above? [y/N] ").strip().lower()
|
|
764
|
+
if answer != "y":
|
|
765
|
+
print(" Aborted.")
|
|
766
|
+
print()
|
|
767
|
+
return
|
|
768
|
+
|
|
769
|
+
print()
|
|
770
|
+
for label, action in actions:
|
|
771
|
+
try:
|
|
772
|
+
action()
|
|
773
|
+
print(f" {label:<45} done")
|
|
774
|
+
except Exception as e:
|
|
775
|
+
print(f" {label:<45} FAILED ({e})")
|
|
776
|
+
|
|
777
|
+
print()
|
|
778
|
+
print(" ✓ Codevira fully removed.")
|
|
779
|
+
print(" To reinstall: pipx install codevira && codevira register")
|
|
780
|
+
print()
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
def _collect_project_cleanup(project_path: Path, actions: list) -> None:
|
|
784
|
+
"""Collect per-project cleanup actions."""
|
|
785
|
+
import shutil
|
|
786
|
+
from mcp_server.ide_inject import remove_codevira_from_config
|
|
787
|
+
|
|
788
|
+
name = project_path.name
|
|
789
|
+
|
|
790
|
+
# Legacy .codevira/ dir
|
|
791
|
+
legacy = project_path / ".codevira"
|
|
792
|
+
if legacy.exists():
|
|
793
|
+
print(f" • {name}/.codevira/")
|
|
794
|
+
actions.append((f"Removed {name}/.codevira/", lambda p=legacy: shutil.rmtree(p, ignore_errors=True)))
|
|
795
|
+
|
|
796
|
+
# Migration backup
|
|
797
|
+
migrated = project_path / ".codevira.migrated"
|
|
798
|
+
if migrated.exists():
|
|
799
|
+
print(f" • {name}/.codevira.migrated/")
|
|
800
|
+
actions.append((f"Removed {name}/.codevira.migrated/", lambda p=migrated: shutil.rmtree(p, ignore_errors=True)))
|
|
801
|
+
|
|
802
|
+
# Git hook
|
|
803
|
+
hook = project_path / ".git" / "hooks" / "post-commit"
|
|
804
|
+
if hook.exists():
|
|
805
|
+
try:
|
|
806
|
+
content = hook.read_text()
|
|
807
|
+
if "codevira" in content.lower() or "Codevira" in content:
|
|
808
|
+
print(f" • {name}/.git/hooks/post-commit")
|
|
809
|
+
# Check if hook has backup
|
|
810
|
+
backup = hook.with_suffix(".bak")
|
|
811
|
+
if backup.exists():
|
|
812
|
+
actions.append((f"Restored {name} git hook from backup",
|
|
813
|
+
lambda h=hook, b=backup: b.rename(h)))
|
|
814
|
+
else:
|
|
815
|
+
actions.append((f"Removed {name} git hook",
|
|
816
|
+
lambda h=hook: h.unlink(missing_ok=True)))
|
|
817
|
+
except Exception:
|
|
818
|
+
pass
|
|
819
|
+
|
|
820
|
+
# Per-project IDE configs
|
|
821
|
+
for ide_name, config_path in [
|
|
822
|
+
("claude", project_path / ".claude" / "settings.json"),
|
|
823
|
+
("cursor", project_path / ".cursor" / "mcp.json"),
|
|
824
|
+
("windsurf", project_path / ".windsurf" / "mcp.json"),
|
|
825
|
+
]:
|
|
826
|
+
if config_path.exists():
|
|
827
|
+
from mcp_server.ide_inject import _read_json_safe
|
|
828
|
+
data = _read_json_safe(config_path)
|
|
829
|
+
if "codevira" in data.get("mcpServers", {}):
|
|
830
|
+
print(f" • {name}/.{ide_name} config")
|
|
831
|
+
actions.append(
|
|
832
|
+
(f"Removed codevira from {name}/{ide_name}",
|
|
833
|
+
lambda p=config_path: remove_codevira_from_config(p))
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
|
|
621
837
|
if __name__ == "__main__":
|
|
622
838
|
main()
|
|
@@ -93,7 +93,7 @@ def _windsurf_global_config_path() -> Path:
|
|
|
93
93
|
return Path.home() / ".windsurf" / "mcp_config.json"
|
|
94
94
|
|
|
95
95
|
def _antigravity_config_path() -> Path:
|
|
96
|
-
return Path.home() / ".gemini" / "
|
|
96
|
+
return Path.home() / ".gemini" / "antigravity" / "mcp_config.json"
|
|
97
97
|
|
|
98
98
|
|
|
99
99
|
# ---------------------------------------------------------------------------
|
|
@@ -128,6 +128,36 @@ def _merge_mcp_config(existing: dict, server_name: str, server_config: dict) ->
|
|
|
128
128
|
return result
|
|
129
129
|
|
|
130
130
|
|
|
131
|
+
def remove_codevira_from_config(config_path: Path, key_prefix: str = "codevira") -> bool:
|
|
132
|
+
"""Remove all codevira entries from an IDE config file.
|
|
133
|
+
|
|
134
|
+
Deletes keys from mcpServers that match `key_prefix` exactly or start
|
|
135
|
+
with `key_prefix-` (for Antigravity per-project entries like codevira-udap).
|
|
136
|
+
|
|
137
|
+
Returns True if any keys were removed, False if nothing to do.
|
|
138
|
+
"""
|
|
139
|
+
if not config_path.exists():
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
data = _read_json_safe(config_path)
|
|
143
|
+
servers = data.get("mcpServers", {})
|
|
144
|
+
if not servers:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
keys_to_remove = [
|
|
148
|
+
k for k in servers
|
|
149
|
+
if k == key_prefix or k.startswith(f"{key_prefix}-")
|
|
150
|
+
]
|
|
151
|
+
if not keys_to_remove:
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
for k in keys_to_remove:
|
|
155
|
+
del servers[k]
|
|
156
|
+
|
|
157
|
+
_write_json_safe(config_path, data)
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
|
|
131
161
|
# ---------------------------------------------------------------------------
|
|
132
162
|
# Resolve the best command to run codevira
|
|
133
163
|
# ---------------------------------------------------------------------------
|
|
@@ -340,6 +370,21 @@ def inject_global_windsurf(cmd_path: str, python_exe: str) -> str | None:
|
|
|
340
370
|
return str(config_path)
|
|
341
371
|
|
|
342
372
|
|
|
373
|
+
def inject_global_antigravity(cmd_path: str, python_exe: str) -> str | None:
|
|
374
|
+
"""Inject global codevira config into Google Antigravity.
|
|
375
|
+
|
|
376
|
+
Uses a single 'codevira' entry with no project path. Antigravity
|
|
377
|
+
sets the working directory when it starts the MCP server process.
|
|
378
|
+
"""
|
|
379
|
+
config_path = _antigravity_config_path()
|
|
380
|
+
existing = _read_json_safe(config_path)
|
|
381
|
+
base_config = _build_global_server_config(cmd_path, python_exe)
|
|
382
|
+
server_config = {"$typeName": "exa.cascade_plugins_pb.CascadePluginCommandTemplate", **base_config}
|
|
383
|
+
merged = _merge_mcp_config(existing, "codevira", server_config)
|
|
384
|
+
_write_json_safe(config_path, merged)
|
|
385
|
+
return str(config_path)
|
|
386
|
+
|
|
387
|
+
|
|
343
388
|
def inject_claude_http_url(url: str) -> str | None:
|
|
344
389
|
"""Inject HTTP URL config into Claude Code global settings.
|
|
345
390
|
|