agentsclimarketplace

Hotspot analysis

Skill baekchangjoon/hotspot-analysis/skills/hotspot-analysis

Use to produce a deterministic, reproducible, ranked prioritization of a Java codebase's REST API endpoints (and methods) for test generation — especially as the input that decides which RestAssured API tests to write first. Drives this repo's Java CLI over a local git working tree, combining recency-weighted git churn, SonarQube-style cognitive complexity, and JaCoCo coverage gap into a Composite Hotspot Score, and emits a machine-readable ranking (CSV/YAML/Markdown/HTML) plus a CI gating exit code. Method-level Java, hunk-accurate, no LLM guesswork. Based on Adam Tornhill's "Your Code as a Crime Scene".From its SKILL.md

Install
npx -y skills add baekchangjoon/hotspot-analysis --skill hotspot-analysis

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

20.4 KB, ~5.2k tokens by cl100k_base, as published. Nobody here has run it

Hotspot analysis → test-generation prioritization (Java)

This skill is a thin wrapper around a Java CLI. The jar does the analysis deterministically; the skill's job is to build it, configure it, run it, and hand the ranked, machine-readable output to whatever generates tests (typically RestAssured API tests, or unit tests for the top methods).

What makes it worth a separate step (vs. asking a model to "guess the risky endpoints"):

  • Deterministic & reproducible — pure JVM computation; same inputs → same scores and same ordering. No randomness, no model variance.
  • Method-level, hunk-accurate Java — churn is attributed to the exact methods whose line ranges a commit's diff hunks touched, not the whole file.
  • Recency-weighted — recent churn counts more via exponential decay (configurable half-life).
  • JaCoCo-integrated — real line-coverage gaps raise priority of untested code; coverage can be a scoring input or an observational column.
  • CI-gating--strict returns a non-zero exit code on empty results.

The primary deliverable is a priority queue of REST API endpoints: for each endpoint you get its HTTP method + route, the aggregated risk over its whole call graph, the call graph itself, and a coverage signal — i.e. exactly what an agent needs to decide which endpoint to test first and which under-tested path to target.

When to use

  • "Generate RestAssured tests for this Spring app, most important endpoints first."
  • "Which API endpoints / methods are riskiest and least tested? In what order?"
  • Producing a deterministic prioritization that a test-generation step consumes.

Not for: non-Java repos; deciding test content (it ranks what to test, the test generator decides how).

Target must be a directory containing a real .git/ folder. Phase 1 runs local-git end-to-end; github target needs a local clone (see Troubleshooting).

Prerequisites

NeedRequirement
Get the jarNothing to build — scripts/get-jar.sh downloads the released fat jar (cached). Building from source instead needs any JDK 17+.
Run the jarNo JDK requiredscripts/ensure-java.sh finds an installed Java 21+ or auto-downloads a Temurin 21 JRE (~46MB, sha256-verified, cached in ~/.cache/hotspot-analysis/jre; refresh by deleting that dir)
Analysis targetA directory with a .git/ folder
API analysis (recommended)apiAnalysis.enabled: true; ideally classpathDirectories for symbol resolution
Coverage signal (recommended)A JaCoCo XML report from the same build

Workflow

