diff --git a/.coverage b/.coverage
new file mode 100644
index 00000000..f7533f76
Binary files /dev/null and b/.coverage differ
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..e4142a4d
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,9 @@
+.git
+target
+node_modules
+*.md
+docs/
+tests/
+.github/
+helm/
+src/osscodeiq/
diff --git a/.github/workflows/beta-java.yml b/.github/workflows/beta-java.yml
new file mode 100644
index 00000000..46aa89f8
--- /dev/null
+++ b/.github/workflows/beta-java.yml
@@ -0,0 +1,70 @@
+name: Beta Release (Java)
+on:
+ workflow_dispatch: # Manual trigger ONLY
+
+jobs:
+ beta:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ packages: write
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '25'
+ cache: 'maven'
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+
+ - name: Determine beta version
+ id: version
+ run: |
+ LATEST_BETA=$(git tag -l 'v0.0.1-beta.*' | sort -V | tail -1)
+ if [ -z "$LATEST_BETA" ]; then
+ NEXT_NUM=0
+ else
+ CURRENT_NUM=$(echo "$LATEST_BETA" | grep -oP 'beta\.\K[0-9]+')
+ NEXT_NUM=$((CURRENT_NUM + 1))
+ fi
+ VERSION="0.0.1-beta.${NEXT_NUM}"
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "tag=v$VERSION" >> $GITHUB_OUTPUT
+ echo "Next beta version: $VERSION"
+
+ - name: Set version in pom.xml
+ run: mvn versions:set -DnewVersion=${{ steps.version.outputs.version }} -B
+
+ - name: Build and test
+ run: mvn clean verify -B
+
+ - name: Deploy to Maven Central
+ env:
+ MAVEN_USERNAME: ${{ secrets.OSS_NEXUS_USER }}
+ MAVEN_PASSWORD: ${{ secrets.OSS_NEXUS_PASS }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+ run: mvn deploy -P release -DskipTests -B
+
+ - name: Create git tag
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+ git tag -a ${{ steps.version.outputs.tag }} -m "Beta release ${{ steps.version.outputs.version }}"
+ git push origin ${{ steps.version.outputs.tag }}
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ steps.version.outputs.tag }}
+ name: "Beta ${{ steps.version.outputs.version }}"
+ prerelease: true
+ generate_release_notes: true
+ files: |
+ target/code-iq-*-cli.jar
diff --git a/.github/workflows/beta.yml b/.github/workflows/beta.yml
deleted file mode 100644
index 67838aa6..00000000
--- a/.github/workflows/beta.yml
+++ /dev/null
@@ -1,110 +0,0 @@
-name: Publish Beta
-
-on:
- push:
- branches: [main]
- paths:
- - "src/**"
- - "tests/**"
- - "pyproject.toml"
- workflow_dispatch:
-
-permissions:
- contents: write
- packages: write
-
-jobs:
- build-and-publish:
- name: Build & publish beta
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.12"
-
- - name: Install build tools
- run: pip install build tomli
-
- - name: Compute beta version
- id: version
- run: |
- # Derive base version from latest stable release tag (e.g. v0.0.1 -> 0.0.1)
- STABLE_TAG=$(git tag -l "v[0-9]*.[0-9]*.[0-9]*" --sort=-v:refname | grep -vE 'b[0-9]+$' | head -n1)
- if [ -n "$STABLE_TAG" ]; then
- BASE="${STABLE_TAG#v}"
- else
- BASE=$(python3 -c "import tomli; print(tomli.load(open('pyproject.toml','rb'))['project']['version'])")
- fi
- # Find latest beta tag number (PEP 440: v0.0.1b0, v0.0.1b1, ...)
- LATEST=$(git tag -l "v${BASE}b*" --sort=-v:refname | head -n1)
- if [ -n "$LATEST" ]; then
- LAST_NUM=$(echo "$LATEST" | sed "s/v${BASE}b//")
- BETA_NUM=$((LAST_NUM + 1))
- else
- BETA_NUM=0
- fi
- PEP_VERSION="${BASE}b${BETA_NUM}"
- SHORT_SHA=$(git rev-parse --short HEAD)
- echo "version=$PEP_VERSION" >> "$GITHUB_OUTPUT"
- echo "tag=v${PEP_VERSION}" >> "$GITHUB_OUTPUT"
- echo "sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
- echo "Beta version: v${PEP_VERSION} @ ${SHORT_SHA}"
-
- - name: Patch version in pyproject.toml
- env:
- BETA_VERSION: ${{ steps.version.outputs.version }}
- run: |
- python3 << 'PYEOF'
- import os, re
- ver = os.environ["BETA_VERSION"]
- with open("pyproject.toml", "r") as f:
- content = f.read()
- content = re.sub(r'version\s*=\s*"[^"]+"', f'version = "{ver}"', content, count=1)
- with open("pyproject.toml", "w") as f:
- f.write(content)
- PYEOF
-
- - name: Build wheel and sdist
- run: python -m build
-
- - name: Verify wheel
- run: |
- pip install dist/*.whl
- python -c "import importlib.metadata; print(f'Built: {importlib.metadata.version(\"osscodeiq\")}')"
- python -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'{len(r.all_detectors())} detectors')"
-
- - name: Delete existing beta tag (if any)
- env:
- BETA_TAG: ${{ steps.version.outputs.tag }}
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release delete "$BETA_TAG" --yes 2>/dev/null || true
- git push origin ":refs/tags/$BETA_TAG" 2>/dev/null || true
-
- - name: Create beta release with wheel
- env:
- BETA_TAG: ${{ steps.version.outputs.tag }}
- BETA_PEP: ${{ steps.version.outputs.version }}
- BETA_SHA: ${{ steps.version.outputs.sha }}
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release create "$BETA_TAG" dist/* \
- --title "${BETA_PEP} (${BETA_SHA})" \
- --notes "Auto-generated beta build from commit $BETA_SHA.
-
- **Install:**
- \`\`\`bash
- pip install https://github.com/RandomCodeSpace/code-iq/releases/download/${BETA_TAG}/osscodeiq-${BETA_PEP}-py3-none-any.whl
- \`\`\`
-
- **Or with osscodeiq CLI:**
- \`\`\`bash
- osscodeiq version
- osscodeiq analyze /path/to/repo
- \`\`\`" \
- --prerelease
diff --git a/.github/workflows/ci-java.yml b/.github/workflows/ci-java.yml
new file mode 100644
index 00000000..22f1f087
--- /dev/null
+++ b/.github/workflows/ci-java.yml
@@ -0,0 +1,45 @@
+name: Java CI
+on:
+ push:
+ branches: [main, java]
+ paths: ['src/**', 'pom.xml']
+ pull_request:
+ branches: [main, java]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '25'
+ cache: 'maven'
+ - run: mvn clean verify -B
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: test-results
+ path: target/surefire-reports/
+ - uses: actions/upload-artifact@v4
+ with:
+ name: coverage-report
+ path: target/site/jacoco/
+
+ cross-platform:
+ needs: build
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-latest, macos-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '25'
+ cache: 'maven'
+ - run: mvn clean verify -B -pl . -Dfrontend.skip=true
+ continue-on-error: true
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 2d090970..00000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: CI
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
-
-permissions:
- contents: read
-
-jobs:
- test:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- python-version: ["3.11", "3.12"]
-
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python-version }}
-
- - name: Install dependencies
- run: pip install -e ".[dev,kuzu]"
-
- - name: Run tests
- run: pytest --tb=short -q
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
deleted file mode 100644
index 2fd0c277..00000000
--- a/.github/workflows/publish.yml
+++ /dev/null
@@ -1,199 +0,0 @@
-name: Publish to PyPI
-
-on:
- workflow_dispatch:
- inputs:
- version:
- description: "Release version (e.g. 0.1.0)"
- required: true
- type: string
- dry_run:
- description: "Dry run (build + test only, no upload)"
- type: boolean
- default: false
-
-permissions:
- contents: write
- id-token: write
-
-jobs:
- build:
- name: Build wheel & sdist
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.12"
-
- - name: Install build tools
- run: pip install build tomli
-
- - name: Patch version in pyproject.toml
- env:
- RELEASE_VERSION: ${{ inputs.version }}
- run: |
- python3 << 'PYEOF'
- import os, re
- ver = os.environ["RELEASE_VERSION"]
- with open("pyproject.toml", "r") as f:
- content = f.read()
- content = re.sub(r'version\s*=\s*"[^"]+"', f'version = "{ver}"', content, count=1)
- with open("pyproject.toml", "w") as f:
- f.write(content)
- print(f"Version set to: {ver}")
- PYEOF
-
- - name: Build wheel and sdist
- run: python -m build
-
- - name: Verify wheel contents
- run: |
- pip install dist/*.whl
- python -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'Build OK: {len(r.all_detectors())} detectors')"
-
- - name: Upload build artifacts
- uses: actions/upload-artifact@v4
- with:
- name: dist
- path: dist/
-
- test-os:
- name: "${{ matrix.os }} / Python ${{ matrix.python }}"
- needs: build
- strategy:
- fail-fast: false
- matrix:
- include:
- # Windows 10/11
- - { os: windows-latest, python: "3.11" }
- - { os: windows-latest, python: "3.12" }
- - { os: windows-latest, python: "3.13" }
- # macOS (Apple Silicon + Intel)
- - { os: macos-latest, python: "3.11" }
- - { os: macos-latest, python: "3.12" }
- - { os: macos-latest, python: "3.13" }
- # Ubuntu / Linux
- - { os: ubuntu-latest, python: "3.11" }
- - { os: ubuntu-latest, python: "3.12" }
- - { os: ubuntu-latest, python: "3.13" }
- - { os: ubuntu-22.04, python: "3.11" }
- - { os: ubuntu-22.04, python: "3.12" }
- runs-on: ${{ matrix.os }}
- steps:
- - name: Download artifacts
- uses: actions/download-artifact@v4
- with:
- name: dist
- path: dist/
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: ${{ matrix.python }}
-
- - name: Install from wheel (no root, no system deps)
- shell: bash
- run: pip install dist/*.whl
-
- - name: Verify CLI
- run: osscodeiq --help
-
- - name: Verify detectors
- run: python -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'{len(r.all_detectors())} detectors')"
-
- test-container:
- name: "${{ matrix.name }}"
- needs: build
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- include:
- # RHEL / UBI — pip install only, NO root, NO dnf
- - { name: "UBI 8 / RHEL 8 (Python 3.11)", container: "registry.access.redhat.com/ubi8/python-311:latest" }
- - { name: "UBI 9 / RHEL 9 (Python 3.11)", container: "registry.access.redhat.com/ubi9/python-311:latest" }
- - { name: "UBI 9 / RHEL 9 (Python 3.12)", container: "registry.access.redhat.com/ubi9/python-312:latest" }
- # Debian / Ubuntu slim
- - { name: "Debian Bookworm (3.11)", container: "python:3.11-slim-bookworm" }
- - { name: "Debian Bookworm (3.12)", container: "python:3.12-slim-bookworm" }
- - { name: "Debian Bookworm (3.13)", container: "python:3.13-slim-bookworm" }
- # Alpine (musl libc)
- - { name: "Alpine (3.11)", container: "python:3.11-alpine" }
- - { name: "Alpine (3.12)", container: "python:3.12-alpine" }
- # Fedora
- - { name: "Fedora 40", container: "fedora:40" }
- container:
- image: ${{ matrix.container }}
- steps:
- - name: Download artifacts
- uses: actions/download-artifact@v4
- with:
- name: dist
- path: dist/
-
- - name: Install Python (Amazon Linux / Fedora only)
- if: contains(matrix.container, 'amazonlinux') || contains(matrix.container, 'fedora')
- run: |
- dnf install -y python3 python3-pip 2>/dev/null || yum install -y python3 python3-pip 2>/dev/null || true
-
- - name: Install from wheel (no root system deps needed)
- run: pip install dist/*.whl || pip3 install dist/*.whl
-
- - name: Verify CLI
- run: osscodeiq --help || python3 -m osscodeiq.cli --help
-
- - name: Verify detectors
- run: python3 -c "from osscodeiq.detectors.registry import DetectorRegistry; r = DetectorRegistry(); r.load_builtin_detectors(); print(f'{len(r.all_detectors())} detectors')"
-
- publish-pypi:
- name: Publish to PyPI
- needs: [test-os, test-container]
- runs-on: ubuntu-latest
- if: inputs.dry_run == false
- environment:
- name: pypi
- url: https://pypi.org/p/osscodeiq
- permissions:
- id-token: write
- attestations: write
- steps:
- - name: Download artifacts
- uses: actions/download-artifact@v4
- with:
- name: dist
- path: dist/
-
- - name: Publish to PyPI
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
- with:
- attestations: true
-
- github-release:
- name: Create GitHub Release
- needs: publish-pypi
- runs-on: ubuntu-latest
- permissions:
- contents: write
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Download artifacts
- uses: actions/download-artifact@v4
- with:
- name: dist
- path: dist/
-
- - name: Create GitHub Release
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- VERSION: ${{ inputs.version }}
- run: |
- gh release create "v${VERSION}" dist/* \
- --title "v${VERSION}" \
- --generate-notes \
- --latest
diff --git a/.github/workflows/release-java.yml b/.github/workflows/release-java.yml
new file mode 100644
index 00000000..96eb7361
--- /dev/null
+++ b/.github/workflows/release-java.yml
@@ -0,0 +1,47 @@
+name: Release to Maven Central
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Release version (e.g., 0.1.0)'
+ required: true
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '25'
+ cache: 'maven'
+ server-id: central
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+ - name: Set release version
+ env:
+ RELEASE_VERSION: ${{ inputs.version }}
+ run: mvn versions:set -DnewVersion="$RELEASE_VERSION"
+ - name: Deploy to Maven Central
+ env:
+ MAVEN_USERNAME: ${{ secrets.OSS_NEXUS_USER }}
+ MAVEN_PASSWORD: ${{ secrets.OSS_NEXUS_PASS }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+ run: mvn clean deploy -P release -B
+ - name: Tag release
+ env:
+ RELEASE_VERSION: ${{ inputs.version }}
+ run: |
+ git tag "v${RELEASE_VERSION}"
+ git push origin "v${RELEASE_VERSION}"
+ - uses: softprops/action-gh-release@v2
+ with:
+ tag_name: v${{ inputs.version }}
+ generate_release_notes: true
+ files: |
+ target/code-iq-*-cli.jar
diff --git a/.github/workflows/sbom.yml b/.github/workflows/sbom.yml
deleted file mode 100644
index 774cbf10..00000000
--- a/.github/workflows/sbom.yml
+++ /dev/null
@@ -1,75 +0,0 @@
-name: SBOM + Dependency Audit
-
-on:
- push:
- branches: [main]
- schedule:
- - cron: "0 6 * * 1"
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- sbom-and-audit:
- name: Generate SBOM & scan dependencies
- runs-on: ubuntu-latest
- continue-on-error: true
- steps:
- - uses: actions/checkout@v4
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.12"
-
- - name: Install project + tools
- run: |
- pip install .
- pip install pip-audit cyclonedx-bom pip-licenses
-
- - name: Generate CycloneDX SBOM (JSON)
- run: |
- cyclonedx-py environment \
- --output-format json \
- --outfile sbom-cyclonedx.json \
- 2>&1 || cyclonedx-py --format json -o sbom-cyclonedx.json 2>&1 || true
- echo "CycloneDX SBOM generated"
-
- - name: Generate dependency list with licenses
- run: |
- pip-licenses --format=json --with-urls --with-description > dependencies-licenses.json
- pip-licenses --format=plain --with-urls
- echo ""
- echo "=== License summary ==="
- pip-licenses --summary
-
- - name: Audit dependencies for vulnerabilities
- run: |
- echo "=== Scanning all installed packages (including transitive) ==="
- pip-audit --desc --format=json --output=audit-report.json 2>&1 || true
- echo ""
- echo "=== Audit Results ==="
- pip-audit --desc 2>&1 || true
-
- - name: Count results
- run: |
- echo "=== Installed packages ==="
- pip list --format=columns | wc -l
- echo ""
- echo "=== Direct dependencies ==="
- python3 -c "import tomli; deps=tomli.load(open('pyproject.toml','rb'))['project']['dependencies']; print(f'{len(deps)} direct dependencies')"
- echo ""
- echo "=== Transitive dependencies ==="
- pip list --format=json | python3 -c "import sys,json; pkgs=json.load(sys.stdin); print(f'{len(pkgs)} total packages installed (direct + transitive)')"
-
- - name: Upload SBOM artifact
- if: always()
- uses: actions/upload-artifact@v4
- with:
- name: sbom-report
- path: |
- sbom-cyclonedx.json
- dependencies-licenses.json
- audit-report.json
- retention-days: 90
diff --git a/.github/workflows/sonarcloud-java.yml b/.github/workflows/sonarcloud-java.yml
new file mode 100644
index 00000000..f135c8f8
--- /dev/null
+++ b/.github/workflows/sonarcloud-java.yml
@@ -0,0 +1,30 @@
+name: SonarCloud Java
+on:
+ push:
+ branches: [main, java]
+ paths: ['src/**', 'pom.xml']
+ pull_request:
+ branches: [main, java]
+
+jobs:
+ sonar:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '25'
+ cache: 'maven'
+ - name: Build and generate coverage
+ run: mvn clean verify -B
+ - name: SonarCloud analysis
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ run: >
+ mvn sonar:sonar -B
+ -Dsonar.projectKey=RandomCodeSpace_code-iq-java
+ -Dsonar.organization=randomcodespace
+ -Dsonar.host.url=https://sonarcloud.io
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
deleted file mode 100644
index c4c2a8ce..00000000
--- a/.github/workflows/sonarcloud.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: SonarCloud Analysis
-
-on:
- push:
- branches: [main]
- pull_request:
- branches: [main]
- workflow_dispatch:
-
-permissions:
- contents: read
-
-jobs:
- sonarcloud:
- name: SonarCloud Scan
- runs-on: ubuntu-latest
- continue-on-error: true
- steps:
- - uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Set up Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.12"
-
- - name: Install project + test deps
- run: pip install -e ".[dev,kuzu]"
-
- - name: Run tests with coverage
- run: |
- pytest tests/ --cov=osscodeiq --cov-report=xml:coverage.xml --junitxml=test-results.xml -q || true
-
- - name: SonarCloud Scan
- uses: SonarSource/sonarqube-scan-action@v6
- env:
- SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 78429b72..585e762b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,29 +1,13 @@
-# Python
-__pycache__/
-*.py[cod]
-*$py.class
-*.so
-*.egg-info/
-*.egg
-.eggs/
-dist/
-build/
-sdist/
-wheels/
-*.whl
-
-# Virtual environments
-.venv/
-venv/
-env/
-ENV/
-
-# Testing
-.pytest_cache/
-.coverage
-htmlcov/
-.tox/
-.nox/
+# Java build
+target/
+*.class
+*.jar
+!src/main/resources/static/js/vendor/*.js
+.classpath
+.project
+.settings/
+.factorypath
+*.iml
# IDE
.idea/
@@ -31,8 +15,6 @@ htmlcov/
*.swp
*.swo
*~
-.project
-.settings/
# OS
.DS_Store
@@ -41,7 +23,7 @@ Thumbs.db
# Project
.osscodeiq/
.superpowers/
-docs/superpowers/
+.code-intelligence/
.code_intelligence_cache*/
*.db
*.db-wal
@@ -56,7 +38,10 @@ docs/superpowers/
# Logs
*.log
+# Frontend
+src/main/frontend/node_modules/
+src/main/frontend/node/
+
# Distribution
*.tar.gz
*.zip
-pytest-of-dev/
diff --git a/CLAUDE.md b/CLAUDE.md
index 45619028..3479205d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,199 +1,300 @@
-# OSSCodeIQ — Project Instructions
+# OSSCodeIQ (Java) -- Project Instructions
## What This Project Is
-**OSSCodeIQ** (`osscodeiq` on PyPI) — a CLI tool + server that scans codebases to build a deterministic code knowledge graph. No AI, no external APIs — pure pattern matching. 97 detectors, 35 languages, 3 storage backends (NetworkX, SQLite, KuzuDB), REST API + MCP server, interactive flow diagrams.
+**OSSCodeIQ** -- a CLI tool + server that scans codebases to build a deterministic code knowledge graph. No AI, no external APIs -- pure static analysis. 106 detectors, 35+ languages, Neo4j Embedded graph database, Hazelcast distributed cache, Spring AI MCP server, REST API, web UI.
-- **PyPI package:** `osscodeiq`
-- **CLI command:** `osscodeiq`
-- **Python package:** `osscodeiq` (under `src/osscodeiq/`)
-- **GitHub repo:** `RandomCodeSpace/code-iq` (repo name differs from package name)
-- **Cache directory on disk:** `.osscodeiq`
+- **Maven coordinates:** `io.github.randomcodespace.iq:code-iq`
+- **CLI command:** `code-iq` (via `java -jar`)
+- **Java package:** `io.github.randomcodespace.iq` (under `src/main/java/`)
+- **GitHub repo:** `RandomCodeSpace/code-iq` (branch: `java`)
+- **Cache directory on disk:** `.code-intelligence` (SQLite analysis cache)
+- **Config file:** `.osscodeiq.yml` (project-level overrides)
+
+## Tech Stack
+
+- Java 25 (virtual threads, pattern matching, records, sealed classes)
+- Spring Boot 4.0.5
+- Neo4j Embedded 2026.02.3 (Community Edition, no external server)
+- Hazelcast 5.6.0 (distributed cache, K8s auto-discovery)
+- Spring AI 1.1.4 (MCP server, streamable HTTP)
+- JavaParser 3.28.0 (Java AST analysis)
+- ANTLR 4.13.2 (TypeScript/JavaScript, Python, Go, C#, Rust, C++ grammars)
+- Picocli 4.7.7 (CLI framework, integrated with Spring Boot)
+- Thymeleaf + HTMX (web UI)
+- SQLite JDBC (incremental analysis cache)
## Architecture
```
-FileDiscovery → Parsers → Detectors → GraphBuilder (buffered) → Linkers → LayerClassifier → GraphStore (backend)
- ↓
- CodeIQService (shared facade)
- ↙ ↘
- FastAPI REST (/api) FastMCP MCP (/mcp)
+FileDiscovery --> Parsers --> Detectors (virtual threads) --> GraphBuilder (buffered) --> Linkers --> LayerClassifier --> Neo4j Embedded
+ |
+ GraphStore (facade)
+ / | \
+ REST API MCP Server Web UI
+ (/api) (/mcp) (/)
```
-- **Detectors** follow the `Detector` Protocol in `detectors/base.py` — implement `name`, `supported_languages`, `detect(ctx) -> DetectorResult`
-- **Backends** follow the `GraphBackend` Protocol in `graph/backend.py` — implement 16 methods. `CypherBackend` is optional for Cypher-capable backends.
-- **GraphStore** is a facade delegating to a backend — never access backends directly
-- **GraphBuilder** buffers all nodes and edges, flushes nodes first then edges (ensures cross-backend parity)
-- **Linkers** run after all detectors, produce cross-file relationship edges
-- **LayerClassifier** runs after linkers, sets `layer` property on every node
-- **CodeIQService** wraps GraphStore + FlowEngine + GraphQuery + Analyzer — shared by REST and MCP
-- **Server** is a single FastAPI app: `/api` (REST), `/mcp` (MCP via fastmcp streamable HTTP), `/` (welcome UI), `/docs` (OpenAPI)
+### Pipeline Components
+- **FileDiscovery** -- discovers files via `git ls-files` or directory walk, maps extensions to languages
+- **StructuredParser** -- routes files to JavaParser (Java), ANTLR (TS/Py/Go/C#/Rust/C++), or raw text
+- **Detectors** -- 97 concrete detector beans (Spring `@Component`), auto-discovered via classpath scan
+- **GraphBuilder** -- buffers all nodes and edges, flushes nodes first then edges (determinism guarantee)
+- **Linkers** -- run after all detectors: `TopicLinker`, `EntityLinker`, `ModuleContainmentLinker`
+- **LayerClassifier** -- sets `layer` property on every node: `frontend | backend | infra | shared | unknown`
+- **GraphStore** -- facade over Neo4j, delegates Cypher operations
+- **AnalysisCache** -- SQLite-backed file hash cache for incremental analysis
-## Critical Rules
+### Spring Profiles
+- **`indexing`** -- active during CLI analyze/stats/graph/query/find/flow/bundle/cache/plugins commands. Starts Neo4j Embedded, runs analysis pipeline.
+- **`serving`** -- active during `serve` command. Starts REST API, MCP server, web UI, health endpoint.
-### Determinism is Non-Negotiable
-- Same input MUST produce same output, every time, on every backend
-- No set iteration without `sorted()` first
-- No dependency on thread completion order (builder uses indexed result slots)
-- All detectors must be stateless pure functions — no class-level mutable state
-
-### Cross-Backend Data Parity
-- All 3 backends (NetworkX, SQLite, KuzuDB) must produce identical node and edge counts
-- Edges are only added if both source and target nodes exist
-- Test parity after any change to builder, store, or backends
-
-### Windows Compatibility
-- Always use `encoding="utf-8"` when reading/writing files (Windows defaults to cp1252)
-- This applies to templates, vendor JS, HTML output, and any file I/O in the server
-
-### pyproject.toml is the Single Source of Truth
-- All dependencies, scripts, metadata, and package config live in `pyproject.toml`
-- After ANY change to pyproject.toml, run `uv lock` and commit both files together
-- Version in pyproject.toml is `0.0.0` (placeholder) — publish/beta workflows patch it at build time
-- Server deps (fastapi, uvicorn, fastmcp) are core dependencies, not optional
-- Only `dev` (pytest) and `kuzu` remain as optional deps
-
-### GitHub References
-- Repo URL is `RandomCodeSpace/code-iq` — do NOT change this even though package is `osscodeiq`
-- SonarCloud project key: `RandomCodeSpace_code-iq`
-- Badge URLs, workflow URLs, and clone URLs all use `code-iq`
+## Package Structure
-## Code Conventions
+```
+io.github.randomcodespace.iq
+ |-- CodeIqApplication.java # Spring Boot main class
+ |-- analyzer/ # Pipeline: Analyzer, FileDiscovery, GraphBuilder, LayerClassifier
+ | |-- linker/ # Cross-file linkers: TopicLinker, EntityLinker, ModuleContainmentLinker
+ |-- api/ # REST controllers: GraphController, FlowController
+ |-- cache/ # AnalysisCache (SQLite), FileHasher
+ |-- cli/ # Picocli commands (12 commands + CodeIqCli parent + CliOutput helper)
+ |-- config/ # Spring config: Neo4jConfig, HazelcastConfig, CodeIqConfig, JacksonConfig
+ |-- detector/ # Detector interface + 97 concrete detectors
+ | |-- auth/ # LDAP, certificate, session/header auth
+ | |-- config/ # YAML, JSON, TOML, INI, properties, K8s, Helm, GHA, etc.
+ | |-- cpp/ # C++ structures
+ | |-- csharp/ # EF Core, Minimal APIs, C# structures
+ | |-- docs/ # Markdown structure
+ | |-- frontend/ # React, Vue, Angular, Svelte, frontend routes
+ | |-- generic/ # Generic imports
+ | |-- go/ # Go web, ORM, structures
+ | |-- iac/ # Terraform, Dockerfile, Bicep
+ | |-- java/ # 27 Java detectors (Spring, JPA, Kafka, gRPC, etc.)
+ | |-- kotlin/ # Ktor, Kotlin structures
+ | |-- proto/ # Proto structures
+ | |-- python/ # Django, FastAPI, Flask, SQLAlchemy, Celery, etc.
+ | |-- rust/ # Actix-web, Rust structures
+ | |-- scala/ # Scala structures
+ | |-- shell/ # Bash, PowerShell
+ | |-- typescript/ # Express, NestJS, Fastify, Prisma, TypeORM, etc.
+ |-- flow/ # FlowEngine, FlowRenderer, FlowViews, FlowModels
+ |-- grammar/ # ANTLR parser factory + generated parsers
+ | |-- cpp/, csharp/, golang/, javascript/, python/, rust/
+ |-- graph/ # GraphStore (facade), GraphRepository (Spring Data Neo4j)
+ |-- health/ # GraphHealthIndicator (Spring Actuator)
+ |-- mcp/ # McpTools (21 Spring AI @Tool methods)
+ |-- model/ # CodeNode, CodeEdge, NodeKind (31), EdgeKind (26)
+ |-- query/ # QueryService (graph queries), StatsService (categorized stats)
+ |-- web/ # ExplorerController (Thymeleaf web UI)
+```
-- Python 3.11+, `from __future__ import annotations`
-- Pydantic for data models, typer for CLI, rich for output
-- FastAPI for REST API, fastmcp for MCP server (streamable HTTP, NOT SSE)
-- Regex-based detection (no tree-sitter dependency for new detectors unless needed)
-- `NodeKind` and `EdgeKind` enums in `models/graph.py` — add new values there
-- ID format: `"{prefix}:{filepath}:{type}:{identifier}"` for cross-file uniqueness
-- Properties dict for detector-specific metadata (`auth_type`, `framework`, `roles`, etc.)
-- `layer` property on every node: `frontend | backend | infra | shared | unknown`
-- Suppress websockets deprecation warnings in serve command (upstream uvicorn issue)
+## Critical Rules
+
+### Determinism is Non-Negotiable
+- Same input MUST produce same output, every time
+- No `Set` iteration without sorting first (`TreeSet` or `stream().sorted()`)
+- No dependency on thread completion order (GraphBuilder uses indexed result slots)
+- All detectors must be stateless -- no mutable instance fields, use method-local state only
+- Collections in results must be deterministically ordered
+
+### Cross-Backend Consistency
+- The Python version has 3 backends (NetworkX, SQLite, KuzuDB). The Java version uses Neo4j Embedded only.
+- Node and edge counts should be consistent across runs (verified by benchmarks: 3 runs, identical counts)
+
+### Virtual Thread Safety
+- All file I/O and Neo4j operations run on virtual threads
+- The SQLite analysis cache uses `synchronized` blocks for thread safety
+- Hazelcast cache operations are thread-safe by design
+- Detectors MUST be stateless -- Spring `@Component` beans are singletons
## CLI Commands
-| Command | Purpose |
-|---------|---------|
-| `osscodeiq analyze [path]` | Scan codebase, build graph |
-| `osscodeiq graph [path]` | Export graph (json, yaml, mermaid, dot) |
-| `osscodeiq query [path]` | Semantic graph queries |
-| `osscodeiq find [what] [path]` | Preset queries (endpoints, guards, entities, etc.) |
-| `osscodeiq cypher [query]` | Raw Cypher (KuzuDB only) |
-| `osscodeiq flow [path]` | Architecture flow diagrams (mermaid, json, html) |
-| `osscodeiq serve [path]` | Start unified server (API + MCP) |
-| `osscodeiq bundle [path]` | Create distributable package |
-| `osscodeiq cache [action]` | Manage analysis cache |
-| `osscodeiq plugins [action]` | List/inspect detectors |
-| `osscodeiq version` | Show version info |
-
-## Server Architecture
-
-### Endpoints
-- `GET /` — Welcome page (self-contained HTML, fetches `/api/stats`)
-- `GET /api/stats` — Graph statistics
-- `GET /api/nodes`, `GET /api/edges` — Paginated queries with `?kind=&limit=&offset=`
-- `GET /api/nodes/{id}/neighbors` — Neighbor traversal
-- `GET /api/ego/{id}` — Ego subgraph
+| Command | Description |
+|---------|-------------|
+| `analyze [path]` | Scan codebase and build knowledge graph |
+| `stats [path]` | Show rich categorized statistics from analyzed graph |
+| `graph [path]` | Export graph (JSON, YAML, Mermaid, DOT) |
+| `query [path]` | Query graph relationships (consumers, producers, callers) |
+| `find [what] [path]` | Preset queries (endpoints, guards, entities, topics, etc.) |
+| `cypher [query]` | Execute raw Cypher queries against Neo4j |
+| `flow [path]` | Generate architecture flow diagrams |
+| `serve [path]` | Start web UI + REST API + MCP server |
+| `bundle [path]` | Package graph + source into distributable ZIP |
+| `cache [action]` | Manage analysis cache |
+| `plugins [action]` | List and inspect detectors |
+| `version` | Show version info |
+
+## Server Endpoints
+
+### REST API (`/api`)
+- `GET /api/stats` -- Graph statistics
+- `GET /api/stats/detailed?category=` -- Rich categorized stats
+- `GET /api/kinds` -- Node kinds with counts
+- `GET /api/kinds/{kind}` -- Paginated nodes by kind
+- `GET /api/nodes`, `GET /api/edges` -- Paginated queries
+- `GET /api/nodes/{id}/detail` -- Full node detail with edges
+- `GET /api/nodes/{id}/neighbors` -- Neighbor traversal
+- `GET /api/ego/{center}` -- Ego subgraph
- `GET /api/query/cycles`, `/shortest-path`, `/consumers/{id}`, `/producers/{id}`, `/callers/{id}`, `/dependencies/{id}`, `/dependents/{id}`
-- `GET /api/flow/{view}` — Flow diagrams (overview, ci, deploy, runtime, auth)
-- `POST /api/analyze` — Trigger analysis
-- `POST /api/cypher` — Raw Cypher (400 if not KuzuDB)
-- `GET /api/triage/component`, `/impact/{id}`, `/endpoints` — Agentic triage tools
-- `GET /api/search?q=` — Free-text graph search
-- `GET /api/file?path=` — Serve source files (path traversal protected)
-- `POST /mcp` — MCP endpoint (20 tools via streamable HTTP)
-
-### MCP Tools (20)
-15 core tools (get_stats, query_nodes, query_edges, get_node_neighbors, get_ego_graph, find_cycles, find_shortest_path, find_consumers, find_producers, find_callers, find_dependencies, find_dependents, generate_flow, analyze_codebase, run_cypher) + 5 agentic triage tools (find_component_by_file, trace_impact, find_related_endpoints, search_graph, read_file).
-
-### Key Server Files
-| File | Purpose |
-|------|---------|
-| `server/app.py` | FastAPI app assembly, mounts /api, /mcp, / |
-| `server/service.py` | CodeIQService — shared facade over GraphStore + FlowEngine + GraphQuery |
-| `server/routes.py` | REST API endpoints (uses Annotated type hints) |
-| `server/mcp_server.py` | FastMCP tool definitions |
-| `server/middleware.py` | Auth middleware stub (no-op, ready for future auth) |
-| `server/templates/welcome.html` | Self-contained welcome page |
+- `GET /api/triage/component?file=`, `/impact/{id}` -- Agentic triage
+- `GET /api/search?q=` -- Free-text search
+- `GET /api/file?path=` -- Source files (path traversal protected)
+- `GET /api/flow/{view}` -- Flow diagrams
+- `POST /api/analyze` -- Trigger analysis
+
+### MCP Tools (21)
+`get_stats`, `get_detailed_stats`, `query_nodes`, `query_edges`, `get_node_neighbors`, `get_ego_graph`, `find_cycles`, `find_shortest_path`, `find_consumers`, `find_producers`, `find_callers`, `find_dependencies`, `find_dependents`, `generate_flow`, `analyze_codebase`, `run_cypher`, `find_component_by_file`, `trace_impact`, `find_related_endpoints`, `search_graph`, `read_file`
+
+## Adding a New Detector
+
+1. Create file in `detector/
- Deterministic code graph discovery and analysis CLI — no AI, pure pattern matching
+ Deterministic code knowledge graph -- scans codebases to build a graph of services, endpoints, entities, infrastructure, auth patterns, and framework usage. No AI, pure static analysis.
OSSCodeIQ
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+