codevira 1.7.0__tar.gz → 1.8.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.
- {codevira-1.7.0 → codevira-1.8.0}/CHANGELOG.md +252 -0
- {codevira-1.7.0/codevira.egg-info → codevira-1.8.0}/PKG-INFO +22 -2
- {codevira-1.7.0 → codevira-1.8.0}/README.md +21 -1
- {codevira-1.7.0 → codevira-1.8.0/codevira.egg-info}/PKG-INFO +22 -2
- {codevira-1.7.0 → codevira-1.8.0}/codevira.egg-info/SOURCES.txt +3 -0
- codevira-1.8.0/docs/local-pypi-https.md +184 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/global_db.py +43 -2
- {codevira-1.7.0 → codevira-1.8.0}/indexer/index_codebase.py +84 -1
- {codevira-1.7.0 → codevira-1.8.0}/indexer/sqlite_graph.py +85 -9
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/__init__.py +1 -1
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/auto_init.py +9 -1
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/cli.py +57 -1
- codevira-1.8.0/mcp_server/cli_configure.py +805 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/http_server.py +7 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/migrate.py +7 -2
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/server.py +16 -1
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/learning.py +92 -12
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/search.py +79 -0
- {codevira-1.7.0 → codevira-1.8.0}/pyproject.toml +1 -1
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_auto_init.py +2 -1
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_cli.py +20 -0
- codevira-1.8.0/tests/test_cli_configure.py +1016 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_index_codebase.py +184 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_migrate.py +2 -1
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_sqlite_graph.py +272 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_learning.py +194 -3
- {codevira-1.7.0 → codevira-1.8.0}/LICENSE +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/MANIFEST.in +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/builder.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/developer.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/documenter.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/orchestrator.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/planner.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/reviewer.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/agents/tester.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/codevira.egg-info/dependency_links.txt +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/codevira.egg-info/entry_points.txt +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/codevira.egg-info/requires.txt +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/codevira.egg-info/top_level.txt +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/config.example.yaml +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/docs/how-i-built-persistent-memory-for-ai-agents.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/docs/linkedin-article-ai-agent-memory.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/docs/linkedin-post-ai-agent-memory.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/docs/medium-your-ai-coding-agent-has-amnesia.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/docs/roadmap.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/graph/_schema.yaml +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/__init__.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/chunker.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/graph_generator.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/outcome_tracker.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/rule_learner.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/indexer/treesitter_parser.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/__main__.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/crash_logger.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/__init__.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/builder.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/developer.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/documenter.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/orchestrator.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/planner.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/reviewer.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/agents/tester.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/config.example.yaml +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/coding-standards.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/engineering-excellence.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/git-cicd-governance.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/git_commits.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/incremental-updates.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/master_rule.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/multi-language.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/persistence.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/resilience-observability.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/smoke-testing.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/data/rules/testing-standards.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/detect.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/gitignore.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/global_sync.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/ide_inject.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/launchd.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/log_retention.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/paths.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/prompts.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/__init__.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/changesets.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/code_reader.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/graph.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/playbook.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/mcp_server/tools/roadmap.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/coding-standards.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/engineering-excellence.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/git-cicd-governance.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/git_commits.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/incremental-updates.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/master_rule.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/persistence.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/resilience-observability.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/smoke-testing.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/rules/testing-standards.md +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/setup.cfg +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_chunker.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_crash_logger.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_detect.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_gitignore.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_global_db.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_global_sync.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_graph_generator.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_http_server.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_ide_inject.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_launchd.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_log_retention.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_outcome_tracker.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_paths.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_prompts.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_rule_learner.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_server.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_changesets.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_code_reader.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_graph.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_playbook.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_roadmap.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_tools_search.py +0 -0
- {codevira-1.7.0 → codevira-1.8.0}/tests/test_treesitter_parser.py +0 -0
|
@@ -13,6 +13,258 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|
|
13
13
|
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
## [1.8.0] — 2026-04-23 — Memory Sharpening + Config UX
|
|
17
|
+
|
|
18
|
+
Three internal improvements that make the memory we already capture **sharper**,
|
|
19
|
+
without making it heavier. Zero new MCP tools. Zero new tables. The public API
|
|
20
|
+
shape changes only one thing: `get_session_context()` gains a `focus_source`
|
|
21
|
+
field (~10 tokens, additive, backwards-compatible).
|
|
22
|
+
|
|
23
|
+
The problem this release solves:
|
|
24
|
+
- `get_session_context()` returned the 3 newest decisions by timestamp —
|
|
25
|
+
regardless of whether they had anything to do with the current task.
|
|
26
|
+
- `search_decisions()` ordered purely by recency — a `file_path` match
|
|
27
|
+
was no better than a match buried in an unrelated session summary.
|
|
28
|
+
- `log_session()` inserted every decision unconditionally — a day of
|
|
29
|
+
iterative agent work logged the same intent 5+ times.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- **MCP `serverInfo.version` reported the MCP library version, not codevira's**
|
|
34
|
+
(pre-existing bug, surfaced during v1.8 install verification on Python
|
|
35
|
+
3.13). `Server("codevira")` was constructed without a `version=` argument,
|
|
36
|
+
so the framework defaulted to its own pip-package version (e.g. `1.27.0`)
|
|
37
|
+
in the JSON-RPC `initialize` handshake response. Clients use this field
|
|
38
|
+
for telemetry and version gating, so the wrong value misled them.
|
|
39
|
+
One-line fix: `Server("codevira", version=__version__)`.
|
|
40
|
+
- **`get_session_context()` read the wrong dict key** (pre-existing bug).
|
|
41
|
+
`list_open_changesets()` returns `{"open_changesets": [...], ...}`, but
|
|
42
|
+
`get_session_context` looked for `"changesets"`. The `open_changesets`
|
|
43
|
+
field in the session-context response was **always empty** in production.
|
|
44
|
+
Tests didn't catch it because mocks used the same wrong key.
|
|
45
|
+
|
|
46
|
+
- **`GlobalDB` concurrent-open race condition** (pre-existing bug — latent
|
|
47
|
+
since v1.6's centralized storage introduced shared `~/.codevira/global.db`).
|
|
48
|
+
`PRAGMA journal_mode=WAL` requires an exclusive lock and — unlike normal
|
|
49
|
+
SQL — does NOT honour `sqlite3`'s `busy_timeout`. When multiple processes
|
|
50
|
+
or threads opened the same fresh database concurrently (e.g. several
|
|
51
|
+
projects' first-ever `codevira register` running in parallel, or the
|
|
52
|
+
`global_sync` background export racing the MCP server thread), one or
|
|
53
|
+
more would raise `OperationalError('database is locked')` and silently
|
|
54
|
+
fail to register. The test `test_concurrent_access_from_threads` was
|
|
55
|
+
flaky at 60% failure rate, hinting at the real issue. Fixed with WAL-
|
|
56
|
+
enable retry loop + short-circuit when WAL is already active. Stability
|
|
57
|
+
verified at 20/20 passes across 20 test runs.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- **Focus-weighted `recent_decisions` in `get_session_context()`**. Instead
|
|
62
|
+
of chronological "newest 3", decisions are now ranked by what the agent
|
|
63
|
+
is currently focused on:
|
|
64
|
+
1. Open changeset with `files_pending` → focus = first file path of the
|
|
65
|
+
most-recently-created changeset.
|
|
66
|
+
2. Strong `current_phase.next_action` signal → focus = extracted keywords
|
|
67
|
+
(rejects short or stop-list-only actions like "continue work").
|
|
68
|
+
3. Otherwise → chronological fallback (unchanged behaviour).
|
|
69
|
+
If focus returns fewer than 3, the list pads with `get_recent_decisions()`.
|
|
70
|
+
New response field `focus_source` (`"open_changeset:<id>"`, `"next_action"`,
|
|
71
|
+
or `null`) lets the agent see *why* it got these decisions.
|
|
72
|
+
|
|
73
|
+
- **Smarter `search_decisions()` ranking**. SQL now adds `file_path` to both
|
|
74
|
+
the WHERE clause and a CASE-based ORDER BY:
|
|
75
|
+
`file_path match (0) > decision text (1) > context (2) > summary-only (3)`,
|
|
76
|
+
then newest first within each tier. Searching for `"src/auth.py"` now
|
|
77
|
+
surfaces file-path matches even when the decision text doesn't mention
|
|
78
|
+
the path.
|
|
79
|
+
|
|
80
|
+
- **Decision dedup on write**. `log_session()` now skips a new decision
|
|
81
|
+
if it has a `file_path` and its token-set overlaps ≥ 80% with any of
|
|
82
|
+
the 5 most recent decisions for that same file. The session row is
|
|
83
|
+
always created; only redundant *decisions* are dropped. Short
|
|
84
|
+
decisions (< 3 tokens) and decisions without `file_path` are always
|
|
85
|
+
inserted.
|
|
86
|
+
|
|
87
|
+
### Added
|
|
88
|
+
|
|
89
|
+
- `focus_source` field on `get_session_context()` response (≈10 tokens).
|
|
90
|
+
- `mcp_server.tools.learning._infer_focus()` — pure helper, module-private.
|
|
91
|
+
- `indexer.sqlite_graph._is_duplicate()` — pure token-overlap helper,
|
|
92
|
+
module-private, independently testable.
|
|
93
|
+
- **`codevira configure`** — new CLI subcommand. Scans your project
|
|
94
|
+
(gitignore-aware via existing `discover_source_files()`), shows discovered
|
|
95
|
+
directories and file extensions with counts, lets you pick via a numbered-
|
|
96
|
+
list prompt, writes the choices back to `.codevira/config.yaml`, and offers
|
|
97
|
+
to rebuild the index. Non-interactive:
|
|
98
|
+
`codevira configure --dirs src,lib --extensions .py,.ts --no-reindex`.
|
|
99
|
+
Solves the AgentStore-style "0 chunks indexed" case where
|
|
100
|
+
`auto_detect_project()` mis-guesses a monorepo layout.
|
|
101
|
+
When `config.yaml` is missing (normal state after `codevira register` but
|
|
102
|
+
before the first MCP tool call), `configure` auto-bootstraps it in full
|
|
103
|
+
parity with `auto_init`'s first-init path: writes `metadata.json` (rename-
|
|
104
|
+
resilient lookup via `git_remote`) and registers the project in
|
|
105
|
+
`~/.codevira/global.db` for cross-project intelligence. Missing these on
|
|
106
|
+
earlier drafts would have left the project invisible to rename-resilient
|
|
107
|
+
path lookup and absent from global memory until the first session log.
|
|
108
|
+
- **Zero-chunks safety hint at index time.** When `codevira index --full` or
|
|
109
|
+
`codevira index` (incremental, project-wide) matches no files against your
|
|
110
|
+
`watched_dirs` + `file_extensions`, the indexer now prints a one-line
|
|
111
|
+
remedy pointing at `codevira configure`. Output goes to **stderr** (not
|
|
112
|
+
stdout) so the hint never leaks into the MCP JSON-RPC wire when
|
|
113
|
+
`start_background_full_index` runs during auto_init inside the MCP server
|
|
114
|
+
process. Also logged at WARNING level so background invocations
|
|
115
|
+
(auto-init, launchd watcher) leave a trace regardless of terminal
|
|
116
|
+
capture. Does NOT fire for caller-scoped incremental runs (e.g. the
|
|
117
|
+
`refresh_index` MCP tool targeting a specific file) — zero matches there
|
|
118
|
+
is the caller's choice, not a misconfiguration.
|
|
119
|
+
- `codevira register` success banner now nudges toward `codevira configure`.
|
|
120
|
+
|
|
121
|
+
### Internal
|
|
122
|
+
|
|
123
|
+
- 87 new tests:
|
|
124
|
+
- 34 for v1.8 memory sharpening (focus inference priority rules, ranking
|
|
125
|
+
tier ordering, dedup threshold behaviour, session-row-always-created
|
|
126
|
+
invariant, NULL file_path fallback, session_id filtering + new ranking SQL)
|
|
127
|
+
- 43 for `codevira configure` (scan_project with centralized-mode
|
|
128
|
+
decoupling + skip_dirs honoring, multi-select prompt incl. non-TTY
|
|
129
|
+
fallback + Ctrl+C clean-abort, config writer preserve/dedupe/idempotency,
|
|
130
|
+
orchestrator edge cases incl. bootstrap on missing config, dry-run disk
|
|
131
|
+
safety, corrupt-YAML handling, empty-extensions safety, PermissionError
|
|
132
|
+
friendly wrapper, `--dirs`/`--extensions` normalization)
|
|
133
|
+
- 10 for the zero-chunks hint (unit tests of the helper proving it writes
|
|
134
|
+
to stderr not stdout + integration tests proving it fires ONLY for full
|
|
135
|
+
or project-wide-incremental scans, not caller-scoped or normal "no files
|
|
136
|
+
changed")
|
|
137
|
+
- Full test suite: **1,398 passing, 0 deterministic failures** (up from
|
|
138
|
+
1,306 at v1.7.1 → +92). The two "pre-existing watchdog failures" that
|
|
139
|
+
haunted earlier drafts of this CHANGELOG turned out to be an environment
|
|
140
|
+
issue in a single dev machine (system Python 3.9 without `watchdog`);
|
|
141
|
+
the pipx-installed v1.8.0 environment has all required deps. The one
|
|
142
|
+
pre-existing flaky test (`test_concurrent_access_from_threads`) is now
|
|
143
|
+
fixed by the `GlobalDB` WAL-enable retry loop described above.
|
|
144
|
+
|
|
145
|
+
### Verified environments
|
|
146
|
+
|
|
147
|
+
- **macOS (APFS)** + Python 3.9 system + Python 3.11 pipx: full regression
|
|
148
|
+
passes; all interactive + non-interactive flows manually verified on three
|
|
149
|
+
real projects (AgentStore, UDAP, ToolsConnector).
|
|
150
|
+
- **Cross-process + thread concurrency**: stress-tested (12 threads × 20
|
|
151
|
+
writes, 8 subprocesses × 25 writes, 100 concurrent-read/write cycles) —
|
|
152
|
+
0 errors, 0 data loss.
|
|
153
|
+
|
|
154
|
+
### Unverified environments / known gaps (candidates for v1.8.1 or v1.9)
|
|
155
|
+
|
|
156
|
+
- **Windows**: `os.replace` atomicity weakens when the destination is open
|
|
157
|
+
by another process. If a Windows user has Claude Code reading
|
|
158
|
+
`config.yaml` at the moment `codevira configure` writes it, the write
|
|
159
|
+
may fail with `PermissionError`. Pre-existing risk; v1.8 does not fix
|
|
160
|
+
and does not regress. Windows smoke-testing is a v1.9 scope item.
|
|
161
|
+
- **Network filesystems (NFS, SMB)**: atomic-replace guarantees are weaker
|
|
162
|
+
on network FS. Unlikely in solo-dev environments (codevira's target);
|
|
163
|
+
possible in enterprise setups.
|
|
164
|
+
- **Python 3.10, 3.12**: The APIs `codevira configure` uses are stable
|
|
165
|
+
across 3.10+. Syntax-verified against 3.10+. **Python 3.13.7
|
|
166
|
+
empirically verified** during v1.8 install validation (full pipx
|
|
167
|
+
install + MCP handshake working). 3.10 and 3.12 are syntax-verified
|
|
168
|
+
only. CI on all Python versions is a v1.8.x task.
|
|
169
|
+
- **Case-insensitive filesystem slugs**: On macOS APFS (default), paths
|
|
170
|
+
differing only in case (`~/Documents` vs `~/documents`) produce
|
|
171
|
+
different slugs for the same physical directory, creating split state.
|
|
172
|
+
Pre-existing since v1.5 — fixing requires a migration step for existing
|
|
173
|
+
users and is scoped to v1.9.
|
|
174
|
+
- **Interactive TTY automated coverage**: The interactive prompt flow is
|
|
175
|
+
tested via mocked stdin + `sys.stdin.isatty`. A real terminal session
|
|
176
|
+
was manually verified during development; automated TTY testing (via
|
|
177
|
+
pexpect or similar) is a v1.8.x nice-to-have.
|
|
178
|
+
- **MCP client post-upgrade reload**: `codevira register` writes config;
|
|
179
|
+
each MCP client (Claude Code, Cursor, Windsurf, Antigravity, Claude
|
|
180
|
+
Desktop) needs to reload to see changes. Verified for Claude Code.
|
|
181
|
+
Other clients may have edge cases that surface post-release.
|
|
182
|
+
|
|
183
|
+
### Known test flake (NOT v1.8; pre-existing)
|
|
184
|
+
|
|
185
|
+
- `test_chunk_error_continues_to_next_file` fails ~3/10 times in the full
|
|
186
|
+
suite on Python 3.9 (system) due to a chromadb+pydantic version
|
|
187
|
+
incompatibility raising `TypeError` during `import chromadb`, which
|
|
188
|
+
`_check_search_deps()` doesn't catch (it only catches `ImportError`).
|
|
189
|
+
**Not introduced by v1.8 and not touched by v1.8 code paths** — verified
|
|
190
|
+
by measuring the same 3/10 flake rate on clean v1.7.1. v1.8 deliberately
|
|
191
|
+
does not widen the exception catch because it would silently mask real
|
|
192
|
+
dep issues; a proper fix belongs in a targeted follow-up PR with its own
|
|
193
|
+
test coverage. Does not affect production users — the condition requires
|
|
194
|
+
a specific dev environment (Py 3.9 + mismatched chromadb/pydantic).
|
|
195
|
+
- Regression guards added by the binocular review pass:
|
|
196
|
+
- `test_centralized_mode_data_dir_and_project_root_decoupled` — catches
|
|
197
|
+
the production bug where `data_dir.parent` was used where
|
|
198
|
+
`get_project_root()` was required (centralized mode v1.6+).
|
|
199
|
+
- `test_bootstraps_config_when_missing` — catches the workflow where
|
|
200
|
+
`codevira register` was run but config.yaml hasn't been written yet
|
|
201
|
+
(auto_init hadn't fired because no MCP tool call had happened).
|
|
202
|
+
- `test_bootstrap_respects_dry_run` — catches bootstrap writing disk
|
|
203
|
+
during `--dry-run`.
|
|
204
|
+
- `test_fires_on_stderr_when_not_quiet` — catches zero-chunks hint
|
|
205
|
+
leaking to stdout, which would corrupt the MCP JSON-RPC wire in stdio
|
|
206
|
+
mode.
|
|
207
|
+
- `test_empty_extensions_non_interactive_errors_exit_2` — catches
|
|
208
|
+
`--extensions ""` being silently accepted, which would write an empty
|
|
209
|
+
`file_extensions: []` and re-create the zero-chunks bug.
|
|
210
|
+
- `test_ctrl_c_in_prompt_returns_exit_0` — catches KeyboardInterrupt
|
|
211
|
+
propagating a traceback to the user.
|
|
212
|
+
- `test_permission_error_on_write_exits_1` — catches PermissionError /
|
|
213
|
+
OSError propagating a traceback when config.yaml isn't writable.
|
|
214
|
+
- `test_honors_user_skip_dirs_from_config` — catches scan_project
|
|
215
|
+
ignoring the user's explicit skip_dirs in config.yaml.
|
|
216
|
+
|
|
217
|
+
### Known limitations
|
|
218
|
+
|
|
219
|
+
- A running file-watcher or live MCP server session won't pick up config
|
|
220
|
+
changes until it restarts (the watcher snapshots `watched_dirs` at boot).
|
|
221
|
+
Restart your AI tool after `codevira configure` to apply changes.
|
|
222
|
+
- `yaml.safe_dump` doesn't preserve comments in `config.yaml`. First-time
|
|
223
|
+
configs are auto-generated and have no comments; users who hand-edited
|
|
224
|
+
may see formatting normalized after `codevira configure` rewrites the file.
|
|
225
|
+
|
|
226
|
+
### Unchanged (intentionally)
|
|
227
|
+
|
|
228
|
+
- No new MCP tools. No new tables. No schema migration.
|
|
229
|
+
- `search_decisions()` method signature unchanged.
|
|
230
|
+
- `log_session()` method signature unchanged.
|
|
231
|
+
- `get_session_context()` keys are additive — no removals.
|
|
232
|
+
- `auto_init.py`, `detect.py`, `gitignore.py`, and `metadata.json` writer
|
|
233
|
+
untouched; `codevira configure` reuses all existing detection machinery.
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## [1.7.1] — 2026-04-22 — Search Timeout Fix & Version Display
|
|
238
|
+
|
|
239
|
+
Two small but user-visible fixes on top of v1.7.0.
|
|
240
|
+
|
|
241
|
+
### Fixed
|
|
242
|
+
|
|
243
|
+
- **`search_codebase` timeout on first call** (reported by a user testing
|
|
244
|
+
on Antigravity). The embedding model (`all-MiniLM-L6-v2`) was being
|
|
245
|
+
instantiated fresh on every MCP tool call, which triggered a ~90MB
|
|
246
|
+
download + PyTorch init on first ever use (30-60s on slow networks)
|
|
247
|
+
and 1-3s of re-init overhead on every subsequent call. Antigravity's
|
|
248
|
+
~30s MCP tool timeout killed the query before the model finished loading.
|
|
249
|
+
|
|
250
|
+
Three-layer fix:
|
|
251
|
+
1. Module-level cache for the chroma client + embedding function,
|
|
252
|
+
keyed by `db_dir`. Subsequent calls are now instant.
|
|
253
|
+
2. Background `prewarm_embedding_model()` spawned at server startup
|
|
254
|
+
(both stdio and HTTP transports). Model loads in parallel with
|
|
255
|
+
the MCP handshake window.
|
|
256
|
+
3. Cold-path timeout guard: if a query arrives while warmup is still
|
|
257
|
+
in progress, returns `{"status": "warming", ...}` within 10 seconds
|
|
258
|
+
instead of blocking until the MCP timeout fires. The agent gets a
|
|
259
|
+
clean retryable response.
|
|
260
|
+
|
|
261
|
+
- **`codevira register` banner showed hardcoded `v1.6`** after upgrading
|
|
262
|
+
to v1.7.0. Now reads `mcp_server.__version__` dynamically. Same fix
|
|
263
|
+
applied to `metadata.json` version field written during auto-init and
|
|
264
|
+
migration.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
16
268
|
## [1.7.0] — 2026-04-18 — Token Efficiency & AI-First Tool Design
|
|
17
269
|
|
|
18
270
|
**The biggest release since v1.0.** We realized Codevira was dumping 15k-60k
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: codevira
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
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
|
|
@@ -44,7 +44,7 @@ Provides-Extra: all
|
|
|
44
44
|
[](https://www.python.org/)
|
|
45
45
|
[](LICENSE)
|
|
46
46
|
[](https://modelcontextprotocol.io)
|
|
47
|
-
[](CHANGELOG.md)
|
|
48
48
|
[](CONTRIBUTING.md)
|
|
49
49
|
|
|
50
50
|
**Built for solo developers** working on local projects with AI agents. Codevira gives every AI tool you use access to the same persistent project memory — so you stop re-explaining your codebase every session, stop losing carefully-made decisions, and stop burning tokens on re-discovery.
|
|
@@ -142,6 +142,24 @@ Ask your AI agent to call `get_roadmap()` — it should return your current phas
|
|
|
142
142
|
|
|
143
143
|
> **Note:** Restart your AI tool after running `codevira register` to pick up the new MCP config.
|
|
144
144
|
|
|
145
|
+
### Customizing what's indexed
|
|
146
|
+
|
|
147
|
+
Codevira tries to auto-detect your project's source layout, but monorepo or non-standard layouts sometimes slip through — you'll notice when `codevira index --full` reports `0 chunks indexed` and prints a hint pointing you here.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
cd your-project
|
|
151
|
+
codevira configure
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Scans your project (gitignore-aware), shows discovered directories and extensions with file counts, and lets you pick which to watch via a numbered-list prompt. It writes your choices back to `.codevira/config.yaml` and offers to rebuild the index.
|
|
155
|
+
|
|
156
|
+
**Non-interactive** (useful in scripts or CI):
|
|
157
|
+
```bash
|
|
158
|
+
codevira configure --dirs src,packages,apps --extensions .py,.ts,.tsx --no-reindex
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
After changing watched directories, **restart your AI tool** — running watchers snapshot the dir set at boot.
|
|
162
|
+
|
|
145
163
|
### Manual config (only if auto-inject didn't detect your tool)
|
|
146
164
|
|
|
147
165
|
Codevira supports two transports. Use the right one for your client:
|
|
@@ -570,6 +588,8 @@ Contributions are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) for the full
|
|
|
570
588
|
**Requesting a feature?** [Open a feature request](https://github.com/sachinshelke/codevira/issues/new?template=feature_request.md)
|
|
571
589
|
**Found a security issue?** Read [SECURITY.md](SECURITY.md) — please don't use public issues for vulnerabilities.
|
|
572
590
|
|
|
591
|
+
**Testing a release candidate locally?** See [docs/local-pypi-https.md](docs/local-pypi-https.md) for setting up a Docker-based HTTPS PyPI registry that mirrors the real PyPI install flow without touching public PyPI.
|
|
592
|
+
|
|
573
593
|
---
|
|
574
594
|
|
|
575
595
|
## FAQ
|
|
@@ -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
|
**Built for solo developers** working on local projects with AI agents. Codevira gives every AI tool you use access to the same persistent project memory — so you stop re-explaining your codebase every session, stop losing carefully-made decisions, and stop burning tokens on re-discovery.
|
|
@@ -103,6 +103,24 @@ Ask your AI agent to call `get_roadmap()` — it should return your current phas
|
|
|
103
103
|
|
|
104
104
|
> **Note:** Restart your AI tool after running `codevira register` to pick up the new MCP config.
|
|
105
105
|
|
|
106
|
+
### Customizing what's indexed
|
|
107
|
+
|
|
108
|
+
Codevira tries to auto-detect your project's source layout, but monorepo or non-standard layouts sometimes slip through — you'll notice when `codevira index --full` reports `0 chunks indexed` and prints a hint pointing you here.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
cd your-project
|
|
112
|
+
codevira configure
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Scans your project (gitignore-aware), shows discovered directories and extensions with file counts, and lets you pick which to watch via a numbered-list prompt. It writes your choices back to `.codevira/config.yaml` and offers to rebuild the index.
|
|
116
|
+
|
|
117
|
+
**Non-interactive** (useful in scripts or CI):
|
|
118
|
+
```bash
|
|
119
|
+
codevira configure --dirs src,packages,apps --extensions .py,.ts,.tsx --no-reindex
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
After changing watched directories, **restart your AI tool** — running watchers snapshot the dir set at boot.
|
|
123
|
+
|
|
106
124
|
### Manual config (only if auto-inject didn't detect your tool)
|
|
107
125
|
|
|
108
126
|
Codevira supports two transports. Use the right one for your client:
|
|
@@ -531,6 +549,8 @@ Contributions are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) for the full
|
|
|
531
549
|
**Requesting a feature?** [Open a feature request](https://github.com/sachinshelke/codevira/issues/new?template=feature_request.md)
|
|
532
550
|
**Found a security issue?** Read [SECURITY.md](SECURITY.md) — please don't use public issues for vulnerabilities.
|
|
533
551
|
|
|
552
|
+
**Testing a release candidate locally?** See [docs/local-pypi-https.md](docs/local-pypi-https.md) for setting up a Docker-based HTTPS PyPI registry that mirrors the real PyPI install flow without touching public PyPI.
|
|
553
|
+
|
|
534
554
|
---
|
|
535
555
|
|
|
536
556
|
## FAQ
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: codevira
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
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
|
|
@@ -44,7 +44,7 @@ Provides-Extra: all
|
|
|
44
44
|
[](https://www.python.org/)
|
|
45
45
|
[](LICENSE)
|
|
46
46
|
[](https://modelcontextprotocol.io)
|
|
47
|
-
[](CHANGELOG.md)
|
|
48
48
|
[](CONTRIBUTING.md)
|
|
49
49
|
|
|
50
50
|
**Built for solo developers** working on local projects with AI agents. Codevira gives every AI tool you use access to the same persistent project memory — so you stop re-explaining your codebase every session, stop losing carefully-made decisions, and stop burning tokens on re-discovery.
|
|
@@ -142,6 +142,24 @@ Ask your AI agent to call `get_roadmap()` — it should return your current phas
|
|
|
142
142
|
|
|
143
143
|
> **Note:** Restart your AI tool after running `codevira register` to pick up the new MCP config.
|
|
144
144
|
|
|
145
|
+
### Customizing what's indexed
|
|
146
|
+
|
|
147
|
+
Codevira tries to auto-detect your project's source layout, but monorepo or non-standard layouts sometimes slip through — you'll notice when `codevira index --full` reports `0 chunks indexed` and prints a hint pointing you here.
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
cd your-project
|
|
151
|
+
codevira configure
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Scans your project (gitignore-aware), shows discovered directories and extensions with file counts, and lets you pick which to watch via a numbered-list prompt. It writes your choices back to `.codevira/config.yaml` and offers to rebuild the index.
|
|
155
|
+
|
|
156
|
+
**Non-interactive** (useful in scripts or CI):
|
|
157
|
+
```bash
|
|
158
|
+
codevira configure --dirs src,packages,apps --extensions .py,.ts,.tsx --no-reindex
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
After changing watched directories, **restart your AI tool** — running watchers snapshot the dir set at boot.
|
|
162
|
+
|
|
145
163
|
### Manual config (only if auto-inject didn't detect your tool)
|
|
146
164
|
|
|
147
165
|
Codevira supports two transports. Use the right one for your client:
|
|
@@ -570,6 +588,8 @@ Contributions are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) for the full
|
|
|
570
588
|
**Requesting a feature?** [Open a feature request](https://github.com/sachinshelke/codevira/issues/new?template=feature_request.md)
|
|
571
589
|
**Found a security issue?** Read [SECURITY.md](SECURITY.md) — please don't use public issues for vulnerabilities.
|
|
572
590
|
|
|
591
|
+
**Testing a release candidate locally?** See [docs/local-pypi-https.md](docs/local-pypi-https.md) for setting up a Docker-based HTTPS PyPI registry that mirrors the real PyPI install flow without touching public PyPI.
|
|
592
|
+
|
|
573
593
|
---
|
|
574
594
|
|
|
575
595
|
## FAQ
|
|
@@ -20,6 +20,7 @@ codevira.egg-info/top_level.txt
|
|
|
20
20
|
docs/how-i-built-persistent-memory-for-ai-agents.md
|
|
21
21
|
docs/linkedin-article-ai-agent-memory.md
|
|
22
22
|
docs/linkedin-post-ai-agent-memory.md
|
|
23
|
+
docs/local-pypi-https.md
|
|
23
24
|
docs/medium-your-ai-coding-agent-has-amnesia.md
|
|
24
25
|
docs/roadmap.md
|
|
25
26
|
graph/_schema.yaml
|
|
@@ -36,6 +37,7 @@ mcp_server/__init__.py
|
|
|
36
37
|
mcp_server/__main__.py
|
|
37
38
|
mcp_server/auto_init.py
|
|
38
39
|
mcp_server/cli.py
|
|
40
|
+
mcp_server/cli_configure.py
|
|
39
41
|
mcp_server/crash_logger.py
|
|
40
42
|
mcp_server/detect.py
|
|
41
43
|
mcp_server/gitignore.py
|
|
@@ -89,6 +91,7 @@ rules/testing-standards.md
|
|
|
89
91
|
tests/test_auto_init.py
|
|
90
92
|
tests/test_chunker.py
|
|
91
93
|
tests/test_cli.py
|
|
94
|
+
tests/test_cli_configure.py
|
|
92
95
|
tests/test_crash_logger.py
|
|
93
96
|
tests/test_detect.py
|
|
94
97
|
tests/test_gitignore.py
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Local PyPI with HTTPS — for private-registry testing and IDE integration
|
|
2
|
+
|
|
3
|
+
This guide sets up a **local HTTPS-secured PyPI registry** using Docker, so
|
|
4
|
+
you can:
|
|
5
|
+
|
|
6
|
+
- Smoke-test a release candidate by uploading to a private registry and
|
|
7
|
+
reinstalling `pipx install codevira` from it (exactly the flow a real
|
|
8
|
+
user will hit, just scoped to your machine).
|
|
9
|
+
- Serve `codevira` packages to team machines or CI over HTTPS.
|
|
10
|
+
- Test HTTPS-aware install flows before pushing to public PyPI.
|
|
11
|
+
|
|
12
|
+
The stack is two containers on a shared docker network:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
┌──────────────────────────┐ ┌────────────────────────────┐
|
|
16
|
+
│ nginx:alpine │ │ pypiserver/pypiserver │
|
|
17
|
+
│ :8443 (HTTPS, self-sig)│ ───▶ │ :8080 (plain HTTP) │
|
|
18
|
+
│ reverse proxy │ │ htpasswd auth on uploads │
|
|
19
|
+
└──────────────────────────┘ └────────────────────────────┘
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The self-signed cert is fine for local testing. For LAN/team use, replace
|
|
23
|
+
it with a real cert from your own CA or Let's Encrypt via DNS-01.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## One-time setup
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
BASE=~/.codevira-local-pypi
|
|
31
|
+
mkdir -p "$BASE/packages" "$BASE/certs"
|
|
32
|
+
|
|
33
|
+
# 1. Create test credentials (replace 'testuser/testpass' for real use)
|
|
34
|
+
htpasswd -cb "$BASE/htpasswd" testuser testpass
|
|
35
|
+
|
|
36
|
+
# 2. Generate a self-signed cert for localhost
|
|
37
|
+
openssl req -x509 -newkey rsa:4096 -nodes \
|
|
38
|
+
-keyout "$BASE/certs/key.pem" -out "$BASE/certs/cert.pem" \
|
|
39
|
+
-days 365 \
|
|
40
|
+
-subj "/CN=localhost" \
|
|
41
|
+
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1"
|
|
42
|
+
|
|
43
|
+
# 3. Write an nginx config that reverse-proxies HTTPS:8443 → pypi:8080
|
|
44
|
+
cat > "$BASE/nginx.conf" <<'CONF'
|
|
45
|
+
events { worker_connections 1024; }
|
|
46
|
+
http {
|
|
47
|
+
server {
|
|
48
|
+
listen 443 ssl;
|
|
49
|
+
server_name localhost;
|
|
50
|
+
ssl_certificate /etc/nginx/certs/cert.pem;
|
|
51
|
+
ssl_certificate_key /etc/nginx/certs/key.pem;
|
|
52
|
+
client_max_body_size 50M;
|
|
53
|
+
|
|
54
|
+
location / {
|
|
55
|
+
proxy_pass http://pypi:8080;
|
|
56
|
+
proxy_set_header Host $host;
|
|
57
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
58
|
+
proxy_set_header X-Forwarded-Proto https;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
CONF
|
|
63
|
+
|
|
64
|
+
# 4. Create a shared network so nginx can resolve 'pypi' by name
|
|
65
|
+
docker network create codevira-pypi-net
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Start the stack
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
BASE=~/.codevira-local-pypi
|
|
72
|
+
|
|
73
|
+
# pypiserver (name it 'pypi' so nginx can resolve it)
|
|
74
|
+
docker run -d \
|
|
75
|
+
--name pypi \
|
|
76
|
+
--network codevira-pypi-net \
|
|
77
|
+
-p 8080:8080 \
|
|
78
|
+
-v "$BASE/packages:/data/packages" \
|
|
79
|
+
-v "$BASE/htpasswd:/data/.htpasswd:ro" \
|
|
80
|
+
pypiserver/pypiserver:latest \
|
|
81
|
+
run \
|
|
82
|
+
--passwords /data/.htpasswd \
|
|
83
|
+
--authenticate update \
|
|
84
|
+
--overwrite \
|
|
85
|
+
/data/packages
|
|
86
|
+
|
|
87
|
+
# nginx HTTPS reverse proxy on 8443
|
|
88
|
+
docker run -d \
|
|
89
|
+
--name codevira-pypi-nginx \
|
|
90
|
+
--network codevira-pypi-net \
|
|
91
|
+
-p 8443:443 \
|
|
92
|
+
-v "$BASE/certs:/etc/nginx/certs:ro" \
|
|
93
|
+
-v "$BASE/nginx.conf:/etc/nginx/nginx.conf:ro" \
|
|
94
|
+
nginx:alpine
|
|
95
|
+
|
|
96
|
+
# Health check (the -k is OK — we know the cert is self-signed)
|
|
97
|
+
curl -sk https://localhost:8443/ | head -3
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Publish a release to your local registry
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
cd /path/to/agent-mcp
|
|
104
|
+
|
|
105
|
+
# 1. Build the distribution artifacts
|
|
106
|
+
rm -rf dist/ build/ *.egg-info
|
|
107
|
+
python3 -m build
|
|
108
|
+
|
|
109
|
+
# 2. Upload via HTTPS
|
|
110
|
+
TWINE_USERNAME=testuser TWINE_PASSWORD=testpass twine upload \
|
|
111
|
+
--repository-url https://localhost:8443 \
|
|
112
|
+
--cert ~/.codevira-local-pypi/certs/cert.pem \
|
|
113
|
+
dist/codevira-1.8.0-py3-none-any.whl \
|
|
114
|
+
dist/codevira-1.8.0.tar.gz
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Install from the local registry
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pipx uninstall codevira # remove any previous install
|
|
121
|
+
|
|
122
|
+
pipx install codevira==1.8.0 \
|
|
123
|
+
--python python3.11 \
|
|
124
|
+
--index-url "https://testuser:testpass@localhost:8443/simple/" \
|
|
125
|
+
--pip-args="--trusted-host localhost --cert $HOME/.codevira-local-pypi/certs/cert.pem"
|
|
126
|
+
|
|
127
|
+
# Verify
|
|
128
|
+
codevira --help
|
|
129
|
+
python3 -c "import mcp_server; print(mcp_server.__version__)"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Cleanup
|
|
133
|
+
|
|
134
|
+
Stop and remove containers + network when done:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
docker rm -f codevira-pypi-nginx pypi
|
|
138
|
+
docker network rm codevira-pypi-net
|
|
139
|
+
# Optional — delete everything:
|
|
140
|
+
rm -rf ~/.codevira-local-pypi
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Common gotchas
|
|
146
|
+
|
|
147
|
+
- **`--skip-existing` not supported.** `pypiserver` doesn't implement this
|
|
148
|
+
twine flag. Either delete the old wheel from `~/.codevira-local-pypi/
|
|
149
|
+
packages/` or set `--overwrite` at container start (as above) so
|
|
150
|
+
re-uploads of the same version replace the existing file.
|
|
151
|
+
|
|
152
|
+
- **Self-signed cert trust.** Every client that hits the registry needs to
|
|
153
|
+
trust `~/.codevira-local-pypi/certs/cert.pem`. `twine` takes
|
|
154
|
+
`--cert <path>`, `pip` takes `--cert <path>` plus `--trusted-host
|
|
155
|
+
localhost`. For system-wide trust on macOS you can add it to the
|
|
156
|
+
Keychain, but for per-command flags it's simpler to pass `--cert` each
|
|
157
|
+
time.
|
|
158
|
+
|
|
159
|
+
- **Port 8443 already in use.** Change the host port in the `-p
|
|
160
|
+
8443:443` mapping. The container-side port stays `443` (inside nginx).
|
|
161
|
+
|
|
162
|
+
- **Adding `codevira` to an IDE/app that requires HTTPS.** Most MCP clients
|
|
163
|
+
(Claude Code, Cursor, Windsurf, Antigravity) use **stdio transport by
|
|
164
|
+
default**, not HTTPS — stdio needs no URL at all, just the `codevira`
|
|
165
|
+
binary path. `codevira register` sets this up for you. HTTPS transport
|
|
166
|
+
is only needed for the preview `codevira serve --https` multi-project
|
|
167
|
+
server mode (see v1.7 HTTPS-preview docs). If your client is asking for
|
|
168
|
+
an HTTPS URL, that's the serve path — different from this local-PyPI
|
|
169
|
+
guide, which covers **package distribution**.
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Why HTTP isn't enough for team/CI use
|
|
174
|
+
|
|
175
|
+
Local HTTP works fine for solo smoke-testing on a single machine, but:
|
|
176
|
+
|
|
177
|
+
- `pip` in modern versions warns on `--index-url http://...` and some
|
|
178
|
+
environments block plain-HTTP index URLs entirely.
|
|
179
|
+
- When team members on other machines `pipx install codevira` from your
|
|
180
|
+
registry, they expect HTTPS.
|
|
181
|
+
- Corporate proxies often block plain-HTTP traffic.
|
|
182
|
+
|
|
183
|
+
The self-signed-cert setup above is a one-command step up from HTTP and
|
|
184
|
+
gives you a realistic distribution flow without touching public PyPI.
|
|
@@ -21,12 +21,53 @@ class GlobalDB:
|
|
|
21
21
|
def __init__(self, db_path: str | Path):
|
|
22
22
|
self.db_path = Path(db_path)
|
|
23
23
|
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
24
|
-
|
|
24
|
+
# 30s timeout (up from 5s): handles later-write contention under load.
|
|
25
|
+
self.conn = sqlite3.connect(str(self.db_path), timeout=30)
|
|
25
26
|
self.conn.row_factory = sqlite3.Row
|
|
26
|
-
|
|
27
|
+
# Enable WAL with retries. `PRAGMA journal_mode=WAL` requires an
|
|
28
|
+
# exclusive lock and — unlike normal SQL — does NOT honour the
|
|
29
|
+
# `busy_timeout`. When multiple threads/processes open the same fresh
|
|
30
|
+
# database file concurrently, they all race to flip the journal mode
|
|
31
|
+
# and some raise `OperationalError('database is locked')`. Skip the
|
|
32
|
+
# PRAGMA if WAL is already the effective mode, otherwise retry with
|
|
33
|
+
# short backoff. After ~1s we give up and fall through — the DB still
|
|
34
|
+
# works in the default rollback-journal mode.
|
|
35
|
+
self._enable_wal_with_retry()
|
|
27
36
|
self.conn.execute("PRAGMA foreign_keys=ON")
|
|
37
|
+
# SQLite-level busy timeout for subsequent writes (complements the
|
|
38
|
+
# `timeout=30` on the connect — matters for later transactions).
|
|
39
|
+
self.conn.execute("PRAGMA busy_timeout=30000")
|
|
28
40
|
self._init_schema()
|
|
29
41
|
|
|
42
|
+
def _enable_wal_with_retry(self, attempts: int = 10, initial_delay: float = 0.02) -> None:
|
|
43
|
+
"""Best-effort enable of WAL journal mode.
|
|
44
|
+
|
|
45
|
+
Survives concurrent-open races by short-backoff retry and by
|
|
46
|
+
short-circuiting when WAL is already active (in which case every
|
|
47
|
+
other connection sees it too — no PRAGMA needed).
|
|
48
|
+
"""
|
|
49
|
+
import time as _time
|
|
50
|
+
try:
|
|
51
|
+
mode = self.conn.execute("PRAGMA journal_mode").fetchone()[0]
|
|
52
|
+
if str(mode).lower() == "wal":
|
|
53
|
+
return # already WAL — nothing to do
|
|
54
|
+
except sqlite3.OperationalError:
|
|
55
|
+
pass # fall through to the retry loop
|
|
56
|
+
delay = initial_delay
|
|
57
|
+
for _ in range(attempts):
|
|
58
|
+
try:
|
|
59
|
+
self.conn.execute("PRAGMA journal_mode=WAL")
|
|
60
|
+
return
|
|
61
|
+
except sqlite3.OperationalError as e:
|
|
62
|
+
if "locked" not in str(e).lower():
|
|
63
|
+
raise
|
|
64
|
+
_time.sleep(delay)
|
|
65
|
+
delay = min(delay * 1.5, 0.2)
|
|
66
|
+
# Last-resort: continue in the default journal mode. Non-fatal —
|
|
67
|
+
# concurrent writers are still serialized, just without WAL's
|
|
68
|
+
# reader-during-writer benefit.
|
|
69
|
+
logger.warning("Could not enable WAL on %s after retries; continuing in default mode", self.db_path)
|
|
70
|
+
|
|
30
71
|
def _init_schema(self) -> None:
|
|
31
72
|
self.conn.executescript("""
|
|
32
73
|
CREATE TABLE IF NOT EXISTS projects (
|