A convenience wrapper, scripts/run-analysis.sh, resolves the jar (downloading the released fat jar if needed) and runs analyze. The steps below show the explicit form.

  1. Get a runtime and the jar. No build and no pre-installed JDK required — ensure-java.sh resolves an installed Java 21+ (or auto-downloads a Temurin JRE), and get-jar.sh downloads the released fat jar to a cache (or reuses a local build, or builds from source as a fallback):

    JAVA="$(skills/hotspot-analysis/scripts/ensure-java.sh)"  # prints a java 21+ path
    JAR="$(skills/hotspot-analysis/scripts/get-jar.sh)"       # prints the jar path
    # manual alternative (no clone needed):
    #   curl -fsSL https://github.com/baekchangjoon/hotspot-analysis/releases/latest/download/hotspot.jar -o hotspot.jar
    #   JAR=hotspot.jar
    # from-source alternative (needs the repo + a JDK):  ./gradlew bootJar
    

    Prefer no downloads at all? Use the Docker image, mounting the target repo at /work: docker run --rm -v "$PWD":/work ghcr.io/baekchangjoon/hotspot-analysis:latest analyze --config /work/hotspot.yml

  2. Generate a config.

    "$JAVA" -jar "$JAR" init -o hotspot.yml
    
  3. Configure for endpoint prioritization. Point analysis.target.path at the target repo, enable API analysis, and (if available) supply a JaCoCo report. If the user can't hand-write the YAML, run the interview below ("Configure interactively") — ask, fill defaults, write the file. See Config reference for every key. The key block:

    analysis:
      apiAnalysis:
        enabled: true
        sharedComponentMode: BOTH            # CUMULATIVE | SEPARATE | BOTH
        classpathDirectories:                # optional but improves call-graph resolution
          - build/libs
      jacocoReportPath: build/reports/jacoco/test/jacocoTestReport.xml
    output:
      formats: [yaml, md, html]
      apiLayout: BOTH                        # COMBINED | STANDALONE | BOTH
      topN: 30
    
  4. Analyze (add --strict in CI).

    "$JAVA" -jar "$JAR" analyze --config hotspot.yml --strict
    # or, in one shot (resolves the runtime AND the jar for you):
    #   skills/hotspot-analysis/scripts/run-analysis.sh hotspot.yml --strict
    

    Outputs land in output.path. With API analysis on and apiLayout: BOTH:

    hotspot-report/
    ├── api_report.yml        ← STANDALONE: apiHotspots + sharedComponents (agent input)
    ├── hotspots.yml          ← COMBINED: file + method + api + shared in one doc
    ├── hotspots.md / .html   ← human-readable
    ├── file_hotspots.csv
    └── method_hotspots.csv
    
  5. Consume the ranking for RestAssured. Read api_report.yml and iterate apiHotspots in compositeRank order. Field-by-field schema: references/api-report-schema.md. Each row carries:

    FieldUse for test generation
    httpMethod, routeThe request: given()...when().<method>(route)
    fqcn, method, parametersController signature → request body/param shape
    callGraphReachable methods → which downstream logic the endpoint exercises
    coverageMultiplier / lineCoverageHow under-tested the endpoint's logic is
    compositeRankThe order to write tests in
    sharedComponents[]Methods many endpoints depend on — high-leverage to cover once

    Generate tests highest-rank first, targeting the least-covered paths in each endpoint's call graph. Do not fabricate the ranking — run the CLI and read the actual file.

Configure interactively (interview)

A freshly-installed user usually can't write hotspot.yml cold. Don't make them. Generate a starting file (init), then fill it by Q&A: auto-detect what you can, ask only what's ambiguous, confirm, and write the file.

Procedure:

  1. Detect first, then ask. Inspect the target repo to pre-fill defaults so most questions become a yes/no confirmation:

    • Spring app? — grep -rl "@RestController\|@RequestMapping" <repo>/src → if hits, default apiAnalysis.enabled: true.
    • Multi-module? — more than one src/main/java root → include both globs.
    • JaCoCo report present? — look for **/jacoco/**/*.xml (e.g. build/reports/jacoco/test/jacocoTestReport.xml) → default jacocoReportPath.
    • Built classes/jars? — build/libs, build/classes → default apiAnalysis.classpathDirectories.
    • Recent activity? — git -C <repo> log -1 --format=%cd → if older than a year, propose absolute window.since/until instead of days.
  2. Ask, one decision at a time (skip any you confidently detected — just state the default and let the user correct it):

    #QuestionMaps toDefault
    1Which repo to analyze? (path to the .git/ working tree)analysis.target.path— (required)
    2Prioritize REST API endpoints for test generation?apiAnalysis.enabledtrue if Spring detected
    3Count churn over the last N days, or an absolute date range?window.days or window.since/untildays: 365
    4File-level, method-level, or both?scope.granularity[file, method]
    5Have a JaCoCo coverage report? Where?analysis.jacocoReportPathdetected path, else omit
    6Dirs with built classes/dep jars (improves call graph)?apiAnalysis.classpathDirectoriesdetected, else []
    7Shared-method handling?apiAnalysis.sharedComponentModeBOTH
    8Output formats / where / how many rows?output.formats/path/topN[csv,yaml,md,html], ./hotspot-report, 30
    9Fail the run if the result is empty (CI)?pass --strict at run timeno
  3. Write hotspot.yml from the answers, show it back to the user for a final OK, then run step 4. If a tool like AskUserQuestion is available, prefer it for crisp multiple-choice prompts; otherwise ask in plain text.

