Skip to content

v0.2.0: probe install + refresh-before-search#1

Merged
dilawarm merged 24 commits into
mainfrom
feat/0.2.0-smoother-ux
Apr 18, 2026
Merged

v0.2.0: probe install + refresh-before-search#1
dilawarm merged 24 commits into
mainfrom
feat/0.2.0-smoother-ux

Conversation

@dilawarm
Copy link
Copy Markdown
Collaborator

Summary

Two independent features that together make probe feel invisible in Claude Code:

  • probe install / probe uninstall — one command registers probe as a user-scope MCP server via claude mcp add-json --scope user. Resolves the API key from --api-key, ZEROENTROPY_API_KEY env (with confirm), or an interactive 3-retry prompt. Post-registration, also clears probe from any project's disabledMcpServers list in ~/.claude.json so the server is enabled out of the box.
  • Refresh-before-search — before every CLI search and every MCP probe_search call, a two-phase (stat → SHA-256) scan re-indexes only files that actually changed since last index. A RefreshGate (TTL, PROBE_REFRESH_TTL env var, default 5s) debounces back-to-back calls. The probe_search JSON response gains a refreshed field with {added, changed, removed, elapsed_ms}. Refresh failures are non-blocking — search runs against whatever's in the current index.

Version bump: 0.1.00.2.0. DB migration for new files.mtime_ns / files.size columns is automatic and idempotent.

Design + plan (committed to the branch)

Each of the 15 plan tasks was implemented via a fresh subagent + two-stage review (spec compliance then code quality). Two late-breaking bugs were caught and fixed during manual end-to-end validation: claude's -e flag is variadic (eats the server-name positional), and new user-scope MCP servers can land in disabledMcpServers per project.

Test plan

  • Unit + integration: 108 tests pass locally on Python 3.13 (uv run pytest).
  • Lint: ruff clean on src/ and tests/.
  • CI: pending (runs on this PR — Python 3.10/3.11/3.12).
  • Manual e2e: probe install against real claude CLI succeeds; /mcp shows probe enabled; querying an indexed toy project returns results with non-zero refreshed.added on first call.
  • Manual e2e: editing a file between queries shows refreshed.changed > 0 on the next query.
  • Manual e2e: probe uninstall removes the registration.

Known follow-ups (not blockers for 0.2.0)

  • CLI search should auto-create .probe/ on first run for parity with MCP's auto-index path (spec-compliant; UX asymmetry only).
  • refresh_changed() would benefit from an explicit project_root parameter so tests and callers don't depend on Path.cwd() fallback.
  • _claude_mcp(...) helper extraction to dedupe the shutil.which + subprocess.run pattern between install and uninstall.

🤖 Generated with Claude Code

dilawarm added 24 commits April 17, 2026 18:12
Spec covers two 0.2.0 features: a `probe install` command that
registers probe as a user-scope MCP server in Claude Code, and
refresh-before-search that incrementally re-indexes changed files
before each query. Awaiting review before implementation planning.
15-task bottom-up plan covering DB migration, _index_file refactor,
RefreshGate, two-phase refresh_changed, CLI/MCP integration, install &
uninstall commands, README updates, version bump, and manual e2e
validation against /Users/dilawar/tmp/probe-toy.
- Remove unused pytest import; split compound for-loops (ruff F401/E701).
- Document that should_refresh() is not a single-flight guard.
…index)

Hash-confirm each stat candidate: re-index on content change (new or
edited file), update signature only on metadata-only change (touch),
and skip unchanged files entirely. Expand save-gate to cover all
mutation paths. Remove unused old_ids variable in _index_file.
…efresh errors

Move RefreshGate from a per-call local to _ServerState so the debounce window
survives across MCP tool invocations. Time the refresh at the call site so the
error branch reports real elapsed_ms instead of hard-coded 0.
Registers the install subcommand with --api-key, --no-embed-key, and --force
flags. First step of install flow detects whether the claude CLI is on PATH;
exits with helpful message if not found. Remaining install flow (API-key
resolution and MCP registration) will be implemented in Task 9.
…cation

Replaces the placeholder install body with the full flow: check
already-installed via `claude mcp get`, resolve API key (flag / env /
3-retry prompt), resolve probe binary, call `claude mcp add --scope user`.
Adds `import subprocess` and three new tests covering the key-resolution
paths.
…ll paths

Add three tests for existing install behavior:
- test_install_no_embed_key_omits_env: verify -e flag omitted and -- separator precedes probe argv
- test_install_already_registered_cancels_without_force: confirm reinstall prompt defaults to no
- test_install_force_skips_confirmation: verify --force skips confirmation and removes/re-adds

All three behaviors implemented in Task 9 already; tests validate Task 9 logic.
Bump version across pyproject.toml and __init__.py to 0.2.0.
Add CHANGELOG.md with entries for the 0.2.0 release.
Update test_version to expect 0.2.0.
Sorts imports, removes unused pytest/pathlib/yaml/RerankResult imports,
wraps a long MagicMock list onto three lines. No behavior change.
claude mcp add's -e flag is variadic and greedily consumes the positional
server name as another env var. Switching to add-json, which takes a single
JSON config and avoids the ambiguity entirely. Also drop --scope from the
mcp get probe call; get doesn't support that flag.
Removes 'probe' from projects.<path>.disabledMcpServers in ~/.claude.json
after claude mcp add-json succeeds. Narrowly scoped: only removes the
literal 'probe' string from those specific lists; doesn't touch any other
key or project entry. Atomic write via temp + os.replace. Silent no-op
on missing file; yellow warning (no failure) on malformed JSON.
…v hygiene

On CI runners with low system uptime, time.monotonic() can be small enough
that the TTL=60 check in test_probe_search_gate_persists_across_calls
blocks the first refresh call, making the test fail. Patching the clock to
a large constant makes the assertion deterministic.

Also replace os.environ[...] = ... in test_search_calls_refresh_when_gate_allows
with monkeypatch.setenv so the env var doesn't leak across tests.
@dilawarm dilawarm force-pushed the feat/0.2.0-smoother-ux branch from 855de69 to 6c76a43 Compare April 18, 2026 03:59
@dilawarm dilawarm merged commit 382f743 into main Apr 18, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant