Skip to content

Commit 8e795f7

Browse files
aksOpsclaude
andauthored
feat(plugin): externalize Maven proxy + JRE source + version pin to config.env (#2)
* feat(plugin): config-driven launcher; refresh docs; NOTICE third-party licenses Builds on the marketplace restructure (#1). Changes: 1. Externalize launcher knobs into plugin/skills/sonar-predictor/config.env - SONAR_MAVEN_REPO_URL Maven proxy where the analyzer bundle is fetched - SONAR_BUNDLE_VERSION Bundle version pin (default: 0.1.1) - SONAR_MIN_JAVA_VERSION Minimum Java major required (default: 17) - SONAR_JRE_URL_TEMPLATE JRE source for auto-download; tokens {os} {arch} {version} are substituted (default: Adoptium API) - SONAR_JRE_VERSION JRE version to fetch (default: 17) - SONAR_DISABLE_JRE_AUTODOWNLOAD Set to 1 to refuse the JRE auto-download Plain KEY=VALUE format parsed by both bash and Windows launchers. Env vars of the same name take precedence over the file (one-off override without editing). Forking workflow: clone, edit config.env, push, install. 2. Bash launcher gains a 'no Java anywhere -> download a JRE' step that runs after the bundle is cached. Searches $JAVA_HOME, PATH, and the same common install dirs the bundle's own launcher probes; if nothing meets the minimum, substitutes {os}/{arch}/{version} into SONAR_JRE_URL_TEMPLATE, downloads, extracts to ~/.cache/sonar-predictor/jre/<VERSION>/, and exports JAVA_HOME for the bundle launcher to pick up. Windows launcher reads the same config.env but still requires Java 17+ on the system (or in JAVA_HOME). Auto-install on Windows is deferred. 3. Refresh README.md and dist/README.md for the marketplace install path: - Quick-start is now the two-command marketplace install (Claude + Copilot) - Add a 'Corporate / air-gapped setup' section documenting config.env and SONAR_PREDICTOR_HOME for fully pre-staged installs - Expand the License section into 'License & third-party components' with the SonarSource analyzer license situation called out honestly 4. Add NOTICE listing third-party components and their licenses: - sonarlint-analysis-engine — LGPL v3 - SonarSource language analyzers — SONAR Source-Available License v1.0 (SSALv1) since SonarSource's 2024 relicense - Apache 2.0 build / runtime dependencies (picocli, jackson, maven plugins) - Adoptium Temurin JRE — GPL-2.0 WITH Classpath-exception-2.0 NOTICE also documents the future direction: have the bootstrap fetch each analyzer JAR individually from Maven Central instead of bundling them into the dist artifact. That eliminates any SSALv1 redistribution question (SonarSource's own Maven Central distribution becomes the sole channel). The plugin's config.env already isolates the Maven URL, so the transition is transparent to corporate proxies. Smoke-tested end to end: - default config: bundle from Maven Central, system Java used. - SONAR_MAVEN_REPO_URL=bogus env override correctly steered the download (failed at DNS as expected) — env-var precedence confirmed. - SONAR_MIN_JAVA_VERSION=99 forced the JRE auto-install path; Adoptium Temurin 17.0.19 downloaded and ran. - SONAR_DISABLE_JRE_AUTODOWNLOAD=1 blocked the install with the right error. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(plugin): add agent-scan subcommand that writes JSON to a file and returns a pointer A new `./bin/sonar agent-scan [scope]` wrapper in the plugin's bootstrap that bakes the out-of-context discipline into the tool, replacing the "agent must remember to redirect to a temp file + jq" pattern. ./bin/sonar agent-scan -> git changeset (default) ./bin/sonar agent-scan check src/Main.java -> specific files ./bin/sonar agent-scan analyze src/ -> whole directory On invocation: 1. Ensures .sonar-predictor/ exists at the project root. 2. If inside a git repo, appends .sonar-predictor/ to .gitignore on first use (with a comment header). Idempotent — re-runs do nothing. 3. Runs `sonar --format json <scope>`, redirecting stdout+stderr to .sonar-predictor/scan.json. 4. Prints a compact summary on stdout: total issue count, severity breakdown, and the file path. Example: sonar-predictor: 47 issues written to .sonar-predictor/scan.json severity: BLOCKER=3 CRITICAL=12 MAJOR=22 MINOR=8 INFO=2 query: jq '...' .sonar-predictor/scan.json 5. Propagates the underlying CLI's exit code (0 clean, 1 issues, 2 error). The summary uses jq when available; if not, falls back to a plain pointer. Both agent variants (sonar-scanner-claude, sonar-scanner-copilot) now invoke `agent-scan` instead of the manual redirect+jq dance. Their prompts retain the jq drill-down recipe for when the orchestrator asks for specifics — the file is right there, the recipe stays cheap. SKILL.md declares the agent-scan contract as the canonical agent invocation pattern. The repo's own .gitignore gains a .sonar-predictor/ entry so dogfooding scans against this repo never get committed. Windows .bat gets parity for the subcommand: writes the file, gitignores the path, prints a pointer. No jq-based summary on Windows (jq isn't typically on PATH); just the pointer. Smoke-tested: agent-scan in this git repo wrote a 0-issue scan.json (116 bytes), exit 0, stdout summary correct, .gitignore unchanged because the line was already present (the idempotent guard works). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a770e87 commit 8e795f7

10 files changed

Lines changed: 557 additions & 153 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ dependency-reduced-pom.xml
1515
# module. Not committed to keep the repo lean. The distribution build (dist
1616
# module) fetches its own copies through Maven and does not use this directory.
1717
daemon/plugins/*.jar
18+
19+
# sonar-predictor scan output, written by `bin/sonar agent-scan`. Never commit.
20+
.sonar-predictor/

NOTICE

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
sonar-predictor
2+
Copyright 2025–2026 Amit Kumar and the sonar-predict contributors
3+
4+
This product is licensed under the Apache License, Version 2.0 (see LICENSE).
5+
6+
================================================================================
7+
THIRD-PARTY COMPONENTS
8+
================================================================================
9+
10+
`sonar-predictor` invokes third-party analyzers and libraries at runtime. The
11+
following components are NOT redistributed inside this repository (the
12+
installable plugin) — the launcher downloads them on first invocation from
13+
Maven Central. They are listed here for transparency about what your machine
14+
runs after you install this plugin.
15+
16+
------------------------------------------------------------------------------
17+
SonarSource analysis engine
18+
------------------------------------------------------------------------------
19+
20+
org.sonarsource.sonarlint.core:sonarlint-analysis-engine
21+
License: GNU Lesser General Public License v3.0 (LGPL-3.0)
22+
Source : https://github.com/SonarSource/sonarlint-core
23+
24+
The embedded analysis runtime — the same engine that powers SonarLint in
25+
IDEs. The CLI and daemon link to it as a normal library dependency, which
26+
is the use LGPL-3.0 is specifically designed to allow under any combining
27+
license (including Apache 2.0).
28+
29+
------------------------------------------------------------------------------
30+
SonarSource language analyzers
31+
------------------------------------------------------------------------------
32+
33+
org.sonarsource.java :sonar-java-plugin
34+
org.sonarsource.python :sonar-python-plugin
35+
org.sonarsource.javascript:sonar-javascript-plugin
36+
org.sonarsource.php :sonar-php-plugin
37+
org.sonarsource.kotlin :sonar-kotlin-plugin
38+
org.sonarsource.slang :sonar-go-plugin
39+
org.sonarsource.slang :sonar-ruby-plugin
40+
org.sonarsource.slang :sonar-scala-plugin
41+
org.sonarsource.html :sonar-html-plugin
42+
org.sonarsource.xml :sonar-xml-plugin
43+
44+
License: SONAR Source-Available License v1.0 (SSALv1)
45+
Text : https://www.sonarsource.com/license/ssal/
46+
47+
These are the per-language rule packs the analysis engine loads at runtime.
48+
SonarSource relicensed them from LGPL v3 to SSALv1 in 2024. SSALv1 is
49+
"source-available": it permits free internal use and free local execution
50+
(what this tool does), and restricts using the analyzers to operate a
51+
competing "Service Offering". Read the full license text at the link above
52+
before using `sonar-predictor` to build a hosted service offering.
53+
54+
No source code from these analyzers is included in this repository.
55+
`sonar-predictor` invokes them only through the public SonarSource APIs
56+
exposed by sonarlint-analysis-engine.
57+
58+
------------------------------------------------------------------------------
59+
Build-time / direct-dependency Apache 2.0 components
60+
------------------------------------------------------------------------------
61+
62+
info.picocli:picocli (CLI framework)
63+
com.fasterxml.jackson.core:jackson-* (JSON serialization)
64+
org.apache.maven.plugins:maven-* and the embedded
65+
build-helper / central-publishing-maven-plugin (build tooling)
66+
67+
All Apache License 2.0.
68+
69+
------------------------------------------------------------------------------
70+
Runtime JRE auto-download (optional)
71+
------------------------------------------------------------------------------
72+
73+
When no Java 17+ runtime is found on the user's machine, the plugin's
74+
bootstrap launcher fetches a JRE from the URL configured in
75+
`plugin/skills/sonar-predictor/config.env`. The public default uses the
76+
Adoptium Temurin API (https://api.adoptium.net), which serves Eclipse
77+
Temurin OpenJDK builds under the GNU General Public License v2 with the
78+
Classpath Exception (GPL-2.0 WITH Classpath-exception-2.0).
79+
80+
An air-gapped or corporate setup can replace this URL with a private JRE
81+
mirror; the bootstrap does not require Adoptium specifically.
82+
83+
================================================================================
84+
NOTES ON THE BUNDLED MAVEN CENTRAL ARTIFACT
85+
================================================================================
86+
87+
The launcher currently downloads a single archive,
88+
`sonar-predictor-dist-<version>.zip`, from Maven Central. That archive (built
89+
and published by this project's release workflow) contains the SonarSource
90+
analyzer JARs alongside this project's own CLI and daemon jars, repackaged
91+
unmodified.
92+
93+
A future release will change the launcher to fetch each analyzer JAR
94+
individually from Maven Central using its published coordinates, so that
95+
SonarSource's own Maven Central distribution is the sole redistribution
96+
channel and `sonar-predict-dist-*.zip` no longer carries third-party JARs.
97+
This eliminates any redistribution question about the SSALv1-licensed
98+
analyzers. The plugin's `config.env` already isolates the Maven repository
99+
URL, so this transition is transparent to corporate Maven proxy setups.

README.md

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,16 @@ It is **scan-only** — it reads your source and reports findings, and never mod
4545

4646
## Quick start
4747

48-
Grab the self-contained bundle from the [latest release](https://github.com/RandomCodeSpace/sonar-predict/releases/latest) — it carries the CLI, the daemon and all 12 analyzers:
48+
`sonar-predictor` installs as a Claude Code / GitHub Copilot CLI plugin from this repo's built-in marketplace. The plugin is tiny (kilobytes); the analyzer bundle is fetched lazily from Maven Central on first scan and cached locally for every subsequent run.
4949

50-
```sh
51-
curl -L -o sonar-predictor.zip \
52-
https://github.com/RandomCodeSpace/sonar-predict/releases/download/v0.1.0/sonar-predict-skill-0.1.0.zip
53-
unzip sonar-predictor.zip -d sonar-predictor
54-
55-
./sonar-predictor/bin/sonar check src/Main.java
50+
```
51+
/plugin marketplace add RandomCodeSpace/sonar-predict
52+
/plugin install sonar-predictor@sonar-predict
5653
```
5754

58-
The launcher auto-discovers a Java 17+ runtime and starts the daemon on first use — no configuration.
55+
The same two commands work on Claude Code and Copilot CLI — they share the `.claude-plugin` format. Two named scanner subagents come with the plugin: `sonar-scanner-claude` (model: haiku) and `sonar-scanner-copilot` (model: gpt-5-mini); selection is by agent name.
56+
57+
Prefer the raw CLI? Grab the skill bundle directly from the [latest release](https://github.com/RandomCodeSpace/sonar-predict/releases/latest) and run `./bin/sonar` from the unpacked directory. The first invocation auto-discovers Java 17+ (or downloads one if none is found); every subsequent call is offline.
5958

6059
## Usage
6160

@@ -77,18 +76,26 @@ sonar --format json check --diff
7776
sonar --coverage target/site/jacoco/jacoco.xml --coverage-min 80 analyze .
7877
```
7978

80-
## Use it as an AI-agent skill
79+
## Corporate / air-gapped setup
80+
81+
The plugin's launcher reads a single configuration file at `plugin/skills/sonar-predictor/config.env`. Edit it (or set the same-named environment variable, which takes precedence) to point at a corporate Maven proxy, a private JRE mirror, or a vendored bundle version — no code changes.
8182

82-
`sonar-predictor` is packaged as a [Claude Code](https://claude.com/claude-code) skill — an
83-
agent discovers it and runs it as a quality gate automatically. Install the bundle into your
84-
skills directory:
83+
| Key | What it controls |
84+
|-----|------------------|
85+
| `SONAR_MAVEN_REPO_URL` | Where the analyzer bundle is downloaded from. Default: Maven Central. |
86+
| `SONAR_BUNDLE_VERSION` | Bundle version pin. Bump in lockstep with each plugin release. |
87+
| `SONAR_MIN_JAVA_VERSION` | Minimum Java major version required. Default: `17`. |
88+
| `SONAR_JRE_URL_TEMPLATE` | JRE source for auto-download (when none is on the system). Tokens `{os}` `{arch}` `{version}` are substituted at runtime. Default: Adoptium Temurin API. |
89+
| `SONAR_JRE_VERSION` | JRE version to fetch. Default: `17`. |
90+
| `SONAR_DISABLE_JRE_AUTODOWNLOAD` | Set to `1` to refuse the JRE auto-download (corp-policy escape hatch). |
91+
92+
For a fully pre-staged install — no network at all on the developer machine — extract a skill bundle by hand into a known location and point the launcher at it:
8593

8694
```sh
87-
unzip sonar-predict-skill-0.1.0.zip -d ~/.claude/skills/
95+
export SONAR_PREDICTOR_HOME=/opt/sonar-predictor
8896
```
8997

90-
The bundled `SKILL.md` tells the agent when to run it and how to read its output; the CLI's
91-
own `--help` is the single source of truth for commands.
98+
The forking workflow: clone this repo, edit `plugin/skills/sonar-predictor/config.env`, push to your fork, `/plugin marketplace add <your-org>/sonar-predict`. One file.
9299

93100
## How it works
94101

@@ -134,6 +141,15 @@ smells, vulnerabilities and security hotspots — and imports coverage. It is a
134141
first-pass, **not** a replacement for release-time gates: the SonarQube server's deep
135142
cross-file taint analysis, Fortify, and dependency/CVE scanning remain their own tools.
136143

137-
## License
144+
## License & third-party components
145+
146+
This project (the CLI, daemon, protocol, dist module, plugin scaffolding) is licensed under the [Apache License 2.0](LICENSE).
147+
148+
The SonarSource analyzers and engine that `sonar-predictor` invokes at runtime are **third-party components with their own licenses**, listed in [NOTICE](NOTICE). Headline points:
149+
150+
- **`sonarlint-analysis-engine`** (the embedded analysis runtime, by SonarSource) — **LGPL v3**.
151+
- **SonarSource language analyzers** (`sonar-java`, `sonar-python`, `sonar-javascript`, `sonar-php`, `sonar-kotlin`, `sonar-go`, `sonar-ruby`, `sonar-scala`, `sonar-html`, `sonar-xml`) — **SONAR Source-Available License v1.0 (SSALv1)** since SonarSource's 2024 license change. Source-available, free for internal use, with restrictions on operating a competing "Service Offering".
152+
153+
The plugin **does not redistribute** these JARs — its launcher fetches them from Maven Central on first invocation (the same channel SonarSource themselves publish to). Editing `SONAR_MAVEN_REPO_URL` to a corporate mirror that proxies Maven Central is equivalent. None of this project's source code is derived from SonarSource code.
138154

139-
[Apache License 2.0](LICENSE).
155+
For a deeper inventory and the legal text of each license, see [NOTICE](NOTICE). If your environment's policy needs the analyzer JARs to come from somewhere other than Maven Central, the corporate / air-gapped setup above is your knob.

dist/README.md

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,61 @@
11
# sonar-predictor :: dist
22

3-
The distribution module. It builds **sonar-predictor** as a self-contained
4-
Claude Code agent skill — a directory an AI agent can use with zero setup.
3+
The distribution module. It assembles the **skill bundle** that `sonar-predictor`'s plugin launcher downloads at first run.
54

6-
## Build
7-
8-
From the repository root:
5+
## What this module builds
96

107
```sh
118
mvn package
129
```
1310

14-
This produces, under `dist/target/`:
11+
produces, under `dist/target/`:
1512

1613
- `skill/sonar-predictor/` — the exploded skill bundle:
17-
- `SKILL.md` — the agent-facing skill definition (the only file loaded into context)
14+
- `SKILL.md` — the agent-facing skill definition
1815
- `bin/sonar`, `bin/sonar.bat` — launchers that auto-discover a Java 17+ runtime
19-
- `lib/` — the CLI and daemon fat jars
16+
- `lib/` — the CLI and daemon shaded fat jars
2017
- `plugins/` — the 10 SonarSource analyzer plugins
21-
- `sonar-predict-skill-<version>.zip` — the same tree zipped, for non-skill use
22-
(manual install, or transfer to an air-gapped host)
18+
- `sonar-predict-skill-<version>.zip` — the same tree zipped, attached to the GitHub Release and **published to Maven Central as `io.github.randomcodespace.sonarpredict:sonar-predictor-dist:<version>:zip`**. The plugin's bootstrap launcher pulls this artifact (from Maven Central, or a corporate proxy of it) on first invocation.
2319

24-
The analyzer plugins are fetched through Maven, so any mirror, Nexus, or
25-
corporate proxy configured in your `settings.xml` is honored — there are no
26-
hardcoded download URLs.
20+
The analyzer plugins are resolved through Maven, so any mirror, Nexus, or corporate proxy configured in your `settings.xml` is honored — there are no hardcoded download URLs in the build.
2721

28-
## Install as a Claude Code skill
22+
## How users install `sonar-predictor`
2923

30-
```sh
31-
cp -r dist/target/skill/sonar-predictor ~/.claude/skills/
24+
End users do not interact with this module directly. They install the plugin from the in-repo `/plugin/` directory through their AI tool's marketplace:
25+
26+
```
27+
/plugin marketplace add RandomCodeSpace/sonar-predict
28+
/plugin install sonar-predictor@sonar-predict
3229
```
3330

34-
The skill is then available to agents in any project.
31+
The plugin's launcher (`/plugin/skills/sonar-predictor/bin/sonar`) downloads the bundle this module builds from Maven Central, verifies its SHA-1, caches it under `~/.cache/sonar-predictor/<version>/`, and execs the cached `bin/sonar` on every subsequent call.
3532

36-
## Prerequisites
33+
For raw / non-plugin use (manual or air-gapped install), the same skill bundle zip is attached to each GitHub Release.
3734

38-
The skill runs on a system Java; nothing is downloaded at runtime.
35+
## Prerequisites at runtime
3936

4037
**Required:**
41-
- **Java 17+** (JDK or JRE) — the CLI and daemon are JVM processes. `bin/sonar` auto-discovers it (`JAVA_HOME``PATH` → common install locations); one must exist somewhere on the machine.
38+
- **Java 17+** (JDK or JRE) — the CLI and daemon are JVM processes. `bin/sonar` auto-discovers it (`JAVA_HOME``PATH` → common install locations); the plugin's bootstrap also auto-downloads one from `SONAR_JRE_URL_TEMPLATE` (defaulting to Adoptium Temurin) if none is found.
4239
- **Linux or macOS** — the daemon uses Unix domain sockets. Windows is not yet supported (the TCP-socket fallback is a TODO).
4340
- A writable temp directory and ~1 GB free RAM (the daemon embeds the analysis engine; idle ~150–350 MB, up to ~1 GB during JS/TS analysis).
4441
- ~165 MB disk for the bundle.
4542

4643
**For specific features:**
47-
- **`git`** on `PATH` — required for `sonar check --diff` (the primary agent workflow). `sonar check <files>` and `sonar analyze <dir>` work without it.
48-
- **Node.js 18.17+** on `PATH` — required for JavaScript/TypeScript/CSS analysis. Without it those 3 languages are skipped (the other 9 still analyze).
44+
- **`git`** on `PATH` — required for `sonar check --diff` (the primary agent workflow).
45+
- **Node.js 18.17+** on `PATH` — required for JavaScript/TypeScript/CSS analysis. Without it those 3 languages are skipped.
4946

50-
**Not required:** no network at runtime (fully offline after install), no per-language SDK or compiler, no Maven (build-time only).
51-
52-
If no Java 17+ is found, the `bin/sonar` launcher exits with a clear message.
47+
**Not required after first invocation:** no network, no per-language SDK or compiler, no Maven.
5348

5449
## Releasing
5550

56-
A release is cut by pushing a version tag. The
57-
`.github/workflows/publish.yml` GitHub Actions pipeline then:
51+
A release is cut by pushing a version tag. The `.github/workflows/publish.yml` GitHub Actions pipeline then:
5852

59-
1. derives the release version from the tag (`v0.1.0``0.1.0`) and strips
60-
`-SNAPSHOT` — Maven Central rejects snapshot versions,
61-
2. builds, tests, GPG-signs and deploys the library modules
62-
`protocol`, `daemon` and `cli` to Maven Central via the Sonatype
63-
Central Portal (this `dist` module sets `maven.deploy.skip=true`, so it
64-
is built but never staged to Central),
65-
3. creates a GitHub Release carrying two bundles:
66-
- **`sonar-predict-<version>-src.zip`** — the whole repository as a
67-
`git archive` of `HEAD`,
68-
- **`sonar-predict-skill-<version>.zip`** — the assembled skill bundle.
53+
1. derives the release version from the tag (`v0.1.0``0.1.0`) and strips `-SNAPSHOT`,
54+
2. builds, tests, GPG-signs and deploys the library modules `protocol`, `daemon` and `cli` to Maven Central via the Sonatype Central Portal,
55+
3. publishes this `dist` module's skill bundle zip to Maven Central as `sonar-predictor-dist:<version>:zip` (this is the artifact the plugin's bootstrap downloads),
56+
4. creates a GitHub Release carrying two bundles:
57+
- `sonar-predict-<version>-src.zip` — the whole repository as a `git archive` of `HEAD`,
58+
- `sonar-predict-skill-<version>.zip` — the assembled skill bundle.
6959

7060
To cut release `X.Y.Z`:
7161

@@ -74,13 +64,11 @@ git tag vX.Y.Z
7464
git push origin vX.Y.Z
7565
```
7666

77-
The workflow can also be run manually from the **Actions** tab
78-
(`workflow_dispatch`), supplying the version explicitly.
67+
When the release is cut, bump the plugin launcher's pinned bundle version in `plugin/skills/sonar-predictor/config.env` (`SONAR_BUNDLE_VERSION`) in the same commit, so a freshly installed plugin downloads the bundle that matches it.
7968

80-
### Required repository secrets
69+
The workflow can also be run manually from the **Actions** tab (`workflow_dispatch`), supplying the version explicitly.
8170

82-
The pipeline needs these secrets on the `RandomCodeSpace/sonar-predict`
83-
repository (Settings → Secrets and variables → Actions):
71+
### Required repository secrets
8472

8573
| Secret | Purpose |
8674
|--------|---------|
@@ -89,6 +77,4 @@ repository (Settings → Secrets and variables → Actions):
8977
| `MAVEN_GPG_PRIVATE_KEY` | ASCII-armored GPG private key used to sign artifacts |
9078
| `MAVEN_GPG_PASSPHRASE` | Passphrase for that GPG key (omit if the key has none) |
9179

92-
`GITHUB_TOKEN` is provided automatically and needs no setup. The
93-
`io.github.randomcodespace` namespace must also be registered and verified
94-
on the Sonatype Central Portal.
80+
`GITHUB_TOKEN` is provided automatically. The `io.github.randomcodespace` namespace must be registered and verified on the Sonatype Central Portal.

0 commit comments

Comments
 (0)