Keep it short: a typical session is "confirm repo path → confirm Spring/API on → accept window default → confirm the detected JaCoCo path → go".

How the score is computed

Four input factors → two scores. Full per-granularity derivations (with source references and worked examples) live in docs/scoring/: file · method · REST API endpoint · shared component.

Factor / scoreDefinition
RevisionsCommits in the window that touched the artifact (method: diff-hunk overlap)
Recency DecayΣ exp(-ln(2)·Δt / halfLife) over those commits — recent weighs more
Cognitive ComplexitySonarQube-style AST walk (file = sum of its methods)
Coverage Multiplier1/(lineCoverage + 0.1) from JaCoCo; 1.0 if no report
Simple ScoreRevisions × LOC (Tornhill's original)
Composite ScoreCognitive Complexity × Recency Decay × Coverage Multiplier

For an API endpoint, each factor is aggregated over the controller method plus its whole call graph; coverage is the average over those methods. Sorted by Composite DESC, ties broken deterministically (route,httpMethod).

Config reference

analysis:
  target:
    type: local-git          # local-git | github (Phase 1 CLI: local-git end-to-end)
    path: /path/to/target/repo   # must contain a .git/ folder
  window:
    days: 365                # Mode A: relative window from now
    # since: "2024-01-01"    # Mode B: absolute ISO range (use INSTEAD of days)
    # until: "2026-01-01"
  scope:
    granularity: [file, method]
    include:
      - "src/main/java/**/*.java"     # single-module repos
      - "**/src/main/java/**/*.java"  # multi-module repos (list both if unsure)
    exclude:
      - "**/generated/**"
      - "**/test/**"
      - "**/build/**"
  scoring:
    decayHalfLifeDays: 90    # half-life for recency decay (days)
    excludeCoverage: false   # true → Composite = CC × Decay; coverage shown raw, not scored
  apiAnalysis:
    enabled: true            # off by default; required for api/shared granularities
    sharedComponentMode: BOTH        # CUMULATIVE | SEPARATE | BOTH
    classpathDirectories: []         # dirs with dependency jars/classes for symbol resolution
  jacocoReportPath: build/reports/jacoco/test/jacocoTestReport.xml   # optional
output:
  formats: [csv, yaml, md, html]   # case-insensitive; ≥1 required
  apiLayout: BOTH          # COMBINED (into hotspots.*) | STANDALONE (api_report.*) | BOTH
  coverageBreakdown: false # true → also write coverage_breakdown.yml: the audit
                           # trail behind every coverage number (per-file counts;
                           # per-endpoint per-method covered/executable lines)
  path: ./hotspot-report
  topN: 30                 # 0 = all rows

Env vars substitute as ${VAR_NAME} in any string value; YAML comment lines (#) are left untouched.

sharedComponentMode

  • CUMULATIVE — shared methods counted inside every endpoint's aggregate; no separate list.
  • SEPARATE — shared methods excluded from endpoint aggregates and reported once on their own.
  • BOTH (default) — endpoint aggregates include them and a separate shared list is emitted.

analyze options

OptionEffect
--config, -c <file>Path to the YAML config (required)
--output-dir, -o <dir>Directory to write the reports into (overrides output.path)
--quiet, -qSuppress the stdout summary
--strict, -sExit code 3 on empty result (zero commits or zero files) — for CI gating

Exit codes: 0 ok · 1 config/pipeline failure · 2 usage error · 3 --strict empty result.

Decision rules (IF → THEN)

  • IF the user wants the order to write API tests in THEN read api_report.yml and iterate apiHotspots by ascending compositeRank.
  • IF apiHotspots is empty but the app clearly has endpoints THEN check, in order: apiAnalysis.enabled: true, controllers carry @RestController/@Controller + a mapping annotation, and apiAnalysis.classpathDirectories includes the dependency jars/classes so cross-type calls resolve. Do not report "no endpoints".
  • IF the run warns that files are "not present in the JaCoCo report" (their coverageMultiplier stays 1.0) THEN the report doesn't cover those files — supply a report from the same build/module; do not conclude "nothing is tested". Broken/zero-coverage/partial reports are all auto-detected and treated as unknown coverage (multiplier 1.0 + warning), never as a silent 10x penalty. Regenerate with e.g. ./gradlew test jacocoTestReport from the same checkout.
  • IF the summary shows Files: 0 THEN fix scope.include (single-module needs src/main/java/**/*.java, multi-module needs **/src/main/java/**/*.java; list both).
  • IF the summary shows Commits: 0 THEN widen window.days or switch to absolute window.since/window.until overlapping real activity.
  • IF target.type is github THEN clone the repo locally and re-run with target.type: local-git (Phase 1 wires only local-git end-to-end).
  • IF running in CI THEN pass --strict so an empty result fails loudly.
  • IF you only need observational coverage, not coverage-driven scoring THEN set scoring.excludeCoverage: true (Composite becomes CC × Decay).

Anti-patterns & pitfalls

  • Don't fabricate or estimate the ranking. Run the jar and read the actual api_report.yml; the whole point is determinism, not a model guess.
  • Don't treat an empty apiHotspots as "no endpoints." It almost always means apiAnalysis is off or the call graph couldn't resolve (missing classpathDirectories).
  • Don't feed a JaCoCo report from a different module/build. Path mismatch reads as 0% coverage → every multiplier maxes at 10 and the ranking is bogus.
  • Don't run the jar on JDK < 21 — it's compiled for 21 (UnsupportedClassVersionError); ensure-java.sh guards this by only accepting 21+.
  • Don't analyze generated or build output — keep **/generated/**, **/build/**, **/target/**, **/test/** in scope.exclude.
  • Don't present the Composite Score as a verdict. It's prioritization evidence; surface the factors (churn, recency, complexity, coverage) so the choice is explainable.
  • Don't reorder by Simple Score when the goal is risk. compositeRank, not simpleRank, is the test-priority signal.

Testing

The project's own suite exercises every layer (parser, scoring, output, E2E):

./gradlew test            # comprehensive; run before trusting a build

Skill-level smoke check — analyze this very repo and assert a non-empty result:

bash -n skills/hotspot-analysis/scripts/ensure-java.sh skills/hotspot-analysis/scripts/get-jar.sh skills/hotspot-analysis/scripts/run-analysis.sh
JAVA="$(skills/hotspot-analysis/scripts/ensure-java.sh)"  # resolves/downloads a java 21+
JAR="$(skills/hotspot-analysis/scripts/get-jar.sh)"   # resolves/downloads the jar
"$JAVA" -jar "$JAR" init -o /tmp/h.yml -f
# set analysis.target.path in /tmp/h.yml to this repo's absolute path, then:
"$JAVA" -jar "$JAR" analyze --config /tmp/h.yml --strict
echo "exit=$?"   # 0 = produced output; 3 = empty (misconfigured)

A green ./gradlew test plus a 0 exit on the smoke run means the skill's toolchain is sound end-to-end.

Changelog

  • 0.1.6 — scoring-trust and report fixes from a fresh-eyes evaluation round: unknown coverage is never a 10x penalty (files absent from a partial JaCoCo report, line-less sourcefile entries, and uninstrumented methods all get multiplier 1.0 + a warning); switch expressions count toward cognitive complexity; duplicate sourcefile entries OR-merge; stale-report and shallow-clone warnings; unparseable files are skipped instead of aborting; HTML column sorting works on every table (pinned by a JS-executing smoke test) and the X-Ray drill-down is documented; clearer config errors (field + value + allowed values), ~/ expansion, days vs since/until now mutually exclusive, init refuses directory targets, analyze skips nothing silently.
  • 0.1.5zero-config: analyze now runs without a config file (auto-detects git root, single/multi-module layout, JaCoCo report, Spring API), takes an optional [path], --print-config dumps the synthesized config, the first run prints the top-3 hotspots + the report path, and linked git worktrees are supported. A one-line installer (curl ... install.sh | bash) provides the hotspot command. And the JDK-21 friction is gone: scripts/ensure-java.sh (and the hotspot wrapper installed by install.sh) finds an installed Java 21+ or auto-downloads a sha256-verified Temurin 21 JRE; brew install baekchangjoon/tap/hotspot installs with the JDK as a brew dependency; each release ships self-contained hotspot-<tag>-<os>-<arch>.tar.gz archives (bundled JRE, verified on 4 native CI runners before attach); an all-zero JaCoCo report (no execution data) now warns and disables coverage instead of silently inflating every multiplier to 10x; analyze -o/--output-dir overrides the report directory.
  • 0.1.4 — endpoint coverage is now line-weighted (Σcovered/Σexecutable over the call graph) instead of a mean of per-method ratios, so a large untested method can no longer hide behind a small covered one; new opt-in output.coverageBreakdown writes coverage_breakdown.yml, the calculation trace behind every coverage number; releases enforce 4-way version consistency (tag = gradle = CLI = plugin/marketplace manifests).
  • 0.1.3 — one-click release button + skills-validation CI gate + tag protection; the button reliably fans out to jar/image via workflow_call (a GITHUB_TOKEN-created release doesn't re-trigger event workflows).
  • 0.1.2 — releases are now event-driven: every published release (incl. one created by gh skill publish) auto-attaches hotspot.jar and builds the Docker image, so a new release never breaks the download. license added to frontmatter.
  • 0.1.1 — distribute the jar via GitHub Releases (version-stable hotspot.jar asset) + ghcr Docker image; a missing jacocoReportPath now warns and disables coverage instead of silently penalizing every artifact.
  • 0.1.0 — initial skill: file / method / REST API endpoint / shared-component prioritization driving the Phase 1 CLI; RestAssured consumption guide; apiAnalysis + JaCoCo + --strict exposed; per-granularity scoring docs.

References

What ships with it: 4 files

12.4 KB alongside SKILL.md, 3 of them executable

references/

scripts/

Gives 0 of the 12 instructions most docs writing skills give in ~5.2k tokens

Counted across 1,637 of the 3,044 authors here whose files we hold, read 2026-08-07

  • Announce the skill at startin 54 of 1637, across 26 files
  • Convert legacy doc files before editingin 45 of 1637, across 7 files
  • Predict questions readers might askin 42 of 1637, across 4 files
  • Generate clarifying questions for initial contextin 42 of 1637, across 3 files
  • Create document scaffold with placeholder textin 42 of 1637, across 3 files
  • Brainstorm content options for each sectionin 42 of 1637, across 3 files
  • Test the document with a fresh context-less instancein 42 of 1637, across 3 files
  • Include exact file paths in every taskin 42 of 1637, across 15 files
  • Ask interview questions one at a timein 42 of 1637, across 27 files
  • Apply surgical edits during refinementin 41 of 1637, across 2 files
  • Offer structured workflow or freeformin 40 of 1637, across 1 file
  • Ask for document meta-contextin 40 of 1637, across 2 files

Said here and by no other author read

  • run the hotspot analysis CLI
  • enable API analysis in config
  • supply a JaCoCo coverage report
  • read the generated API report
  • generate tests highest-rank first
  • target least-covered paths

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.