Install
pipx install echolot
First time
cd ~/my-app
//open your agent
/echolot init
That installs the .claude/ layer — the skill, the perf-hunter agent, the
commands — checks that this machine computes correctly, and ends with the next
step. Then open Claude Code in the project and type /echolot. That is the
one door: it asks the tool where the project stands and takes the next step
itself — the first time, that is building echolot.yml from the repository
and a probe trace (four questions to you along the way); every time after,
finding the cause of the regression you describe.
/echolot # reads the state, does what is next
/echolot why is cold start slow # hunt, with that as the question
/echolot init · setup · hunt · reflect · doctor # that thing, explicitly
Coming back
echolot
Where things stand — layer, config, traces, last report, last doctor — and
one line saying what to do next; usually /echolot. After updating the
package, run echolot init again: it brings the layer up to date and leaves
the files you edited alone.
Without an agent, or in CI:
echolot collect -c echolot.yml -n 5 # 5 repeats of the scenario
echolot analyze .echolot/traces/*.perfetto-trace -c echolot.yml # the report
echolot doctor -q # exit 0/1: does this environment compute correctly?
The output is .echolot/out/report.md for humans and report.json for the
agent. doctor builds a synthetic trace with problems planted in advance and
checks the answers against them — no device, about a second — good both as a
CI gate and as the agent's first move.
What it looks for
| detector | what it catches |
|---|---|
main_thread_block |
where the main thread spent its time (by self time) |
gc_pressure |
frequent or expensive GC, and waits on allocation |
monitor_contention |
monitor contention, with the owner's tid as evidence |
binder_txn |
long synchronous IPC, and death by a thousand cuts |
runnable_starvation |
thread ready but preempted on CPU |
uninstrumented_cpu |
threads burning CPU with no instrumentation |
The last one is the only detector that finds a problem inside uninstrumented
code. It does not guess; it presents a fact: "thread
DefaultDispatcher-worker-2 was Running for 340 ms, zero slices". That is
where to add trace{} and re-record.
Each detector is one self-contained .sql file with its metadata in the
header. Drop a file into echolot/sql/detectors/ and it is picked up — there
is no registration in code.
The commands
Two are yours to remember — init and echolot with nothing after it. The
rest split by who calls them.
You
echolot where this project stands, and the next step
init install or update the .claude/ layer; checks the environment
analyze run the detectors, build a Marker Report — CI, or traces by hand
collect capture N traces of one scenario: launch | command | gradle
doctor environment + self-check on a synthetic trace + is the layer current; exit 0/1, -q for three lines
The agent, behind /echolot — you do not call
these:
probe processes, threads by CPU, scenario anchor candidates
names slice name inventory and detector mask coverage
domains slice-to-code map and instrumentation coverage
mark the first temporary markers for a project with none — from the manifest and the SDK, not from names; --apply / --remove
calibrate thresholds derived from known-healthy runs
explain list the detectors and their parameters
Improving the tool
reflect the same kind of report over an agent session — how the tool was used, where it got in the way
Where things live
android-project/
├── echolot.yml ← the project half, committed
├── local.yml ← device serials, binary path; in .gitignore
└── .echolot/ ← traces, reports, the run log, reflect reports; in .gitignore
Read it like gradle.properties and local.properties: one tool per machine,
the binding to a project inside that project's repository.
Documentation
| document | about |
|---|---|
| docs/collecting.md | collect, the three modes, merging repeats |
| docs/analysing.md | probe, names, domains — from a trace to a place in the code |
| docs/mark.md | mark — the first markers for a project with no instrumentation, from the platform's vocabulary |
| docs/detectors.md | writing your own, the context views, self time versus total |
| docs/calibrate.md | thresholds from healthy runs, why rank beats percentile |
| docs/determinism.md | the pinned trace_processor, doctor, the self-check |
| docs/agent-layer.md | the .claude/ layer and why the loop lives in a subagent |
| docs/reflect.md | reflect — the report over the agent's session, for improving the tool |
The agent-facing reference material ships inside the package, under
echolot/claude/skills/echolot/references/ — the report schema, the config
schema, how ART names things, and how to capture a trace by hand.
Status
v0. Everything planned for it is in place except CI mode.
The detectors were validated on a synthetic trace (46 checks in doctor) and
on live traces from Android 14 (emulator) and Android 13 (Galaxy A51). The
naming masks for GC, locks and binder were narrowed against those real traces,
and every narrowing is pinned by a check.
Not done: CI mode. scenario.budget_ms is declared in the config but not
read by the code. What is needed is analyze with an exit code — either
against the budget or on "any detector fired", which after calibrate amounts
to a comparison against the baseline.
A failed detector never fails the run — the error goes to stderr and into
report.json.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file echolot-0.1.0.tar.gz.
File metadata
- Download URL: echolot-0.1.0.tar.gz
- Upload date:
- Size: 135.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fe5f8cdb596e9f322339d6d772786e165571e369b4e7fdc885d7692adcfc5e8
|
|
| MD5 |
cd116741475226a1503dc78735289b4d
|
|
| BLAKE2b-256 |
440ecc72b27c42eaa8e1109f800ebc7617ceb741ab724cea9dfcd8421291b9c0
|
Provenance
The following attestation bundles were made for echolot-0.1.0.tar.gz:
Publisher:
publish.yml on grishan0v/echolot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
echolot-0.1.0.tar.gz -
Subject digest:
9fe5f8cdb596e9f322339d6d772786e165571e369b4e7fdc885d7692adcfc5e8 - Sigstore transparency entry: 2492413118
- Sigstore integration time:
-
Permalink:
grishan0v/echolot@8fdb4ce519b662ffa013b5c2e120a8b468c0cede -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/grishan0v
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8fdb4ce519b662ffa013b5c2e120a8b468c0cede -
Trigger Event:
push
-
Statement type:
File details
Details for the file echolot-0.1.0-py3-none-any.whl.
File metadata
- Download URL: echolot-0.1.0-py3-none-any.whl
- Upload date:
- Size: 150.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8cd855729512b5b11853db5039c5888836c202d8a8d0d5c04724effa38c14d34
|
|
| MD5 |
04581d11d503f81c60576b5004bc4852
|
|
| BLAKE2b-256 |
d0c9c4134b6f9b5e6536294667b8e81df6b84ecca5fb00757667ab5fc90af350
|
Provenance
The following attestation bundles were made for echolot-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on grishan0v/echolot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
echolot-0.1.0-py3-none-any.whl -
Subject digest:
8cd855729512b5b11853db5039c5888836c202d8a8d0d5c04724effa38c14d34 - Sigstore transparency entry: 2492413247
- Sigstore integration time:
-
Permalink:
grishan0v/echolot@8fdb4ce519b662ffa013b5c2e120a8b468c0cede -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/grishan0v
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8fdb4ce519b662ffa013b5c2e120a8b468c0cede -
Trigger Event:
push
-
Statement type: