Skip to content

Latest commit

 

History

History
192 lines (153 loc) · 8.45 KB

TELEMETRY.md

File metadata and controls

192 lines (153 loc) · 8.45 KB

Telemetry

Repowise collects completely anonymous usage telemetry to help us decide what to build and fix. It is impossible to tie any event to you, your machine, or your code. There are no usernames, no IP addresses, no file paths, and no source content anywhere in what we send.

This page documents exactly what is collected, what is never collected, how to verify it yourself, and how to turn it off.

Why we collect it

A tiny amount of anonymous data answers questions we otherwise have to guess at:

  • Which commands do people actually run? (where to invest)
  • Which CLI versions and Python versions are in use? (what we can drop support for)
  • What share of runs fail, and on which command? (where the bugs are)
  • Which OSes matter? (what to test on)
  • Which MCP tools do coding agents reach for, and how often do answers come back stale? (where the retrieval work pays off)

That is the whole purpose. We do not sell it, and we do not use it to identify anyone.

What is collected

Every event, whatever its type, carries the same anonymous envelope:

Field Example Purpose
event command_run / mcp_tool_call Event type
anon_id 194639…d48a Random per-install id (see below)
session_id e2ecb2…1158 Random per-process id, groups one run
cli_version 0.21.0 Version adoption
os darwin / linux / windows Platform priorities
arch arm64 / x86_64 Architecture priorities
python_version 3.12 major.minor only
is_ci true / false Separate automation from humans

Every event's properties.client names the host that ran repowise, as one fixed label: claude_code, codex, cursor, vscode, ci or terminal. It comes from which host-specific environment variables are set (never their values), from a hook's --client flag, or for an MCP tool call from the name the client announces when it connects, which can also name another supported agent such as opencode (unknown when nothing matches).

command_run — once per CLI invocation

Field Example Purpose
properties.command init, update, health Which command ran
properties.subcommand decision.add, telemetry.status A known subcommand name only
properties.flags ["--resume", "--provider"] Option names only, never values
properties.status ok / error / usage_error / interrupted Success, failure, a mis-invocation (bad/unknown flag), or user-cancelled (Ctrl-C)
properties.error_type LLMProviderError Exception class name only
properties.duration_ms 71840 Performance in the wild
properties.failure_reason no_sync_base A fixed reason code for a known failure, never the message

failure_reason codes include store_open_failed, db_url_unreachable and workspace_default_failed for mcp, and no_index, no_sync_base, no_provider_configured and embed_failed for update. An mcp run also reports served_no_index (true when it serves a directory with no index), and an update that defers to one already running reports outcome: deferred.

For init and update, a few coarse buckets and enums describe the shape of the run so we can see the size of repos people index and whether docs generation is used. Never exact counts, never repo or file names:

Field Example Purpose
properties.file_count_bucket 500-999 Repo size distribution (a range, not a count)
properties.top_language python Which languages to prioritise
properties.docs_mode none / deterministic / llm How the wiki was produced: not at all, from templates, or by a model. Sent as true / false by versions before template wikis existed
properties.provider / properties.model anthropic / claude-opus Which models generate docs
properties.embedder openai Which embedders are in use
properties.pages_bucket 100-499 Docs volume (a range)

mcp_tool_call — once per MCP tool call

Emitted by the MCP server (repowise mcp / repowise serve) so we can see which tools coding agents actually use and how often answers come back stale or degraded. Only the tool name, coarse enums/booleans, and the size of the response, never the question, query, results, paths, or repo/symbol names:

Field Example Purpose
properties.tool get_answer, search_codebase Which tool was called
properties.status ok / error Tool error rate
properties.error_reason not_found / invalid_args / no_index / index_behind / server_stale / internal / other Why an error call failed, a fixed code
properties.duration_ms 820 Tool latency in the wild
properties.confidence high / medium / low Answer confidence distribution
properties.retrieval_quality high / partial / weak Retrieval quality distribution; sent only when the call asked for include=["diagnostics"]
properties.results_bucket 1-3, 4-10 Result count for searches (a range)
properties.index_behind true / false How often served content is stale
properties.embedder_degraded true / false How often search runs on mock vectors
properties.grounding symbol_body / extracted What a confident answer rests on
properties.degraded no-llm-provider / synthesis-failed Why an answer carries no synthesised prose
properties.semantic_search true / false Whether retrieval had a vector leg
properties.response_chars 10979 Serialised size of the response, a count only
properties.response_tokens 2744 The same size as the budget's token estimate

Example command_run payload:

{
  "event": "command_run",
  "anon_id": "194639251e854669ae2f56081620d48a",
  "session_id": "e2ecb2aeb4df422883c5af451f2b1158",
  "cli_version": "0.21.0",
  "os": "darwin",
  "arch": "arm64",
  "python_version": "3.12",
  "is_ci": false,
  "properties": {
    "command": "init",
    "subcommand": null,
    "flags": ["--resume"],
    "status": "ok",
    "error_type": null,
    "duration_ms": 71840
  }
}

What is never collected

  • Source code or file contents
  • File paths or directory names
  • Repository names, package names, symbol/function names
  • Generated documentation text
  • Flag values (only the flag name is recorded)
  • Environment variable values
  • API keys or credentials
  • Error messages or stack traces (only the exception class name)
  • IP addresses (the server does not record the request source)
  • Usernames, hostnames, email, or anything personally identifiable

How the anonymous id works

anon_id is a random UUID generated once and stored in ~/.repowise/platform.json. It is not derived from your hostname, username, or any machine identifier, so it cannot be reversed to a person. It only lets us count distinct installs. Delete the file and a new, unrelated id is generated; nothing links the two.

When events are sent

A command never waits on the network to record an event. It appends the envelope to ~/.repowise/telemetry-spool.jsonl and, on exit, starts a small detached process that sends what is queued and removes the file. So the send costs you nothing in command time, and you can read the queued file to see exactly what is about to leave your machine.

Nothing is queued or sent while a test runner is driving repowise (PYTEST_CURRENT_TEST or PYTEST_VERSION is set, or pytest is loaded).

If the send fails the queued events are discarded, not retried. Disabling telemetry also discards anything still queued, so a disable is never followed by one last batch.

Verify it yourself

Run any command with REPOWISE_TELEMETRY_DEBUG=1 to print the exact payload to stderr without sending anything:

REPOWISE_TELEMETRY_DEBUG=1 repowise status

How to opt out

Any one of these disables all telemetry:

repowise telemetry disable        # persisted to ~/.repowise/platform.json
export REPOWISE_TELEMETRY_DISABLED=1
export DO_NOT_TRACK=1             # the cross-tool standard (https://consoledonottrack.com)
export REPOWISE_OFFLINE=1         # also skips the PyPI release check and agent-CLI login probes

Check the current state anytime:

repowise telemetry status

Re-enable with repowise telemetry enable.

Data retention

Events are retained for 90 days and then deleted. Only aggregate figures (e.g. "X% of runs are on Windows") are ever used or shared.