-
Notifications
You must be signed in to change notification settings - Fork 1
Code Analysis
Introduced in v0.35.0. Three independent in-process tools that read the workspace tree:
| Tool | URL | Purpose |
|---|---|---|
| Gradle wrapper | /projects/{id}/wrapper |
Show current wrapper version + 1-click upgrade |
| Code statistics | /projects/{id}/stats |
File / LoC / size per language |
| Workspace grep | /code-search |
Cross-project source-tree search |
None of these depend on external binaries (no cloc, no ripgrep). The
walks are sequential JVM I/O — fine for typical Android projects (a few
thousand files), noticeable but acceptable on multi-GB monorepos.
/projects/{id}/wrapper parses
gradle/wrapper/gradle-wrapper.properties:
-
Current version — from
distributionUrl(https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip→9.5.1). -
Distribution type —
bin(binary only, ~150 MB download) orall(binary + source + docs, ~400 MB). - Raw URL — copy/inspect.
- Properties file path — for direct shell editing if you need it.
Type a new version (regex ^[0-9]+(\.[0-9]+)*(-rc-[0-9]+)?$), pick bin
or all, submit. Server:
- Reads the existing file.
- Replaces only the
distributionUrl=line. Other properties (zipStoreBase,validateDistributionUrl, etc.) are preserved. - Writes to
.tmpand atomically moves into place.
Next ./gradlew invocation will download the new distribution
automatically. The audit log records wrapper.update with the version
in the detail JSON.
- Version regex blocks shell / path injection.
- Distribution type whitelist (
bin/all) only. - Atomic write — a crashed save can't leave a half-written
.properties.
- It does not edit
gradle.properties(use Env Files Quick Edit for that). - It does not validate that the upgraded version is compatible with
your project's plugins. A
--refresh-dependenciesrebuild is the fastest way to catch issues.
/projects/{id}/stats walks the project root and:
- Filters out
.git,build,.gradle,node_modules,.idea,.vibecoder/. - Skips binary extensions (30+ —
.apk/.aab/.jar/.png/.mp4/.ttf/ …). - Skips files larger than 5 MB.
- Classifies remaining files by extension into one of 35+ languages (Kotlin / Java / Swift / Go / Rust / TS / JS / Vue / Svelte / Python / Ruby / SQL / GraphQL / Terraform / …).
- Counts lines (
Files.lines(p).count()).
This is not cloc-grade accuracy — blank lines and comments are counted. But for "is this app mostly Kotlin or mostly XML?" the result is right.
| Column | Notes |
|---|---|
| Language | Friendly name (e.g. Kotlin, not kt) |
| Files | Count |
| Lines | Total |
| Bar | Visual share of total lines (relative to the biggest language) |
Top-line summary cards: total files / total lines / total bytes / walk duration ms.
The companion to /history (conversation search) and /logs (build-log
search) — this one walks source files.
- Query — required (≥ 1 char). Empty query returns an empty result, preventing accidental "dump everything" requests.
- Project filter — optional substring match against the project id.
- Case sensitive — checkbox.
- Walks every project directory at the top of
workspace.root(skipping.vibecoderand other dotfiles). - Per file: skips if
>5 MB, skips if a known binary extension. - Reads line-by-line; stops the file scan as soon as 200 total matches accumulate.
- Match preview shows the full line (clipped to 400 chars) with the
matched substring wrapped in
<mark style="background:#facc15">. - File link goes to
/projects/{id}/view?path=<file>— the existing file viewer renders the file with syntax highlighting (highlight.js) and navigates to the line.
| Workspace size | Typical scan time |
|---|---|
| 1 small Android project (~500 files, 50 K LoC) | < 1 s |
| 5 mid-size projects (~5 K files, 500 K LoC) | 1-3 s |
| 10+ large projects (10 K+ files, 1M+ LoC) | 5-15 s |
Future improvement: detect rg in $PATH and use it for big workspaces.
The current input field treats the query as a literal substring (with
Regex.escape applied for the <mark> highlight). Regex would be more
powerful but also a footgun (catastrophic backtracking on user input).
A future version may add an explicit "regex mode" checkbox with a
timeout-bounded Matcher.
Added in Phase 33. Best-effort "jump to definition" for Kotlin and Java without spawning a real Language Server. The lookup walks the project tree and matches six declaration patterns:
| Pattern | Kind |
|---|---|
fun <name>( (incl. generics + receiver) |
fun |
(open|abstract|sealed|inner|data|enum|annotation|value)* (class|interface|object) <name> |
class |
(val|var) <name> [:=] (top-level + property) |
val |
Java (public|protected|private|static|final|abstract|synchronized|native) ... <name>(
|
fun |
Java (class|interface|enum) <name>
|
class |
typealias <name> = |
typealias |
/projects/{id}/symbols is an admin SSR page with a single
text-input form. The query must be a valid Kotlin / Java identifier
([A-Za-z_][A-Za-z0-9_]{0,79}); arbitrary regex is rejected so users
can't slow the scan or smuggle injection.
Each hit links to /projects/{id}/view?path=<rel>&line=<n> —
the file viewer (v0.13.0) gains a small JS handler in v0.54.0 that
reads the line query parameter, smooth-scrolls the highlighted code
to the target row, and flashes a yellow outline for 1.5 s.
┌────────────────────────────────────────────────────────────────┐
│ 심볼 정의 찾기 — my-app (my-app) │
│ │
│ 심볼 이름 [ onCreate ___________________ ] [검색] [grep으로→] │
│ │
│ Kind Location Source line │
│ fun app/src/.../MainActivity.kt:24 override fun onCreate│
│ fun app/src/.../SettingsActivity.kt:18 override fun onCreate│
└────────────────────────────────────────────────────────────────┘
The console page sidebar gets a ⇢ 정의 검색 chip next to git
so the search is reachable from any project workflow.
curl -H "Authorization: Bearer $TOKEN" \
'http://localhost:17880/api/projects/my-app/symbols?name=onCreate'{
"hits": [
{ "relPath": "app/src/.../MainActivity.kt",
"lineNumber": 24, "kind": "fun",
"line": "override fun onCreate(savedInstanceState: Bundle?) {" }
]
}A real kotlin-language-server (or bash-language-server, etc.)
would give precise resolution (overloads, nested types, references,
rename refactoring) but adds a separate JVM (~300 MB image, ~200 MB
RAM at idle, 10–30 s cold start) for the Kotlin path alone. For the
single-user dev profile defined in
CLAUDE.md §1
the simpler regex scan covers ~90% of "jump to definition" cases in
single-digit milliseconds with zero new dependencies.
LSP integration remains the right move when:
- You need references-to (
Find Usages). - You routinely jump into nested types with shadowed names.
- You want IDE-grade hover / signature popups.
Track interest in the roadmap section below.
- Java method patterns recognize the most common modifiers
(
public/protected/private/static/final/abstract/synchronized/ native). Methods with a very long generic return type, or with a leading comment block on the same line, may not match. - Hits are line-level; the actual declaration token is highlighted in the file viewer (whole line outlined, not the symbol itself).
- 100-hit hard cap, 5 MB per-file size cap, plus the standard
exclusion list (
build//.gradle//node_modules//.idea/). -
.kt,.kts,.java,.groovyonly. XML / properties / Markdown declarations don't appear here — use workspace grep.
- CVE matching on dependencies — see Dependency Audit for the related Gradle dependency tree feature; CVE lookup is the obvious add.
-
ripgrep fallback for code-search — auto-detect
rgin$PATHand delegate. - Per-language tokei-style stats — separate code / comment / blank line columns. Probably a small library import.
- ✅ Symbol definition lookup — landed in v0.54.0 as a regex-based best-effort. Full LSP integration (references, rename, hover) is the natural next step but requires a separate JVM / image bump and is tracked separately.
- Build Cache Management — clean Gradle / Android / npm caches when stats show your project ballooning.
- Dependency Audit — Gradle dependency tree with coordinate extraction (sibling feature).
-
Backup & Restore —
/backupexcludes the same caches that bloat your stats.