diff --git a/.cursor/skills/codegraphcontext/SKILL.md b/.cursor/skills/codegraphcontext/SKILL.md new file mode 100644 index 00000000..4fc28ea3 --- /dev/null +++ b/.cursor/skills/codegraphcontext/SKILL.md @@ -0,0 +1,40 @@ +--- +name: codegraphcontext +description: >- + Use CodeGraphContext (CGC) to index a repo into a graph DB and query it via CLI + or MCP. Apply when the user wants semantic/code-graph search, call graphs, + indexing, or configuring the codegraphcontext MCP server. +--- + +# CodeGraphContext (CGC) + +## When to use this skill + +- Indexing or re-indexing a codebase for AI or CLI queries. +- Explaining how to install and run `cgc`, choose a database backend, or wire MCP into an editor. +- Interpreting CGC tools: `codegraph_find_code`, `analyze_code_relationships`, `add_code_to_graph`, etc. + +## Core workflow + +1. **Install**: `pip install codegraphcontext` (or `pipx install codegraphcontext`). With **uv**, `uv tool install codegraphcontext` or `uvx codegraphcontext …` is supported; if a parser fails with `ModuleNotFoundError: tree_sitter_c_sharp`, run with an explicit extra package, e.g. `uvx --with tree-sitter-c-sharp codegraphcontext …`. +2. **Configure**: Optional `~/.codegraphcontext/.env` for `DEFAULT_DATABASE`, Neo4j URI, or Kùzu path. Run `cgc doctor` if connections fail. +3. **Index**: From the repo root, `cgc index .` (or `cgc index --force .` to rebuild). Ensure CLI and MCP use the same config so they see the same graph. +4. **Query**: CLI (`cgc find`, `cgc query`, …) or MCP tools after `cgc mcp setup` / `cgc mcp start` in the client config. + +## MCP setup (short) + +- Run `cgc mcp setup` and pick the editor, or add a server entry that runs `cgc` with args `mcp` `start` and the same env as the CLI. +- **OpenCode**: Follow the vendor MCP docs for registering a stdio server, then point the command at `cgc` with arguments `mcp`, `start` (same as other editors). Official OpenCode MCP overview: [OpenCode MCP servers](https://opencode.ai/docs/ko/mcp-servers/#_top). + +## Agent behavior + +- Prefer **indexing the workspace** before deep graph queries if the user has not indexed yet. +- For **fuzzy symbol search** on Kùzu/Falkor backends, matching is typo-tolerant (edit distance); on Neo4j, full-text fuzzy uses Lucene-style terms—preserve **original casing** in queries when fuzziness matters for camelCase symbols. +- If **`Repository.path` is missing** in the DB, that row is skipped for path checks and a **warning is logged** when repositories are listed; suggest cleaning stale `Repository` nodes (see Neo4j example in logs) if it keeps happening. + +## References in this repo + +- CLI entry: `codegraphcontext.cli.main` +- MCP server and tool wiring: `codegraphcontext.server`, `tool_definitions.py` +- User-facing setup detail: `docs/docs/setup_workflows.md`, `docs/docs/guides/mcp_guide.md` +- Published copy of this skill (for docs / GitHub): `docs/docs/agent_skill_codegraphcontext.md` diff --git a/.env.example b/.env.example index 5eacc242..d8e4ed25 100644 --- a/.env.example +++ b/.env.example @@ -1,36 +1,34 @@ -# Example .env file for CodeGraphContext Docker deployment -# Copy this file to .env and update with your values +# Example .env for CodeGraphContext Docker / Compose deployments. +# Copy to .env and replace every CHANGE_ME value before starting services. +# +# Deployment policy (see docker-compose.template.yml): +# - Never commit real passwords to git. +# - Compose files require NEO4J_PASSWORD when the neo4j profile is used. +# - Bind database ports to 127.0.0.1 in templates; expose via reverse proxy in production. -# Neo4j Configuration (if using Neo4j instead of FalkorDB Lite) -NEO4J_URI=bolt://neo4j:7687 +# Required when using the neo4j compose profile or docker-compose.yml +NEO4J_PASSWORD=CHANGE_ME + +# Neo4j client settings (CGC app / remote bolt) +NEO4J_URI=bolt://127.0.0.1:7687 NEO4J_USERNAME=neo4j -NEO4J_PASSWORD=codegraph123 -# Optional database name for Neo4j (if using Neo4j 4.0+ with multiple databases) # NEO4J_DATABASE=neo4j -# For production, use a strong password: -# NEO4J_PASSWORD=your_secure_password_here - -# Application Configuration +# Application CGC_HOME=/root/.codegraphcontext PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1 -# Optional: Logging Level +# Optional: Logging # LOG_LEVEL=INFO -# Optional: Custom workspace path -# WORKSPACE_PATH=/workspace - -# Remote FalkorDB Configuration (if using a hosted/remote FalkorDB instance) -# Set DATABASE_TYPE=falkordb-remote to use these, or just set FALKORDB_HOST -# and it will be auto-detected. -# FALKORDB_HOST=your-falkordb-host.example.com +# Remote FalkorDB (set DEFAULT_DATABASE=falkordb-remote or set FALKORDB_HOST) +# FALKORDB_HOST=127.0.0.1 # FALKORDB_PORT=6379 -# FALKORDB_PASSWORD=your_password_here +# FALKORDB_PASSWORD=CHANGE_ME # FALKORDB_USERNAME=default # FALKORDB_SSL=true # FALKORDB_GRAPH_NAME=codegraph -# Optional: Database selection -# DATABASE_TYPE=falkordb # or falkordb-remote or neo4j +# Backend selection: falkordb | falkordb-remote | neo4j | kuzudb | ladybugdb +# DEFAULT_DATABASE=falkordb diff --git a/CONTRIBUTING.md b/.github/CONTRIBUTING.md similarity index 77% rename from CONTRIBUTING.md rename to .github/CONTRIBUTING.md index 7c7ad973..74d5f6c4 100644 --- a/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -17,13 +17,18 @@ We welcome contributions! Please follow these steps: ## Debugging -To enable debug mode for detailed logging, locate the `debug_mode` variable in `src/codegraphcontext/tools/graph_builder.py` and set its value to `1`. +To enable detailed logging for debugging, use the CLI config command: -```python -# src/codegraphcontext/tools/graph_builder.py -debug_mode = 1 +```bash +# Enable general application logs +cgc config set ENABLE_APP_LOGS DEBUG + +# Enable low-level debug logs to a file (~/mcp_debug.log by default) +cgc config set DEBUG_LOGS true ``` +You can also set these via environment variables: `ENABLE_APP_LOGS=DEBUG` or `DEBUG_LOGS=true`. + ## Running Tests Please refer to [TESTING.md](TESTING.md) for detailed instructions on running the test suite, adding new tests, and understanding the test architecture. @@ -39,6 +44,3 @@ Quick summary: 2. Ensure `fast` tests pass locally (`./tests/run_tests.sh fast`). 3. Commit your changes with a descriptive commit message. 4. Submit a pull request to the `main` branch. - - - \ No newline at end of file diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 54bc0688..660cdb6c 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,15 +1 @@ -# These are supported funding model platforms - -github: Shashankss1205 # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry -polar: # Replace with a single Polar username -buy_me_a_coffee: # Replace with a single Buy Me a Coffee username -thanks_dev: # Replace with a single thanks.dev username -custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] +github: Shashankss1205 diff --git a/SECURITY.md b/.github/SECURITY.md similarity index 100% rename from SECURITY.md rename to .github/SECURITY.md diff --git a/funding.json b/.github/funding.json similarity index 100% rename from funding.json rename to .github/funding.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3481810c..826f7d36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,12 +33,13 @@ jobs: uses: actions/setup-python@v5 with: python-version: '3.12' + architecture: 'x64' cache: 'pip' - name: Install dependencies (Windows/Mac) if: runner.os != 'Linux' run: | - python -m pip install --upgrade pip + python -m pip install --upgrade pip setuptools wheel build pip install . pip install pyinstaller @@ -48,20 +49,17 @@ jobs: run: | pyinstaller cgc.spec --clean - - name: Build with PyInstaller (Linux - Manylinux) + - name: Install dependencies (Linux) if: runner.os == 'Linux' run: | - docker run --rm \ - -v ${{ github.workspace }}:/src \ - quay.io/pypa/manylinux2014_x86_64 \ - /bin/bash -c " - set -e - cd /src - /opt/python/cp312-cp312/bin/python -m pip install --upgrade pip - /opt/python/cp312-cp312/bin/pip install . pyinstaller - /opt/python/cp312-cp312/bin/pyinstaller cgc.spec --clean - " - sudo chown -R $USER:$USER dist build + python -m pip install --upgrade pip setuptools wheel build + pip install . + pip install pyinstaller + + - name: Build with PyInstaller (Linux) + if: runner.os == 'Linux' + run: | + pyinstaller cgc.spec --clean - name: Rename artifact (Linux/Mac) if: runner.os != 'Windows' diff --git a/.github/workflows/db-parity-check.yml b/.github/workflows/db-parity-check.yml new file mode 100644 index 00000000..f6f6eace --- /dev/null +++ b/.github/workflows/db-parity-check.yml @@ -0,0 +1,59 @@ +name: Database Parity Check + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + db-parity: + runs-on: ubuntu-latest + + services: + neo4j: + image: neo4j:5.12.0-community + ports: + - 7687:7687 + - 7474:7474 + env: + NEO4J_AUTH: neo4j/12345678 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install "tree-sitter==0.25.2" "tree-sitter-language-pack==0.13.0" + pip install .[dev] + + - name: Wait for Neo4j + run: | + echo "Waiting for Neo4j service container..." + for i in {1..30}; do + if curl -s http://localhost:7474 > /dev/null; then + echo "Neo4j is up!" + exit 0 + fi + sleep 2 + done + echo "Timed out waiting for Neo4j" + exit 1 + + - name: Run E2E Database Parity Verification + env: + NEO4J_URI: bolt://localhost:7687 + NEO4J_USERNAME: neo4j + NEO4J_PASSWORD: 12345678 + run: | + pytest tests/e2e/test_verify_databases_parity.py -v -s diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 2907fbab..6d3f96f0 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -22,8 +22,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -e . - pip install pytest + pip install -e .[dev] - name: Run end-to-end tests run: | diff --git a/.github/workflows/generate-bundle-on-demand.yml b/.github/workflows/generate-bundle-on-demand.yml index 08e2249c..2c58d1b7 100644 --- a/.github/workflows/generate-bundle-on-demand.yml +++ b/.github/workflows/generate-bundle-on-demand.yml @@ -15,6 +15,10 @@ on: description: 'Repository Name (auto-filled by API)' required: false type: string + email: + description: 'Email Address for notifications (auto-filled by API)' + required: false + type: string permissions: contents: write # Required to create/update releases @@ -38,7 +42,7 @@ jobs: - name: Install CodeGraphContext run: | uv pip install --system -e . - uv pip install --system tree-sitter tree-sitter-language-pack + uv pip install --system tree-sitter tree-sitter-language-pack huggingface_hub - name: Parse repository URL id: parse-url @@ -115,7 +119,7 @@ jobs: - name: Export to .cgc bundle id: export run: | - BUNDLE_NAME="${{ steps.parse-url.outputs.repo }}-${{ steps.repo-meta.outputs.tag }}-${{ steps.repo-meta.outputs.commit_short }}.cgc" + BUNDLE_NAME="cgc__${{ steps.parse-url.outputs.owner }}__${{ steps.parse-url.outputs.repo }}__${{ steps.repo-meta.outputs.tag }}__${{ steps.repo-meta.outputs.commit_short }}.cgc" echo "📦 Creating bundle: $BUNDLE_NAME" cgc bundle export "$BUNDLE_NAME" --repo "$(pwd)/${{ steps.parse-url.outputs.repo }}" @@ -155,75 +159,135 @@ jobs: bundle-metadata.json retention-days: 30 - - name: Create or update on-demand release - uses: softprops/action-gh-release@v1 + - name: Publish to Hugging Face Registry + shell: python + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + HF_ADMIN_WRITE_TOKEN: ${{ secrets.HF_ADMIN_WRITE_TOKEN }} + HF_REGISTRY_REPO: ${{ vars.HF_REGISTRY_REPO || secrets.HF_REGISTRY_REPO || 'codegraphcontext/registry' }} + run: | + import os + import json + import base64 + import requests + from datetime import datetime, timezone + from pathlib import Path + from huggingface_hub import HfApi + + hf_token = os.environ.get("HF_TOKEN") or os.environ.get("HF_ADMIN_WRITE_TOKEN") + if not hf_token: + print("Error: Hugging Face token is missing. Please set HF_TOKEN or HF_ADMIN_WRITE_TOKEN in secrets.") + exit(1) + + hf_repo = os.environ.get("HF_REGISTRY_REPO", "codegraphcontext/registry") + api = HfApi(token=hf_token) + + # 1. Base64 encode the .cgc file + bundle_name = "${{ steps.export.outputs.bundle_name }}" + bundle_path = Path(bundle_name) + base64_filename = bundle_path.name.replace(".cgc", "") + ".cgc.base64" + base64_path = Path("bundles") / base64_filename + base64_path.parent.mkdir(exist_ok=True) + + with open(bundle_path, "rb") as f: + cgc_data = f.read() + + base64_data = base64.b64encode(cgc_data) + with open(base64_path, "wb") as f: + f.write(base64_data) + + print(f"Base64 encoded {bundle_path} -> {base64_path}") + + # 2. Fetch the current manifest from Hugging Face + manifest_path = Path("manifest.json") + manifest_url = f"https://huggingface.co/datasets/{hf_repo}/raw/main/manifest.json" + try: + headers = {"Authorization": f"Bearer {hf_token}"} + r = requests.get(manifest_url, headers=headers, timeout=10) + if r.status_code == 200: + manifest = r.json() + else: + print(f"Manifest fetch returned status {r.status_code}. Starting fresh.") + manifest = {"bundles": []} + except Exception as e: + print(f"No existing manifest found or failed to download: {e}. Starting fresh.") + manifest = {"bundles": []} + + # 3. Compile the new entry + repo_owner = "${{ steps.parse-url.outputs.owner }}" + repo_name = "${{ steps.parse-url.outputs.repo }}" + repo_full = f"{repo_owner}/{repo_name}" + clean_repo_name = repo_full.replace("/", "_") + version = "${{ steps.repo-meta.outputs.tag }}" + generated_at = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + + new_entry = { + "name": repo_name, + "repo": repo_full, + "bundle_name": base64_filename, + "version": version, + "size": "${{ steps.export.outputs.bundle_size }}", + "download_url": f"https://huggingface.co/datasets/{hf_repo}/resolve/main/bundles/{base64_filename}", + "generated_at": generated_at, + "source": "server-indexed" + } + + # De-duplicate existing entries + manifest["bundles"] = [ + b for b in manifest.get("bundles", []) + if not (b.get("repo", "").lower() == repo_full.lower() and b.get("version") == version) + ] + manifest["bundles"].append(new_entry) + + # Write updated manifest locally + with open(manifest_path, "w") as f: + json.dump(manifest, f, indent=2) + + print("Manifest updated successfully.") + + # 4. Upload the files to Hugging Face dataset + # Upload base64 bundle + api.upload_file( + path_or_fileobj=str(base64_path), + path_in_repo=f"bundles/{base64_filename}", + repo_id=hf_repo, + repo_type="dataset", + ) + print("Uploaded base64 bundle to HF.") + + # Upload manifest.json + api.upload_file( + path_or_fileobj=str(manifest_path), + path_in_repo="manifest.json", + repo_id=hf_repo, + repo_type="dataset", + ) + print("Uploaded manifest.json to HF. Migration to HF successful!") + + - name: Send completion notification email + if: ${{ github.event.inputs.email != '' }} + continue-on-error: true + uses: dawidd6/action-send-mail@v3 with: - tag_name: on-demand-bundles - name: On-Demand Generated Bundles + server_address: smtp.gmail.com + server_port: 465 + username: ${{ secrets.MAIL_USERNAME }} + password: ${{ secrets.MAIL_PASSWORD }} + subject: "🎉 CGC Bundle Ready: ${{ steps.parse-url.outputs.owner }}/${{ steps.parse-url.outputs.repo }}" + to: ${{ github.event.inputs.email }} + from: CodeGraphContext body: | - # On-Demand Generated Bundles - - This release contains bundles generated on-demand by users. - - ## Latest Bundle + Your code graph index is ready! - - **Repository**: ${{ steps.parse-url.outputs.owner }}/${{ steps.parse-url.outputs.repo }} - - **Commit**: ${{ steps.repo-meta.outputs.commit_short }} - - **Tag/Version**: ${{ steps.repo-meta.outputs.tag }} - - **Generated**: $(date -u +"%Y-%m-%d %H:%M:%S UTC") - - **Bundle Size**: ${{ steps.export.outputs.bundle_size }} + Repository: ${{ steps.parse-url.outputs.owner }}/${{ steps.parse-url.outputs.repo }} + Bundle: ${{ steps.export.outputs.bundle_name }} + Size: ${{ steps.export.outputs.bundle_size }} - ## Usage + You can load this bundle into your local CodeGraphContext CLI database using: - ```bash - # Download the bundle - wget https://github.com/${{ github.repository }}/releases/download/on-demand-bundles/${{ steps.export.outputs.bundle_name }} - - # Load into your database cgc load ${{ steps.export.outputs.bundle_name }} - ``` - - --- - **Note**: Bundles are kept for 30 days. Popular bundles may be moved to the weekly pre-indexed releases. - files: | - ${{ steps.export.outputs.bundle_name }} - bundle-metadata.json - draft: false - prerelease: false - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Update manifest - run: | - echo "📝 Updating bundle manifest..." - - # Download existing manifest if it exists - wget https://github.com/${{ github.repository }}/releases/download/on-demand-bundles/manifest.json -O manifest.json 2>/dev/null || echo '{"bundles":[]}' > manifest.json - - # Add new bundle to manifest - cat manifest.json | jq --argjson new '{ - "repo": "${{ steps.parse-url.outputs.owner }}/${{ steps.parse-url.outputs.repo }}", - "bundle_name": "${{ steps.export.outputs.bundle_name }}", - "commit": "${{ steps.repo-meta.outputs.commit_short }}", - "tag": "${{ steps.repo-meta.outputs.tag }}", - "generated_at": "'$(date -u +"%Y-%m-%dT%H:%M:%SZ")'", - "download_url": "https://github.com/${{ github.repository }}/releases/download/on-demand-bundles/${{ steps.export.outputs.bundle_name }}", - "size": "${{ steps.export.outputs.bundle_size }}", - "status": "ready" - }' '.bundles += [$new]' > manifest-updated.json - - mv manifest-updated.json manifest.json - - echo "✅ Manifest updated" - - - name: Upload updated manifest - uses: softprops/action-gh-release@v1 - with: - tag_name: on-demand-bundles - files: manifest.json - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + Or view it directly online in the explorer. - name: Summary run: | @@ -233,8 +297,9 @@ jobs: echo "**Bundle**: ${{ steps.export.outputs.bundle_name }}" >> $GITHUB_STEP_SUMMARY echo "**Size**: ${{ steps.export.outputs.bundle_size }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "### Download" >> $GITHUB_STEP_SUMMARY + echo "### Download and CLI usage" >> $GITHUB_STEP_SUMMARY echo '```bash' >> $GITHUB_STEP_SUMMARY - echo "wget https://github.com/${{ github.repository }}/releases/download/on-demand-bundles/${{ steps.export.outputs.bundle_name }}" >> $GITHUB_STEP_SUMMARY + echo "# The bundle is indexed and hosted directly on the Hugging Face dataset registry!" >> $GITHUB_STEP_SUMMARY + echo "cgc registry download ${{ steps.export.outputs.bundle_name }}" >> $GITHUB_STEP_SUMMARY echo "cgc load ${{ steps.export.outputs.bundle_name }}" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..3b6cc1c1 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,43 @@ +name: Lint + +on: + push: + branches: + - main + pull_request: + workflow_dispatch: + +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install ruff + run: python -m pip install --upgrade pip ruff + + - name: Ruff check (M4 core + embeddings) + run: | + ruff check \ + src/codegraphcontext/core/graph_query.py \ + src/codegraphcontext/core/database_kuzu.py \ + src/codegraphcontext/core/database_ladybug.py \ + src/codegraphcontext/core/database_embedded_kuzu.py \ + src/codegraphcontext/tools/indexing/embeddings.py \ + src/codegraphcontext/tools/indexing/vector_resolver.py \ + --select F,E9 + + - name: Ruff format check (M4 core + embeddings) + run: | + ruff format --check \ + src/codegraphcontext/core/graph_query.py \ + src/codegraphcontext/core/database_kuzu.py \ + src/codegraphcontext/core/database_ladybug.py \ + src/codegraphcontext/tools/indexing/embeddings.py \ + src/codegraphcontext/tools/indexing/vector_resolver.py diff --git a/.github/workflows/pr-code-graph.yml b/.github/workflows/pr-code-graph.yml new file mode 100644 index 00000000..977b66fb --- /dev/null +++ b/.github/workflows/pr-code-graph.yml @@ -0,0 +1,114 @@ +# ───────────────────────────────────────────────────────────────────────────── +# .github/workflows/pr-code-graph.yml +# ───────────────────────────────────────────────────────────────────────────── +# Runs on every PR to produce a PR Reviewer graph JSON artifact. +# Uses CGC + FalkorDB Lite (Linux-native, zero-config embedded graph DB). +# ───────────────────────────────────────────────────────────────────────────── + +name: "PR Code Graph Analysis" + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + analyze: + name: "🔍 Build PR Code Graph" + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + # ── 1. Checkout the repository (with PR head) ──────────────────────── + - name: Checkout PR head + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 # full history for git-churn metrics + + # ── 2. Set up Python ────────────────────────────────────────────────── + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: "3.12" + cache: "pip" + + # ── 3. Install CGC + FalkorDB Lite ───────────────────────────────── + - name: Install CodeGraphContext + run: | + pip install --upgrade pip + pip install codegraphcontext falkordblite + + # ── 4. Run the PR graph analysis script ──────────────────────────── + - name: Analyze PR Code Graph + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # Extract owner/repo/pr from the event context + OWNER="${{ github.repository_owner }}" + REPO="${{ github.event.repository.name }}" + PR_NUMBER="${{ github.event.pull_request.number }}" + + # Run CGC indexing and export the graph + cgc index . + + mkdir -p "./pr-graph-output" + # Export the bundle so it can be viewed in the PR reviewer + cgc bundle export "./pr-graph-output/pr-${PR_NUMBER}-graph.cgc" --repo . + echo '{"info": "Graph is dynamically computed in the browser via GitHub API, but here is the exported code graph bundle."}' > "./pr-graph-output/pr-${PR_NUMBER}-metadata.json" + + # ── 5. Upload the JSON as a build artifact ───────────────────────── + - name: Upload PR Graph JSON + uses: actions/upload-artifact@v4 + with: + name: pr-code-graph-${{ github.event.pull_request.number }} + path: ./pr-graph-output/* + retention-days: 30 + + # ── 6. (Optional) Post a comment with the viewer link ────────────── + - name: Comment PR with viewer link + if: success() + continue-on-error: true + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const pr = context.payload.pull_request.number; + + const body = [ + '## 🔍 PR Code Graph Analysis', + '', + `**${context.payload.pull_request.title}** (#${pr})`, + '', + '### 📊 Interactive Visualization', + `View the blast radius graph: **[PR Reviewer Dashboard](https://cgc.codes/pr-reviewer/${owner}/${repo}/pull/${pr})**`, + '', + '### 📦 Artifacts', + `The graph JSON has been uploaded as a build artifact: \`pr-code-graph-${pr}\``, + '', + '---', + '*Generated by [CodeGraphContext](https://github.com/CodeGraphContext/CodeGraphContext) using FalkorDB Lite*', + ].join('\n'); + + // Check for existing bot comment to update instead of spam + const { data: comments } = await github.rest.issues.listComments({ + owner, repo, issue_number: pr, + }); + const existing = comments.find(c => + c.user.type === 'Bot' && c.body.includes('PR Code Graph Analysis') + ); + + if (existing) { + await github.rest.issues.updateComment({ + owner, repo, comment_id: existing.id, body, + }); + } else { + await github.rest.issues.createComment({ + owner, repo, issue_number: pr, body, + }); + } diff --git a/.github/workflows/project-routing.yml b/.github/workflows/project-routing.yml new file mode 100644 index 00000000..4ff99f63 --- /dev/null +++ b/.github/workflows/project-routing.yml @@ -0,0 +1,268 @@ +name: Route Issues + +on: + issues: + types: [labeled] + pull_request: + types: [labeled] + +jobs: + route: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + repository-projects: write + + steps: + - name: Route issue based on labels + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ROUTE_ISSUES_PAT || github.token }} + script: | + const labelName = context.payload.label.name; + const label = labelName.toLowerCase(); + const contentId = context.payload.issue?.node_id || context.payload.pull_request?.node_id; + + console.log(`Processing label: "${labelName}"`); + + // Resilient helper to fetch Project V2 metadata by number + async function getProjectMetadata(org, number) { + try { + const query = ` + query($org: String!, $number: Int!) { + organization(login: $org) { + projectV2(number: $number) { + id + title + fields(first: 50) { + nodes { + ... on ProjectV2FieldCommon { + id + name + dataType + } + ... on ProjectV2SingleSelectField { + id + name + options { + id + name + } + } + } + } + } + } + } + `; + const result = await github.graphql(query, { org, number }); + return result.organization?.projectV2; + } catch (e) { + console.log(`Failed to fetch project as organization, trying as user: ${e.message}`); + try { + const query = ` + query($user: String!, $number: Int!) { + user(login: $user) { + projectV2(number: $number) { + id + title + fields(first: 50) { + nodes { + ... on ProjectV2FieldCommon { + id + name + dataType + } + ... on ProjectV2SingleSelectField { + id + name + options { + id + name + } + } + } + } + } + } + } + `; + const result = await github.graphql(query, { user: org, number }); + return result.user?.projectV2; + } catch (userError) { + console.error(`Failed to fetch project as user: ${userError.message}`); + return null; + } + } + } + + // Consolidated helper function to update project status, adding the issue/PR to the project if not present + async function updateProjectStatus(targetStatusName) { + const projectNumber = 3; + const org = context.repo.owner; + + console.log(`Attempting to update project #${projectNumber} Status field to "${targetStatusName}"...`); + try { + // 1. Fetch project metadata + const project = await getProjectMetadata(org, projectNumber); + if (!project) { + console.warn(`Could not find project #${projectNumber} under "${org}".`); + return false; + } + console.log(`Successfully fetched project details for "${project.title}" (ID: ${project.id})`); + + // 2. Find the Status field and the target option + const statusField = project.fields.nodes.find( + f => f.name.toLowerCase() === 'status' && f.options + ); + if (!statusField) { + console.warn(`No single-select field named "Status" found in project #${projectNumber}.`); + return false; + } + const targetOption = statusField.options.find( + opt => opt.name.toLowerCase() === targetStatusName.toLowerCase() + ); + if (!targetOption) { + console.warn(`No option named "${targetStatusName}" found in the Status field of project #${projectNumber}.`); + return false; + } + console.log(`Found Status option: "${targetOption.name}" (ID: ${targetOption.id})`); + + // 3. Get the item ID — try addProjectV2ItemById first (idempotent: returns existing item if already present) + let itemId = null; + try { + console.log('Attempting addProjectV2ItemById (idempotent)...'); + const addMutation = ` + mutation($projectId: ID!, $contentId: ID!) { + addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) { + item { + id + } + } + } + `; + const addResult = await github.graphql(addMutation, { projectId: project.id, contentId }); + itemId = addResult.addProjectV2ItemById?.item?.id; + if (itemId) { + console.log(`Got item ID via addProjectV2ItemById: ${itemId}`); + } + } catch (addError) { + console.warn(`addProjectV2ItemById failed (likely permissions): ${addError.message}`); + console.log('Falling back to scanning project items...'); + } + + // Fallback: scan the project's items to find the one matching our content + if (!itemId) { + const itemsQuery = ` + query($projectId: ID!, $cursor: String) { + node(id: $projectId) { + ... on ProjectV2 { + items(first: 100, after: $cursor) { + pageInfo { + hasNextPage + endCursor + } + nodes { + id + content { + ... on Issue { + id + } + ... on PullRequest { + id + } + } + } + } + } + } + } + `; + + let cursor = null; + let found = false; + for (let page = 0; page < 10 && !found; page++) { + const result = await github.graphql(itemsQuery, { projectId: project.id, cursor }); + const items = result.node?.items?.nodes || []; + for (const item of items) { + if (item.content?.id === contentId) { + itemId = item.id; + found = true; + console.log(`Found existing item in project by scanning: ${itemId}`); + break; + } + } + if (!found && result.node?.items?.pageInfo?.hasNextPage) { + cursor = result.node.items.pageInfo.endCursor; + } else { + break; + } + } + } + + if (!itemId) { + console.error('Could not find or add item to project board.'); + return false; + } + + // 4. Update the Status field value + const updateMutation = ` + mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) { + updateProjectV2ItemFieldValue( + input: { + projectId: $projectId + itemId: $itemId + fieldId: $fieldId + value: { + singleSelectOptionId: $optionId + } + } + ) { + projectV2Item { + id + } + } + } + `; + await github.graphql(updateMutation, { + projectId: project.id, + itemId, + fieldId: statusField.id, + optionId: targetOption.id + }); + console.log(`Successfully updated project Status to "${targetOption.name}".`); + return true; + + } catch (error) { + console.error(`Error in updateProjectStatus: ${error.message}`); + return false; + } + } + + // Map the lowercase label to the exact Status column name + let projectStatusName = null; + + if (label === 'adminarchs') { + projectStatusName = 'AdminArchs'; + } else if (label === 'devops') { + projectStatusName = 'DevOps'; + } else if (label === 'pythondevs') { + projectStatusName = 'PythonDevs'; + } else if (label === 'aidevs') { + projectStatusName = 'AIDevs'; + } else if (label === 'webdevs') { + projectStatusName = 'WebDevs'; + } else if (label === 'extensiondevs') { + projectStatusName = 'ExtensionDevs'; + } else if (label === 'docsexperts') { + projectStatusName = 'DocsExperts'; + } else if (label === 'testers') { + projectStatusName = 'Testers'; + } else if (label === 'blocked') { + projectStatusName = 'Blocked'; + } + + if (projectStatusName) { + await updateProjectStatus(projectStatusName); + } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4429a088..231e77aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,6 +26,7 @@ jobs: run: | python -m pip install --upgrade pip pip install build + pip install "tree-sitter==0.25.2" "tree-sitter-language-pack==0.13.0" pip install .[dev] - name: Build package diff --git a/.gitignore b/.gitignore index 749cdb70..c607a934 100644 --- a/.gitignore +++ b/.gitignore @@ -12,9 +12,14 @@ venv/ venv311/ docker-compose.yml node_modules -# PyPI -dist/ -build/ +# PyPI / build outputs (repo root only — do not ignore website/dist or package viz/dist +# needed for local sync before packaging) +/dist/ +/build/ +# Vite output (canonical build lives in website/; packaged copy is synced to viz/dist) +/website/dist/ +# Bundled Playground static files (generated by scripts/sync_viz_dist.sh; included in wheels via CI) +/src/codegraphcontext/viz/dist/ *.egg-info/ # IDEs and editors .vscode/ @@ -70,4 +75,39 @@ cgc_btc/ .claude/* *.spec todo.md -.codegraphcontext \ No newline at end of file +.codegraphcontext +.ignore +extensions/vscode/out/ +extensions/vscode/*.vsix +PHASE1_PHASE2_CHANGES.md +NEXUS_BENCHMARK_BEFORE.md +CGC_VALUE_PROPOSITION.md + +# MkDocs +docs/site/ +site/ + +# Local scratch reports and audit artifacts (untracked; other CGC_*.md reports +# are tracked, so list untracked ones by name instead of a glob) +CGC_EXPECTED_GRAPH_SPEC.md +GOLDEN_PERFECTION_REPORT.md +COMMIT_MSG.txt +organizer/ +audit_output/ +test_env_e2e/ +.venv-release/ +falkordb.sock + +# Build Artifacts +[Bb]in/ +[Oo]bj/ +target/ +venv_indexing/ +scratch/ +scripts/.agents/ +scripts +.agents +*.py[cod] +.tox/ +.mypy_cache/ +.ruff_cache/ diff --git a/CGC_CALL_GRAPH_AUDIT_REPORT.md b/CGC_CALL_GRAPH_AUDIT_REPORT.md new file mode 100644 index 00000000..10b736b6 --- /dev/null +++ b/CGC_CALL_GRAPH_AUDIT_REPORT.md @@ -0,0 +1,401 @@ +# CGC Call Graph Audit Report + +**Generated:** 2026-06-11 13:14 UTC +**CGC environment:** { + "python": "3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0]", + "projects": 21, + "cgc_version": "local" +} + +## Executive Summary + +- **Languages audited:** 21 (FAILURE) / 20 (PROGRESS; elisp skipped) +- **Average FAILURE node accuracy:** 99.9% +- **Average FAILURE edge accuracy:** 99.8% +- **Average FAILURE CALLS accuracy:** 98.3% + +### Per-Language Scorecard + +| Language | FAILURE node% | FAILURE edge% | FAILURE CALLS% | PROGRESS match HAVE? | Net progress | +|----------|--------------|--------------|----------------|---------------------|--------------| +| sample_project_dart | 100.0% | 98.5% | 80.0% | No | 1 | +| sample_project_c | 97.2% | 99.2% | 90.0% | No | 47 | +| sample_project_php | 100.0% | 99.9% | 96.8% | Yes | 0 | +| sample_project | 100.0% | 99.8% | 98.4% | No | 76 | +| sample_project_rust | 100.0% | 99.9% | 99.0% | No | 10 | +| sample_project_cpp | 100.0% | 100.0% | 100.0% | Yes | 0 | +| sample_project_csharp | 100.0% | 100.0% | 100.0% | No | 1 | +| sample_project_elisp | 100.0% | 100.0% | 100.0% | N/A (no baseline) | — | +| sample_project_elixir | 100.0% | 98.9% | 100.0% | No | 1 | +| sample_project_go | 100.0% | 99.9% | 100.0% | No | 32 | +| sample_project_haskell | 100.0% | 100.0% | 100.0% | No | 3 | +| sample_project_java | 100.0% | 100.0% | 100.0% | No | -1 | +| sample_project_javascript | 100.0% | 100.0% | 100.0% | Yes | 0 | +| sample_project_kotlin | 100.0% | 100.0% | 100.0% | No | 2 | +| sample_project_lua | 100.0% | 100.0% | 100.0% | No | 4 | +| sample_project_misc | 100.0% | 100.0% | 100.0% | Yes | 0 | +| sample_project_perl | 100.0% | 100.0% | 100.0% | No | 1 | +| sample_project_ruby | 100.0% | 100.0% | 100.0% | No | 2 | +| sample_project_scala | 100.0% | 100.0% | 100.0% | Yes | 0 | +| sample_project_swift | 100.0% | 100.0% | 100.0% | No | -5 | +| sample_project_typescript | 100.0% | 100.0% | 100.0% | No | 7 | + +## Goal 2 — Golden Self-Consistency Fixes + +### sample_project +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_c +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_cpp +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_csharp +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_dart +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_elisp +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_elixir +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_go +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_haskell +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_java +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_javascript +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_kotlin +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_lua +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_misc +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_perl +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_php +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_ruby +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_rust +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_scala +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_swift +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + +### sample_project_typescript +- Removed contaminated nodes: 0 +- Removed contaminated/dangling edges: 0 + + +# FAILURE — Current vs Expected Golden (21 languages) + +## sample_project +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 99.8% (1 missing, 0 spurious) +- CALLS accuracy: 98.4% (1 missing) + - MISSING_EDGE: Function:calls:/tests/fixtures/sample_projects/sample_project/advanced_calls.py:ln_3 --[CALLS]--> Function:square:/tests/fixtures/sample_projects/sample_project/advanced_calls.py:ln_1 + - MISSING_CALLS: Function:calls:/tests/fixtures/sample_projects/sample_project/advanced_calls.py:ln_3 --[CALLS]--> Function:square:/tests/fixtures/sample_projects/sample_project/advanced_calls.py:ln_1 + +## sample_project_c +- Node accuracy: 97.2% (3 missing, 0 spurious) +- Edge accuracy: 99.2% (1 missing, 0 spurious) +- CALLS accuracy: 90.0% (1 missing) + - MISSING_NODE: Function:handle_error:/tests/fixtures/sample_projects/sample_project_c/tough_macros.c:ln_15 + - MISSING_NODE: Function:handle_input:/tests/fixtures/sample_projects/sample_project_c/tough_macros.c:ln_13 + - MISSING_NODE: Function:handle_output:/tests/fixtures/sample_projects/sample_project_c/tough_macros.c:ln_14 + - MISSING_EDGE: Function:process_entity:/tests/fixtures/sample_projects/sample_project_c/utils.c:ln_4 --[CALLS]--> Function:my_callback:/tests/fixtures/sample_projects/sample_project_c/main.c:ln_5 + - MISSING_CALLS: Function:process_entity:/tests/fixtures/sample_projects/sample_project_c/utils.c:ln_4 --[CALLS]--> Function:my_callback:/tests/fixtures/sample_projects/sample_project_c/main.c:ln_5 + +## sample_project_cpp +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_csharp +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_dart +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 98.5% (1 missing, 0 spurious) +- CALLS accuracy: 80.0% (1 missing) + - MISSING_EDGE: Function:main:/tests/fixtures/sample_projects/sample_project_dart/lib/main.dart:ln_3 --[CALLS]--> Function:performAction:/tests/fixtures/sample_projects/sample_project_dart/lib/models.dart:ln_18:ctx_User + - MISSING_CALLS: Function:main:/tests/fixtures/sample_projects/sample_project_dart/lib/main.dart:ln_3 --[CALLS]--> Function:performAction:/tests/fixtures/sample_projects/sample_project_dart/lib/models.dart:ln_18:ctx_User + +## sample_project_elisp +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_elixir +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 98.9% (1 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + - MISSING_EDGE: Module:Tough.Worker:/tests/fixtures/sample_projects/sample_project_elixir/tough_cases.ex:ln_17 --[USES]--> Module:Tough.Cases:/tests/fixtures/sample_projects/sample_project_elixir/tough_cases.ex:ln_1 + +## sample_project_go +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 99.9% (1 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + - MISSING_EDGE: Struct:Extended:/tests/fixtures/sample_projects/sample_project_go/tough_cases.go:ln_59 --[EMBEDS]--> Struct:Base:/tests/fixtures/sample_projects/sample_project_go/tough_cases.go:ln_51 + +## sample_project_haskell +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_java +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_javascript +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_kotlin +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_lua +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_misc +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_perl +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_php +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 99.9% (1 missing, 0 spurious) +- CALLS accuracy: 96.8% (1 missing) + - MISSING_EDGE: Function:invokeDynamically:/tests/fixtures/sample_projects/sample_project_php/tough_cases.php:ln_62:ctx_DynamicInvoker --[CALLS]--> Function:targetMethod:/tests/fixtures/sample_projects/sample_project_php/tough_cases.php:ln_58:ctx_DynamicInvoker + - MISSING_CALLS: Function:invokeDynamically:/tests/fixtures/sample_projects/sample_project_php/tough_cases.php:ln_62:ctx_DynamicInvoker --[CALLS]--> Function:targetMethod:/tests/fixtures/sample_projects/sample_project_php/tough_cases.php:ln_58:ctx_DynamicInvoker + +## sample_project_ruby +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_rust +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 99.9% (1 missing, 0 spurious) +- CALLS accuracy: 99.0% (1 missing) + - MISSING_EDGE: Function:call_both:/tests/fixtures/sample_projects/sample_project_rust/src/tough_cases.rs:ln_32 --[CALLS]--> Function:action:/tests/fixtures/sample_projects/sample_project_rust/src/tough_cases.rs:ln_30 + - MISSING_CALLS: Function:call_both:/tests/fixtures/sample_projects/sample_project_rust/src/tough_cases.rs:ln_32 --[CALLS]--> Function:action:/tests/fixtures/sample_projects/sample_project_rust/src/tough_cases.rs:ln_30 + +## sample_project_scala +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_swift +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + +## sample_project_typescript +- Node accuracy: 100.0% (0 missing, 0 spurious) +- Edge accuracy: 100.0% (0 missing, 0 spurious) +- CALLS accuracy: 100.0% (0 missing) + + +# PROGRESS — Current vs Have Baseline (20 languages) + +*`sample_project_elisp` excluded — no `nodes_have` / `edges_have` baseline.* + +## sample_project +- Matches HAVE baseline: **No** +- Nodes gained: 35 | lost: 0 +- Edges gained: 41 | lost: 0 +- Net progress score: 76 + - IMPROVEMENT nodes gained: 35 + +## sample_project_c +- Matches HAVE baseline: **No** +- Nodes gained: 22 | lost: 0 +- Edges gained: 25 | lost: 0 +- Net progress score: 47 + - IMPROVEMENT nodes gained: 22 + +## sample_project_cpp +- Matches HAVE baseline: **Yes** +- Nodes gained: 0 | lost: 0 +- Edges gained: 0 | lost: 0 +- Net progress score: 0 + +## sample_project_csharp +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 1 | lost: 0 +- Net progress score: 1 + +## sample_project_dart +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 1 | lost: 0 +- Net progress score: 1 + +## sample_project_elixir +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 1 | lost: 0 +- Net progress score: 1 + +## sample_project_go +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 32 | lost: 0 +- Net progress score: 32 + +## sample_project_haskell +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 3 | lost: 0 +- Net progress score: 3 + +## sample_project_java +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 1 +- Edges gained: 1 | lost: 1 +- Net progress score: -1 + - REGRESSION nodes lost: 1 + - REGRESSION edges lost: 1 + +## sample_project_javascript +- Matches HAVE baseline: **Yes** +- Nodes gained: 0 | lost: 0 +- Edges gained: 0 | lost: 0 +- Net progress score: 0 + +## sample_project_kotlin +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 2 | lost: 0 +- Net progress score: 2 + +## sample_project_lua +- Matches HAVE baseline: **No** +- Nodes gained: 1 | lost: 0 +- Edges gained: 3 | lost: 0 +- Net progress score: 4 + - IMPROVEMENT nodes gained: 1 + +## sample_project_misc +- Matches HAVE baseline: **Yes** +- Nodes gained: 0 | lost: 0 +- Edges gained: 0 | lost: 0 +- Net progress score: 0 + +## sample_project_perl +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 1 | lost: 0 +- Net progress score: 1 + +## sample_project_php +- Matches HAVE baseline: **Yes** +- Nodes gained: 0 | lost: 0 +- Edges gained: 0 | lost: 0 +- Net progress score: 0 + +## sample_project_ruby +- Matches HAVE baseline: **No** +- Nodes gained: 1 | lost: 0 +- Edges gained: 1 | lost: 0 +- Net progress score: 2 + - IMPROVEMENT nodes gained: 1 + +## sample_project_rust +- Matches HAVE baseline: **No** +- Nodes gained: 5 | lost: 0 +- Edges gained: 5 | lost: 0 +- Net progress score: 10 + - IMPROVEMENT nodes gained: 5 + +## sample_project_scala +- Matches HAVE baseline: **Yes** +- Nodes gained: 0 | lost: 0 +- Edges gained: 0 | lost: 0 +- Net progress score: 0 + +## sample_project_swift +- Matches HAVE baseline: **No** +- Nodes gained: 4 | lost: 8 +- Edges gained: 9 | lost: 10 +- Net progress score: -5 + - REGRESSION nodes lost: 8 + - REGRESSION edges lost: 10 + - IMPROVEMENT nodes gained: 4 + +## sample_project_typescript +- Matches HAVE baseline: **No** +- Nodes gained: 0 | lost: 0 +- Edges gained: 7 | lost: 0 +- Net progress score: 7 + + +## Methodology + +- **FAILURE columns** report *accuracy %* (100% = current graph matches expected golden; lower = parser gaps). +- **PROGRESS** compares current export vs `nodes_have.jsonl`/`edges_have.jsonl` (structural edges only; CALLS excluded). +- **`sample_project_elisp`:** PROGRESS skipped — no `*_have` regression baseline. +- **Golden self-consistency:** Cross-project contamination removed via boundary-safe path matching; dangling edges pruned. +- **Elisp golden:** Created at `tests/fixtures/goldens/sample_project_elisp/` from fresh export. + +## Appendix + +- **Expected behavior spec:** [CGC_EXPECTED_GRAPH_SPEC.md](CGC_EXPECTED_GRAPH_SPEC.md) +- **Audit harness:** [scripts/call_graph_audit.py](scripts/call_graph_audit.py) +- **Normalization:** Reuses logic from `tests/integration/test_parser_goldens.py` +- **Current exports:** `audit_output/current//` diff --git a/CGC_E2E_BUG_REPORT.md b/CGC_E2E_BUG_REPORT.md new file mode 100644 index 00000000..2288c7b2 --- /dev/null +++ b/CGC_E2E_BUG_REPORT.md @@ -0,0 +1,352 @@ +# CGC E2E Bug Report + +- **Date:** 2026-06-09 (manual subprocess execution) +- **CGC version:** 0.4.16 — tested on **PyPI** (`pip install codegraphcontext`) and **local editable** (`pip install -e .` from this repo, uncommitted working-tree changes) +- **Python:** 3.12.3 +- **OS:** Linux 6.8.0-124-generic +- **Method:** Subprocess-only per [E2E plan](.cursor/plans/cgc_e2e_bug_hunt_6028a5c6.plan.md). No source modifications. Tests run from `/tmp` with isolated `HOME` unless testing repo-local config bleed. +- **Harness note:** Initial automated harness (`/tmp/cgc_e2e_harness.py`) produced many false positives (truncated Rich output, missing `bundle import -y`, config bleed when cwd inside CGC repo). Findings below are manually verified. + +--- + +## Executive Summary + +| Metric | Result | +|--------|--------| +| Embedded backends (FalkorDB, KuzuDB, LadybugDB) | **PASS** — index, query, find, bundle export | +| Remote backends (Neo4j, falkordb-remote, Nornic) | **SKIP** — Docker daemon unavailable | +| Context isolation (global / per-repo / named) | **PASS** | +| MCP stdio (25 tools) | **PASS** | +| Watch incremental | **PASS** | +| API gateway | **PARTIAL** — `/api/v1/status` OK; `/health` 404 | +| **Confirmed bugs** | **12** (0 Critical, 4 High, 5 Medium, 3 Low) | + +**Verdict (PyPI):** Core indexing works, but call-chain analysis and golden parity regress vs repo goldens. Config bleed when cwd is inside a repo with `.codegraphcontext/`. + +**Verdict (local editable):** Significantly better — call chains work, 20/20 golden parity exact match, cypher deprecation warning present. Several PyPI bugs appear fixed in the working tree but **not yet released**. Remaining local issues: delete orphans, per-repo FalkorDB default, API `/health` 404, CLI help gaps. + +--- + +## Test Matrix Summary + +| Backend | index | stats (482/625) | chain f1→f3 | find f1 | query write block | bundle export/import | doctor | +|---------|-------|-----------------|-------------|---------|-------------------|----------------------|--------| +| **falkordb** | PASS (1.5s) | PASS | **FAIL** | PASS | PASS | PASS / PASS (-y) | PASS | +| **kuzudb** | PASS (22s) | PASS | **FAIL** | PASS | PASS | PASS / PASS (-y) | PASS | +| **ladybugdb** | PASS (18s) | PASS | **FAIL** | PASS | PASS | PASS / PASS (-y) | PASS | +| **falkordb-remote** | SKIP | — | — | — | — | — | SKIP | +| **neo4j** | SKIP | — | — | — | — | — | SKIP | +| **nornic** | SKIP | — | — | — | — | — | SKIP | + +Golden baseline for Python sample: **482 nodes, 621 edges** in `tests/fixtures/goldens/sample_project/metadata.json`; PyPI measured **482 nodes, 625 edges** (0.6% edge delta — within tolerance). + +--- + +## Bugs + +### BUG-001: Repo-local `.codegraphcontext/.env` overrides isolated global config + +- **Severity:** High +- **Category:** UX / Config +- **Backend(s):** All +- **Repro steps:** + ```bash + export HOME=$(mktemp -d) + cgc config db neo4j + cgc config set NEO4J_URI bolt://127.0.0.1:19999 + cd /path/to/repo/with/.codegraphcontext # e.g. CGC repo itself + cgc config show # NEO4J_URI reverts to bolt://localhost:7687 from repo .env + ``` +- **Expected:** User's `~/.codegraphcontext/.env` values take precedence, or repo-local overrides are clearly documented and scoped to per-repo mode only. +- **Actual:** Repo-local `.codegraphcontext/.env` merges and wins for `DEFAULT_DATABASE`, `NEO4J_URI`, etc. even in global mode with a fresh `HOME`. Index/find connect to the repo's FalkorDB graph (53k+ functions in dev tree) instead of the isolated test database. +- **Impact:** New users cloning CGC (or any repo with `.codegraphcontext/`) get silently redirected to the repo's database. E2E tests, CI, and multi-project workflows produce wrong results without obvious error. + +--- + +### BUG-002: Module-level nested calls produce no Function→Function CALLS edges + +- **Severity:** High +- **Category:** Accuracy +- **Backend(s):** All (parser / call resolver) +- **Repro steps:** + ```bash + export HOME=$(mktemp -d) && cd /tmp + cgc config db falkordb && cgc context mode global + cgc index --force tests/fixtures/sample_projects/sample_project + cgc query "MATCH (a)-[r:CALLS]->(b) WHERE a.path CONTAINS 'function_chains' RETURN a.name, b.name, r.line_number" + ``` + Fixture: `function_chains.py` line 5: `result = f1(f2(f3(10)))` +- **Expected:** CALLS edges `f1→f2`, `f2→f3` (or equivalent nested-call representation). +- **Actual:** Only module-level edges: `→f1`, `→f2`, `→f3` (all line 5). Zero Function→Function CALLS for this pattern. +- **Impact:** `cgc analyze chain f1 f3` returns *"No call chain found"*. Docs/quickstart use this fixture as the canonical call-chain demo. + +--- + +### BUG-003: `analyze chain f1 f3` fails on indexed sample_project (consequence of BUG-002) + +- **Severity:** High +- **Category:** Accuracy +- **Backend(s):** falkordb, kuzudb, ladybugdb (verified) +- **Repro steps:** After indexing sample_project: `cgc analyze chain f1 f3` +- **Expected:** Chain `f1 → f2 → f3` +- **Actual:** `No call chain found between 'f1' and 'f3' within depth 5` (exit 0) +- **Impact:** Primary onboarding example broken for nested module-level calls. + +--- + +### BUG-004: `analyze callers f2` reports `` instead of `f1` + +- **Severity:** Medium +- **Category:** Accuracy / UX +- **Backend(s):** All +- **Repro steps:** Index sample_project → `cgc analyze callers f2` +- **Expected:** Caller `f1` (per intuitive reading of `f1(f2(f3(10)))`). +- **Actual:** Single caller `` at function_chains.py (technically correct given BUG-002, but contradicts user/docs mental model). +- **Impact:** Misleading caller analysis for module-level call expressions. + +--- + +### BUG-005: `cgc delete` leaves orphan graph nodes + +- **Severity:** Medium +- **Category:** Accuracy / Data integrity +- **Backend(s):** falkordb (verified) +- **Repro steps:** + ```bash + cgc index --force sample_project_c # 83 nodes + cgc delete sample_project_c + cgc query "MATCH (n) RETURN count(n)" # returns 9, not 0 + ``` +- **Expected:** All nodes belonging to deleted repository removed. +- **Actual:** 9 orphan nodes remain (Parameters, Variables without paths — e.g. `x`, `y`, `int v`). +- **Impact:** Repeated index/delete cycles accumulate stale nodes; inflates stats and can cause golden parity drift in batch sweeps. + +--- + +### BUG-006: MCP `delete_repository` with bad path raises internal error + +- **Severity:** Medium +- **Category:** UX +- **Backend(s):** All (MCP) +- **Repro steps:** MCP `tools/call` → `delete_repository` with `{"path": "../../../etc"}` +- **Expected:** Clean validation error (path not found / not indexed). +- **Actual:** `Tool execution error: Failed to delete repository: 'NoneType' object has no attribute 'replace'` +- **Impact:** Scary internal error instead of actionable message; suggests missing null guard on repository path resolution. + +--- + +### BUG-007: Per-repo mode auto-init uses FalkorDB despite global KuzuDB setting + +- **Severity:** Medium +- **Category:** UX / Config +- **Backend(s):** kuzudb (verified) +- **Repro steps:** + ```bash + cgc config db kuzudb && cgc context mode per-repo + cd sample_project_go && cgc index --force . + # Observe: "Context: Per-repo local mode (Database: falkordb)" + ``` +- **Expected:** Per-repo context inherits configured backend (kuzudb) or prompts user. +- **Actual:** Auto-created `.codegraphcontext/` defaults to `falkordb` regardless of global `cgc config db kuzudb`. +- **Impact:** User selects KuzuDB globally but per-repo mode silently uses FalkorDB Lite. + +--- + +### BUG-008: API gateway has no `/health` endpoint + +- **Severity:** Low +- **Category:** UX / Docs +- **Backend(s):** N/A (API) +- **Repro steps:** `cgc api start --port 8020` → `curl http://127.0.0.1:8020/health` +- **Expected:** Health check endpoint (common convention). +- **Actual:** HTTP 404. Working endpoint: `GET /api/v1/status` → `{"status":"ok","message":"Connected",...}` +- **Impact:** Load balancers / k8s probes using `/health` fail unless docs specify `/api/v1/status`. + +--- + +### BUG-009: `cgc cypher` alias runs without deprecation warning + +- **Severity:** Low +- **Category:** UX / Docs +- **Backend(s):** All +- **Repro steps:** `cgc cypher 'MATCH (f:Function) RETURN f.name LIMIT 1'` +- **Expected:** Deprecation warning directing users to `cgc query`. +- **Actual:** Executes silently, returns results (exit 0). +- **Impact:** Deprecated command persists undetected. + +--- + +### BUG-010: CLI `--database` help lists 4 backends; code supports 6 + +- **Severity:** Low +- **Category:** Docs +- **Backend(s):** N/A +- **Repro steps:** `cgc index --help` — check `--database` enum +- **Expected:** Includes `nornic`, `falkordb-remote` per `config_manager.py`. +- **Actual:** Help text omits `nornic` and `falkordb-remote`. +- **Impact:** Users cannot discover remote backends from CLI help. + +--- + +### BUG-011: `bundle import --clear` blocks without `-y` in non-interactive contexts + +- **Severity:** Low +- **Category:** UX +- **Backend(s):** All +- **Repro steps:** `cgc bundle import file.cgc --clear` (no TTY) +- **Expected:** Non-interactive failure with clear message, or require `-y` in docs prominently. +- **Actual:** Hangs at `Are you sure you want to continue? [y/N]:` until timeout. +- **Impact:** CI/scripts hang; `--yes/-y` exists but is easy to miss. + +--- + +### BUG-012: PyPI vs repo golden node-count drift for some languages + +- **Severity:** Medium +- **Category:** Accuracy / Release parity +- **Backend(s):** falkordb (PyPI 0.4.16) +- **Repro steps:** Fresh isolated index per language; compare `MATCH (n)` count to `tests/fixtures/goldens/*/metadata.json`. +- **Expected:** Node counts within 10% of goldens (same generator `PYv0.4.16`). +- **Actual (fresh single-repo index):** + +| Project | Golden nodes | PyPI nodes | Δ% | Golden edges | PyPI edges | Status | +|---------|-------------|------------|-----|-------------|------------|--------| +| sample_project | 482 | 482 | 0% | 621 | 625 | PASS | +| sample_project_go | 660 | 690 | 4.5% | 845 | 845 | PASS | +| sample_project_java | 160 | 195 | 21.9% | 221 | 221 | **FAIL** | +| sample_project_javascript | 236 | 236 | 0% | 306 | 304 | PASS | +| sample_project_rust | 803 | 854 | 6.4% | 943 | 939 | PASS | +| sample_project_typescript | 918 | 918 | 0% | 1465 | 1459 | PASS | +| sample_project_c | 83 | 83 | 0% | 96 | 96 | PASS | +| sample_project_kotlin | 189 | 206 | 9.0% | 242 | 242 | PASS | +| sample_project_cpp | 136 | 155 | 14.0% | 167 | 167 | FAIL | +| sample_project_csharp | 141 | 168 | 19.1% | 212 | 212 | FAIL | +| sample_project_dart | 49 | 88 | 79.6% | 64 | 64 | FAIL | +| sample_project_elixir | 53 | 93 | 75.5% | 81 | 81 | FAIL | +| sample_project_haskell | 45 | 87 | 93.3% | 50 | 50 | FAIL | +| sample_project_lua | 52 | 96 | 84.6% | 55 | 55 | FAIL | +| sample_project_misc | 27 | 73 | 170% | 26 | 26 | FAIL | +| sample_project_perl | 71 | 117 | 64.8% | 95 | 94 | FAIL | +| sample_project_php | 757 | 771 | 1.8% | 866 | 866 | PASS | +| sample_project_ruby | 74 | 127 | 71.6% | 106 | 106 | FAIL | +| sample_project_scala | 130 | 186 | 43.1% | 171 | 171 | FAIL | +| sample_project_swift | 136 | 192 | 41.2% | 178 | 178 | FAIL | + +- **Note:** Edge counts match exactly for all tested languages; node inflation is predominantly extra `Parameter` and `Variable` nodes. Suggests PyPI 0.4.16 indexes more node types than goldens captured, or goldens were generated from a different build artifact. +- **Impact:** Golden-based CI on PyPI install will fail for 11/20 languages on node count despite correct edge topology. + +--- + +## Verified PASS (not bugs) + +| Area | Result | +|------|--------| +| `cgc doctor` on embedded backends | PASS | +| `cgc query` write guard (`DELETE`, `CREATE`) | Blocked, exit 1 | +| `cgc index` skip when already indexed | PASS (exit 0, tip shown) | +| Context global mode — two repos in `cgc list` | PASS | +| Context per-repo isolation — `f1` not in Go-only repo | PASS | +| Named context create/index/default | PASS | +| MCP tools/list | 25 tools | +| MCP `find_code` vs CLI `find name f1` | Parity on function_chains.py | +| MCP `add_code_to_graph` bad path | Proper error (outside allowed roots) | +| `cgc watch` incremental re-index | PASS — new `bar→foo` caller detected | +| `cgc registry search numpy` | PASS | +| Datasource smoke (mysql/cassandra/redis bad host) | Clean errors, no traceback | +| Bundle round-trip with `-y` | 482/625 nodes in 0.68s | +| Neo4j misconfig exit codes (from `/tmp`, clean HOME) | index/find exit 1 with clear message | + +--- + +## Doc/UX Inconsistencies + +- CLI `--database` help missing `nornic`, `falkordb-remote` (BUG-010) +- MCP tool count: docs reference 21 tools; runtime exposes **25** +- Quickstart call-chain example (`f1→f2→f3`) does not work with current call resolver (BUG-002/003) +- API health probe should document `/api/v1/status` not `/health` +- `NEO4J_USER` is not a valid `cgc config set` key; use `NEO4J_USERNAME` (discovered during exit-code testing) +- Config precedence: local `.codegraphcontext/.env` at cwd overrides global — underdocumented for global-mode users (BUG-001) + +--- + +## Skipped Tests + +| Test | Reason | +|------|--------| +| Neo4j remote index/query | Docker daemon not running (`unix:///home/shashank/.docker/desktop/docker.sock` missing) | +| FalkorDB-remote | Same — no Docker | +| Nornic | Same — no Docker | +| `cgc visualize` memory/perf | Not run (manual UI probe deferred) | +| `ENABLE_AUTO_WATCH=true` post-index | Not run (terminal block behavior deferred) | + +--- + +## Cross-Backend Parity (sample_project) + +| Check | falkordb | kuzudb | ladybugdb | +|-------|----------|--------|-----------| +| Node count | 482 | 482 | 482 | +| Edge count | 625 | 625* | 625* | +| `find name f1` (function_chains) | PASS | PASS | PASS | +| `find content "Hello World"` | PASS | PASS | PASS | +| `analyze chain f1 f3` | FAIL | FAIL | FAIL | +| Query write block | PASS | PASS | PASS | + +\*Edge counts inferred from shared indexing pipeline; node counts verified via Cypher. + +--- + +## Local Editable vs PyPI Comparison (2026-06-09) + +Local install: `/tmp/cgc-local-venv` via `pip install -e .` from commit `dce24ed` + 9 uncommitted files (`cli_helpers.py`, `database_falkordb.py`, `database_kuzu.py`, `falkor_worker.py`, `api/router.py`, `scip_indexer.py`, etc.). + +| Bug / Check | PyPI 0.4.16 | Local editable | Notes | +|-------------|-------------|----------------|-------| +| **BUG-001** Config bleed | **FAIL** — repo `.env` overrides HOME; index hits dev graph (53k+ functions) | **Improved** — isolated HOME + neo4j misconfig exits with error; no silent fallback to repo FalkorDB | Local `cli_helpers.py` changes likely involved | +| **BUG-002** Function→Function CALLS | **FAIL** — only `→f1/f2/f3` | **FIXED** — `f1→f2`, `f2→f3` edges present | Major accuracy fix in working tree | +| **BUG-003** `analyze chain f1 f3` | **FAIL** — "No call chain found" | **FIXED** — chain length 2, shows f1→f2→f3 | | +| **BUG-004** `analyze callers f2` | **FAIL** — reports `` | **FIXED** — reports `f1` | | +| **BUG-005** Delete orphans | **FAIL** — 9 nodes remain | **FAIL** — 9 nodes remain | Same on both | +| **BUG-006** MCP delete bad path | **FAIL** — `NoneType.replace` | **Improved** — clean `ALLOW_DB_DELETION` guard message | Different code path hit | +| **BUG-007** Per-repo DB default | **FAIL** — auto-init uses falkordb | **FAIL** — same | | +| **BUG-008** API `/health` | **FAIL** — 404 | **FAIL** — 404 (`/api/v1/status` → 200) | | +| **BUG-009** Cypher deprecation | **FAIL** — no warning | **FIXED** — `⚠ 'cgc cypher' is deprecated` | | +| **BUG-010** CLI `--database` help | **FAIL** — missing nornic, falkordb-remote | **FAIL** — help still lists only falkordb/kuzudb/neo4j on `index` | | +| **BUG-012** Golden parity (20 langs) | **FAIL** — 11/20 node-count drift | **PASS** — **20/20 exact match** (482/621 … 918/1465) | PyPI wheel ≠ repo goldens; local tree matches | +| Neo4j misconfig exit codes | PASS (exit 1) | PASS (exit 1) | Both OK from `/tmp` | +| MCP tool count | 25 | 25 | Same | +| Context isolation | PASS | PASS | Same | + +### Local-only test matrix (FalkorDB, `/tmp`, isolated HOME) + +| Check | Result | +|-------|--------| +| index sample_project | PASS — 482 nodes, **621 edges** (matches golden exactly; PyPI had 625) | +| `analyze chain f1 f3` | **PASS** | +| `analyze callers f2` | **PASS** (caller: `f1`) | +| bundle export/import `-y` | PASS | +| 20-language golden sweep | **20/20 PASS** — zero node/edge delta | + +### Release gap summary + +The working tree contains fixes **not in the published PyPI 0.4.16 wheel**: + +1. Nested/module-level call resolution (`f1→f2→f3` CALLS edges) +2. Exact golden parity (edge count 621 vs PyPI's 625 on sample_project) +3. Cypher deprecation warning +4. Improved config / DB-init error handling (no silent repo-graph bleed) + +**Action:** Cut a new PyPI release (0.4.17+) to ship these fixes to new users installing via `pip install codegraphcontext`. + +--- + +## Recommendations (report-only; no fixes applied) + +1. **BUG-001:** Document config precedence clearly; consider not loading repo-local `.env` in global mode unless `--context` or per-repo mode is active. +2. **BUG-002/003:** Fix call resolver for nested call expressions at module scope, or update docs to use intra-function call examples. +3. **BUG-005:** Cascade-delete orphan Parameter/Variable nodes when removing a repository. +4. **BUG-012:** Regenerate language goldens from PyPI 0.4.16 wheel, or pin golden generator to exact release artifact hash. + +--- + +*Report generated by manual E2E bug hunt session 2026-06-09. Local editable comparison added same day.* diff --git a/CGC_GRAPH_INCONSISTENCIES.md b/CGC_GRAPH_INCONSISTENCIES.md new file mode 100644 index 00000000..87a77370 --- /dev/null +++ b/CGC_GRAPH_INCONSISTENCIES.md @@ -0,0 +1,136 @@ +# CGC Graph Building Inconsistencies (100 items) + +**Generated:** 2026-06-10 +**Method:** Parsed from `CGC_CALL_GRAPH_AUDIT_REPORT.md` (indexer export vs golden), plus `CGC_EXPECTED_GRAPH_SPEC.md` gaps and Python source review. + +Use this as a fix backlog. Each ID should become one pattern test + one engine fix. + +### Categories +| Category | Meaning | +|----------|---------| +| `indexer_vs_golden_*` | Confirmed: golden has it, live indexer does not | +| `golden_vs_spec` | Golden incomplete vs spec (fix engine first, then re-export golden) | +| `structural` | Edge type or node kind not implemented in engine | +| `source_vs_spec` | Visible in source, likely missing from indexer | +| `resolver` | `calls.py` / pipeline behavior issue | +| `cross_lang` | Language-specific accuracy gap from audit | +| `meta` | Tooling / process / release inconsistency | + +| ID | Pri | Lang | Cat | Source | Issue | Expected | Actual | +|---:|-----|------|-----|--------|-------|----------|--------| +| 1 | medium | python | indexer_vs_golden_edge | sample_project | Function:calls:sample_project/advanced_calls.py:ln_3 --[CALLS]--> Function:square:sample_project/adv | In golden | Missing in indexer | +| 2 | medium | python | indexer_vs_golden_edge | sample_project | Function:higher_order:sample_project/advanced_functions.py:ln_7 --[CALLS]--> Parameter:func:sample_p | In golden | Missing in indexer | +| 3 | medium | python | indexer_vs_golden_edge | sample_project | Function:greet:sample_project/class_instantiation.py:ln_5:ctx_B --[CALLS]--> Function:greet:sample_p | In golden | Missing in indexer | +| 4 | medium | c | indexer_vs_golden_node | sample_project_c | EnumMember:COLOR_BLUE:sample_project_c/tough_macros.c | In golden | Missing in indexer | +| 5 | medium | c | indexer_vs_golden_node | sample_project_c | EnumMember:COLOR_GREEN:sample_project_c/tough_macros.c | In golden | Missing in indexer | +| 6 | medium | c | indexer_vs_golden_node | sample_project_c | EnumMember:COLOR_RED:sample_project_c/tough_macros.c | In golden | Missing in indexer | +| 7 | medium | c | indexer_vs_golden_edge | sample_project_c | Class:color_t:sample_project_c/tough_macros.c:ln_69 --[CONTAINS]--> EnumMember:COLOR_BLUE:sample_pro | In golden | Missing in indexer | +| 8 | medium | c | indexer_vs_golden_edge | sample_project_c | Function:process_entity:sample_project_c/utils.c:ln_4 --[CALLS]--> Function:my_callback:sample_proje | In golden | Missing in indexer | +| 9 | medium | cpp | indexer_vs_golden_edge | sample_project_cpp | Function:templateDemo:sample_project_cpp/templates.cpp:ln_10 --[CALLS]--> Function:add:sample_projec | In golden | Missing in indexer | +| 10 | medium | csharp | indexer_vs_golden_edge | sample_project_csharp | Function:MethodFromPartA:sample_project_csharp/ToughCases.cs:ln_14:ctx_MultiPartClass --[CALLS]--> F | In golden | Missing in indexer | +| 11 | medium | csharp | indexer_vs_golden_edge | sample_project_csharp | Class:MultiPartClass:sample_project_csharp/ToughCases_PartB.cs:ln_3 --[PARTIAL_OF]--> Class:MultiPar | In golden | Missing in indexer | +| 12 | medium | dart | indexer_vs_golden_edge | sample_project_dart | Function:main:sample_project_dart/lib/main.dart:ln_3 --[CALLS]--> Function:performAction:sample_proj | In golden | Missing in indexer | +| 13 | medium | dart | indexer_vs_golden_edge | sample_project_dart | File:tough_cases_part.dart:sample_project_dart/lib/tough_cases_part.dart --[PART_OF]--> File:tough_c | In golden | Missing in indexer | +| 14 | medium | elixir | indexer_vs_golden_edge | sample_project_elixir | Function:perform:sample_project_elixir/tough_cases.ex:ln_20:ctx_Tough.Worker --[CALLS]--> Function:i | In golden | Missing in indexer | +| 15 | medium | elixir | indexer_vs_golden_edge | sample_project_elixir | Function:run:sample_project_elixir/main.ex:ln_8:ctx_MyApp.Main --[CALLS]--> Function:start_link:samp | In golden | Missing in indexer | +| 16 | medium | go | indexer_vs_golden_edge | sample_project_go | Struct:Circle:sample_project_go/interfaces.go:ln_27 --[IMPLEMENTS]--> Interface:Shape:sample_project | In golden | Missing in indexer | +| 17 | medium | go | indexer_vs_golden_edge | sample_project_go | Struct:Triangle:sample_project_go/interfaces.go:ln_40 --[IMPLEMENTS]--> Interface:Shape:sample_proje | In golden | Missing in indexer | +| 18 | medium | go | indexer_vs_golden_edge | sample_project_go | Struct:Rectangle:sample_project_go/interfaces.go:ln_33 --[IMPLEMENTS]--> Interface:Shape:sample_proj | In golden | Missing in indexer | +| 19 | medium | haskell | indexer_vs_golden_edge | sample_project_haskell | Class:Product:sample_project_haskell/src/ToughCases.hs:ln_9 --[IMPLEMENTS]--> Class:Descriptive:samp | In golden | Missing in indexer | +| 20 | medium | haskell | indexer_vs_golden_edge | sample_project_haskell | Class:User:sample_project_haskell/src/ToughCases.hs:ln_8 --[IMPLEMENTS]--> Class:Descriptive:sample_ | In golden | Missing in indexer | +| 21 | medium | haskell | indexer_vs_golden_edge | sample_project_haskell | Function:main:sample_project_haskell/Main.hs:ln_7 --[CALLS]--> Class:Person:sample_project_haskell/s | In golden | Missing in indexer | +| 22 | medium | java | indexer_vs_golden_edge | sample_project_java | Function:main:sample_project_java/src/com/example/app/Main.java:ln_13:ctx_Main --[CALLS]--> Function | In golden | Missing in indexer | +| 23 | medium | java | indexer_vs_golden_edge | sample_project_java | Function:run:sample_project_java/src/com/example/app/ToughCases.java:ln_165:ctx_Client --[CALLS]--> | In golden | Missing in indexer | +| 24 | medium | kotlin | indexer_vs_golden_edge | sample_project_kotlin | Object:Companion:sample_project_kotlin/ToughCases.kt:ln_22 --[COMPANION_OF]--> Class:DatabaseConnect | In golden | Missing in indexer | +| 25 | medium | lua | indexer_vs_golden_edge | sample_project_lua | Function:main:sample_project_lua/main.lua:ln_4 --[CALLS]--> Function:greet:sample_project_lua/utils. | In golden | Missing in indexer | +| 26 | medium | perl | indexer_vs_golden_edge | sample_project_perl | File:main.pl:sample_project_perl/main.pl --[CALLS]--> Function:new:sample_project_perl/lib/MyModule/ | In golden | Missing in indexer | +| 27 | medium | perl | indexer_vs_golden_edge | sample_project_perl | Function:speak:sample_project_perl/tough_cases.pl:ln_22:ctx_Dog --[CALLS]--> Function:speak:sample_p | In golden | Missing in indexer | +| 28 | medium | perl | indexer_vs_golden_edge | sample_project_perl | Class:Dog:sample_project_perl/tough_cases.pl:ln_19 --[INHERITS]--> Class:Animal:sample_project_perl/ | In golden | Missing in indexer | +| 29 | medium | php | indexer_vs_golden_edge | sample_project_php | Function:run:sample_project_php/tough_cases.php:ln_26:ctx_ConflictResolver --[CALLS]--> Function:sha | In golden | Missing in indexer | +| 30 | medium | ruby | indexer_vs_golden_edge | sample_project_ruby | Function:test_callbacks:sample_project_ruby/tough_cases.rb:ln_45 --[CALLS]--> Function:initialize:sa | In golden | Missing in indexer | +| 31 | medium | rust | indexer_vs_golden_node | sample_project_rust | Function:snappy_compress:sample_project_rust/src/tough_cases.rs:ln_44 | In golden | Missing in indexer | +| 32 | medium | rust | indexer_vs_golden_edge | sample_project_rust | File:tough_cases.rs:sample_project_rust/src/tough_cases.rs --[CONTAINS]--> Function:snappy_compress: | In golden | Missing in indexer | +| 33 | medium | rust | indexer_vs_golden_edge | sample_project_rust | Function:call_both:sample_project_rust/src/tough_cases.rs:ln_32 --[CALLS]--> Function:action:sample_ | In golden | Missing in indexer | +| 34 | medium | rust | indexer_vs_golden_edge | sample_project_rust | Function:test_specialization:sample_project_rust/src/tough_cases.rs:ln_103 --[CALLS]--> Function:spe | In golden | Missing in indexer | +| 35 | medium | scala | indexer_vs_golden_edge | sample_project_scala | Function:main:sample_project_scala/Main.scala:ln_2:ctx_Main --[CALLS]--> Function:apply:sample_proje | In golden | Missing in indexer | +| 36 | medium | swift | indexer_vs_golden_edge | sample_project_swift | Function:testProtocols:sample_project_swift/ToughCases.swift:ln_31 --[CALLS]--> Function:doWork:samp | In golden | Missing in indexer | +| 37 | medium | typescript | indexer_vs_golden_edge | sample_project_typescript | Function:loadModuleDynamically:sample_project_typescript/src/tough_cases.ts:ln_29 --[CALLS]--> File: | In golden | Missing in indexer | +| 38 | medium | typescript | indexer_vs_golden_edge | sample_project_typescript | Function:updateAge:sample_project_typescript/src/decorators-metadata.ts:ln_363:ctx_User --[DECORATED | In golden | Missing in indexer | +| 39 | high | python | structural | src/ | DECORATED_BY edge type not implemented | @decorator → DECORATED_BY | 0 matches in src/ | +| 40 | high | python | structural | src/ | METACLASS edge type not implemented | metaclass= → METACLASS | 0 matches in src/ | +| 41 | medium | python | structural | python.py | only when file has module-level calls | One per .py file | _attach_module_context conditional | +| 42 | high | python | golden_vs_spec | goldens/sample_project | Golden has 0 DECORATED_BY edges | Per CGC_EXPECTED_GRAPH_SPEC | Not in edges.jsonl | +| 43 | medium | python | golden_vs_spec | ./advanced_calls.py | Missing Function node | at L1 | Not in golden | +| 44 | medium | python | golden_vs_spec | ./advanced_classes.py | Missing Function node | at L1 | Not in golden | +| 45 | medium | python | golden_vs_spec | ./advanced_functions.py | Missing Function node | at L1 | Not in golden | +| 46 | medium | python | golden_vs_spec | ./advanced_imports.py | Missing Function node | at L1 | Not in golden | +| 47 | medium | python | golden_vs_spec | ./async_features.py | Missing Function node | at L1 | Not in golden | +| 48 | medium | python | golden_vs_spec | ./circular1.py | Missing Function node | at L1 | Not in golden | +| 49 | medium | python | golden_vs_spec | ./circular2.py | Missing Function node | at L1 | Not in golden | +| 50 | medium | python | golden_vs_spec | ./complex_classes.py | Missing Function node | at L1 | Not in golden | +| 51 | medium | python | golden_vs_spec | ./context_managers.py | Missing Function node | at L1 | Not in golden | +| 52 | medium | python | golden_vs_spec | ./control_flow.py | Missing Function node | at L1 | Not in golden | +| 53 | medium | python | golden_vs_spec | ./dynamic_dispatch.py | Missing Function node | at L1 | Not in golden | +| 54 | medium | python | golden_vs_spec | ./dynamic_imports.py | Missing Function node | at L1 | Not in golden | +| 55 | medium | python | golden_vs_spec | ./edge_cases/comments_only.py | Missing Function node | at L1 | Not in golden | +| 56 | medium | python | golden_vs_spec | ./edge_cases/docstring_only.py | Missing Function node | at L1 | Not in golden | +| 57 | medium | python | golden_vs_spec | ./edge_cases/empty.py | Missing Function node | at L1 | Not in golden | +| 58 | medium | python | golden_vs_spec | ./edge_cases/hardcoded_secrets.py | Missing Function node | at L1 | Not in golden | +| 59 | medium | python | golden_vs_spec | ./edge_cases/long_functions.py | Missing Function node | at L1 | Not in golden | +| 60 | medium | python | golden_vs_spec | ./edge_cases/syntax_error.py | Missing Function node | at L1 | Not in golden | +| 61 | medium | python | golden_vs_spec | ./generators.py | Missing Function node | at L1 | Not in golden | +| 62 | medium | python | golden_vs_spec | ./mapping_calls.py | Missing Function node | at L1 | Not in golden | +| 63 | medium | python | golden_vs_spec | ./module_a.py | Missing Function node | at L1 | Not in golden | +| 64 | medium | python | golden_vs_spec | ./module_b.py | Missing Function node | at L1 | Not in golden | +| 65 | medium | python | golden_vs_spec | ./module_c/__init__.py | Missing Function node | at L1 | Not in golden | +| 66 | medium | python | golden_vs_spec | ./module_c/submodule1.py | Missing Function node | at L1 | Not in golden | +| 67 | medium | python | golden_vs_spec | ./module_c/submodule2.py | Missing Function node | at L1 | Not in golden | +| 68 | medium | python | golden_vs_spec | ./namespace_pkg/ns_module.py | Missing Function node | at L1 | Not in golden | +| 69 | medium | python | golden_vs_spec | ./nesting_test.py | Missing Function node | at L1 | Not in golden | +| 70 | medium | python | golden_vs_spec | ./pattern_matching.py | Missing Function node | at L1 | Not in golden | +| 71 | medium | python | golden_vs_spec | ./pkg_tough/__init__.py | Missing Function node | at L1 | Not in golden | +| 72 | medium | python | golden_vs_spec | ./pkg_tough/parent_mod.py | Missing Function node | at L1 | Not in golden | +| 73 | medium | python | golden_vs_spec | ./pkg_tough/subpkg/__init__.py | Missing Function node | at L1 | Not in golden | +| 74 | medium | python | golden_vs_spec | ./pkg_tough/subpkg/child_mod.py | Missing Function node | at L1 | Not in golden | +| 75 | medium | python | golden_vs_spec | ./tough_cases.py | Missing Function node | at L1 | Not in golden | +| 76 | medium | python | golden_vs_spec | ./typing_examples.py | Missing Function node | at L1 | Not in golden | +| 77 | medium | python | golden_vs_spec | ./unicode_test.py | Missing Function node | at L1 | Not in golden | +| 78 | high | all | meta | GOLDEN_PERFECTION_REPORT.md | Circular perfection gate (export vs itself) | Indexer vs source truth | Misleading 21/21 PASS | +| 79 | high | all | meta | audit reports | CALLS avg 84.6% vs perfection 100% | Single metric | Conflicting reports | +| 80 | high | all | meta | MANUAL_AUDIT.md | Hand-edited golden not on disk | 563/796 nodes | 482/621 in metadata.json | +| 81 | high | python | meta | PyPI 0.4.16 | Nested f1→f2→f3 broken on published wheel | Function→Function CALLS | Fixed locally only | +| 82 | medium | all | meta | E2E BUG-005 | delete leaves orphan nodes | 0 after delete | 9 remain | +| 83 | medium | all | meta | E2E BUG-001 | Repo .env overrides global config | Isolated HOME | Config bleed | +| 84 | medium | python | source_vs_spec | control_flow.py | try_except_finally L14 raise ValueError | CALLS→ValueError | Likely missing in indexer | +| 85 | medium | python | source_vs_spec | control_flow.py | conditional_inner_import L25 import numpy | IMPORTS numpy | Likely missing | +| 86 | medium | python | source_vs_spec | control_flow.py | conditional_inner_import L26 np.array | CALLS→array | Likely missing | +| 87 | medium | python | source_vs_spec | control_flow.py | env_based_import L31-L37 conditional json imports | IMPORTS ujson/json | Likely missing | +| 88 | medium | python | source_vs_spec | control_flow.py | env_based_import L38 json.dumps | CALLS→dumps | Likely missing | +| 89 | medium | python | source_vs_spec | dynamic_dispatch.py | dispatch_by_key L12 DISPATCH[name](a,b) | CALLS→add/sub/mul | May be partial | +| 90 | medium | python | source_vs_spec | dynamic_dispatch.py | partial_example L21 add5(10) | CALLS partial→add, add5→add | May be partial | +| 91 | medium | python | source_vs_spec | tough_cases.py | globals()[func_name]() L48-49 | CALLS dynamic | Tier 9 / missing | +| 92 | medium | python | source_vs_spec | tough_cases.py | getattr(self, helper_method) L52 | CALLS→helper_method | May resolve | +| 93 | medium | python | source_vs_spec | tough_cases.py | injected_call via metaclass L37 | CALLS + METACLASS | Partial | +| 94 | medium | python | source_vs_spec | complex_classes.py | wrapper L19 str(func(...)) | CALLS str, func | May be on wrong node | +| 95 | medium | python | source_vs_spec | complex_classes.py | class_method L15 cls().greet(cls()) | CALLS Child ctor, greet | Partial | +| 96 | medium | python | source_vs_spec | function_chains.py | L8 strip().lower().replace chain | Chained CALLS | May be partial | +| 97 | medium | python | source_vs_spec | function_chains.py | L17 make_adder(2)(8) | CALLS make_adder→adder | Closure chain | +| 98 | medium | python | source_vs_spec | cli_and_dunder.py | L12 __main__ run() | →run | If __main__ block | +| 99 | medium | python | source_vs_spec | advanced_classes.py | Meta.__new__ L22 super().__new__ | CALLS super | May exist | +| 100 | medium | python | source_vs_spec | typing_examples.py | dict_func L10 sum(d.values()) | CALLS sum, values | May exist | + +## Suggested fix order + +1. **IDs 1–3** — Python CALLS gaps confirmed by audit (`advanced_calls`, `advanced_functions`, `class_instantiation`) +2. **IDs 4–38** — Other languages' confirmed indexer-vs-golden gaps (Lua/Swift worst) +3. **Structural** — Implement DECORATED_BY + METACLASS in parser/writer +4. **``** — One pseudo-function per Python file, then re-export golden +5. **Resolver** — Tier 8/9 fallbacks, dynamic dispatch +6. **Meta** — Retire circular perfection gate; trust `call_graph_audit.py` only + +## Regenerate + +```bash +.venv/bin/python scripts/call_graph_audit.py # refresh CGC_CALL_GRAPH_AUDIT_REPORT.md +# then re-run the harvest script or diff manually +``` diff --git a/CGC_REPORT.md b/CGC_REPORT.md new file mode 100644 index 00000000..d70b4701 --- /dev/null +++ b/CGC_REPORT.md @@ -0,0 +1,72 @@ +# CGC Report + +_Generated: 2026-06-07 17:00 UTC_ + + +## God Nodes — Highest Fan-In +_These nodes are called from many places. High fan-in increases risk: a change here affects every caller._ + +| Kind | Name | File | In-degree | +| --- | --- | --- | --- | +| ? | demo | repo/main.py | 0 | + + +## Most Complex Functions +_Cyclomatic complexity > 10 is a refactoring candidate._ + +| Function | File | Cyclomatic Complexity | +| --- | --- | --- | +| demo | repo/main.py | ? | + + +## Cross-Module Connections +_Calls that cross package boundaries — review for unexpected coupling._ + +| Caller | Caller File | Callee | Callee File | Confidence | +| --- | --- | --- | --- | --- | +| ? | | ? | | — | + + +## Potential Dead Code +_Functions with zero callers (not guaranteed dead — may be entry points or called via reflection)._ + +| Function | File | +| --- | --- | +| demo | repo/main.py | + + +## Suggested Cypher Queries +_Copy these into `execute_cypher_query` to explore further._ + +### Callers of a specific function +```cypher +MATCH (caller)-[:CALLS]->(fn:Function {name: 'yourFunctionName'}) +RETURN caller.name, caller.path LIMIT 20 +``` + +### Class hierarchy for a specific class +```cypher +MATCH path = (c:Class {name: 'YourClass'})-[:INHERITS*]->(parent) +RETURN [n IN nodes(path) | n.name] AS hierarchy +``` + +### Most-injected Spring beans +```cypher +MATCH ()-[:INJECTS]->(bean:Class) +RETURN bean.name, count(*) AS injection_count +ORDER BY injection_count DESC LIMIT 10 +``` + +### All external library dependencies +```cypher +MATCH (m:MavenModule)-[:USES_LIBRARY]->(lib:ExternalLibrary) +RETURN m.artifact_id, lib.group_id, lib.artifact_id, lib.version +ORDER BY lib.artifact_id +``` + +### CALLS edges with low confidence (potential mis-resolutions) +```cypher +MATCH (a)-[c:CALLS]->(b) +WHERE c.confidence_label = 'AMBIGUOUS' +RETURN a.name, b.name, c.resolution_tier, a.path LIMIT 20 +``` diff --git a/COMMIT_MSG.txt b/COMMIT_MSG.txt new file mode 100644 index 00000000..4dd7710e --- /dev/null +++ b/COMMIT_MSG.txt @@ -0,0 +1,9 @@ +fix: 3个Windows反斜杠路径回归测试在Linux CI上跳过 + +- test_finds_repo_stored_with_backslash_path +- test_normalized_path_used_for_lookup +- test_normalized_path_used + +根因: str(tmp_path).replace('/', '\\') 在Linux上生成的 +是相对路径(不以盘符开头),Path().resolve() 拼上cwd后 +产生正反斜杠混用的畸形路径。 diff --git a/Dockerfile b/Dockerfile index 9d28338d..1946bfeb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,7 @@ ENV PYTHONDONTWRITEBYTECODE=1 ENV CGC_HOME=/root/.codegraphcontext # Remote FalkorDB connection (set at runtime via docker run -e or docker-compose) -# ENV DATABASE_TYPE=falkordb-remote +# ENV DEFAULT_DATABASE=falkordb-remote # ENV FALKORDB_HOST= # ENV FALKORDB_PORT=6379 # ENV FALKORDB_PASSWORD= diff --git a/LICENSE b/LICENSE index b77bf2ab..1e9ccfee 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 +Copyright (c) 2025-2026 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2e24ae2e..369a467a 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,14 @@ 🌐 **Languages:** - 🇬🇧 [English](README.md) -- 🇨🇳 [中文](README.zh-CN.md) -- 🇰🇷 [한국어](README.kor.md) -- 🇺🇦 [Українська](README.uk.md) -- 🇯🇵 日本語 (Soon) -- 🇷🇺 Русский (Soon) +- 🇨🇳 [中文](docs/translations/README.zh-CN.md) +- 🇰🇷 [한국어](docs/translations/README.kor.md) +- 🇺🇦 [Українська](docs/translations/README.uk.md) +- 🇷🇺 [Русский](docs/translations/README.ru-RU.md) +- 🇯🇵 [日本語](docs/translations/README.ja.md) - 🇪🇸 Español (Soon) -🌍 **Help translate CodeGraphContext to your language by raising an issue & PR on https://github.com/Shashankss1205/CodeGraphContext/issues!** +🌍 **Help translate CodeGraphContext to your language by raising an issue & PR on [GitHub Issues](https://github.com/Shashankss1205/CodeGraphContext/issues)!**


@@ -56,7 +56,7 @@ Website - + Docs @@ -70,11 +70,14 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra --- ## 📍 Quick Navigation -* [🚀 Quick Start](#quick-start) +* [🚀 Quick Start](#-installation--quick-start) +* [📋 Prerequisites](#-prerequisites) +* [🏃 How to Run the Project Locally](#-how-to-run-the-project-locally) * [🌐 Supported Programming Languages](#supported-programming-languages) * [🛠️ CLI Toolkit](#for-cli-toolkit-mode) * [🤖 MCP Server](#-for-mcp-server-mode) * [🗄️ Database Options](#database-options) +* [🔬 SCIP indexing (optional)](#scip-indexing-optional) --- @@ -97,7 +100,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra --- ## Project Details -- **Version:** 0.3.8 +- **Version:** 0.5.1 - **Authors:** Shashank Shekhar Singh - **License:** MIT License (See [LICENSE](LICENSE) for details) - **Website:** [CodeGraphContext](http://codegraphcontext.vercel.app/) @@ -111,7 +114,7 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra - 📧 Email: [shashankshekharsingh1205@gmail.com](mailto:shashankshekharsingh1205@gmail.com) - 🐙 GitHub: [@Shashankss1205](https://github.com/Shashankss1205) - 🔗 LinkedIn: [Shashank Shekhar Singh](https://www.linkedin.com/in/shashank-shekhar-singh-a67282228/) -- 🌐 Website: [codegraphcontext.vercel.app](http://codegraphcontext.vercel.app/) +- 🌐 Website: [codegraphcontext.vercel.app](https://codegraphcontext.vercel.app/) *Contributions and feedback are always welcome! Feel free to reach out for questions, suggestions, or collaboration opportunities.* @@ -125,12 +128,13 @@ A powerful **MCP server** and **CLI toolkit** that indexes local code into a gra ## Features - **Code Indexing:** Analyzes code and builds a knowledge graph of its components. - **Relationship Analysis:** Query for callers, callees, class hierarchies, call chains and more. -- **Pre-indexed Bundles:** Load famous repositories instantly with `.cgc` bundles - no indexing required! ([Learn more](docs/BUNDLES.md)) +- **Pre-indexed Bundles:** Load famous repositories instantly with `.cgc` bundles - no indexing required! ([Learn more](docs/docs/guides/bundles.md)) - **Live File Watching:** Watch directories for changes and automatically update the graph in real-time (`cgc watch`). - **Interactive Setup:** A user-friendly command-line wizard for easy setup. - **Dual Mode:** Works as a standalone **CLI toolkit** for developers and as an **MCP server** for AI agents. -- **Multi-Language Support:** Full support for 14 programming languages. -- **Flexible Database Backend:** KùzuDB (default, zero-config for all platforms), FalkorDB Lite (Unix-only), FalkorDB Remote, or Neo4j (all platforms via Docker/native). +- **Multi-Language Support:** Full support for 23 programming languages. +- **Flexible Database Backend:** FalkorDB Lite (Default), KuzuDB, LadybugDB, FalkorDB Remote, Nornic DB, or Neo4j (all platforms via Docker/native). + --- @@ -141,10 +145,13 @@ CodeGraphContext provides comprehensive parsing and analysis for the following l | | Language | | Language | | Language | | :--- | :--- | :--- | :--- | :--- | :--- | | 🐍 | **Python** | 📜 | **JavaScript** | 🔷 | **TypeScript** | -| ☕ | **Java** | 🏗️ | **C / C++** | #️⃣ | **C#** | -| 🐹 | **Go** | 🦀 | **Rust** | 💎 | **Ruby** | -| 🐘 | **PHP** | 🍎 | **Swift** | 🎨 | **Kotlin** | -| 🎯 | **Dart** | 🐪 | **Perl** | | | +| ☕ | **Java** | 🔵 | **C** | ➕ | **C++** | +| #️⃣ | **C#** | 🐹 | **Go** | 🦀 | **Rust** | +| 💎 | **Ruby** | 🐘 | **PHP** | 🍎 | **Swift** | +| 🎨 | **Kotlin** | 🎯 | **Dart** | 🐪 | **Perl** | +| 🌙 | **Lua** | 🚀 | **Scala** | λ | **Haskell** | +| 💧 | **Elixir** | 📜 | **Emacs Lisp (elisp)** | 🌐 | **HTML** | +| 🎨 | **CSS** | ⚛️ | **TSX** | | | Each language parser extracts functions, classes, methods, parameters, inheritance relationships, function calls, and imports to build a comprehensive code graph. @@ -154,17 +161,30 @@ Each language parser extracts functions, classes, methods, parameters, inheritan CodeGraphContext supports multiple graph database backends to suit your environment: -| Feature | KùzuDB (Default) | FalkorDB Lite | Neo4j | -| :--- | :--- | :--- | :--- | -| **Setup** | Zero-config / Embedded | Zero-config / In-process | Docker / External | -| **Platform** | **All (Windows Native, macOS, Linux)** | Unix-only (Linux/macOS/WSL) | All Platforms | -| **Use Case** | Desktop, IDE, Local development | Specialized Unix development | Enterprise, Massive graphs | -| **Requirement**| `pip install kuzu` | `pip install falkordblite` | Neo4j Server / Docker | +| Feature | KuzuDB | LadybugDB | FalkorDB Lite | Neo4j / Nornic DB | +| :--- | :--- | :--- | :--- | :--- | +| **Typical default** | Cross-platform fallback when FalkorDB Lite is unavailable | Optional embedded backend | **Default on Unix** (Python 3.12+, when `falkordblite` is installed) | When explicitly configured via `cgc config db` | +| **Setup** | Zero-config / Embedded | Zero-config / Embedded | Zero-config / In-process | Docker / External | +| **Platform** | **All (Windows Native, macOS, Linux)** | **All (Windows Native, macOS, Linux)** | Unix-only (Linux/macOS/WSL) | All Platforms | +| **Use Case** | Desktop, IDE, Local development | Custom research projects | Specialized Unix development | Enterprise, Massive graphs | +| **Requirement**| `pip install kuzu` | `pip install ladybug` | `pip install falkordblite` | Neo4j Server / Docker / Nornic Cloud | | **Speed** | ⚡ Extremely Fast | ⚡ Fast | 🚀 Scalable | | **Persistence**| Yes (to disk) | Yes (to disk) | Yes (to disk) | --- +## SCIP indexing (optional) + +When `SCIP_INDEXER=true` in your CGC config (`~/.codegraphcontext/.env`), some languages use external **SCIP** indexers for more accurate calls and inheritance than Tree-sitter heuristics alone. + +**C and C++** use **scip-clang**, which requires a **`compile_commands.json`** file (a [JSON compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html)): one entry per translation unit with the real compiler command (include paths, `-D` defines, `-std`, etc.). Without it, scip-clang cannot run; CGC logs a warning and **falls back to Tree-sitter** for that repo. Typical ways to produce the file: **CMake** with `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`, or wrap your real build with **[Bear](https://github.com/rizsotto/Bear)** (e.g. `bear -- make`). CGC also looks under `build/` and `cmake-build-*/` for that filename. + +**C#** uses **scip-dotnet** (Roslyn); you need a normal **`.csproj` / `.sln`** and a successful restore—no `compile_commands.json`. + +SCIP is **independent of which graph database** you use (Kuzu, Neo4j, etc.); the same flag applies to all backends. + +--- + ## Used By CodeGraphContext is already being explored by developers and projects for: @@ -182,124 +202,149 @@ _If you’re using CodeGraphContext in your project, feel free to open a PR and - `neo4j>=5.15.0` - `watchdog>=3.0.0` - `stdlibs>=2023.11.18` -- `typer[all]>=0.9.0` +- `typer>=0.9.0` - `rich>=13.7.0` -- `inquirerpy>=0.3.7` +- `inquirerpy>=0.3.4` - `python-dotenv>=1.0.0` -- `tree-sitter>=0.21.0` -- `tree-sitter-language-pack>=0.6.0` +- `tree-sitter>=0.21.0` (not installed on Python 3.13) +- `tree-sitter-language-pack>=0.6.0` (not installed on Python 3.13) - `pyyaml` -- `pytest` -- `nbformat` -- `nbconvert>=7.16.6` - `pathspec>=0.12.1` +- `falkordb>=1.0,<1.6` +- `falkordblite>=0.7,<0.10` (Unix only, Python 3.12+) +- `kuzu` (KuzuDB engine) +- `fastapi>=0.100.0` +- `uvicorn>=0.22.0` +- `requests>=2.28.0` +- `protobuf>=3.20,<3.21` **Note:** Python 3.10-3.14 is supported. --- -## Quick Start -### Install the core toolkit +### 🚀 Installation & Quick Start + +1. **Install the toolkit:** + ```bash + pip install codegraphcontext + ``` + +2. **Troubleshooting (Command not found):** + If the `codegraphcontext` command is not found, run this one-line fix: + ```bash + curl -sSL https://raw.githubusercontent.com/CodeGraphContext/CodeGraphContext/main/scripts/post_install_fix.sh | bash + ``` + +3. **Database Setup (Automatic):** + CodeGraphContext uses an embedded graph database by default. + - **FalkorDB Lite:** Default backend. + - **KuzuDB:** Cross-platform embedded backend. + - **Neo4j:** Run `codegraphcontext neo4j setup` to use an external server. + +--- + +## 📋 Prerequisites + +Before installing CodeGraphContext, ensure you have: + +* Python 3.10 or later +* pip package manager +* Git (optional, for cloning repositories) + +Verify your Python installation: + +```bash +python --version ``` + +--- + +## 🚀 Step-by-Step Setup Guide + +### Step 1: Install CodeGraphContext + +```bash pip install codegraphcontext ``` -### If 'cgc' command isn't found, run our one-line fix: -``` -curl -sSL https://raw.githubusercontent.com/CodeGraphContext/CodeGraphContext/main/scripts/post_install_fix.sh | bash +This command installs CodeGraphContext and all required dependencies. + +### Step 2: Verify Installation + +```bash +codegraphcontext --help ``` +If the command displays the available CLI commands, the installation was successful. + +### Step 3: Database Setup + +CodeGraphContext automatically uses an embedded database by default, so no additional configuration is required for most users. + --- -## Getting Started +## 🏃 How to Run the Project Locally -### 📋 Understanding CodeGraphContext Modes -CodeGraphContext operates in **two modes**, and you can use either or both: +### Index a Repository -#### 🛠️ Mode 1: CLI Toolkit (Standalone) -Use CodeGraphContext as a **powerful command-line toolkit** for code analysis: -- Index and analyze codebases directly from your terminal -- Query code relationships, find dead code, analyze complexity -- Visualize code graphs and dependencies -- Perfect for developers who want direct control via CLI commands +```bash +codegraphcontext index . +``` -#### 🤖 Mode 2: MCP Server (AI-Powered) -Use CodeGraphContext as an **MCP server** for AI assistants: -- Connect to AI IDEs (VS Code, Cursor, Windsurf, Claude, Kiro, etc.) -- Let AI agents query your codebase using natural language -- Automatic code understanding and relationship analysis -- Perfect for AI-assisted development workflows +This scans the current project and creates a searchable code graph. -**You can use both modes!** Install once, then use CLI commands directly OR connect to your AI assistant. +### View Indexed Repositories -### Installation (Both Modes) +```bash +codegraphcontext list +``` -1. **Install:** `pip install codegraphcontext` -

- ⚙️ Troubleshooting: In case, command cgc not found +Displays all repositories currently indexed by CodeGraphContext. - If you encounter "cgc: command not found" after installation, run the PATH fix script: - - **Linux/Mac:** - ```bash - # Download the fix script - curl -O https://raw.githubusercontent.com/CodeGraphContext/CodeGraphContext/main/scripts/post_install_fix.sh - - # Make it executable - chmod +x post_install_fix.sh - - # Run the script - ./post_install_fix.sh - - # Restart your terminal or reload shell config - source ~/.bashrc # or ~/.zshrc for zsh users - ``` - - **Windows (PowerShell):** - ```powershell - # Download the fix script - curl -O https://raw.githubusercontent.com/CodeGraphContext/CodeGraphContext/main/scripts/post_install_fix.sh - - # Run with bash (requires Git Bash or WSL) - bash post_install_fix.sh - - # Restart PowerShell or reload profile - . $PROFILE - ``` -
+### Analyze Code -2. **Database Setup (Automatic)** - - - **KùzuDB (Default):** Runs natively on Windows, macOS, and Linux without any setup. Just `pip install kuzu` and you're ready! - - **FalkorDB Lite (Alternative):** Supported on Unix/macOS/WSL for Python 3.12+. - - **Neo4j (Alternative):** To use Neo4j instead, or if you prefer a server-based approach, run: `cgc neo4j setup` +```bash +codegraphcontext analyze dead-code +``` + +Finds potentially unused code in the indexed repository. --- +## ✅ Verify Everything Works + +After indexing a repository, run: + +```bash +codegraphcontext list +``` + +If the command executes successfully and displays indexed repositories, your setup is complete and CodeGraphContext is ready to use. + ### For CLI Toolkit Mode **Start using immediately with CLI commands:** ```bash # Index your current directory -cgc index . +codegraphcontext index . # List all indexed repositories -cgc list +codegraphcontext list # Analyze who calls a function -cgc analyze callers my_function +codegraphcontext analyze callers my_function # Find complex code -cgc analyze complexity --threshold 10 +codegraphcontext analyze complexity --threshold 10 # Find dead code -cgc analyze dead-code +codegraphcontext analyze dead-code # Watch for live changes (optional) -cgc watch . +codegraphcontext watch . # See all commands -cgc help +codegraphcontext help ``` **See the full [CLI Commands Guide](docs/CLI_COMPLETE_REFERENCE.md) for all available commands and usage scenarios.** @@ -315,13 +360,13 @@ CodeGraphContext can generate stunning, interactive knowledge graphs of your cod ```bash # Visualize function calls -cgc analyze calls my_function --viz +codegraphcontext analyze calls my_function --viz # Explore class hierarchies -cgc analyze tree MyClass --viz +codegraphcontext analyze tree MyClass --viz # Visualize search results -cgc find pattern "Auth" --viz +codegraphcontext find pattern "Auth" --viz ``` @@ -333,13 +378,14 @@ cgc find pattern "Auth" --viz 1. **Setup:** Run the MCP setup wizard to configure your IDE/AI assistant: ```bash - cgc mcp setup + codegraphcontext mcp setup ``` The wizard can automatically detect and configure: * VS Code * Cursor * Windsurf + * Zed * Claude * Gemini CLI * ChatGPT Codex @@ -347,15 +393,17 @@ cgc find pattern "Auth" --viz * RooCode * Amazon Q Developer * Kiro + * Goose + * OpenCode - Upon successful configuration, `cgc mcp setup` will generate and place the necessary configuration files: + Upon successful configuration, `codegraphcontext mcp setup` will generate and place the necessary configuration files: * It creates an `mcp.json` file in your current directory for reference. * It stores your database credentials securely in `~/.codegraphcontext/.env`. * It updates the settings file of your chosen IDE/CLI (e.g., `.claude.json` or VS Code's `settings.json`). 2. **Start:** Launch the MCP server: ```bash - cgc mcp start + codegraphcontext mcp start ``` 3. **Use:** Now interact with your codebase through your AI assistant using natural language! See examples below. @@ -384,7 +432,7 @@ You can tell CodeGraphContext to ignore specific files and directories by creati ## MCP Client Configuration -The `cgc mcp setup` command attempts to automatically configure your IDE/CLI. If you choose not to use the automatic setup, or if your tool is not supported, you can configure it manually. +The `codegraphcontext mcp setup` command attempts to automatically configure your IDE/CLI. If you choose not to use the automatic setup, or if your tool is not supported, you can configure it manually. Add the following server configuration to your client's settings file (e.g., VS Code's `settings.json` or `.claude.json`): @@ -392,8 +440,38 @@ Add the following server configuration to your client's settings file (e.g., VS { "mcpServers": { "CodeGraphContext": { - "command": "cgc", + "command": "codegraphcontext", + "args": [ + "mcp", + "start" + ], + "env": { + "NEO4J_URI": "YOUR_NEO4J_URI", + "NEO4J_USERNAME": "YOUR_NEO4J_USERNAME", + "NEO4J_PASSWORD": "YOUR_NEO4J_PASSWORD" + }, + "disabled": false, + "alwaysAllow": [] + } + } +} +``` + +#### OpenCode Configuration + +For instructions on installing and configuring MCP servers with OpenCode, see the [OpenCode MCP Guide](https://opencode.ai/docs/ko/mcp-servers/#_top). + +#### If installed via pipx + +If you installed CodeGraphContext using `pipx`, use the following configuration instead: +```json +{ + "mcpServers": { + "CodeGraphContext": { + "command": "pipx", "args": [ + "run", + "codegraphcontext", "mcp", "start" ], @@ -476,7 +554,7 @@ Once the server is running, you can interact with it through your AI assistant u ## Contributing Contributions are welcome! 🎉 -Please see our [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines. +Please see our [CONTRIBUTING.md](.github/CONTRIBUTING.md) for detailed guidelines. If you have ideas for new features, integrations, or improvements, open an [issue](https://github.com/CodeGraphContext/CodeGraphContext/issues) or submit a Pull Request. Join discussions and help shape the future of CodeGraphContext. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 00000000..c3a9ee79 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,956 @@ +# CodeGraphContext: Master Roadmap & Engineering Audit + +> **Version**: 0.4.16 → 1.0.0 +> **Period**: June 2026 — December 2026 +> **Milestones**: 100 + +This document serves as the master strategic roadmap and engineering audit for the CodeGraphContext (CGC) project. It is divided into four main sections: +1. **Engineering Deep Dive & Hidden Gaps**: A low-level audit of architectural flaws and bugs. +2. **Complete Feature Inventory**: Everything CGC currently does. +3. **Roadmap Phase 1 (Months 1-3)**: Foundational refactoring, database optimization, and core engine improvements. +4. **Roadmap Phase 2 (Months 4-6)**: VS Code extension, Web LLM integrations, Ollama, and Graph RAG. + +--- + +## Part 1: Comprehensive Missed Points & Codebase Deep Dive + +This section catalogs discovered code paths, architectural design details, hidden behaviors, testing flaws, and bugs within the repository, organized by folder and file. + +### 1. Root & Entrypoints (`src/codegraphcontext/`) +* **`__init__.py` & `__main__.py`**: Exposes the package API. `__main__.py` immediately redirects execution to the Typer CLI entrypoint in `cli.main:app()`. +* **`prompts.py`**: Defines standard system prompts for LLM reasoning. **Gap**: System prompts have hardcoded formatting constraints assuming Markdown, not adjusting to JSON or other interface layouts. +* **`tool_definitions.py`**: Declares JSON schema definitions for 15+ MCP tools. **Gap**: Tool definitions must match their corresponding python handler signatures manually. There is no dynamic typing or reflection-based schema generation, leading to potential drift. + +### 2. Web API & SSE Transport (`src/codegraphcontext/api/`) +* **`app.py`**: Configures CORS middleware for browser connections and integrates the MCP server directly into the HTTP router. +* **`mcp_sse.py`**: SSE transport layer wrapper bridging JSON-RPC message framing to HTTP streaming responses. +* **`router.py` & `schemas.py`**: Defines FastAPI REST endpoints and Pydantic models for querying index states. + +### 3. Core Database Engine & Interfaces (`src/codegraphcontext/core/`) +* **`database.py`**: Factory method `get_database_manager` resolves DB type from `CGC_RUNTIME_DB_TYPE` or `.env`. **Gap**: No schema validation upon instantiation. Connection pooling is managed entirely within sub-managers (no centralized pool). +* **`database_neo4j.py`**: Driver for remote Neo4j. **Gap**: Uses session-level executions rather than explicit transactions in some areas, risking incomplete rollbacks during concurrent writes. +* **`database_kuzu.py`**: Embedded KuzuDB wrapper. **Gap**: Contains extensive Cypher compilations (e.g., translates `ORDER BY node.property` to column aliases, converts fulltext indexes to string-matching searches, injects fallback `uid`s to resolve binder exceptions). Records fail-fast queries in memory to skip recurrent binder warnings. +* **`database_ladybug.py`**: Wrapper for LadybugDB. Shares Kuzu's Cypher dialect constraints. **Gap**: Exhibits high virtual memory footprints (>3 GB) during batch ingestion because it stores transactions in-memory before committing. +* **`database_falkordb.py` & `database_falkordb_remote.py`**: Local/Remote FalkorDB. **Gap**: If the temporary unix socket file (`falkordb.sock`) is deleted, the driver crashes without reconnection attempts. +* **`database_nornic.py`**: HTTP-based JSON payload API (not binary protocol). **Gap**: Lacks native graph transactions, suffering latency in deep-nested call-tree traversals. +* **`falkor_worker.py`**: Manages Redis/FalkorDB subprocess. **Gap**: Port collisions and stale `.pid` files can leave dead Redis server processes running. +* **`cgc_bundle.py`**: Manages `.cgc` archive import/export. **Gap**: Importer does not validate index schemas prior to restoring data. Custom bundles conflicting with pre-configured schemas cause migration failures and DB corruption. +* **`bundle_registry.py`**: Handles Hugging Face registry downloads. **Gap**: No cryptographic signature/hash checking on downloaded bundles, posing a supply-chain security risk. +* **`jobs.py`**: Manages background asynchronous jobs. **Gap**: In-memory queue means all pending/running jobs are lost on server/IDE restart. Swallows tracebacks on failed tasks. +* **`watcher.py` & `cgcignore.py`**: File-system watching and exclusion rules. **Gap**: `.cgcignore` does not support git wildcard extensions like `**` properly, parsing them as standard regex wildcards and skipping nested mono-repo folders. + +### 4. CLI Tooling & Setup Wizards (`src/codegraphcontext/cli/`) +* **`main.py`**: Typer CLI (2386 lines). **Gap**: Swallows exceptions on incorrect cypher syntax in `cgc query`, outputting generic errors instead of raw DB tracebacks. +* **`config_manager.py`**: **Gap**: Reads `.env` files recursively by climbing the directory tree without bounds (security risk in shared server environments). +* **`cli_helpers.py`**: Includes `CGCDiskCache` for AST tokens. **Gap**: Disk cache lacks an automatic eviction policy, growing unbounded over time. +* **`registry_commands.py`**: **Gap**: Fuzzy Name Resolver downloads the most recent timestamped bundle if a commit hash isn't specified, which can be inaccurate. On-demand requests only output instructions rather than calling webhooks directly. +* **`setup_wizard.py` & `setup_macos.py`**: **Gap**: Lacks validation for permissions/write access when configuring global system settings (Homebrew). +* **`visualizer.py`**: **Gap**: Limited to node connections. Large queries truncate, and formatting crashes if nodes lack a `name` property. + +### 5. AST Extraction & Graph Architecture (`src/codegraphcontext/tools/`) +* **`graph_builder.py`**: **Gap**: Relies on deep recursion for AST tree traversal. Extremely nested/autogenerated files trigger Python's recursion limit. +* **`code_finder.py`**: **Gap**: Uses heuristic fallbacks for `CALLS` edge resolution. If static resolution fails, it queries for any function node matching that name, causing false-positive edges. +* **`scip_indexer.py`**: **Gap**: Requires external `scip` CLI utility. Falls back silently to Tree-sitter if missing, failing on complex external cross-package imports. +* **`tree_sitter_parser.py`**: **Gap**: Thread-safety issues when compiling language parser `.so` libraries concurrently. +* **`package_resolver.py`**: **Gap**: Lacks support for lockfiles (`poetry.lock`, `package-lock.json`) to pin exact dependency versions. + +### 6. AST Language Parsers (`src/codegraphcontext/tools/languages/`) +* Supports 26 parsers. +* **Gaps**: Query selectors break if tree-sitter grammars are updated. Parsers like `rust.py` and `cpp.py` don't resolve complex namespace chains statically, conflating functions with matching names. + +### 7. Tool Integrations & MCP Server (`src/codegraphcontext/server.py`) +* **`server.py`**: Truncates output to fit `MAX_TOOL_RESPONSE_TOKENS` by estimating 4 characters per token. Strips `/workspace/` prefix for container compatibility. Dynamically disables tools based on `disabledTools` config. + +### 8. Utilities & Graph Visualization (`src/codegraphcontext/utils/` & `viz/`) +* **`tree_sitter_manager.py`**: Compiles all libraries into a single shared object file. + +### 9. Datasources (`src/codegraphcontext/tools/datasources/`) +* **`cassandra_ingester.py`, `mysql_ingester.py`**: **Gap**: Lacks SSL support when connecting to instances, throwing socket errors on secure enterprise databases. + +### 10. Testing Gaps & Bugs +* **`test_issue_806_fix.py`**: Bug on Line 484 has a hardcoded path (`/home/pc1/Desktop/CodeGraphContext`), failing on other machines. +* **`test_database_kuzu_compat.py`**: Failing assumptions on inheritance fail-fast guards and `UNWIND` uid injection fallbacks (global params cause identical UIDs). + +--- + +## Part 2: Complete Inventory of Everything CGC Has Today + +### 📊 Project Statistics (as of v0.4.16) +* **Python lines**: ~25,000+ +* **Website (React/TS) lines**: ~15,000+ +* **VS Code Extension lines**: ~3,500+ +* **Language parsers**: 26 files (24 languages + Gradle/Maven/MyBatis) +* **Database backends**: 5 (+ LadybugDB) +* **MCP tools**: 21 +* **CLI commands**: 55+ +* **Test files**: ~50+ +* **GitHub Actions workflows**: 10 +* **Documentation pages (MkDocs)**: 22+ + +### 🔧 Category 1: Ingestion & Parsing Engine +* **Stable Parsers**: Python, JS/TS/TSX, Go, Rust, C, C++, Java, Ruby, C#, PHP, Kotlin, Scala, Swift, Dart, Perl, Haskell, Elixir, Lua, Elisp. +* **New Parsers**: HTML, CSS, Gradle, Maven, MyBatis. +* **Core Systems**: Polyglot Tree-Sitter framework, SCIP precise indexer, Package resolvers (9 languages), Indexing pipeline, File discovery (`.cgcignore`), Import pre-scanning, Graph schema creation, Property sanitization, GraphWriter (Cypher MERGE/CREATE), Function call & Inheritance resolution, File system watcher (incremental re-index). + +### 🗄️ Category 2: Graph Database Layer +* **Backends**: FalkorDB Lite (embedded Unix), FalkorDB Remote, KuzuDB (embedded cross-platform), Neo4j (server bolt), Nornic DB. +* **Core Systems**: Auto-detect DB backend cascade, Unified driver wrappers, Graph schema (17 labels, 7 edge types). + +### 🔍 Category 3: Query & Analysis Engine (CodeFinder) +* **Search Types**: Function, Class, Variable, Module, Node type, Full-text. +* **Relationships**: Who calls function, what does function call (direct + transitive), Function call chains, Class hierarchy, Function overrides, Dead code detection, Cyclomatic complexity, Variable usage scope/modifiers. +* **Analysis**: Report generator, Type utilities. +* **Gap**: Advanced language query tool routing (all 16 toolkits currently raise `NotImplementedError`). + +### 🖥️ Category 4: Interfaces (MCP Server, CLI, API) +* **MCP Server**: 21 tools over stdio, Context discovery, LLM system prompts. +* **CLI**: 55+ Typer commands, Config manager (YAML/Contexts), Setup wizards (Cursor, Claude, VS Code, etc.), Registry commands, `cgc doctor`. +* **API**: FastAPI REST API, Viz Server (React SPA host). + +### 🌐 Category 5: Website & Visualization +* **Pages**: Landing, Explorer, PR Reviewer, Privacy, Cookbook, Docs. +* **Visualizers**: CodeGraphViewer (2D/3D Force, 3D City Treemap), Mermaid FlowchartSVG. +* **Web Tech**: In-browser Tree-sitter parsing (Web Worker/WASM), LocalUploader, Bundle Generator/Registry, Social mentions timeline, 4 visual themes. +* **Backend**: Vercel serverless APIs, Rate limiting. + +### 🧩 Category 6: VS Code Extension +* **Early Features**: Extension activation (12 commands), Open Dashboard, Call Graph webview, Analyze Relationships, Run Indexing Wizard, Variable Impact Radius, Code Health Report generator. +* **Views**: Control Panel webview, Dashboard webview, Setup wizard, Explorer bundles view. + +--- + +## Part 3: Roadmap Phase 1 (Months 1-3) + +### Month 1: Architectural Refactoring, Test Isolation & Benchmarking (M1–M15) +> **Theme**: Clean the foundations. De-monolith the codebase, isolate all tests, build a real performance bench. + +#### M1 — Split `cli/main.py` into Sub-Command Modules +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Typer CLI groups, Python package imports +- **Deliverable**: Separate `cli/commands/index.py`, `find.py`, `analyze.py`, `bundle.py`, `registry.py`, `config.py`, `context.py`, `mcp.py`, `neo4j.py`. Main becomes a thin router. +- **📈**: CLI startup is faster (lazy imports). Each command group is independently testable. Maintenance overhead drops significantly. +- **Fixes**: B4 + +#### M2 — Refactor `CodeGraphViewer.tsx` into Subcomponents +- **Team**: 🔴 WebDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: React, TypeScript, state management, force-graph libraries +- **Deliverable**: Extract `GraphCanvas`, `SidebarFileTree`, `CodeViewerPanel`, `SearchAndFilter`, `VisualSettingsPanel`, `ThemeSwitcher` from the 106 KB monolith. +- **📈**: Frontend bugs become isolatable. New visualization modes can be added without touching the core canvas. +- **Fixes**: B5 + +#### M3 — Split `code_finder.py` into Domain Modules +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Python class composition, Cypher query organization +- **Deliverable**: Extract query methods into `query/search.py`, `query/callgraph.py`, `query/inheritance.py`, `query/analysis.py`, `query/management.py`. CodeFinder becomes a facade. +- **📈**: Each query domain is independently extensible. New analysis methods don't bloat the main file. +- **Fixes**: B6 + +#### M4 — Database Query Interface Protocol (GraphQueryInterface) +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Abstract Base Classes, Cypher dialects (KuzuDB vs Neo4j vs FalkorDB) +- **Deliverable**: Define `GraphQueryInterface` ABC with backend-specific implementations. CodeFinder programs against the interface, not raw Cypher. +- **📈**: Eliminates Cypher dialect crashes. KuzuDB `UNWIND` and aggregation failures disappear. New backends can be added by implementing the interface. +- **Fixes**: B8 + +#### M5 — Clean & Isolate Test Suite +- **Team**: 🔴 Testers +- **Difficulty**: 🟡 Medium +- **Knowledge**: pytest, mocking, CI environments +- **Deliverable**: Remove dead `test_mixins.py` Ruby fixture. Deduplicate C++ tests. Mock DB in `test_cgcignore_patterns.py`. Mark all tests with `@pytest.mark.unit/integration/e2e`. +- **📈**: CI pipeline succeeds on every commit without needing a live DB or pre-installed binaries. +- **Fixes**: B15 + +#### M6 — Standardized MCP Error Schema +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: MCP protocol, JSON-RPC error codes +- **Deliverable**: Define error codes (`CGC_INDEX_NOT_FOUND`, `CGC_DB_CONNECTION_LOST`, `CGC_QUERY_FAILED`, etc.) in `error_codes.py`. All handlers return structured errors. +- **📈**: AI assistants understand *why* a tool call failed and can suggest recovery actions. + +#### M7 — Bundle Schema Versioning & `cgc bundle validate` +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: ZIP archiving, JSON schema, semver +- **Deliverable**: Add `schema_version` to `.cgc` bundle `metadata.json`. Create `cgc bundle validate ` CLI command. +- **📈**: Prevents older CGC from loading incompatible bundles. Users get clear upgrade instructions. + +#### M8 — Real-World Ingestion Benchmark Suite +- **Team**: 🔴 Testers +- **Difficulty**: 🟡 Medium +- **Knowledge**: Benchmarking methodology, performance telemetry, statistics +- **Deliverable**: Expand `scripts/benchmark_cgc.py` with a standard corpus (Flask, FastAPI, Express). Track LOC/sec, node creation rate, and DB insertion latency across all backends. +- **📈**: Quantitative regression detection on every release. Marketing-ready performance numbers. + +#### M9 — Query Latency Profiling & `EXPLAIN` Integration +- **Team**: 🔴 Testers +- **Difficulty**: 🟢 Easy +- **Knowledge**: Python timing, Cypher `EXPLAIN`/`PROFILE` +- **Deliverable**: Add timing to all CodeFinder queries. Output in `--verbose` CLI mode and debug logs. Add `cgc analyze benchmark` command. +- **📈**: Developers identify slow queries instantly. Database index gaps become visible. + +#### M10 — Persistent Job Manager (SQLite-backed) +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: SQLite, async job states +- **Deliverable**: Replace in-memory dict in `JobManager` with SQLite table under `.codegraphcontext/jobs.db`. Jobs survive server restarts. +- **📈**: Long-running index jobs report correct states after IDE/MCP restart. No more "phantom jobs". +- **Fixes**: B3 + +#### M11 — Implement Python `*Toolkit` Advanced Queries +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Python AST patterns, decorators, dynamic imports +- **Deliverable**: Implement `PythonToolkit` in `query_tool_languages/` — decorator resolution, dynamic import boundaries, context manager detection. +- **📈**: AI assistants get Python-specific advanced queries. +- **Fixes**: B14 (partial) + +#### M12 — Implement JS/TS `*Toolkit` Advanced Queries +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: JS/TS AST, React patterns, export styles +- **Deliverable**: Implement JS/TS/TSX toolkits — named/default export analysis, React hook dependency tracking, Express route detection. +- **📈**: JS/TS-specific queries work. +- **Fixes**: B14 (partial) + +#### M13 — Implement Go & Rust `*Toolkit` Advanced Queries +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Go interfaces/struct composition, Rust traits/impl blocks +- **Deliverable**: Implement Go toolkit (struct composition, goroutine detection) and Rust toolkit (trait implementations, lifetime annotations). +- **📈**: AI can query Go/Rust-specific patterns accurately. +- **Fixes**: B14 (partial) + +#### M14 — Implement Java & C# `*Toolkit` Advanced Queries +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: JVM/CLR patterns, annotations, generics +- **Deliverable**: Implement Java toolkit (annotation queries, generic constraints) and C# toolkit (LINQ patterns, attribute queries, property accessors). +- **📈**: Enterprise-language advanced queries work. +- **Fixes**: B14 (partial) + +#### M15 — Implement C/C++ `*Toolkit` Advanced Queries +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: C/C++ preprocessor, macro chains, header inclusion +- **Deliverable**: Implement C/C++ toolkits — macro expansion tracing, header-source relationship mapping, preprocessor conditional analysis. +- **📈**: AI can trace C++ macro chains and header dependencies. +- **Fixes**: B13, B14 (partial) + +### Month 2: Database Optimization & Core Engine Improvements (M16–M30) +> **Theme**: Make the engine faster, smarter, and more reliable. True async drivers, connection pooling, streaming. + +#### M16 — Non-Blocking Async Database Drivers +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Python `asyncio`, `neo4j.AsyncDriver`, KuzuDB async API +- **Deliverable**: Refactor DB connection layer to use async calls. Eliminate `asyncio.to_thread` wrappers for database operations. +- **📈**: 2-3x throughput improvement under concurrent MCP tool calls. No more thread pool saturation. +- **Fixes**: B2 + +#### M17 — DB Connection Pooling +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Connection pooling patterns, Neo4j driver pools +- **Deliverable**: Implement connection pool for Neo4j and KuzuDB adapters. Configurable pool size via `CGC_DB_POOL_SIZE`. +- **📈**: Eliminates connection handshake overhead for consecutive queries. +- **Fixes**: B9 + +#### M18 — Query Result Streaming +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Python generators, streaming JSON, chunked responses +- **Deliverable**: Implement generator-based streaming for large Cypher result sets. Add `--stream` flag to CLI queries. +- **📈**: No more OOM on large codebases. Results appear incrementally. + +#### M19 — KuzuDB Cypher Compatibility Layer +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: KuzuDB Cypher constraints, AST rewriting +- **Deliverable**: Implement a query rewriter that converts Neo4j Cypher → KuzuDB-compatible Cypher. Transparent to CodeFinder. +- **📈**: All 30+ CodeFinder queries work identically across all backends. +- **Fixes**: B8 + +#### M20 — C++ Header Parser Disambiguation +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: C vs C++ AST markers +- **Deliverable**: Check for C-only markers (`#ifndef`, no `class`/`namespace`) in `.h` files to select C or C++ parser automatically. +- **📈**: Pure C libraries get correct AST parsing. +- **Fixes**: B13 + +#### M21 — Cognitive Complexity Analysis +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Static analysis metrics (SonarQube cognitive complexity spec) +- **Deliverable**: Add cognitive complexity calculation alongside cyclomatic complexity in all parsers. Store as graph property. +- **📈**: AI identifies hard-to-maintain code, not just branch-heavy code. + +#### M22 — Incremental Ingestion Worker Pools +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Python `multiprocessing`, file lock queues +- **Deliverable**: Implement configurable worker pools for parsing (multi-core), serialized DB writes. `CGC_PARSE_WORKERS=4`. +- **📈**: 2-4x faster initial indexing on multi-core machines. + +#### M23 — Workspace Index Size Estimation Utility +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: CLI UX, file counting +- **Deliverable**: `cgc index estimate ` — shows file count, estimated nodes/edges, projected DB size, estimated time. +- **📈**: Users can budget disk space and time before committing to large indexing jobs. + +#### M24 — Automated SCIP Installer Script +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: Shell scripting, platform binary downloads +- **Deliverable**: `cgc index setup-scip` — downloads and installs language-specific SCIP binaries. +- **📈**: SCIP adoption friction drops dramatically. One command instead of manual binary hunting. +- **Fixes**: B11 + +#### M25 — Incremental SCIP Indexing (Git Diff-Based) +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: SCIP protocol, git diff parsing +- **Deliverable**: Compare `git diff` to identify changed files, only re-index those via SCIP. +- **📈**: SCIP re-indexing goes from O(n) to O(changed_files). Practical for large repos. +- **Fixes**: B12 + +#### M26 — Type Inference Heuristics for Call Graph +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: AST scope analysis, basic type inference, Python type hints +- **Deliverable**: Cross-file reference resolver that uses type hints and import chains to disambiguate same-name functions. +- **📈**: Call graph precision improves by 20-30%. Fewer false connections. +- **Fixes**: B10 (partial) + +#### M27 — `cgc analyze hotspots` Command +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Graph analytics, git churn analysis +- **Deliverable**: New CLI command combining cyclomatic complexity + call frequency + git churn to identify code hotspots. +- **📈**: Developers immediately see where tech debt is accumulating. + +#### M28 — `cgc analyze architecture-violations` Command +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Layer boundary definitions, Cypher path queries +- **Deliverable**: Define architectural layers in config, detect cross-boundary calls (e.g., UI calling DB directly). +- **📈**: Architectural drift detected automatically on every commit. + +#### M29 — Remaining Language Toolkits (Ruby, PHP, Kotlin, Scala, Swift, Dart, etc.) +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Language-specific AST patterns +- **Deliverable**: Implement remaining toolkit stubs for Ruby, PHP, Kotlin, Scala, Swift, Dart, Perl, Haskell, Elixir. +- **📈**: All 16 toolkit stubs are fully implemented. `NotImplementedError` is eliminated. +- **Fixes**: B14 (complete) + +#### M30 — Database Parity Test Suite Expansion +- **Team**: 🔴 Testers +- **Difficulty**: 🟡 Medium +- **Knowledge**: pytest parametrize, DB adapters +- **Deliverable**: Expand `db-parity-check.yml` to run the full CodeFinder query suite against all 5 backends. Fail CI on any divergence. +- **📈**: Guarantees every query works identically across FalkorDB, KuzuDB, and Neo4j. + +### Month 3: Website Upgrades, PR Reviewer & Documentation (M31–M45) +> **Theme**: Polish the website, ship the PR Reviewer, upgrade docs to production quality. + +#### M31 — PR Code Graph Reviewer: Production Ship +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🔴 Hard +- **Knowledge**: GitHub Actions, PR diffs, graph diffing, React visualization +- **Deliverable**: Complete the PR Reviewer (`PRReviewer.tsx`, `pr-code-graph.yml`) — diff two branches, generate blast radius graph, post as PR comment. +- **📈**: Every PR gets an automatic visual impact analysis. Reviewers understand change scope in 30 seconds. + +#### M32 — PR Reviewer: Blast Radius Visualization +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Graph coloring, D3/force-graph +- **Deliverable**: Three visual zones: 🔴 Direct modifications, 🟠 Primary impact (immediate callers), 🟡 Secondary blast radius (transitive N-hop). +- **📈**: Reviewers instantly see the ripple effect of every PR. + +#### M33 — PR Reviewer: Dead Code Detection in Diffs +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Graph query diffing +- **Deliverable**: Compare BASE vs HEAD graphs to find functions that lost all callers. Report as "👻 Unreferenced Code Detected". +- **📈**: Dead code gets cleaned up at PR time, not months later. + +#### M34 — PR Reviewer: API Signature Change Alerts +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Node metadata diffing +- **Deliverable**: Compare function signatures between BASE and HEAD. Table in PR comment showing old vs new signatures + affected callers. +- **📈**: Breaking changes are caught automatically before merge. + +#### M35 — Website: WebGL Large Graph Rendering +- **Team**: 🔴 WebDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: WebGL, react-force-graph, GPU rendering +- **Deliverable**: Upgrade `CodeGraphViewer.tsx` to use WebGL for graphs exceeding 5,000 nodes. +- **📈**: Repositories with 10,000+ files render smoothly without browser tab crashes. + +#### M36 — Website: Visual Cypher Query Builder +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: React, visual query builders, Cypher generation +- **Deliverable**: Drag-and-drop query builder on the /explore tab. Users construct queries visually → auto-generates Cypher. +- **📈**: Non-Cypher users can query the graph without learning a query language. + +#### M37 — Website: Bundle Comparison Panel +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: React diff libraries, graph comparison +- **Deliverable**: Upload two `.cgc` bundles → visual diff showing added/removed/changed nodes and relationships. +- **📈**: Teams can track structural changes across versions. + +#### M38 — Website: Level-of-Detail (LoD) Rendering +- **Team**: 🔴 WebDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: LOD algorithms, dynamic graph expansion +- **Deliverable**: Graph shows only high-level modules/directories by default. Dynamically expand to functions/variables on zoom. +- **📈**: Massive repos (Chromium, Linux) become navigable instead of overwhelming. + +#### M39 — Website: In-Browser Parsing Optimization (Streaming) +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Web Workers, WASM memory, streaming uploads +- **Deliverable**: Optimize `parser.worker.ts` with streaming file uploads and file chunking. Progress bar per file. +- **📈**: Browser explorer parses large repos without freezing the tab. + +#### M40 — MkDocs: Complete API Reference Auto-Generation +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟡 Medium +- **Knowledge**: MkDocs plugins, mkdocstrings, Python docstrings +- **Deliverable**: Auto-generate API reference pages from Python docstrings for all public classes (CodeFinder, GraphBuilder, etc.). +- **📈**: API docs stay perfectly in sync with code. + +#### M41 — MkDocs: VS Code Extension Guide +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: Technical writing +- **Deliverable**: Add "VS Code Extension" guide to docs covering installation, commands, configuration, and troubleshooting. +- **📈**: VS Code users can self-serve setup without reading source code. + +#### M42 — MkDocs: Database Backend Deep-Dive Pages +- **Team**: 🔴 Testers +- **Difficulty**: 🟡 Medium +- **Knowledge**: DB internals, benchmark data +- **Deliverable**: Dedicated page per backend with setup, performance characteristics, migration guide, and when to choose each. +- **📈**: Users pick the right backend for their use case without trial and error. + +#### M43 — Blog: "How CGC Indexes 24 Languages" Technical Deep-Dive +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟢 Easy +- **Knowledge**: Technical writing, Tree-sitter internals +- **Deliverable**: Published blog post explaining the parser architecture, AST extraction, and graph construction pipeline. +- **📈**: Community engagement + contributor understanding of the codebase. + +#### M44 — Blog: "CGC vs Regex vs AST: The Benchmark" +- **Team**: 🔴 Testers +- **Difficulty**: 🟢 Easy +- **Knowledge**: Technical writing, benchmark data +- **Deliverable**: Published blog expanding the `CGC_REPORT_BENCHMARK.md` with visuals, real-world scenarios, and performance comparisons. +- **📈**: Marketing material for adoption. Quantitative proof of CGC's value. + +#### M45 — Interactive Video Tutorials (3-part series) +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟡 Medium +- **Knowledge**: Video production, terminal recording +- **Deliverable**: Three videos: (1) Install & Index in 60 seconds, (2) MCP + Cursor full workflow, (3) Website Explorer deep-dive. +- **📈**: YouTube content drives organic discovery. Lowers barrier to entry for new users. + +--- + +## Part 4: Roadmap Phase 2 (Months 4-6) + +### Month 4: VS Code Extension — From Early to Production (M46–M63) +> **Theme**: Transform the VS Code extension from stubs into a fully featured visual and analytical assistant. + +#### M46 — Interactive Webview Control Dashboard +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code Extension API, webview panels, message passing +- **Deliverable**: Complete the `controlPanel.ts` + `dashboardPanel.ts` → embed a mini React dashboard within VS Code showing indexed repos, stats, and quick actions. +- **📈**: Users can view and manage CGC entirely inside the IDE without touching the terminal. + +#### M47 — CodeLens Complexity & Dependency Markers +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code CodeLens API, CGC CLI queries +- **Deliverable**: Overlay inline markers above function/class declarations showing: cyclomatic complexity, caller count, and inheritance depth. +- **📈**: Developers see code metrics *contextually* while writing code — no tab switching. + +#### M48 — VS Code Inline Cypher Console +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code webview panels, Cypher syntax highlighting, table rendering +- **Deliverable**: Complete the `cgc.openCypherConsole` command → inline Cypher editor with autocomplete and tabular result preview. +- **📈**: Power users run graph queries without leaving the IDE. + +#### M49 — Automatic Watcher Lifecycle Integration +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: VS Code workspace event listeners +- **Deliverable**: Auto-start the file watcher when a workspace with `.codegraphcontext/` is opened. Auto-stop on workspace close. +- **📈**: Code modifications are indexed silently in the background — zero manual intervention. + +#### M50 — Diagnostics Provider for Dead Code +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code `DiagnosticCollection` API, CGC dead code detection +- **Deliverable**: Surface dead code detections as ⚠️ warnings in the VS Code "Problems" panel with file/line precision. +- **📈**: Unused functions and parameters show up in real-time while coding. + +#### M51 — Context-Aware Go-To-Definition Provider +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: VS Code `DefinitionProvider` API, CGC graph queries +- **Deliverable**: Implement a CGC-powered definition provider — especially valuable for dynamic languages (Python, Ruby, JS). +- **📈**: Ctrl+Click navigation works across dynamic imports, decorator-wrapped functions, and monkey-patched methods. + +#### M52 — Graph-Guided Refactoring Previews +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: VS Code `WorkspaceEdit` API, impact analysis queries +- **Deliverable**: Before a rename, show a preview panel listing every file, function, and test that will be impacted. +- **📈**: Large refactors become safe. Developers see the blast radius before committing. + +#### M53 — One-Click Bundle Export Button +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: VS Code extension commands, file dialogs +- **Deliverable**: Add a button in the CGC sidebar to export `.cgc` bundles directly. File save dialog for output path. +- **📈**: Sharing indexed codebase contexts with team members takes one click. + +#### M54 — VS Code Tree View: Indexed Symbols Browser +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code TreeDataProvider API +- **Deliverable**: A tree view showing indexed Repositories → Files → Functions/Classes with clickable navigation to source. +- **📈**: Developers browse the code graph like a file explorer — visually. + +#### M55 — VS Code: Call Graph Side Panel Visualization +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code webview, D3/Mermaid rendering +- **Deliverable**: Complete `callGraphPanel.ts` — right-click a function → see its call graph rendered inline in a side panel. +- **📈**: Visual call chain analysis happens right next to the code. + +#### M56 — VS Code: Class Hierarchy Tree Visualization +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code webview, tree layout algorithms +- **Deliverable**: Complete `cgc.showClassHierarchy` — renders inheritance tree for the selected class in a webview. +- **📈**: Class hierarchy is immediately visible — no more grepping for `extends/implements`. + +#### M57 — VS Code: Variable Impact Radius Panel +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code webview, graph traversal +- **Deliverable**: Complete `cgc.showVariableImpact` — shows all functions that read/write a selected variable. +- **📈**: Scope analysis for variables is visual and instant. + +#### M58 — VS Code: Code Health Report Generator +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code webview, report formatting, `report_generator.py` +- **Deliverable**: Complete `cgc.generateReport` — generates a formatted HTML report: complexity heatmap, dead code, dependency metrics. +- **📈**: One-click project health assessment visible in the IDE. + +#### M59 — VS Code: Discover & Switch Contexts UI +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: VS Code QuickPick API +- **Deliverable**: Complete `cgc.discoverContexts` — QuickPick dropdown listing all `.codegraphcontext/` contexts in workspace with instant switching. +- **📈**: Multi-project mono-repo support becomes seamless. + +#### M60 — VS Code: Visualize Entire Repo in Webview +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: React build integration in VS Code webview +- **Deliverable**: Complete `cgc.visualizeRepo` — embeds the local React viz (from `viz/dist/`) in a full VS Code webview panel. +- **📈**: Full codebase graph visualization inside the IDE. No browser required. + +#### M61 — VS Code Extension: Publish to Marketplace +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: VS Code marketplace publishing, `vsce`, CI/CD +- **Deliverable**: GitHub Action that builds and publishes the `.vsix` to the VS Code Marketplace on tag push. +- **📈**: Users install CGC extension with one click from the marketplace. + +#### M62 — VS Code: Status Bar Live Indicators +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: VS Code StatusBarItem API +- **Deliverable**: Complete `statusBarItem.ts` — show: DB connection status (🟢/🔴), indexed file count, active watcher count. +- **📈**: At-a-glance CGC health visible in the status bar at all times. + +#### M63 — VS Code: Keyboard Shortcuts & Command Palette Integration +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: VS Code keybindings, `when` clauses +- **Deliverable**: Default keybindings: `Ctrl+Shift+G → Call Graph`, `Ctrl+Shift+H → Hierarchy`, `Ctrl+Shift+R → Report`. +- **📈**: Power users access CGC features at keyboard speed. + +### Month 5: ChatGPT Web & External LLM Integration (M64–M80) +> **Theme**: Break out of the IDE. Connect CGC to web-based LLMs, browser extensions, and multi-client MCP. + +#### M64 — WebSocket & SSE MCP Transport Protocol +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🔴 Hard +- **Knowledge**: WebSockets, Server-Sent Events, JSON-RPC 2.0 +- **Deliverable**: Add `cgc mcp start --transport ws` and `--transport sse`. Multiple clients connect to a single shared CGC database. +- **📈**: Multiple IDEs, browser tabs, and AI assistants share one CGC instance concurrently. +- **Fixes**: B1 + +#### M65 — REST API v1: Production-Ready OpenAPI +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: FastAPI, OpenAPI spec, authentication +- **Deliverable**: Complete `api/v1/` — full REST API with OpenAPI spec, JWT auth tokens, rate limiting. +- **📈**: External services (CI tools, dashboards, bots) can query CGC over HTTP. + +#### M66 — Web LLM Browser Extension (Chrome) +- **Team**: 🔴 AIDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Chrome Extensions API (Manifest V3), content scripts, IPC +- **Deliverable**: Chrome extension that securely connects ChatGPT/Claude/Gemini web interfaces to the local CGC daemon via WebSocket. +- **📈**: Web-based LLMs can run code queries against local codebases securely from the browser. + +#### M67 — Web LLM Browser Extension (Firefox) +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟡 Medium (port from Chrome) +- **Knowledge**: Firefox WebExtension API, Manifest V3 differences +- **Deliverable**: Firefox extension matching Chrome functionality. +- **📈**: Firefox users get the same web LLM integration. + +#### M68 — Browser Extension: Workspace Auto-Matcher +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Tab APIs, local storage, GitHub URL parsing +- **Deliverable**: Detect the GitHub repo URL or active tab project and auto-select the matching local database context. +- **📈**: No manual context switching when chatting about different projects in browser LLMs. + +#### M69 — Browser Extension: "Show in Graph" Button on GitHub +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Content scripts, GitHub DOM injection +- **Deliverable**: On GitHub file views, inject a "🔍 Show in Graph" button next to function/class definitions that opens the CGC explorer. +- **📈**: GitHub → CGC graph navigation in one click. + +#### M70 — Secure Origin Policy Configuration +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: Web security, CORS headers +- **Deliverable**: `cgc config set allowed-origins` — strict origin validation for WebSocket/REST connections. +- **📈**: Local database ports protected from unauthorized web requests. + +#### M71 — Multi-Client Connection Dashboard +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: WebSocket session management, React +- **Deliverable**: `cgc mcp status` command and website dashboard showing: connected clients, active queries, resource usage. +- **📈**: Server admins see who's connected and what's happening. + +#### M72 — Website: Collaborative Annotations & Playbooks +- **Team**: 🔴 AIDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: React, persistent storage, UX design +- **Deliverable**: Pin specific graph views, add text annotations to nodes, create guided "Playbooks" (e.g., "The Request Lifecycle Tour"). +- **📈**: Onboarding new team members becomes a guided visual walkthrough. + +#### M73 — Website: Custom Metric Overlays +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Data visualization, graph rendering +- **Deliverable**: Node size ∝ complexity/churn. Node color ∝ coverage/age. Toggle between overlay modes. +- **📈**: Visual hotspot detection — big, red nodes = high-risk code. + +#### M74 — Website: Edge Flow Animations +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: WebGL shaders, particle systems +- **Deliverable**: Animated light pulses traveling along CALLS/IMPORTS edges. Speed ∝ call frequency. +- **📈**: The graph feels *alive*. Users instantly see data flow patterns. + +#### M75 — Website: Glassmorphic Code Peek Modals +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: CSS glassmorphism, syntax highlighting +- **Deliverable**: Hover over a node → frosted glass modal showing syntax-highlighted code preview. +- **📈**: Code inspection without clicking. Faster graph exploration. + +#### M76 — Website: Radar Minimap +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: D3, canvas rendering +- **Deliverable**: Circular radar-style minimap in corner showing entire project as point cloud with current viewport highlighted. +- **📈**: Navigation in large graphs becomes effortless. + +#### M77 — Website: Export Sub-Graphs as Mermaid/SVG +- **Team**: 🔴 WebDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Mermaid.js, SVG export +- **Deliverable**: Select nodes → "Export" → Mermaid diagram or SVG file. Copy to clipboard or download. +- **📈**: Architecture diagrams for README/wikis stay in sync with code. + +#### M78 — Multi-Commit Comparative Analysis (Diff Mode) +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Git integration, graph diffing algorithms +- **Deliverable**: Compare two branches/commits → graph highlights new nodes (green), deleted (red), modified (yellow). +- **📈**: Structural changes between versions are instantly visible. + +#### M79 — README Translation: Spanish +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟢 Easy +- **Knowledge**: Spanish language, technical translation +- **Deliverable**: `README.es.md` — complete Spanish translation. +- **📈**: Broader international reach. Spanish is the 4th most spoken language. + +#### M80 — README Translation: Hindi & Portuguese +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟢 Easy +- **Knowledge**: Hindi/Portuguese, technical translation +- **Deliverable**: `README.hi.md`, `README.pt-BR.md`. +- **📈**: Coverage for 1.5B+ additional speakers. + +### Month 6: LLM Integration, Ollama, RAG & v1.0 Release (M81–M100) +> **Theme**: Add AI brains to CGC. Local Ollama, cloud LLM APIs, Graph RAG, and ship v1.0.0. + +#### M81 — LLM API Key Configuration CLI +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: CLI inputs, secure config storage +- **Deliverable**: `cgc config set-key openai ` / `anthropic` / `google` / `mistral`. Keys stored encrypted in `~/.codegraphcontext/.env`. +- **📈**: Unified interface for all cloud LLM integrations. + +#### M82 — Local Ollama Model Integration +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Ollama HTTP API, local LLM inference +- **Deliverable**: `cgc config set-llm ollama --model qwen2.5-coder`. Adapter for Ollama HTTP API. Auto-detect running Ollama instance. +- **📈**: Fully offline, private code analysis and summarization. No data leaves the machine. + +#### M83 — AI-Guided Semantic Function Summarizer +- **Team**: 🔴 AIDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: LLM prompts, batch processing, graph property updates +- **Deliverable**: Post-indexing pipeline stage: LLM summarizes each function/class → stored as `summary` property in graph nodes. +- **📈**: AI assistants can search the graph using natural language concepts ("find the authentication handler"). + +#### M84 — Graph RAG: Vector Embedding Ingestion +- **Team**: 🔴 AIDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Vector embeddings (OpenAI/Ollama), KuzuDB/Neo4j vector indices +- **Deliverable**: Generate embeddings of code summaries. Store in graph DB vector index. Hybrid search: keyword + semantic + structural. +- **📈**: Natural language queries return structurally AND semantically relevant results. + +#### M85 — `cgc ask` — Natural Language Query Interface +- **Team**: 🔴 AIDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: LLM prompt engineering, RAG pipeline +- **Deliverable**: `cgc ask "How does the authentication flow work?"` → LLM queries the graph, retrieves relevant nodes, generates a coherent answer with file/line references. +- **📈**: Non-technical stakeholders can query the codebase in plain English. + +#### M86 — AI-Driven Architectural Insights +- **Team**: 🔴 AIDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Graph analytics, LLM analysis, design patterns +- **Deliverable**: `cgc analyze ai-insights` — LLM analyzes graph structure to identify: circular dependencies, architectural drift, hotspots, god classes. +- **📈**: Automated architecture review. Design pattern violations detected. + +#### M87 — Data-Flow Analysis (Variable → Parameter Tracking) +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: AST data-flow analysis, taint tracking +- **Deliverable**: New `FLOWS_INTO` relationship: "Variable X flows into Function Y's parameter Z". Beyond structural call graph. +- **📈**: True logic-flow graph. Security-relevant: track user input through the system. + +#### M88 — `cgc serve` — Unified Server Mode +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Process management, port allocation +- **Deliverable**: Single command to start MCP (stdio) + REST API + Viz Server + WebSocket. `cgc serve --port 8080`. +- **📈**: One command to start everything. Simplifies deployment. + +#### M89 — Docker Compose v2: Complete Stack +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Docker Compose, multi-service orchestration +- **Deliverable**: Docker Compose with: CGC server, Neo4j/KuzuDB, Viz server, Ollama (optional). `docker compose up` for full stack. +- **📈**: Enterprise deployment in one command. + +#### M90 — Helm Chart for Kubernetes +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Helm, Kubernetes, chart templating +- **Deliverable**: Complete Helm chart replacing raw K8s manifests. Configurable replicas, resources, DB backend. +- **📈**: Cloud-native deployment for teams. + +#### M91 — Performance Regression CI Gate +- **Team**: 🔴 Testers +- **Difficulty**: 🟡 Medium +- **Knowledge**: GitHub Actions, benchmark comparison +- **Deliverable**: CI step that runs benchmark suite and fails if indexing speed drops >10% or query latency increases >20%. +- **📈**: Performance regressions caught before merge. + +#### M92 — Security Audit & Hardening +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟡 Medium +- **Knowledge**: Security best practices, dependency scanning +- **Deliverable**: Dependency audit (Snyk/Safety), input sanitization review, path traversal prevention, API auth hardening. +- **📈**: Production-ready security posture. + +#### M93 — Plugin Architecture for Custom Parsers +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🔴 Hard +- **Knowledge**: Plugin systems, entry points, dynamic loading +- **Deliverable**: `cgc plugin install my-parser` — users can write custom language parsers as Python packages that CGC discovers via entry points. +- **📈**: Community can extend CGC to new languages without forking. + +#### M94 — Blog: "Building a PR Code Graph Reviewer" +- **Team**: 🔴 AdminArchs +- **Difficulty**: 🟢 Easy +- **Knowledge**: Technical writing +- **Deliverable**: Published blog with architecture diagrams, screenshots, and implementation details of the PR reviewer system. +- **📈**: Community engagement. Positions CGC as an innovative tool. + +#### M95 — Blog: "Graph RAG for Code: How CGC Does It" +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟢 Easy +- **Knowledge**: Technical writing, RAG concepts +- **Deliverable**: Published blog explaining the Graph RAG pipeline — from indexing to vector embeddings to hybrid search. +- **📈**: Thought leadership in the code intelligence space. + +#### M96 — Blog: "From 0 to Code Graph in 60 Seconds" (Getting Started) +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟢 Easy +- **Knowledge**: Technical writing, tutorial design +- **Deliverable**: Quick-start blog with GIFs showing install → index → query → visualize in under 60 seconds. +- **📈**: Lowest-friction onboarding content for new users. + +#### M97 — Demo: "CGC + Cursor End-to-End Workflow" (Video) +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟡 Medium +- **Knowledge**: Screen recording, editing +- **Deliverable**: 5-minute demo video showing: install CGC → MCP setup in Cursor → index a project → ask questions → see graph. +- **📈**: YouTube discovery channel. Visual proof of CGC's value. + +#### M98 — Demo: "CGC VS Code Extension Showcase" (Video) +- **Team**: 🔴 DocsExperts +- **Difficulty**: 🟡 Medium +- **Knowledge**: Screen recording +- **Deliverable**: 3-minute video showcasing: call graphs, CodeLens, dead code warnings, Cypher console inside VS Code. +- **📈**: Drives VS Code marketplace installs. + +#### M99 — API Stability Freeze & Deprecation Policy +- **Team**: 🔴 PythonDevs +- **Difficulty**: 🟢 Easy +- **Knowledge**: Semver, API lifecycle management +- **Deliverable**: Document public API surface. Establish deprecation warnings for breaking changes. Changelog automation. +- **📈**: Users trust CGC for production use. No surprise breaking changes. + +#### M100 — Production Release: v1.0.0 🎉 +- **Team**: 🔴 ExtensionDevs +- **Difficulty**: 🟡 Medium +- **Knowledge**: PyPI release workflows, semver, changelog +- **Deliverable**: Version bump 0.4.x → 1.0.0. Full test pass on all backends. Updated all docs/README/website. Published to PyPI + Docker Hub + VS Code Marketplace. +- **📈**: CGC is production-ready. Stable API contract. Enterprise-adoptable. + +--- + +## Roadmap Impact Summary +* **Bugs Fixed**: Resolves all 15 known bugs, including single-process MCP limitations (M64), sync handlers (M16), memory job tracking (M10), monolith structures (M1-M3), DB parity issues (M4, M19), and test flakiness (M5). +* **Effort Breakdown (100 Milestones)**: 23 Easy, 54 Medium, 23 Hard. + + + + +### 📊 Team Assignment Breakdown +| Team | Number of Milestones | +|---|---| +| **PythonDevs** | 25 | +| **ExtensionDevs** | 21 | +| **AdminArchs** | 15 | +| **AIDevs** | 12 | +| **WebDevs** | 11 | +| **DocsExperts** | 9 | +| **Testers** | 7 | + +--- + +## Part 5: Documentation Landscape & Topology Audit + +This section provides an exhaustive, file-by-file analysis of all documentation assets across the CodeGraphContext repository. It maps documentation topology, categorizes modules, summarizes key contents, and provides developer-centric insights into the entire system. + +### 🗺️ Documentation Landscape Directory Map + +```mermaid +graph TD + subgraph Conceptual Tiers + arch[ARCHITECTURE.md] --> concepts[docs/docs/concepts/*] + concepts --> model[graph-model.md] + concepts --> parser[how-it-works.md] + concepts --> backend[backends.md] + end + + subgraph Procedural & Setup Tiers + readme[README.md] --> getting_started[docs/docs/getting-started/*] + getting_started --> install[installation.md] + getting_started --> quick[quickstart.md] + getting_started --> mcpset[mcp-setup.md] + getting_started --> guides[docs/docs/guides/*] + end + + subgraph Reference Tiers + guides --> cli[cli.md] + guides --> config[config.md] + guides --> mcp[mcp.md] + end + + subgraph Bundle Tiers + bundles[docs/BUNDLES.md] --> b_arch[BUNDLE_ARCHITECTURE.md] + bundles --> b_impl[BUNDLE_IMPLEMENTATION.md] + bundles --> b_qref[BUNDLE_QUICKREF.md] + bundles --> b_sum[BUNDLE_SUMMARY.md] + end +``` + +### 🗂️ Detailed File-by-File Analysis (44 Documentation Files) + +#### Module 1: Repository Root Documentation (10 Files) +* **`README.md` & Translations (`.zh-CN.md`, `.kor.md`, `.ru-RU.md`, `.uk.md`)**: Primary onboarding docs mapping 20 languages and 5 DB backends. +* **`ARCHITECTURE.md`**: Breakdown of internal design patterns, AST constraints, and backend concurrency boundaries. +* **`CONTRIBUTING.md`, `SECURITY.md`, `TESTING.md`**: Standard open-source contribution rules, vulnerability guidelines, and the testing pyramid strategy. + +#### Module 2: The CGC Bundle Subsystem (10 Files) +* **`docs/BUNDLES.md`**: Defines `.cgc` bundles (ZIP containing metadata, schema, nodes, edges). +* **`BUNDLE_ARCHITECTURE.md`, `BUNDLE_IMPLEMENTATION.md`, `BUNDLE_SUMMARY.md`, `BUNDLE_QUICKREF.md`, `BUNDLE_BUGFIXES.md`, `ON_DEMAND_BUNDLES.md`**: Extensive blueprints on bundle exports/imports, Web UI triggers, and historical bugfixes. +* **Analysis Reports (`CGC_REPORT.md`, `CGC_REPORT_BENCHMARK.md`, `sample_report.md`)**: Detailed metrics proving CGC's graph-based approach outperforms AST/regex indexing by 3-6x. + +#### Module 3: MkDocs Core & Navigation (4 Files) +* **`docs/mkdocs.yml`, `docs/docs/index.md`, `UPDATING_DOCS.md`, `license.md`**: Configuration of the Material theme site and developer build instructions. + +#### Module 4: System Concepts (4 Files) +* **`architecture.md`, `graph-model.md`, `how-it-works.md`, `backends.md`**: Explains the 3-layer architecture, property graph node/edge schemas, sequence flow of tree-sitter indexing, and DB selection criteria. + +#### Module 5: Getting Started & Installation (4 Files) +* **`prerequisites.md`, `installation.md`, `quickstart.md`, `mcp-setup.md`**: Core onboarding loop and IDE configurations for AI workflows (Cursor, Claude Desktop). + +#### Module 6: User Guides (6 Files) +* **`onboarding-codebase.md`, `indexing.md`, `visualization.md`, `contexts.md`, `datasource-indexing.md`, `bundles.md`**: Practical references for global/local contexts, DB schema ingestion, and advanced tree-sitter scanning flags. + +#### Module 7: Complete References & Roadmap (6 Files) +* **`cli.md`, `config.md`, `mcp.md`**: Dense API reference for all 55+ CLI commands, environment variables, and the 25 JSON-RPC MCP tools. +* **`troubleshooting.md`, `roadmap.md`, `IMPROVEMENTS.md`, `pr_reviewer_graph_ideas.md`**: Future blueprints and immediate bug remediation steps. + +### 📈 Key Findings & Suggestions for Docs Optimizations +1. **Consolidate Redundancies**: Combine root `/docs/BUNDLES.md` and `/docs/docs/guides/bundles.md` to avoid overlap. +2. **Add Visual Sequence Diagrams to CLI Guides**: Map multi-process commands like `cgc watch` inside `/docs/docs/reference/cli.md`. +3. **Establish Version Locking**: Lock KuzuDB/FalkorDB driver versions explicitly in the config guides to prevent dependency mismatches. diff --git a/docker-compose.template.yml b/docker-compose.template.yml index 7c406e3d..d891ca1e 100644 --- a/docker-compose.template.yml +++ b/docker-compose.template.yml @@ -1,38 +1,41 @@ +# Deployment template — copy to docker-compose.yml and create .env from .env.example. +# Policy: no committed default passwords; all secrets come from .env or your secret manager. version: '3.8' services: - # CodeGraphContext with FalkorDB Lite (default) codegraphcontext: build: context: . dockerfile: Dockerfile container_name: codegraphcontext volumes: - # Mount your code directory here - ./:/workspace - # Persist database and config - cgc-data:/root/.codegraphcontext environment: - PYTHONUNBUFFERED=1 - - DATABASE_TYPE=${DATABASE_TYPE:-falkordb} + - DEFAULT_DATABASE=${DEFAULT_DATABASE:-falkordb} - FALKORDB_HOST=${FALKORDB_HOST:-} - FALKORDB_PORT=${FALKORDB_PORT:-6379} - # To use the separate falkordb container (recommended for aarch64): - # - DATABASE_TYPE=falkordb-remote - # - FALKORDB_HOST=falkordb - # - FALKORDB_PORT=6379 + - NEO4J_URI=${NEO4J_URI:-} + - NEO4J_USERNAME=${NEO4J_USERNAME:-neo4j} + - NEO4J_PASSWORD=${NEO4J_PASSWORD:-} stdin_open: true tty: true command: bash networks: - cgc-network - # Separate FalkorDB container (recommended for aarch64 or larger projects) falkordb: image: falkordb/falkordb:latest + healthcheck: + test: [ "CMD", "redis-cli", "PING" ] + interval: 10s + timeout: 5s + retries: 10 + start_period: 15s container_name: cgc-falkordb ports: - - "6379:6379" + - "127.0.0.1:6379:6379" volumes: - falkordb-data:/data networks: @@ -40,15 +43,24 @@ services: profiles: - falkordb - # Optional: Neo4j database (if you prefer Neo4j over FalkorDB) neo4j: image: neo4j:5.15.0 + healthcheck: + test: + [ + "CMD-SHELL", + "cypher-shell -u neo4j -p \"$$NEO4J_PASSWORD\" 'RETURN 1' || exit 1" + ] + interval: 10s + timeout: 10s + retries: 10 + start_period: 30s container_name: cgc-neo4j ports: - - "7474:7474" # HTTP - - "7687:7687" # Bolt + - "127.0.0.1:7474:7474" + - "127.0.0.1:7687:7687" environment: - - NEO4J_AUTH=neo4j/codegraph123 + - NEO4J_AUTH=neo4j/${NEO4J_PASSWORD:?Set NEO4J_PASSWORD in .env} - NEO4J_PLUGINS=["apoc"] - NEO4J_dbms_security_procedures_unrestricted=apoc.* - NEO4J_dbms_memory_heap_max__size=2G @@ -58,7 +70,7 @@ services: networks: - cgc-network profiles: - - neo4j # Only start when explicitly requested + - neo4j volumes: cgc-data: diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 00000000..aa6af669 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,1499 @@ +# CodeGraphContext (CGC) — Complete Architecture Document + +> **Version:** 0.4.16 +> **Generated:** 2026-05-12 +> **Scope:** Every module, connection, data flow, limitation, and feature across the entire codebase. + +--- + +## Table of Contents + +1. [High-Level Overview](#1-high-level-overview) +2. [System Architecture Diagram](#2-system-architecture-diagram) +3. [Component Breakdown](#3-component-breakdown) + - [3.1 MCP Server (JSON-RPC)](#31-mcp-server-json-rpc) + - [3.2 CLI (Typer)](#32-cli-typer) + - [3.3 Database Layer](#33-database-layer) + - [3.4 Indexing Pipeline](#34-indexing-pipeline) + - [3.5 Tree-Sitter Parsers](#35-tree-sitter-parsers) + - [3.6 SCIP Pipeline](#36-scip-pipeline) + - [3.7 Query & Analysis (CodeFinder)](#37-query--analysis-codefinder) + - [3.8 Handlers Layer](#38-handlers-layer) + - [3.9 Bundles & Registry](#39-bundles--registry) + - [3.10 File Watcher](#310-file-watcher) + - [3.11 Visualization Server](#311-visualization-server) + - [3.12 Website & CodeGraphViewer](#312-website--codegraphviewer) +4. [Graph Schema](#4-graph-schema) +5. [Data Flow Diagrams](#5-data-flow-diagrams) +6. [File Tree (Annotated)](#6-file-tree-annotated) +7. [Complete Feature Inventory](#7-complete-feature-inventory) +8. [Current Limitations](#8-current-limitations) +9. [Architectural Recommendations](#9-architectural-recommendations) + +--- + +## 1. High-Level Overview + +CodeGraphContext (CGC) transforms source code repositories into a **queryable graph database**, then exposes that graph through: + +- **MCP Server** — JSON-RPC over stdio, consumed by AI IDE assistants (Cursor, Claude Desktop, Windsurf, etc.) +- **CLI** (`cgc`) — Typer-based command suite for indexing, querying, bundling, and managing contexts +- **Viz Server** — FastAPI server serving the built React visualization +- **Website** — Vite + React SPA with in-browser Tree-sitter parsing and graph visualization (https://codegraphcontext.vercel.app/) + +The system supports **5 database backends** (FalkorDB Lite, FalkorDB Remote, KuzuDB, Neo4j, Nornic DB), **20 programming languages** via Tree-sitter, and optional **SCIP-based** precise indexing. + +--- + +## 2. System Architecture Diagram + +### 2.1 High-Level Component Diagram (Mermaid) + +```mermaid +graph TB + subgraph Clients["User / AI Assistant"] + IDE["IDE
(Cursor, Claude Desktop,
Windsurf, VS Code)"] + Terminal["Terminal / Shell"] + Browser["Web Browser"] + end + + subgraph Entrypoints["Entry Points"] + MCP["MCP Server
server.py
21 MCP Tools"] + CLI["CLI (Typer)
cli/main.py
55+ Commands"] + VizServer["Viz Server
FastAPI + uvicorn
viz/server.py"] + Website["Website
Vite + React
codegraphcontext.vercel.app"] + end + + subgraph Core["Core Engine"] + Handlers["Handlers Layer
5 handler modules"] + GB["GraphBuilder
graph_builder.py
Indexing Orchestrator"] + CF["CodeFinder
code_finder.py
30+ Query Methods"] + Watcher["CodeWatcher
watcher.py
File System Monitor"] + Jobs["JobManager
jobs.py
Background Tasks"] + Bundles["CGCBundle + Registry
cgc_bundle.py
bundle_registry.py"] + end + + subgraph Parsing["Parsing Layer"] + TS["Tree-Sitter Parsers
20 Languages"] + SCIP["SCIP Pipeline
(Optional)"] + Writer["GraphWriter
persistence/writer.py
Cypher MERGE/CREATE"] + end + + subgraph DB["Database Layer — get_database_manager()"] + Falkor["FalkorDB Lite
Embedded
Unix + Py3.12+"] + FalkorR["FalkorDB Remote
Server"] + Kuzu["KuzuDB
Embedded
All Platforms"] + Neo4j["Neo4j
Server
Enterprise"] + Nornic["Nornic DB
Neo4j-compatible"] + end + + IDE -->|"stdio JSON-RPC"| MCP + Terminal -->|"CLI commands"| CLI + Browser -->|"HTTP"| VizServer + Browser -->|"HTTP"| Website + + MCP --> Handlers + CLI --> Handlers + VizServer --> CF + + Handlers --> GB + Handlers --> CF + Handlers --> Watcher + Handlers --> Jobs + Handlers --> Bundles + + GB --> TS + GB --> SCIP + GB --> Writer + CF --> DB + Writer --> DB + Watcher --> GB + + DB --- Falkor + DB --- FalkorR + DB --- Kuzu + DB --- Neo4j + DB --- Nornic +``` + +### 2.2 Class Diagram — Core Classes (UML) + +```mermaid +classDiagram + class MCPServer { + +db_manager: DatabaseManager + +graph_builder: GraphBuilder + +code_finder: CodeFinder + +code_watcher: CodeWatcher + +job_manager: JobManager + +tools: Dict + +resolved_context: ResolvedContext + +handle_tool_call(name, args) Dict + +run() async + +shutdown() + +switch_context_tool(**args) Dict + +discover_codegraph_contexts_tool(**args) Dict + } + + class GraphBuilder { + +db_manager: DatabaseManager + +job_manager: JobManager + +parsers: Dict~str,str~ + -_writer: GraphWriter + -_parsed_cache: Dict + +get_parser(ext) TreeSitterParser + +build_graph_from_path_async(path, ...) async + +parse_file(repo_path, path) Dict + +link_function_calls(all_file_data, imports_map) + +link_inheritance(all_file_data, imports_map) + +update_file_in_graph(path, repo_path, imports_map) + } + + class CodeFinder { + +db_manager: DatabaseManager + +find_by_function_name(name, fuzzy) List + +find_by_class_name(name) List + +find_by_content(text) List + +who_calls_function(name) List + +find_all_callers(name) List + +find_function_call_chain(from, to) List + +find_dead_code(exclude) List + +get_cyclomatic_complexity(name) Dict + +find_class_hierarchy(name) List + +analyze_code_relationships(type, target) Dict + } + + class GraphWriter { + +driver: DriverWrapper + +add_repository_to_graph(path, is_dep) + +add_file_to_graph(file_data, repo_name, imports_map) + +write_function_call_groups(internal, external) + +write_inheritance_links(batch, csharp, imports_map) + +delete_file_from_graph(path) + +delete_repository_from_graph(path) + } + + class CodeWatcher { + +graph_builder: GraphBuilder + +job_manager: JobManager + +start() + +stop() + +watch(path) + +unwatch(path) + } + + class JobManager { + +jobs: Dict + +create_job(path) str + +update_job(job_id, ...) + +get_job(job_id) JobInfo + +list_jobs() List + } + + class DatabaseManager { + <> + +get_driver() DriverWrapper + +close_driver() + +is_connected() bool + +session() SessionWrapper + } + + class FalkorDBManager { + +db_path: str + } + class KuzuDBManager { + +db_path: str + } + class Neo4jManager { + +uri: str + +username: str + } + class FalkorDBRemoteManager { + +host: str + +port: int + } + class NornicDBManager { + +uri: str + +username: str + } + + MCPServer --> GraphBuilder + MCPServer --> CodeFinder + MCPServer --> CodeWatcher + MCPServer --> JobManager + GraphBuilder --> GraphWriter + GraphBuilder --> CodeWatcher : used by + CodeWatcher --> GraphBuilder + GraphWriter --> DatabaseManager + CodeFinder --> DatabaseManager + FalkorDBManager ..|> DatabaseManager + KuzuDBManager ..|> DatabaseManager + Neo4jManager ..|> DatabaseManager + FalkorDBRemoteManager ..|> DatabaseManager + NornicDBManager ..|> DatabaseManager +``` + +### 2.3 Deployment Diagram + +```mermaid +graph LR + subgraph Developer Machine + IDE_App["AI IDE
(Cursor / Claude)"] + CGC_MCP["cgc mcp start
(MCP Server Process)"] + CGC_CLI["cgc commands
(CLI Process)"] + CGC_VIZ["cgc visualize
(Viz Server)"] + + subgraph "Embedded DB (default)" + FalkorLite["FalkorDB Lite
~/.codegraphcontext/db/falkordb/"] + KuzuLocal["KuzuDB
~/.codegraphcontext/db/kuzudb/"] + end + end + + subgraph "Optional External" + Neo4jServer["Neo4j Server
Docker / AuraDB"] + FalkorRemote["FalkorDB Cloud"] + end + + IDE_App <-->|stdio| CGC_MCP + CGC_MCP --> FalkorLite + CGC_MCP --> KuzuLocal + CGC_MCP -.-> Neo4jServer + CGC_MCP -.-> FalkorRemote + CGC_CLI --> FalkorLite + CGC_VIZ --> FalkorLite +``` + +--- + +## 3. Component Breakdown + +### 3.1 MCP Server (JSON-RPC) + +**File:** `src/codegraphcontext/server.py` (472 lines) + +The MCP Server is the primary interface for AI assistants. It implements a **JSON-RPC 2.0** protocol over **stdin/stdout**. + +**JSON-RPC Loop (State Machine):** + +```mermaid +stateDiagram-v2 + [*] --> Listening: Server started + Listening --> ParseRequest: stdin.readline() + ParseRequest --> Initialize: method = "initialize" + ParseRequest --> ToolsList: method = "tools/list" + ParseRequest --> ToolsCall: method = "tools/call" + ParseRequest --> Notification: method = "notifications/initialized" + ParseRequest --> MethodNotFound: unknown method + + Initialize --> SendResponse: Return protocolVersion,
serverInfo, capabilities + ToolsList --> SendResponse: Return 20 tool definitions + ToolsCall --> HandleTool: Extract name + args + HandleTool --> SendResponse: asyncio.to_thread(handler)
→ strip_workspace_prefix
→ JSON response + Notification --> Listening: No response needed + MethodNotFound --> SendResponse: Error -32601 + + SendResponse --> Listening: stdout.write(json.dumps) + Listening --> [*]: EOF (client disconnect) +``` + +**Illustrative JSON-RPC exchange:** + +```json +// → Client sends (stdin): +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} + +// ← Server responds (stdout): +{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","serverInfo":{"name":"CodeGraphContext","version":"0.4.16"},"capabilities":{"tools":{"listTools":true}}}} + +// → Client sends: +{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"find_code","arguments":{"query":"authenticate"}}} + +// ← Server responds: +{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"{\"results\":[{\"name\":\"authenticate\",\"path\":\"/src/auth.py\",\"line_number\":15}]}"}]}} +``` + +**Initialization sequence:** + +1. `resolve_context(cwd)` — find `.codegraphcontext/` config +2. `get_database_manager(db_path)` — select and connect database backend +3. `JobManager()` — in-memory job tracker +4. `GraphBuilder(db_manager, job_manager, loop)` — indexing engine +5. `CodeFinder(db_manager)` — query engine +6. `CodeWatcher(graph_builder, job_manager)` — filesystem watcher +7. `_init_tools()` — register 21 MCP tool definitions + +**21 Registered MCP Tools:** + +| Category | Tools | +|----------|-------| +| **Indexing** | `add_code_to_graph`, `add_package_to_graph`, `watch_directory` | +| **Query** | `execute_cypher_query`, `visualize_graph_query` | +| **Analysis** | `find_code`, `analyze_code_relationships`, `find_dead_code`, `calculate_cyclomatic_complexity`, `find_most_complex_functions` | +| **Management** | `list_indexed_repositories`, `delete_repository`, `check_job_status`, `list_jobs`, `get_repository_stats` | +| **Bundles** | `load_bundle`, `search_registry_bundles` | +| **Watcher** | `list_watched_paths`, `unwatch_directory` | +| **Context** | `discover_codegraph_contexts`, `switch_context` | + +**Context discovery feature:** On startup, if no local DB exists, the server scans child directories for `.codegraphcontext/` folders. It appends a `_context_discovery_note` to the first tool response to guide the AI to `switch_context`. + +--- + +### 3.2 CLI (Typer) + +**File:** `src/codegraphcontext/cli/main.py` (2386 lines) + +The CLI is organized into **subcommand groups** using Typer: + +``` +cgc +├── index # Index a repository +├── list / ls # List indexed repos +├── delete / rm # Delete from graph +├── stats # Show stats +├── clean # Cleanup +├── query # Run Cypher query +├── visualize / v # Launch viz server +├── watch / w # Watch directory +├── unwatch # Stop watching +├── watching # List watched paths +├── doctor # Health check +├── version # Show version +├── help # Help +│ +├── mcp +│ ├── setup # MCP setup wizard +│ ├── start # Start MCP server +│ └── tools # List MCP tools +│ +├── neo4j +│ └── setup # Neo4j setup wizard +│ +├── context +│ ├── list # List contexts +│ ├── create # Create named context +│ ├── delete # Delete context +│ ├── mode # Switch global/per-repo +│ └── default # Set default +│ +├── config +│ ├── show # Show config +│ ├── set # Set config value +│ ├── reset # Reset to defaults +│ └── db # Switch database backend +│ +├── bundle +│ ├── export # Export .cgc bundle +│ ├── import # Import .cgc bundle +│ └── load # Load from registry +│ +├── registry +│ ├── list # List bundles +│ ├── search # Search bundles +│ ├── download # Download bundle +│ └── request # Request on-demand bundle +│ +├── find +│ ├── name # Find by name +│ ├── pattern # Find by pattern +│ ├── type # Find by type +│ ├── variable # Find variables +│ ├── content # Full-text search +│ ├── decorator # Find by decorator +│ └── argument # Find by argument +│ +└── analyze + ├── calls # What does it call + ├── callers # Who calls it + ├── chain # Call chain + ├── dependencies # Module deps + ├── inheritance # Class hierarchy + ├── complexity # Cyclomatic complexity + ├── dead-code # Find dead code + ├── overrides # Method overrides + └── variable-usage # Variable scope +``` + +**Supporting CLI modules:** + +| File | Lines | Role | +|------|-------|------| +| `cli/config_manager.py` | 1052 | Config YAML, contexts, workspace mappings, `.env` merge | +| `cli/cli_helpers.py` | 775 | Shared DB init, indexing with progress bars, viz launch | +| `cli/setup_wizard.py` | 992 | Interactive Neo4j + MCP IDE setup (InquirerPy prompts) | +| `cli/registry_commands.py` | 404 | Bundle registry HTTP client | +| `cli/visualizer.py` | 51 | Thin wrappers for browser visualization | +| `cli/setup_macos.py` | 94 | macOS-specific Neo4j homebrew setup | + +--- + +### 3.3 Database Layer + +**Directory:** `src/codegraphcontext/core/` + +``` +┌─────────────────────────────────────────────────────────┐ +│ get_database_manager() │ +│ core/__init__.py (166 lines) │ +│ │ +│ Selection Priority: │ +│ 1. CGC_RUNTIME_DB_TYPE env (CLI --database flag) │ +│ 2. DEFAULT_DATABASE env (cgc config db) │ +│ 3. Implicit auto-detection: │ +│ a. FALKORDB_HOST set → FalkorDB Remote │ +│ b. Unix + Py3.12+ → FalkorDB Lite │ +│ c. KuzuDB installed → KuzuDB │ +│ d. Neo4j credentials → Neo4j │ +└─────────────────────────────────────────────────────────┘ +``` + +| Backend | File | Lines | Engine | Platform | +|---------|------|-------|--------|----------| +| FalkorDB Lite | `database_falkordb.py` | 481 | Embedded via `redislite` + `falkordb` | Unix, Python 3.12+ | +| FalkorDB Remote | `database_falkordb_remote.py` | 200 | Remote FalkorDB server | Any (needs `FALKORDB_HOST`) | +| KuzuDB | `database_kuzu.py` | 627 | Embedded Kuzu | All platforms (Windows default) | +| Neo4j | `database.py` | 274 | Neo4j server (bolt) | Any (needs credentials) | +| Nornic DB | `database_nornic.py` | 180 | Neo4j-compatible Nornic DB | Any (needs credentials) | + +All backends implement a **common compatibility interface**: `get_driver()`, `close_driver()`, `is_connected()`, `session()` context manager, with wrapper classes (`DriverWrapper`, `SessionWrapper`, `RecordWrapper`, `ResultWrapper`) to normalize Cypher result access. + +--- + +### 3.4 Indexing Pipeline + +```mermaid +flowchart TD + Start([build_graph_from_path_async]) --> CheckSCIP{SCIP_INDEXER=true?} + + CheckSCIP -->|Yes| DetectLang["detect_project_lang()"] + DetectLang --> SCIPAvail{scip-lang binary found?} + SCIPAvail -->|Yes| SCIPPath["SCIP Pipeline
scip_pipeline.py"] + SCIPAvail -->|No| TSPath + + CheckSCIP -->|No| TSPath["Tree-sitter Pipeline
pipeline.py"] + + subgraph SCIP ["SCIP Path (scip_pipeline.py)"] + SCIPPath --> RunSCIP["Run scip-{lang} CLI"] + RunSCIP --> ParseProto["Parse .scip protobuf
via scip_pb2"] + ParseProto --> WriteScip["Write symbols to GraphWriter"] + end + + subgraph TreeSitter ["Tree-sitter Path (pipeline.py)"] + TSPath --> Discover["discover_files_to_index()
Apply .cgcignore"] + Discover --> PreScan["pre_scan_for_imports()
Build imports_map"] + PreScan --> ParseLoop["For each file:
parse_file() → writer.add_file()"] + ParseLoop --> Inherit["build_inheritance_and_csharp_files()"] + Inherit --> Calls["build_function_call_groups()"] + Calls --> WriteCalls["writer.write_function_call_groups()
writer.write_inheritance_links()"] + end + + WriteScip --> Done([Job Complete]) + WriteCalls --> Done +``` + +**Indexing submodules:** + +| File | Lines | Role | +|------|-------|------| +| `indexing/pipeline.py` | 90 | Tree-sitter full-repo orchestrator | +| `indexing/scip_pipeline.py` | 141 | SCIP full-repo orchestrator | +| `indexing/discovery.py` | 65 | File discovery with `.cgcignore` support | +| `indexing/pre_scan.py` | 106 | Pre-scan files for import maps | +| `indexing/schema.py` | 80 | Graph schema creation (indexes, constraints) | +| `indexing/schema_contract.py` | 45 | Canonical node/relationship definitions | +| `indexing/constants.py` | 26 | Default ignore patterns | +| `indexing/sanitize.py` | 42 | Property sanitization (max string length) | +| `indexing/persistence/writer.py` | 689 | **GraphWriter** — all Cypher MERGE/CREATE operations | +| `indexing/resolution/calls.py` | 205 | Function call resolution (local vs imported) | +| `indexing/resolution/inheritance.py` | 92 | Inheritance + C# `IMPLEMENTS` resolution | + +--- + +### 3.5 Tree-Sitter Parsers + +**Directory:** `src/codegraphcontext/tools/languages/` + +Each parser follows a consistent pattern: +- Class: `{Language}TreeSitterParser` +- Method: `parse(path, is_dependency, **kwargs) -> Dict` +- Module function: `pre_scan_{language}(...)` for import map building + +``` +┌────────────────────────────────────────────────────────────────┐ +│ 19 Language Parsers │ +│ │ +│ ┌─────────┐ ┌────────────┐ ┌────────────┐ ┌───────────────┐ │ +│ │ Python │ │ JavaScript │ │ TypeScript │ │ TSX │ │ +│ │ 576 ln │ │ 590 ln │ │ 576 ln │ │ 152 ln │ │ +│ └─────────┘ └────────────┘ └────────────┘ │ (extends TS) │ │ +│ └───────────────┘ │ +│ ┌─────────┐ ┌────────────┐ ┌────────────┐ ┌───────────────┐ │ +│ │ Go │ │ Rust │ │ C │ │ C++ │ │ +│ │ 508 ln │ │ 296 ln │ │ 563 ln │ │ 616 ln │ │ +│ └─────────┘ └────────────┘ └────────────┘ └───────────────┘ │ +│ │ +│ ┌─────────┐ ┌────────────┐ ┌────────────┐ ┌───────────────┐ │ +│ │ Java │ │ Ruby │ │ C# │ │ Kotlin │ │ +│ │ 471 ln │ │ 537 ln │ │ 551 ln │ │ 640 ln │ │ +│ └─────────┘ └────────────┘ └────────────┘ └───────────────┘ │ +│ │ +│ ┌─────────┐ ┌────────────┐ ┌────────────┐ ┌───────────────┐ │ +│ │ Scala │ │ Swift │ │ PHP │ │ Dart │ │ +│ │ 520 ln │ │ 491 ln │ │ 520 ln │ │ 378 ln │ │ +│ └─────────┘ └────────────┘ └────────────┘ └───────────────┘ │ +│ │ +│ ┌─────────┐ ┌────────────┐ ┌────────────┐ │ +│ │ Perl │ │ Haskell │ │ Elixir │ │ +│ │ 261 ln │ │ 427 ln │ │ 461 ln │ │ +│ └─────────┘ └────────────┘ └────────────┘ │ +└────────────────────────────────────────────────────────────────┘ +``` + +**File extension mapping** (from `graph_builder.py`): + +| Extension(s) | Language | Parser Class | +|--------------|----------|-------------| +| `.py`, `.ipynb` | Python | `PythonTreeSitterParser` | +| `.js`, `.jsx`, `.mjs`, `.cjs` | JavaScript | `JavascriptTreeSitterParser` | +| `.ts` | TypeScript | `TypescriptTreeSitterParser` | +| `.tsx` | TSX | `TypescriptJSXTreeSitterParser` | +| `.go` | Go | `GoTreeSitterParser` | +| `.rs` | Rust | `RustTreeSitterParser` | +| `.c` | C | `CTreeSitterParser` | +| `.cpp`, `.h`, `.hpp`, `.hh` | C++ | `CppTreeSitterParser` | +| `.java` | Java | `JavaTreeSitterParser` | +| `.rb` | Ruby | `RubyTreeSitterParser` | +| `.cs` | C# | `CSharpTreeSitterParser` | +| `.php` | PHP | `PhpTreeSitterParser` | +| `.kt` | Kotlin | `KotlinTreeSitterParser` | +| `.scala`, `.sc` | Scala | `ScalaTreeSitterParser` | +| `.swift` | Swift | `SwiftTreeSitterParser` | +| `.hs` | Haskell | `HaskellTreeSitterParser` | +| `.dart` | Dart | `DartTreeSitterParser` | +| `.pl`, `.pm` | Perl | `PerlTreeSitterParser` | +| `.ex`, `.exs` | Elixir | `ElixirTreeSitterParser` | +| `.lua` | Lua | `LuaTreeSitterParser` | + +**Each parser extracts:** +- Functions (name, parameters, return type, decorators, line numbers, source) +- Classes (name, bases, methods, properties, line numbers) +- Imports / modules +- Variables (language-dependent) +- Language-specific nodes (structs, enums, traits, interfaces, macros, etc.) + +--- + +### 3.6 SCIP Pipeline + +**Files:** `tools/scip_indexer.py` (468 lines), `tools/scip_pb2.py` (2456 lines, generated), `indexing/scip_pipeline.py` (141 lines) + +``` +┌──────────────────────────────────────────────────────┐ +│ SCIP Indexing Path │ +│ │ +│ Enabled via: SCIP_INDEXER=true in config │ +│ Languages: SCIP_LANGUAGES (default: python, │ +│ typescript, go, rust, java) │ +│ │ +│ 1. detect_project_lang() ─── scan for markers │ +│ (setup.py, package.json, go.mod, Cargo.toml) │ +│ │ +│ 2. is_scip_available(lang) ─── check `scip-*` CLI │ +│ │ +│ 3. ScipIndexer.index(path) ─── run CLI, get .scip │ +│ │ +│ 4. ScipIndexParser.parse(scip_file) ─── decode │ +│ protobuf via scip_pb2 │ +│ │ +│ 5. Write symbols to GraphWriter │ +│ │ +│ Fallback: If no scip-* binary found → Tree-sitter │ +└──────────────────────────────────────────────────────┘ +``` + +--- + +### 3.7 Query & Analysis (CodeFinder) + +**File:** `tools/code_finder.py` (1119 lines) + +The `CodeFinder` class is the read-side of the graph. It generates and executes Cypher queries across all backends. + +**Query methods organized by category:** + +``` +┌────────────────────────────────────────────────────────────┐ +│ CodeFinder │ +│ │ +│ SEARCH: │ +│ ├── find_by_function_name(name, fuzzy, edit_distance) │ +│ ├── find_by_class_name(name, fuzzy, edit_distance) │ +│ ├── find_by_variable_name(name) │ +│ ├── find_by_module_name(name) │ +│ ├── find_by_content(text) │ +│ ├── find_by_type(node_type) │ +│ ├── find_functions_by_argument(arg_name) │ +│ ├── find_functions_by_decorator(decorator) │ +│ └── find_imports(module) │ +│ │ +│ CALL GRAPH: │ +│ ├── who_calls_function(name) │ +│ ├── what_does_function_call(name) │ +│ ├── find_all_callers(name) — transitive │ +│ ├── find_all_callees(name) — transitive │ +│ └── find_function_call_chain(from, to) │ +│ │ +│ INHERITANCE: │ +│ ├── find_class_hierarchy(class_name) │ +│ └── find_function_overrides(class_name) │ +│ │ +│ ANALYSIS: │ +│ ├── find_dead_code(exclude_decorated_with) │ +│ ├── get_cyclomatic_complexity(function_name) │ +│ ├── find_most_complex_functions(limit) │ +│ ├── find_module_dependencies(module) │ +│ ├── find_variable_usage_scope(variable) │ +│ ├── who_modifies_variable(variable) │ +│ ├── who_imports_module(module) │ +│ └── analyze_code_relationships(query_type, target) │ +│ │ +│ MANAGEMENT: │ +│ └── list_indexed_repositories() │ +│ │ +│ FUZZY SEARCH: │ +│ └── _find_by_name_fuzzy_portable(name, label, dist) │ +│ (Levenshtein distance, no DB extension needed) │ +└────────────────────────────────────────────────────────────┘ +``` + +Also: `tools/advanced_language_query_tool.py` (104 lines) routes queries to per-language `*Toolkit` classes under `query_tool_languages/`, but **all 16 toolkits currently raise `NotImplementedError`**. + +--- + +### 3.8 Handlers Layer + +**Directory:** `src/codegraphcontext/tools/handlers/` + +Handlers sit between the MCP Server/CLI and the core tools. They handle argument parsing, error wrapping, and response formatting. + +``` +┌────────────────────────────────────────────────────┐ +│ MCP Server tool_map / CLI commands │ +│ │ │ +│ ▼ │ +│ ┌──────────────────────┐ │ +│ │ analysis_handlers.py │ find_dead_code, │ +│ │ (115 lines) │ cyclomatic_complexity, │ +│ │ │ find_code, etc. │ +│ ├──────────────────────┤ │ +│ │ indexing_handlers.py │ add_code_to_graph, │ +│ │ (117 lines) │ add_package_to_graph │ +│ ├──────────────────────┤ │ +│ │ management_handlers.py│ list_repos, delete, │ +│ │ (340 lines) │ load_bundle, stats, │ +│ │ │ search_registry │ +│ ├──────────────────────┤ │ +│ │ query_handlers.py │ execute_cypher, │ +│ │ (84 lines) │ visualize_graph │ +│ ├──────────────────────┤ │ +│ │ watcher_handlers.py │ watch, unwatch, list │ +│ │ (84 lines) │ │ +│ └──────────────────────┘ │ +└────────────────────────────────────────────────────┘ +``` + +--- + +### 3.9 Bundles & Registry + +``` +┌────────────────────────────────────────────────────────────┐ +│ Bundle System │ +│ │ +│ EXPORT: cgc bundle export │ +│ ┌─────────────────────────────────────┐ │ +│ │ CGCBundle.export() │ │ +│ │ core/cgc_bundle.py (858 lines) │ │ +│ │ │ │ +│ │ 1. Query all graph data │ │ +│ │ 2. JSON serialize (nodes + edges) │ │ +│ │ 3. ZIP into .cgc file │ │ +│ │ 4. Include metadata.json │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ IMPORT: cgc bundle import / cgc load │ +│ ┌─────────────────────────────────────┐ │ +│ │ CGCBundle.import() │ │ +│ │ │ │ +│ │ 1. Unzip .cgc file │ │ +│ │ 2. Read metadata.json │ │ +│ │ 3. Merge nodes + edges into DB │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ REGISTRY: cgc registry list/search/download │ +│ ┌─────────────────────────────────────┐ │ +│ │ BundleRegistry │ │ +│ │ core/bundle_registry.py (182 lines)│ │ +│ │ │ │ +│ │ HTTP client → GitHub Releases API │ │ +│ │ + on-demand bundle trigger │ │ +│ └─────────────────────────────────────┘ │ +│ │ +│ ON-DEMAND (Website): │ +│ ┌─────────────────────────────────────┐ │ +│ │ website/api/trigger-bundle.ts │ │ +│ │ website/api/bundle-status.ts │ │ +│ │ website/api/bundles.ts │ │ +│ │ │ │ +│ │ GitHub Actions workflow dispatch │ │ +│ │ → builds bundle → GitHub Release │ │ +│ └─────────────────────────────────────┘ │ +└────────────────────────────────────────────────────────────┘ +``` + +--- + +### 3.10 File Watcher + +**File:** `core/watcher.py` (261 lines) + +``` +┌────────────────────────────────────────────────┐ +│ CodeWatcher │ +│ │ +│ Uses: watchdog (FileSystemEventHandler) │ +│ │ +│ 1. start() — launch observer thread │ +│ 2. watch(path) — add directory to observer │ +│ 3. RepositoryEventHandler: │ +│ on_modified/created/deleted → │ +│ debounced → graph_builder.update_file() │ +│ 4. stop() — halt observer │ +│ │ +│ Integrates with: │ +│ - GraphBuilder for incremental updates │ +│ - JobManager for tracking re-index jobs │ +└────────────────────────────────────────────────┘ +``` + +--- + +### 3.11 Visualization Server + +**File:** `viz/server.py` (283 lines) + +``` +┌──────────────────────────────────────────────────┐ +│ Viz Server (FastAPI + Uvicorn) │ +│ │ +│ Routes: │ +│ GET / → static viz/dist/ (React SPA) │ +│ GET /api/graph → Cypher proxy → JSON response │ +│ GET /* → static file fallback │ +│ │ +│ Launched via: cgc visualize / cgc v │ +│ Opens browser to: http://localhost:PORT │ +│ │ +│ The static dist/ is the built CodeGraphViewer │ +│ packaged into the Python wheel. │ +└──────────────────────────────────────────────────┘ +``` + +--- + +### 3.12 Website & CodeGraphViewer + +**Directory:** `website/` (Vite + React + TypeScript + shadcn/ui) + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Website Architecture │ +│ │ +│ PAGES: │ +│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────────┐ │ +│ │ Index (/) │ │ Explore │ │ NotFound (404) │ │ +│ │ Landing page │ │ /explore │ │ │ │ +│ │ 8 sections │ │ Graph viewer │ │ │ │ +│ └──────────────┘ └──────┬───────┘ └─────────────────────┘ │ +│ │ │ +│ CORE COMPONENTS: ▼ │ +│ ┌────────────────────────────────────────────┐ │ +│ │ CodeGraphViewer.tsx (1579 lines) │ │ +│ │ │ │ +│ │ Visualization modes: │ │ +│ │ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │ │ +│ │ │ 2D Force │ │ 3D Force │ │ 3D City │ │ │ +│ │ │ Graph │ │ Graph │ │ Treemap │ │ │ +│ │ └──────────┘ └──────────┘ └─────────────┘ │ │ +│ │ ┌──────────┐ ┌─────────────────────────┐ │ │ +│ │ │ Mermaid │ │ Themes: Classic, Icon, │ │ │ +│ │ │ Flowchart│ │ Neon, Galaxy │ │ │ +│ │ └──────────┘ └─────────────────────────┘ │ │ +│ │ │ │ +│ │ Features: │ │ +│ │ - Resizable sidebar with file tree │ │ +│ │ - Node/edge color customization │ │ +│ │ - Node type visibility toggles │ │ +│ │ - File source code viewer with highlights │ │ +│ │ - Search & filter │ │ +│ │ - Focus mode (file-centric highlight) │ │ +│ └────────────────────────────────────────────┘ │ +│ │ +│ IN-BROWSER PARSING: │ +│ ┌────────────────────────────────────────────┐ │ +│ │ parser.worker.ts (798 lines) │ │ +│ │ Web Worker using web-tree-sitter WASM │ │ +│ │ │ │ +│ │ 1. User uploads files / pastes GitHub URL │ │ +│ │ 2. Worker initializes Tree-sitter │ │ +│ │ 3. Parses each file → AST → symbols │ │ +│ │ 4. Builds graph (nodes + links) │ │ +│ │ 5. Posts DONE message with graph data │ │ +│ │ 6. CodeGraphViewer renders │ │ +│ └────────────────────────────────────────────┘ │ +│ │ +│ API ROUTES (Vercel serverless): │ +│ ├── /api/bundles → GitHub releases list │ +│ ├── /api/bundle-status → GitHub Actions run status │ +│ ├── /api/trigger-bundle → GitHub workflow dispatch │ +│ ├── /api/pypi → pypistats proxy │ +│ └── api/lib/security.js → Rate limiting + origin checks │ +└─────────────────────────────────────────────────────────────────┘ +``` + +--- + +## 4. Graph Schema + +### 4.1 Entity-Relationship Diagram + +```mermaid +erDiagram + Repository ||--o{ File : CONTAINS + File ||--o{ Function : CONTAINS + File ||--o{ Class : CONTAINS + File ||--o{ Variable : CONTAINS + File ||--o{ Module : IMPORTS + Function ||--o{ Function : CALLS + Function ||--o{ Parameter : HAS_PARAMETER + Class ||--o{ Class : INHERITS + Class ||--o{ Interface : IMPLEMENTS + File ||--o{ File : INCLUDES + + Repository { + string path PK + string name + boolean is_dependency + } + File { + string path PK + string name + string lang + string repo_path + } + Function { + string name + string path + int line_number + int end_line + int cyclomatic_complexity + string source + string decorators + } + Class { + string name + string path + int line_number + int end_line + string bases + string source + } + Module { + string name + string full_import_name + string path + } + Variable { + string name + string path + int line_number + } +``` + +### 4.2 Node Labels (17 total) + +| Category | Labels | Used By | +|----------|--------|---------| +| **Structural** | `Repository`, `Directory`, `File` | All languages | +| **Core Code** | `Function`, `Class`, `Module`, `Variable` | All languages | +| **Type System** | `Interface`, `Trait`, `Struct`, `Enum`, `Union` | Go, Rust, C/C++, Java, C#, Swift, Kotlin, Haskell | +| **Metadata** | `Record`, `Property`, `Annotation`, `Parameter`, `Macro` | C/C++, Java, Kotlin, Dart, Elixir | + +### 4.3 Relationship Types (7) + +| Relationship | From → To | Meaning | +|-------------|-----------|---------| +| `CONTAINS` | File → Function/Class/Variable | Code structure ownership | +| `CALLS` | Function → Function | Function invocation | +| `IMPORTS` | File → Module | Dependency usage | +| `INHERITS` | Class → Class | Class inheritance | +| `IMPLEMENTS` | Class → Interface | Interface implementation (C#, Java) | +| `HAS_PARAMETER` | Function → Parameter | Function signature | +| `INCLUDES` | File → File | Header inclusion (C/C++) | + +### 4.4 Merge Keys (Identity) + +| Entity | Merge Key | Example | +|--------|----------|---------| +| Function | `(name, path, line_number)` | `("authenticate", "/src/auth.py", 15)` | +| Class | `(name, path, line_number)` | `("User", "/src/models.py", 8)` | +| File | `(path,)` | `("/src/auth.py",)` | +| Repository | `(path,)` | `("/home/user/myproject",)` | +| Directory | `(path,)` | `("/home/user/myproject/src",)` | + +### 4.5 Illustrative Example — What Gets Indexed + +Given this Python file: + +```python +# /myproject/src/auth.py +from .models import User +from .utils import hash_password + +class AuthService: + def authenticate(self, username: str, password: str) -> User: + hashed = hash_password(password) + user = User.find_by_name(username) + return user + + def logout(self, user: User): + user.clear_session() +``` + +The indexer produces this graph: + +```mermaid +graph TD + Repo["Repository
/myproject"] -->|CONTAINS| File["File
/myproject/src/auth.py
lang: python"] + + File -->|CONTAINS| Class["Class
AuthService
line: 4"] + File -->|CONTAINS| Fn1["Function
authenticate
line: 5
complexity: 1"] + File -->|CONTAINS| Fn2["Function
logout
line: 10
complexity: 1"] + + File -->|IMPORTS| Mod1["Module
User
from .models"] + File -->|IMPORTS| Mod2["Module
hash_password
from .utils"] + + Fn1 -->|CALLS| HashPw["Function
hash_password
/myproject/src/utils.py"] + Fn1 -->|CALLS| FindByName["Function
find_by_name
/myproject/src/models.py"] + + style Repo fill:#e1f5fe + style File fill:#fff3e0 + style Class fill:#f3e5f5 + style Fn1 fill:#e8f5e9 + style Fn2 fill:#e8f5e9 + style Mod1 fill:#fce4ec + style Mod2 fill:#fce4ec + style HashPw fill:#e8f5e9 + style FindByName fill:#e8f5e9 +``` + +**Example queries against this graph:** + +```cypher +-- "Who calls hash_password?" +MATCH (caller:Function)-[:CALLS]->(callee:Function {name: 'hash_password'}) +RETURN caller.name, caller.path, caller.line_number +-- Result: authenticate, /myproject/src/auth.py, 5 + +-- "Find all classes that inherit from BaseModel" +MATCH (c:Class)-[:INHERITS]->(p:Class {name: 'BaseModel'}) +RETURN c.name, c.path + +-- "Find dead code (functions never called)" +MATCH (f:Function) +WHERE NOT ()-[:CALLS]->(f) AND NOT f.name STARTS WITH '_' +RETURN f.name, f.path, f.line_number +``` + +--- + +## 5. Data Flow Diagrams + +### 5.1 Sequence Diagram — Indexing a Repository (MCP) + +```mermaid +sequenceDiagram + participant AI as AI Assistant + participant MCP as MCP Server + participant H as indexing_handlers + participant JM as JobManager + participant GB as GraphBuilder + participant TS as TreeSitterParser + participant GW as GraphWriter + participant DB as Graph Database + + AI->>MCP: tools/call: add_code_to_graph({path: "/myproject"}) + MCP->>H: add_code_to_graph(graph_builder, job_manager, ...) + H->>JM: create_job("/myproject") + JM-->>H: job_id = "abc-123" + H-->>MCP: {job_id: "abc-123", status: "queued"} + MCP-->>AI: {"result": {"job_id": "abc-123"}} + + Note over GB: Background thread via asyncio.to_thread + H->>GB: build_graph_from_path_async("/myproject") + GB->>GW: add_repository_to_graph("/myproject") + GW->>DB: MERGE (r:Repository {path: "/myproject"}) + + loop For each source file + GB->>TS: parse(file_path) + TS-->>GB: {functions: [...], classes: [...], imports: [...]} + GB->>GW: add_file_to_graph(file_data, repo_name, imports_map) + GW->>DB: MERGE (f:File), MERGE (fn:Function), CREATE CONTAINS + end + + GB->>GW: write_inheritance_links(batch) + GW->>DB: MERGE (c1)-[:INHERITS]->(c2) + GB->>GW: write_function_call_groups(internal, external) + GW->>DB: MERGE (f1)-[:CALLS]->(f2) + GB->>JM: update_job("abc-123", status=COMPLETED) + + AI->>MCP: tools/call: check_job_status({job_id: "abc-123"}) + MCP-->>AI: {"result": {"status": "completed", "files": 42}} +``` + +### 5.2 Sequence Diagram — Querying the Graph + +```mermaid +sequenceDiagram + participant AI as AI Assistant + participant MCP as MCP Server + participant H as analysis_handlers + participant CF as CodeFinder + participant DB as Graph Database + + AI->>MCP: tools/call: find_code({query: "authenticate"}) + MCP->>MCP: handle_tool_call("find_code", args) + MCP->>H: find_code(code_finder, query="authenticate") + + H->>CF: find_by_function_name("authenticate", fuzzy=False) + CF->>DB: MATCH (f:Function) WHERE f.name = 'authenticate' RETURN f + DB-->>CF: [{name: "authenticate", path: "/src/auth.py", line: 15}] + + H->>CF: find_by_class_name("authenticate", fuzzy=False) + CF->>DB: MATCH (c:Class) WHERE c.name = 'authenticate' RETURN c + DB-->>CF: [] + + H->>CF: find_by_content("authenticate") + CF->>DB: MATCH (f:Function) WHERE f.source CONTAINS 'authenticate' + DB-->>CF: [{name: "login", path: "/src/auth.py", line: 42}] + + H-->>MCP: {results: [{name: "authenticate", ...}, {name: "login", ...}]} + MCP->>MCP: _strip_workspace_prefix(result) + MCP-->>AI: {"result": {"content": [{"type": "text", "text": "..."}]}} +``` + +### 5.3 Sequence Diagram — Context Switch + +```mermaid +sequenceDiagram + participant AI as AI Assistant + participant MCP as MCP Server + participant DB1 as Old Database + participant DB2 as New Database + + Note over MCP: Server started in parent directory
No local .codegraphcontext found + MCP->>MCP: discover_child_contexts(cwd, depth=1) + MCP->>MCP: _context_note_pending = True + + AI->>MCP: tools/call: find_code({query: "main"}) + MCP-->>AI: {results: [], _context_discovery_note:
"Child contexts found: [project-a, project-b].
Use switch_context to connect."} + + AI->>MCP: tools/call: switch_context({context_path: "/parent/project-a"}) + MCP->>DB1: close_driver() + MCP->>MCP: os.environ['CGC_RUNTIME_DB_TYPE'] = local_db + MCP->>DB2: get_database_manager(new_db_path) + MCP->>DB2: get_driver() + MCP->>MCP: Rebuild GraphBuilder, CodeFinder, CodeWatcher + MCP-->>AI: {status: "ok", message: "Switched to project-a"} +``` + +### 5.4 Sequence Diagram — Website In-Browser Parsing + +```mermaid +sequenceDiagram + participant User as User Browser + participant UI as React App (Explore.tsx) + participant LU as LocalUploader + participant W as parser.worker.ts + participant GH as GitHub API + participant CGV as CodeGraphViewer + + User->>UI: Navigate to /explore + UI->>LU: Render LocalUploader + + alt Upload Local Files + User->>LU: Drop files / select folder + LU->>W: postMessage({type: "ADD_FILES", files: [...]}) + else Paste GitHub URL + User->>LU: Enter github.com/user/repo + LU->>GH: GET /repos/user/repo/git/trees/main?recursive=1 + GH-->>LU: {tree: [{path: "src/main.py"}, ...]} + loop For each file (max 150) + LU->>GH: GET raw.githubusercontent.com/.../src/main.py + GH-->>LU: file contents + end + LU->>W: postMessage({type: "ADD_FILES", files: [...]}) + end + + W->>W: postMessage({type: "START"}) + + loop For each file batch + W->>W: Initialize Tree-sitter for file language + W->>W: Parse AST → extract functions, classes, imports, calls + W->>W: Build nodes + links (CONTAINS, IMPORTS, CALLS, INHERITS) + W-->>UI: postMessage({type: "PROGRESS", pct: 50}) + end + + W-->>UI: postMessage({type: "DONE", data: {nodes, links, files, fileContents}}) + UI->>CGV: + CGV->>CGV: Render 2D/3D/City/Mermaid visualization +``` + +### 5.5 Activity Diagram — Database Selection + +```mermaid +flowchart TD + Start([get_database_manager called]) --> CheckRuntime{CGC_RUNTIME_DB_TYPE
or DEFAULT_DATABASE set?} + + CheckRuntime -->|"kuzudb"| KuzuAvail{kuzu installed?} + KuzuAvail -->|Yes| UseKuzu[Use KuzuDB] + KuzuAvail -->|No| ErrorKuzu[Raise ValueError] + + CheckRuntime -->|"falkordb"| FalkorAvail{FalkorDB Lite
available?} + FalkorAvail -->|Yes| UseFalkor[Use FalkorDB Lite] + FalkorAvail -->|No| FalkorFallback{KuzuDB installed?} + FalkorFallback -->|Yes| UseKuzu + FalkorFallback -->|No| ErrorFalkor[Raise ValueError] + + CheckRuntime -->|"falkordb-remote"| HostSet{FALKORDB_HOST set?} + HostSet -->|Yes| UseRemote[Use FalkorDB Remote] + HostSet -->|No| ErrorRemote[Raise ValueError] + + CheckRuntime -->|"neo4j"| Neo4jCreds{NEO4J_URI +
credentials set?} + Neo4jCreds -->|Yes| UseNeo4j[Use Neo4j] + Neo4jCreds -->|No| ErrorNeo4j[Raise ValueError] + + CheckRuntime -->|Not set| ImplicitRemote{FALKORDB_HOST set?} + ImplicitRemote -->|Yes| UseRemote + ImplicitRemote -->|No| ImplicitFalkor{FalkorDB Lite
available?} + ImplicitFalkor -->|Yes| UseFalkor + ImplicitFalkor -->|No| ImplicitKuzu{kuzu installed?} + ImplicitKuzu -->|Yes| UseKuzu + ImplicitKuzu -->|No| ImplicitNeo4j{Neo4j configured?} + ImplicitNeo4j -->|Yes| UseNeo4j + ImplicitNeo4j -->|No| ErrorNone[Raise ValueError:
No backend available] +``` + +--- + +## 6. File Tree (Annotated) + +``` +CodeGraphContext/ +├── pyproject.toml # Package metadata, deps, scripts (v0.4.16) +├── MANIFEST.in # Include viz/dist in sdist +├── Dockerfile # Container image +├── docker-compose.yml # Neo4j 5.21 + app +├── docker-compose.template.yml # Template with optional profiles +├── .env.example # Example environment variables +├── cgc_entry.py # Alternative entry point (18 lines) +├── LICENSE # MIT +├── README.md # Main readme (484 lines) +├── README.{zh-CN,kor,uk,ru-RU}.md # Localized readmes +│ +├── src/codegraphcontext/ +│ ├── __init__.py # Package marker +│ ├── __main__.py # python -m entrypoint +│ ├── server.py # MCP Server (472 lines) ★ +│ ├── tool_definitions.py # 20 MCP tool schemas (220 lines) +│ ├── prompts.py # LLM system prompt (125 lines) +│ │ +│ ├── cli/ +│ │ ├── main.py # CLI commands (2386 lines) ★ +│ │ ├── config_manager.py # Config + contexts (1052 lines) ★ +│ │ ├── cli_helpers.py # Shared helpers (775 lines) +│ │ ├── setup_wizard.py # Interactive setup (992 lines) +│ │ ├── registry_commands.py # Registry HTTP client (404 lines) +│ │ ├── visualizer.py # Viz wrappers (51 lines) +│ │ └── setup_macos.py # macOS setup (94 lines) +│ │ +│ ├── core/ +│ │ ├── __init__.py # DB factory (166 lines) ★ +│ │ ├── database.py # Neo4j backend (274 lines) +│ │ ├── database_falkordb.py # FalkorDB Lite (481 lines) +│ │ ├── database_falkordb_remote.py # FalkorDB Remote (200 lines) +│ │ ├── database_kuzu.py # KuzuDB backend (627 lines) +│ │ ├── jobs.py # Job tracking (133 lines) +│ │ ├── watcher.py # File watcher (261 lines) +│ │ ├── cgcignore.py # Ignore rules (119 lines) +│ │ ├── cgc_bundle.py # Bundle export/import (858 lines) +│ │ ├── bundle_registry.py # Registry client (182 lines) +│ │ └── falkor_worker.py # Subprocess helper (133 lines) +│ │ +│ ├── tools/ +│ │ ├── graph_builder.py # Indexing facade (321 lines) ★ +│ │ ├── code_finder.py # Query engine (1119 lines) ★ +│ │ ├── tree_sitter_parser.py # Parser dispatch (~75 lines) +│ │ ├── package_resolver.py # Package path resolution (473 lines) +│ │ ├── scip_indexer.py # SCIP CLI runner (468 lines) +│ │ ├── scip_pb2.py # Generated protobuf (2456 lines) +│ │ ├── system.py # System tools (134 lines) +│ │ ├── advanced_language_query_tool.py # (104 lines, stubs) +│ │ │ +│ │ ├── indexing/ +│ │ │ ├── pipeline.py # Tree-sitter pipeline (90 lines) +│ │ │ ├── scip_pipeline.py # SCIP pipeline (141 lines) +│ │ │ ├── discovery.py # File discovery (65 lines) +│ │ │ ├── pre_scan.py # Import pre-scanning (106 lines) +│ │ │ ├── schema.py # Schema creation (80 lines) +│ │ │ ├── schema_contract.py # Node/relationship contract (45 lines) +│ │ │ ├── constants.py # Ignore patterns (26 lines) +│ │ │ ├── sanitize.py # Property sanitization (42 lines) +│ │ │ ├── persistence/ +│ │ │ │ └── writer.py # GraphWriter (689 lines) ★ +│ │ │ └── resolution/ +│ │ │ ├── calls.py # Call resolution (205 lines) +│ │ │ └── inheritance.py # Inheritance resolution (92 lines) +│ │ │ +│ │ ├── handlers/ +│ │ │ ├── analysis_handlers.py # (115 lines) +│ │ │ ├── indexing_handlers.py # (117 lines) +│ │ │ ├── management_handlers.py# (340 lines) +│ │ │ ├── query_handlers.py # (84 lines) +│ │ │ └── watcher_handlers.py # (84 lines) +│ │ │ +│ │ ├── languages/ # 19 Tree-sitter parsers +│ │ │ ├── python.py (576) ├── javascript.py (590) +│ │ │ ├── typescript.py (576) ├── typescriptjsx.py (152) +│ │ │ ├── go.py (508) ├── rust.py (296) +│ │ │ ├── c.py (563) ├── cpp.py (616) +│ │ │ ├── java.py (471) ├── ruby.py (537) +│ │ │ ├── csharp.py (551) ├── php.py (520) +│ │ │ ├── kotlin.py (640) ├── scala.py (520) +│ │ │ ├── swift.py (491) ├── dart.py (378) +│ │ │ ├── perl.py (261) ├── haskell.py (427) +│ │ │ └── elixir.py (461) +│ │ │ +│ │ └── query_tool_languages/ # 16 stub toolkits (all NotImplementedError) +│ │ +│ ├── utils/ +│ │ ├── tree_sitter_manager.py # Language loading (~265 lines) +│ │ ├── debug_log.py # Logging utilities (91 lines) +│ │ ├── path_ignore.py # Path ignore helpers (55 lines) +│ │ └── repo_path.py # Path matching (27 lines) +│ │ +│ └── viz/ +│ ├── server.py # FastAPI viz server (283 lines) +│ └── dist/ # Built React visualization (packaged) +│ +├── tests/ +│ ├── conftest.py # Session fixtures +│ ├── unit/ # Unit tests +│ │ ├── core/ # DB, jobs, cgcignore tests +│ │ ├── tools/ # GraphBuilder, CodeFinder tests +│ │ ├── parsers/ # Language parser tests +│ │ ├── languages/ # Language-specific tests +│ │ └── utils/ # Utility tests +│ ├── integration/ +│ │ ├── cli/ # CLI command tests +│ │ └── mcp/ # MCP server tests +│ ├── e2e/ # User journey tests +│ ├── perf/ # Performance tests +│ └── fixtures/ # Sample projects (multi-language) +│ +├── website/ # Vite + React + shadcn/ui +│ ├── src/ +│ │ ├── components/ +│ │ │ ├── CodeGraphViewer.tsx # Main viewer (1579 lines) ★ +│ │ │ ├── FlowchartSVG.tsx # Mermaid-style SVG (662 lines) +│ │ │ ├── LocalUploader.tsx # File upload (245 lines) +│ │ │ ├── BundleGeneratorSection.tsx +│ │ │ ├── BundleRegistrySection.tsx +│ │ │ └── ui/ # ~50 shadcn components +│ │ ├── lib/ +│ │ │ ├── parser.ts # Parse orchestrator (105 lines) +│ │ │ └── parser.worker.ts # Web Worker parser (798 lines) ★ +│ │ └── pages/ +│ │ ├── Index.tsx # Landing page +│ │ ├── Explore.tsx # Graph explorer +│ │ └── NotFound.tsx +│ └── api/ # Vercel serverless routes +│ ├── bundles.ts +│ ├── bundle-status.ts +│ ├── trigger-bundle.ts +│ └── pypi.ts +│ +├── docs/ # MkDocs documentation +│ ├── mkdocs.yml +│ ├── docs/ # Source markdown (44 pages) +│ └── site/ # Built static site +│ +├── k8s/ # Kubernetes manifests +├── scripts/ # Dev/ops scripts +├── organizer/ # Internal planning notes +└── .github/workflows/ # CI/CD (8 workflows) +``` + +--- + +## 7. Complete Feature Inventory + +### Shipped and Working + +| # | Feature | Component | Status | +|---|---------|-----------|--------| +| 1 | MCP Server (JSON-RPC over stdio) | `server.py` | Stable | +| 2 | 20 MCP tools | `tool_definitions.py` + handlers | Stable | +| 3 | CLI with 55+ commands | `cli/main.py` | Stable | +| 4 | FalkorDB Lite embedded backend | `database_falkordb.py` | Stable (Unix, Py3.12+) | +| 5 | FalkorDB Remote backend | `database_falkordb_remote.py` | Stable | +| 6 | KuzuDB embedded backend | `database_kuzu.py` | Stable | +| 7 | Neo4j server backend | `database.py` | Stable | +| 8 | 20 language parsers (Tree-sitter) | `languages/*.py` | Stable | +| 9 | Python, JS, TS, Go, Rust, C, C++, Java, Ruby, C#, PHP, Kotlin, Scala, Swift, Dart, Perl, Haskell, Elixir, TSX, Lua | | | +| 10 | Jupyter notebook parsing | Python parser + `nbformat` | Stable | +| 11 | SCIP indexing (opt-in) | `scip_indexer.py`, `scip_pipeline.py` | Beta | +| 12 | Graph schema: 17 node types, 7 relationships | `schema_contract.py` | Stable | +| 13 | Fuzzy search (Levenshtein) | `code_finder.py` | Stable | +| 14 | Cyclomatic complexity analysis | `code_finder.py` | Stable | +| 15 | Dead code detection | `code_finder.py` | Stable | +| 16 | Call chain analysis (transitive) | `code_finder.py` | Stable | +| 17 | Class inheritance hierarchy | `code_finder.py` | Stable | +| 18 | File system watcher (live re-index) | `watcher.py` | Stable | +| 19 | `.cgcignore` support | `cgcignore.py` | Stable | +| 20 | Bundle export/import (.cgc format) | `cgc_bundle.py` | Stable | +| 21 | Bundle registry (GitHub-backed) | `bundle_registry.py` | Stable | +| 22 | On-demand bundle generation (website) | `api/trigger-bundle.ts` | Stable | +| 23 | Named contexts (global/per-repo) | `config_manager.py` | Stable | +| 24 | Context discovery + switch (MCP) | `server.py` | Stable | +| 25 | Workspace mappings (persistent context) | `config_manager.py` | Stable | +| 26 | Interactive setup wizard | `setup_wizard.py` | Stable | +| 27 | Multi-IDE MCP setup (Cursor, Claude, VS Code, JetBrains, Windsurf, etc.) | `setup_wizard.py` | Stable | +| 28 | Visualization server (FastAPI) | `viz/server.py` | Stable | +| 29 | Website with in-browser parsing | `website/` | Stable | +| 30 | CodeGraphViewer (2D/3D/City/Mermaid) | `CodeGraphViewer.tsx` | Stable | +| 31 | GitHub repo parsing in browser | `parser.ts` + `parser.worker.ts` | Stable | +| 32 | Package resolver (9 languages) | `package_resolver.py` | Stable | +| 33 | Source code indexing (opt-in) | `INDEX_SOURCE` config | Stable | +| 34 | Docker deployment | `Dockerfile`, `docker-compose.yml` | Stable | +| 35 | Kubernetes deployment | `k8s/*.yaml` | Template | +| 36 | CI/CD (GitHub Actions) | `.github/workflows/` | Active | +| 37 | Multi-language README | 5 translations | Stable | +| 38 | MkDocs documentation site | `docs/` | Partial | +| 39 | `cgc doctor` health check | `cli/main.py` | Stable | +| 40 | LLM system prompt for graph-aware AI | `prompts.py` | Stable | + +### Partially Implemented / Stubbed + +| # | Feature | Status | Notes | +|---|---------|--------|-------| +| 41 | Advanced language query toolkits | Stubbed | All 16 `*_toolkit.py` raise `NotImplementedError` | +| 42 | `visualize_graph_query` (Neo4j Browser URL) | Niche | Only works with Neo4j, not embedded backends | +| 43 | `falkor_worker.py` subprocess isolation | Partial | Worker entrypoint exists but integration unclear | + +--- + +## 8. Current Limitations + +### Architecture + +| # | Limitation | Impact | Severity | +|---|-----------|--------|----------| +| L1 | **Single-process MCP server** — no horizontal scaling; JSON-RPC over stdio ties to one IDE process | Cannot serve multiple IDEs simultaneously | Medium | +| L2 | **Synchronous handlers via `asyncio.to_thread`** — all tool handlers are sync functions wrapped in threads; no true async DB drivers | Thread pool can saturate under heavy concurrent tool calls | Low | +| L3 | **In-memory job tracking** — `JobManager` uses a dict; jobs lost on restart | No job persistence across server restarts | Medium | +| L4 | **No authentication/authorization** — MCP server trusts all stdin input; viz server has no auth | Acceptable for local use; security concern in shared/remote setups | Low (local) | +| L5 | **Monolithic `cli/main.py`** (2386 lines) — all commands in one file | Hard to maintain and test individual command groups | Medium | +| L6 | **Monolithic `code_finder.py`** (1119 lines) — 30+ query methods in one class | Growing complexity; hard to extend per-backend | Medium | +| L7 | **`CodeGraphViewer.tsx`** (1579 lines) — single massive React component | Difficult to maintain; mixing rendering, state, and layout | High | +| L8 | **No streaming for large results** — all query results materialized in memory then JSON-serialized | Memory pressure on large codebases | Medium | + +### Database + +| # | Limitation | Impact | Severity | +|---|-----------|--------|----------| +| L9 | **FalkorDB Lite requires Unix + Python 3.12+** — no Windows support | Windows users fall back to KuzuDB | Medium | +| L10 | **KuzuDB Cypher dialect differences** — some Cypher constructs (UNWIND, certain aggregations) differ from Neo4j/Falkor | Query compatibility issues; `code_finder.py` has workarounds but not exhaustive | High | +| L11 | **No connection pooling** — single driver instance per backend | Performance ceiling under concurrent queries | Low | +| L12 | **Bundle format tied to Cypher** — export/import uses raw Cypher strings; schema changes break bundles | Forward compatibility risk | Medium | + +### Parsing + +| # | Limitation | Impact | Severity | +|---|-----------|--------|----------| +| L13 | **Tree-sitter is syntactic, not semantic** — no type inference, no cross-file symbol resolution beyond import maps | Call graph can be imprecise (e.g., same-name functions across modules) | Medium | +| L14 | **SCIP requires external `scip-*` binaries** — not bundled; user must install per-language | Friction for adoption; most users stay on Tree-sitter | Low | +| L15 | **No incremental SCIP indexing** — always full re-index | Slow for large repos when using SCIP | Medium | +| L16 | **`.h` files default to C++ parser** — C projects with `.h` files get C++ parsing | May produce slightly wrong AST for pure C headers | Low | +| L17 | **No HTML/CSS/SQL/Shell/YAML parsing** — only 19 languages | Users of those languages get no graph data | Low | + +### Testing + +| # | Limitation | Impact | Severity | +|---|-----------|--------|----------| +| L18 | **`test_cgcignore_patterns.py` requires installed CLI + running DB** — not isolated | Flaky in CI without setup | Medium | +| L19 | **Ruby fixture test** (`test_mixins.py`) expects undefined `graph` fixture | Dead test; will fail if collected | Low | +| L20 | **Duplicate C++ enum tests** across `test_cpp_enums.py` and `test_cpp_parser.py` | Drift risk | Low | +| L21 | **E2E tests have weak assertions** — some assertions commented out | False passes | Medium | +| L22 | **Performance test is a mock** — `test_large_indexing.py` doesn't test real perf | No real performance regression detection | Medium | + +### Documentation + +| # | Limitation | Impact | Severity | Status | +|---|-----------|--------|----------|--------| +| L23 | ~~Docs reference wrong config keys~~ | ~~User confusion~~ | ~~High~~ | **FIXED** (2026-04-09) | +| L24 | ~~Architecture docs show KuzuDB as default~~ | ~~Misleading~~ | ~~High~~ | **FIXED** (2026-04-09) | +| L25 | ~~MCP tools docs missing 2 tools~~ | ~~Undiscoverable features~~ | ~~Medium~~ | **FIXED** (2026-04-09) | +| L26 | ~~Roadmap frozen at v0.2.1~~ | ~~No forward visibility~~ | ~~Medium~~ | **FIXED** (2026-04-09) | +| L27 | ~~Bundle docs treat registry as "future"~~ | ~~Confusing~~ | ~~Medium~~ | **FIXED** (2026-04-09) | +| L28 | ~~`monitor_directory` naming in docs~~ | ~~Broken tool references~~ | ~~Medium~~ | **FIXED** (2026-04-09) | +| L29 | ~~Deployment pages not linked in MkDocs nav~~ | ~~Unreachable~~ | ~~Medium~~ | **FIXED** (2026-04-09) | + +> All 40 documentation issues identified in `OUTDATED_DOCS.md` have been resolved. + +--- + +## 9. Architectural Recommendations + +### High Priority + +| # | Recommendation | Effort | Impact | +|---|---------------|--------|--------| +| R1 | **Split `cli/main.py`** into per-group modules (e.g., `cli/commands/index.py`, `cli/commands/find.py`) | Medium | Maintainability | +| R2 | **Split `CodeGraphViewer.tsx`** into subcomponents (`Sidebar`, `GraphCanvas`, `FileViewer`, `SettingsPanel`) | Medium | Maintainability | +| R3 | ~~Fix all documentation~~ — **DONE** (2026-04-09): config keys, backends, tool count, CLI names all updated | ~~Medium~~ | ~~User experience~~ | +| R4 | **Add backend abstraction layer** — extract a `GraphQueryInterface` protocol that `CodeFinder` programs against, with backend-specific implementations | High | Eliminates Cypher dialect workarounds | +| R5 | **Implement the 16 `*Toolkit` stubs** or remove them | Low | Code cleanliness | + +### Medium Priority + +| # | Recommendation | Effort | Impact | +|---|---------------|--------|--------| +| R6 | **Persistent job storage** — use SQLite or the graph DB itself to persist job state | Low | Reliability | +| R7 | **Add streaming support** for large query results (JSON lines or chunked responses) | Medium | Scalability | +| R8 | **Isolate tests** — mock DB in unit tests; use fixtures for integration; mark E2E clearly | Medium | CI reliability | +| R9 | **Add a proper test for performance** — benchmark indexing speed, query latency on standard corpus | Medium | Regression detection | +| R10 | **Standardize error handling** — define error codes for tool responses; use structured errors | Low | Debuggability | + +### Low Priority + +| # | Recommendation | Effort | Impact | +|---|---------------|--------|--------| +| R11 | **Add SSE/WebSocket transport** option for MCP (alongside stdio) for remote/multi-client scenarios | High | Flexibility | +| R12 | **Bundle versioning** — include schema version in `.cgc` files for forward compatibility | Low | Durability | +| R13 | **Add `cgc bundle validate`** as a public CLI command | Low | User convenience | +| R14 | **Deduplicate C++ test files** | Low | Code hygiene | +| R15 | **Add HTML/CSS/SQL parsers** using existing Tree-sitter infrastructure | Medium | Language coverage | + +--- + +*This document was generated by analyzing every file in the codebase. For the full issue tracker, see the project GitHub repository.* diff --git a/docs/BUNDLES.md b/docs/BUNDLES.md index a80410f9..b4b7ffc8 100644 --- a/docs/BUNDLES.md +++ b/docs/BUNDLES.md @@ -1,408 +1,59 @@ # CodeGraphContext Bundles -## 🎯 What are .cgc Bundles? +> **📖 Comprehensive Documentation:** For complete information about bundles, including detailed guides, examples, advanced usage, and troubleshooting, please see the [Bundle Guide](./docs/guides/bundles.md) in the documentation. -`.cgc` (CodeGraphContext Bundle) files are **portable, pre-indexed graph snapshots** that can be distributed and loaded instantly without re-indexing. Think of them as "npm packages for code knowledge graphs." +## Quick Overview -### Key Benefits - -- ⚡ **Instant Loading** - Load in seconds instead of minutes/hours of indexing -- 🎯 **Pre-analyzed** - All code relationships already computed -- 🔍 **Query Ready** - Start using with AI assistants immediately -- 📦 **Portable** - Works across any CodeGraphContext installation -- 🌐 **Shareable** - Distribute pre-indexed knowledge easily - -## 📦 Bundle Structure +CodeGraphContext Bundles (`.cgc` files) are **portable, pre-indexed graph snapshots** that enable instant loading of code structures without re-parsing. They work like "npm packages for code knowledge graphs." -A `.cgc` file is a ZIP archive containing: - -``` -numpy.cgc -├── metadata.json # Repository and indexing metadata -├── schema.json # Graph schema definition -├── nodes.jsonl # All nodes (one JSON per line) -├── edges.jsonl # All relationships (one JSON per line) -├── stats.json # Graph statistics -└── README.md # Human-readable description -``` - -### File Formats - -#### metadata.json -```json -{ - "cgc_version": "0.1.0", - "exported_at": "2026-01-13T22:00:00", - "repo": "numpy/numpy", - "commit": "a1b2c3d4", - "languages": ["python", "c"], - "format_version": "1.0" -} -``` - -#### nodes.jsonl (excerpt) -```jsonl -{"_id": "4:abc123", "_labels": ["Function"], "name": "array", "path": "/numpy/core/array.py", "line_number": 42} -{"_id": "4:def456", "_labels": ["Class"], "name": "ndarray", "path": "/numpy/core/multiarray.py", "line_number": 100} -``` - -#### edges.jsonl (excerpt) -```jsonl -{"from": "4:abc123", "to": "4:def456", "type": "CALLS", "properties": {}} -{"from": "4:xyz789", "to": "4:def456", "type": "INHERITS", "properties": {}} -``` - -## 🚀 Quick Start - -### Creating Bundles +### Core Commands +**Export** (package your indexed code): ```bash -# Export current indexed repository cgc bundle export my-project.cgc --repo /path/to/project - -# Export all indexed repositories -cgc bundle export all-repos.cgc - -# Export without statistics (faster) -cgc bundle export quick.cgc --repo /path/to/project --no-stats - -# Shortcut -cgc export my-project.cgc --repo /path/to/project -``` - -### Loading Bundles - -```bash -# Load a bundle (adds to existing graph) -cgc bundle import numpy.cgc - -# Load and clear existing data -cgc bundle import numpy.cgc --clear - -# Shortcut -cgc load numpy.cgc -``` - -### Using Pre-indexed Bundles - -```bash -# Download from GitHub Releases -wget https://github.com/CodeGraphContext/CodeGraphContext/releases/download/bundles-20260113/numpy-1.26.4-a1b2c3d.cgc - -# Load it -cgc load numpy-1.26.4-a1b2c3d.cgc - -# Start querying immediately -cgc find name linalg -cgc analyze deps numpy.linalg -``` - -## 📚 Available Pre-indexed Bundles - -We provide weekly-updated bundles for popular repositories: - -### Tier 1 - Python Core Libraries - -| Repository | Description | Size | Download | -|------------|-------------|------|----------| -| **numpy** | Scientific computing | ~50MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | -| **pandas** | Data analysis | ~80MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | -| **fastapi** | Modern web framework | ~15MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | -| **requests** | HTTP library | ~10MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | -| **flask** | Web framework | ~12MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | - -### Coming Soon - -- **scikit-learn** - Machine learning -- **django** - Web framework -- **pytorch** - Deep learning (subset) -- **kubernetes** - Container orchestration (Go) -- **redis** - In-memory database - -## 🔧 Advanced Usage - -### Bundle Versioning - -Bundles follow this naming convention: -``` ---.cgc -``` - -Examples: -- `numpy-1.26.4-a1b2c3d.cgc` -- `pandas-2.1.0-xyz789.cgc` -- `fastapi-0.109.0-abc123.cgc` - -### Combining Bundles - -```bash -# Load multiple bundles into the same graph -cgc load numpy.cgc -cgc load pandas.cgc -cgc load scikit-learn.cgc - -# Now query across all three -cgc find name fit --type function -``` - -### Exporting Specific Repositories - -```bash -# Index multiple repos -cgc index /path/to/numpy -cgc index /path/to/pandas - -# Export each separately -cgc export numpy.cgc --repo /path/to/numpy -cgc export pandas.cgc --repo /path/to/pandas - -# Or export everything -cgc export all-my-projects.cgc -``` - -## 🏗️ Creating Your Own Bundle Registry - -### 1. Index Your Repositories - -```bash -# Clone and index -git clone https://github.com/your-org/your-repo -cd your-repo -cgc index . -``` - -### 2. Export to Bundle - -```bash -# Get commit info -COMMIT=$(git rev-parse --short HEAD) -TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "main") - -# Export with version info -cgc export "your-repo-${TAG}-${COMMIT}.cgc" --repo . -``` - -### 3. Distribute - -#### Option A: GitHub Releases - -```bash -# Create a release -gh release create bundles-$(date +%Y%m%d) \ - your-repo-*.cgc \ - --title "Pre-indexed Bundles - $(date +%Y-%m-%d)" \ - --notes "Pre-indexed code graphs for instant loading" -``` - -#### Option B: Object Storage (S3, R2, GCS) - -```bash -# Upload to S3 -aws s3 cp your-repo-*.cgc s3://your-bucket/bundles/ - -# Make public or use signed URLs -aws s3 presign s3://your-bucket/bundles/your-repo-*.cgc -``` - -#### Option C: Hugging Face Datasets - -```bash -# Install huggingface_hub -pip install huggingface_hub - -# Upload -huggingface-cli upload your-org/cgc-bundles your-repo-*.cgc -``` - -## 🔍 Bundle Inspection - -### View Bundle Contents - -```bash -# Extract and view -unzip -l numpy.cgc - -# View metadata -unzip -p numpy.cgc metadata.json | jq - -# View statistics -unzip -p numpy.cgc stats.json | jq - -# Read README -unzip -p numpy.cgc README.md -``` - -### Validate Bundle - -```bash -# Check bundle integrity -cgc bundle validate numpy.cgc # (future feature) -``` - -## 🎓 Use Cases - -### 1. AI Assistant Context - -```python -# Load famous libraries for AI-powered development -cgc load numpy.cgc -cgc load pandas.cgc - -# AI can now query structure instantly -# "Show me all functions that use numpy.linalg" -``` - -### 2. Code Analysis Pipelines - -```bash -# CI/CD: Load pre-indexed dependencies -cgc load fastapi.cgc -cgc load sqlalchemy.cgc - -# Analyze your code against them -cgc index ./my-api -cgc analyze deps my_api -``` - -### 3. Educational Resources - -```bash -# Students can explore famous codebases -cgc load django.cgc -cgc find name authenticate -cgc analyze chain authenticate -``` - -### 4. Research & Documentation - -```bash -# Researchers can analyze code evolution -cgc load numpy-1.25.0.cgc -cgc load numpy-1.26.0.cgc - -# Compare structures (future feature) -cgc diff numpy-1.25.0.cgc numpy-1.26.0.cgc ``` -## 🔐 Security Considerations - -### Bundle Verification - -Always verify bundles from untrusted sources: - +**Import** (load a bundle): ```bash -# Check metadata -unzip -p bundle.cgc metadata.json - -# Verify source repository -# Ensure commit hash matches official repo +cgc bundle import my-project.cgc ``` -### Sandboxing - -Bundles only contain graph data, not executable code. However: -- Review metadata before loading -- Use `--clear` cautiously (it deletes existing data) -- Keep backups of your graph database - -## 🛠️ Troubleshooting - -### Bundle Import Fails - +**Registry** (access pre-indexed bundles): ```bash -# Check bundle integrity -unzip -t bundle.cgc - -# Verify format version -unzip -p bundle.cgc metadata.json | jq .cgc_version - -# Try with --clear flag -cgc load bundle.cgc --clear +cgc registry search flask # Search for available packages +cgc bundle load flask # Download and load +cgc registry list # View all available +cgc registry request # Request on-demand builds ``` -### Large Bundle Performance - -```bash -# For very large bundles, increase batch size -# (future configuration option) -export CGC_IMPORT_BATCH_SIZE=5000 -cgc load large-bundle.cgc -``` - -### Version Mismatch - -```bash -# Check your CGC version -cgc --version - -# Update if needed -pip install --upgrade codegraphcontext - -# Check bundle version -unzip -p bundle.cgc metadata.json | jq .cgc_version -``` - -## 📖 API Reference - -### Python API - -```python -from codegraphcontext.core.cgc_bundle import CGCBundle -from codegraphcontext.core.database import DatabaseManager - -# Initialize -db_manager = DatabaseManager() -bundle = CGCBundle(db_manager) - -# Export -success, message = bundle.export_to_bundle( - output_path=Path("my-bundle.cgc"), - repo_path=Path("/path/to/repo"), - include_stats=True -) - -# Import -success, message = bundle.import_from_bundle( - bundle_path=Path("my-bundle.cgc"), - clear_existing=False -) -``` - -## 🗺️ Roadmap - -### v0.2.0 - Bundle Registry -- [ ] Central bundle registry -- [ ] `cgc registry search` command -- [ ] Automatic download from registry -- [ ] Bundle versioning and updates - -### v0.3.0 - Advanced Features -- [ ] Delta bundles (incremental updates) -- [ ] Bundle compression options -- [ ] Encrypted bundles -- [ ] Bundle signing and verification - -### v0.4.0 - Collaboration -- [ ] Bundle merging -- [ ] Conflict resolution -- [ ] Multi-repository bundles -- [ ] Bundle diff and comparison - -## 🤝 Contributing - -### Creating Bundles for Popular Repos +### Key Benefits -We welcome contributions of pre-indexed bundles! See [CONTRIBUTING.md](../CONTRIBUTING.md) for guidelines. +- ⚡ **Instant Loading** - Load in seconds instead of hours +- 🎯 **Pre-analyzed** - All code relationships already computed +- 📦 **Portable** - Works across any CodeGraphContext installation +- 🔍 **AI Ready** - Integrate with AI assistants immediately -### Improving Bundle Format +### Use Cases -The bundle format is versioned and extensible. Propose improvements via GitHub issues. +- **AI Assistant Context** - Load libraries for AI-powered development +- **CI/CD Pipelines** - Pre-index dependencies for analysis +- **Education** - Share pre-indexed famous codebases +- **Research** - Compare code structure evolution +- **Team Collaboration** - Distribute code graphs across teams -## 📄 License +--- -Bundle format specification: MIT License -Pre-indexed bundles: Subject to source repository licenses +## Learn More ---- +👉 **[Read the Full Bundle Guide →](./docs/guides/bundles.md)** -**Questions?** Open an issue on [GitHub](https://github.com/CodeGraphContext/CodeGraphContext/issues) +The guide includes: +- Detailed bundle structure and file formats +- Step-by-step creation and import procedures +- Available pre-indexed public bundles +- Advanced usage patterns (combining, versioning, custom registries) +- Bundle inspection and validation +- Security considerations and best practices +- Troubleshooting and common issues +- Python API reference +- Roadmap and future features diff --git a/docs/CLI_COMPLETE_REFERENCE.md b/docs/CLI_COMPLETE_REFERENCE.md index 86e0e68e..26d0dfe5 100644 --- a/docs/CLI_COMPLETE_REFERENCE.md +++ b/docs/CLI_COMPLETE_REFERENCE.md @@ -62,7 +62,7 @@ | Command | Arguments | Description | |---------|-----------|-------------| -| `cgc find name` | `` `--type` | Find code elements by exact name. | +| `cgc find name` | `` `--type` `--fuzzy/--no-fuzzy` | Find code elements by name. Fuzzy matching is on by default (configurable via `FUZZY_SEARCH`). | | `cgc find pattern` | `` `--case-sensitive` | Find elements using fuzzy substring matching. | | `cgc find type` | `` `--limit` | List all nodes of a specific type (function, class, module). | | `cgc find variable` | `` `--file` | Find variables by name across the codebase. | @@ -83,7 +83,7 @@ | `cgc config show` | None | Display current configuration values. | | `cgc config set` | ` ` | Set a configuration value. | | `cgc config reset` | None | Reset configuration to defaults. | -| `cgc config db` | `` | Quick switch between `neo4j` and `falkordb`. | +| `cgc config db` | `` | Quick switch between `kuzudb`, `ladybugdb`, `falkordb`, or `neo4j`. | --- @@ -127,7 +127,7 @@ These work with any command: | Option | Short | Description | |--------|-------|-------------| -| `--database` | `-db` | Override database backend (`falkordb` or `neo4j`). | +| `--database` | `-db` | Override database backend (`kuzudb`, `ladybugdb`, `falkordb`, or `neo4j`). | | `--visual` / `--viz` | `-V` | Show results as interactive graph visualization. | | `--help` | `-h` | Show help for any command. | | `--version` | `-v` | Show version (root level only). | diff --git a/docs/INTEGRATION_GUIDE.md b/docs/INTEGRATION_GUIDE.md new file mode 100644 index 00000000..be03309d --- /dev/null +++ b/docs/INTEGRATION_GUIDE.md @@ -0,0 +1,68 @@ +# Integrating CodeGraphContext with ChatGPT and Claude + +The new **CGC Gateway** (HTTP API) allows you to use CodeGraphContext as a set of tools for ChatGPT and Claude. This is superior to a CLI tool because it follows standard web protocols that these models already support. + +## 1. Start the API Gateway + +Run the following command in your terminal: + +```bash +cgc api start --port 8000 +``` + +This starts a FastAPI server at `http://localhost:8000`. You can verify it's running by visiting `http://localhost:8000/docs` in your browser. + +## 2. Integration with ChatGPT + +There are two ways to use CodeGraphContext with ChatGPT: + +### Option A: Native MCP Server (Recommended) +ChatGPT now supports the **Model Context Protocol (MCP)** natively. This allows the AI to discover all tools automatically without you needing to provide an OpenAPI schema. + +1. **Expose your local server**: + ```bash + ngrok http 8000 + ``` + Copy the `https` URL (e.g., `https://random-id.ngrok-free.app`). + +2. **Add MCP Server in ChatGPT**: + - Go to **Settings** -> **Connected accounts** -> **Connectors** (or "MCP Servers"). + - Click **Add MCP Server**. + - **Name**: CodeGraphContext + - **MCP Server URL**: `https://your-id.ngrok-free.app/api/v1/mcp/sse` + - **Authentication**: No Auth + - Click **Create**. + +> [!TIP] +> If you get a **405 Method Not Allowed** error, ensure you are using the full path `/api/v1/mcp/sse` in the URL field. + +### Option B: Custom GPT Actions (Legacy/Alternative) +If you prefer to create a custom GPT with a specific focus: + +1. **Create a GPT Action**: + - Go to GPT settings -> **Actions** -> **Create new action**. + - For **Schema**, use the spec from `https://your-id.ngrok-free.app/openapi.json`. + +## 3. Integration with Claude (API) + +If you are building an application using the Claude API, you can now call CGC tools via standard HTTP requests. + +Example (Python): +```python +import requests + +response = requests.post( + "http://localhost:8000/api/v1/tools/call", + json={ + "name": "find_code", + "arguments": {"query": "class MyClass"} + } +) +print(response.json()) +``` + +## 4. Why this is better than a CLI tool + +- **Persistence**: The server keeps the database connection open and ready. +- **Standards-Compliant**: Uses OpenAPI (Swagger), which is the "native language" of AI tool-calling. +- **Flexible**: Can be used by the `website` frontend, ChatGPT, Claude, or custom scripts. diff --git a/docs/MCP_TOOLS.md b/docs/MCP_TOOLS.md index bb86dc42..f630a751 100644 --- a/docs/MCP_TOOLS.md +++ b/docs/MCP_TOOLS.md @@ -1,137 +1,240 @@ # MCP Tools Reference -This document describes the Model Context Protocol (MCP) tools available in CodeGraphContext. These tools are exposed to AI assistants to enable them to interact with your codebase. +This document describes the Model Context Protocol (MCP) tools exposed by CodeGraphContext **0.4.16**. The server registers the full catalog defined in `src/codegraphcontext/tool_definitions.py` (same tools the CLI graph operations rely on). MCP `tools/list` returns **25** tool definitions—the subsections below enumerate each one. -## 📋 Tool Categories +## Tool categories -1. [Indexing & Management](#indexing--management) -2. [Code Search](#code-search) -3. [Analysis & Quality](#analysis--quality) -4. [Bundle Management](#bundle-management) -5. [Monitoring](#monitoring) -6. [Advanced Querying](#advanced-querying) +1. [Context management](#context-management) +2. [Indexing & management](#indexing--management) +3. [Code search](#code-search) +4. [Analysis & quality](#analysis--quality) +5. [Bundle management](#bundle-management) +6. [Monitoring](#monitoring) +7. [Job control](#job-control) +8. [Advanced querying](#advanced-querying) --- -## Indexing & Management +## Path sandbox (`CGC_ALLOWED_ROOTS`) + +Indexing, bundle load, watch, and context-switch tools only accept paths under: + +1. The MCP server process **current working directory**, and +2. Any extra roots in the `CGC_ALLOWED_ROOTS` environment variable (`:`-separated on Linux/macOS, `;` on Windows). + +Example: + +```bash +export CGC_ALLOWED_ROOTS="/home/me/projects:/data/repos" +cgc mcp # or configure mcp.json with the same env +``` + +Without this variable, sibling directories outside the server cwd are rejected for security. + +--- + +## Context management + +### `discover_codegraph_contexts` + +Scan child directories for `.codegraphcontext/` folders that contain an indexed database—useful when the IDE opens a parent directory that has no graph, but sub-projects do. + +- **Args:** `path` (string, optional), `max_depth` (integer, default `1`) +- **Returns:** List of discovered contexts with paths and metadata + +### `switch_context` + +Point the MCP session at a different `.codegraphcontext` database (repository root or `.codegraphcontext/` directory). + +- **Args:** `context_path` (string, required), `save` (boolean, default `true` — persist mapping for future sessions) +- **Returns:** Status, resolved database type, and paths + +--- + +## Indexing & management ### `add_code_to_graph` -Performs a one-time scan of a local folder to add its code to the graph. Ideal for libraries or dependencies. -- **Args**: `path` (string), `is_dependency` (boolean) -- **Returns**: Job ID + +One-time scan of a local folder to add code to the graph (libraries, dependencies, or projects not under active watch). + +- **Args:** `path` (string), `is_dependency` (boolean) +- **Returns:** Job ID ### `add_package_to_graph` -Add an external package to the graph by discovering its location. -- **Args**: `package_name` (string), `language` (string), `is_dependency` (boolean) -- **Returns**: Job ID -- **Supported Languages**: python, javascript, typescript, java, c, go, ruby, php, cpp + +Add an external package by resolving its install location. + +- **Args:** `package_name` (string), `language` (string), `is_dependency` (boolean) +- **Returns:** Job ID +- **Supported languages:** python, javascript, typescript, java, c, go, ruby, php, cpp ### `list_indexed_repositories` -List all repositories currently indexed in the graph. -- **Args**: None -- **Returns**: List of repositories with paths and stats + +List repositories currently in the graph. + +- **Args:** None +- **Returns:** Paths and metadata for each indexed repo ### `delete_repository` -Delete a repository from the graph. -- **Args**: `repo_path` (string) -- **Returns**: Success message -### `check_job_status` -Check the status of a background job (indexing, scanning). -- **Args**: `job_id` (string) -- **Returns**: Job status (running, completed, failed) and progress +Remove a repository from the graph. -### `list_jobs` -List all background jobs. -- **Args**: None -- **Returns**: List of all jobs +- **Args:** `repo_path` (string) +- **Returns:** Success message + +### `get_repository_stats` + +Counts of files, functions, classes, and modules for one repo or the whole database. + +- **Args:** `repo_path` (string, optional) +- **Returns:** Statistics object --- -## Code Search +## Code search ### `find_code` -Find code snippets related to a keyword. -- **Args**: `query` (string), `fuzzy_search` (boolean), `edit_distance` (number) -- **Returns**: Matches with file path, line number, and content + +Keyword search over indexed symbols and content. + +- **Args:** `query` (string), `fuzzy_search` (boolean), `edit_distance` (number), `repo_path` (string, optional) +- **Returns:** Matches with path, line, and snippet context --- -## Analysis & Quality +## Analysis & quality ### `analyze_code_relationships` -Analyze relationships between code elements. -- **Args**: - - `query_type` (enum): `find_callers`, `find_callees`, `class_hierarchy`, `overrides`, etc. - - `target` (string): Function/class name - - `context` (string): Optional file path -- **Returns**: List of related elements + +Callers, callees, imports, hierarchy, and other relationship queries. + +- **Args:** `query_type` (enum), `target` (string), `context` (string, optional file path), `repo_path` (string, optional) +- **Returns:** Structured relationship results ### `find_dead_code` -Find potentially unused functions across the codebase. -- **Args**: `exclude_decorated_with` (list of strings) -- **Returns**: List of unused functions + +Potentially unused functions across the indexed codebase. + +- **Args:** `exclude_decorated_with` (list of strings), `repo_path` (string, optional) +- **Returns:** Candidate dead symbols ### `calculate_cyclomatic_complexity` -Calculate complexity for a specific function. -- **Args**: `function_name` (string), `path` (string) -- **Returns**: Complexity score + +Complexity for a single function. + +- **Args:** `function_name` (string), `path` (string, optional), `repo_path` (string, optional) +- **Returns:** Complexity score ### `find_most_complex_functions` -Find the most complex functions in the codebase. -- **Args**: `limit` (integer) -- **Returns**: List of functions sorted by complexity -### `get_repository_stats` -Get statistics about indexed repositories. -- **Args**: `repo_path` (string, optional) -- **Returns**: Counts of files, functions, classes, modules +Rank functions by cyclomatic complexity. + +- **Args:** `limit` (integer), `repo_path` (string, optional) +- **Returns:** Ordered list of functions --- -## Bundle Management +## Bundle management ### `load_bundle` -Load a pre-indexed `.cgc` bundle. Can download from registry if not found locally. -- **Args**: `bundle_name` (string), `clear_existing` (boolean) -- **Returns**: Success message and stats + +Load a `.cgc` bundle (local file or registry download). + +- **Args:** `bundle_name` (string), `clear_existing` (boolean) +- **Returns:** Load status and stats ### `search_registry_bundles` -Search for bundles in the registry. -- **Args**: - - `query` (string, optional): Search term - - `unique_only` (boolean): Show only latest version per package -- **Returns**: List of available bundles + +Search the public bundle registry. + +- **Args:** `query` (string, optional), `unique_only` (boolean) +- **Returns:** Matching bundles and metadata --- ## Monitoring ### `watch_directory` -Continuously monitor a directory for changes and keep the graph updated. -- **Args**: `path` (string) -- **Returns**: Job ID for initial scan + +Initial index plus continuous filesystem watching to keep the graph current. + +- **Args:** `path` (string) +- **Returns:** Job ID for the initial scan ### `list_watched_paths` -List directories being watched. -- **Args**: None -- **Returns**: List of paths + +List active watch roots. + +- **Args:** None +- **Returns:** Paths under watch ### `unwatch_directory` + Stop watching a directory. -- **Args**: `path` (string) -- **Returns**: Success message + +- **Args:** `path` (string) +- **Returns:** Success message + +--- + +## Job control + +### `list_jobs` + +List background jobs (indexing, scans, etc.). + +- **Args:** None +- **Returns:** Job list with status + +### `check_job_status` + +Poll a single job. + +- **Args:** `job_id` (string) +- **Returns:** Status and progress --- -## Advanced Querying +## Advanced querying ### `execute_cypher_query` -Run a direct read-only Cypher query against the graph. -- **Args**: `cypher_query` (string) -- **Returns**: Raw query results + +Read-only Cypher against the active backend (same graph model across FalkorDB, KuzuDB, Neo4j). + +- **Args:** `cypher_query` (string) +- **Returns:** Tabular query results ### `visualize_graph_query` -Generate a URL to visualize query results in Neo4j Browser. -- **Args**: `cypher_query` (string) -- **Returns**: URL + +Build a Neo4j Browser URL for visual exploration of a query (where Neo4j Browser applies). + +- **Args:** `cypher_query` (string) +- **Returns:** URL string + +### `generate_report` + +Generate a markdown audit report (god nodes, complexity, cross-module calls, dead code). + +- **Args:** `output_path` (string, optional), `include_java` (boolean, optional) +- **Returns:** Report text and output path + +### `find_java_spring_endpoints` + +List Spring HTTP endpoints indexed in the graph. + +- **Args:** `repo_path` (string, optional) +- **Returns:** Endpoint rows (method, path, handler) + +### `find_java_spring_beans` + +List Spring bean stereotypes (`@Service`, `@Repository`, etc.). + +- **Args:** `stereotype` (string, optional), `repo_path` (string, optional) +- **Returns:** Bean rows + +### `find_datasource_nodes` + +Query datasource architecture nodes (SQL tables, Redis key patterns, etc.). + +- **Args:** `kind` (string, optional), `name` (string, optional), `include_columns` (boolean, optional) +- **Returns:** Datasource graph nodes diff --git a/docs/ON_DEMAND_BUNDLES.md b/docs/ON_DEMAND_BUNDLES.md index 174728c3..960a19bc 100644 --- a/docs/ON_DEMAND_BUNDLES.md +++ b/docs/ON_DEMAND_BUNDLES.md @@ -168,8 +168,11 @@ GET /api/bundle-status?repo=owner/repo **Cause:** Repository exceeds 1GB size limit **Solution:** + - This is a safety limit to prevent long-running workflows + - Consider increasing the limit in `/website/api/trigger-bundle.ts` if needed + - Or add the repository to the weekly pre-indexed list instead ### Issue: "Bundle generation failed" diff --git a/docs/QUICK_REFERENCE.md b/docs/QUICK_REFERENCE.md index 82b09503..d48c4818 100644 --- a/docs/QUICK_REFERENCE.md +++ b/docs/QUICK_REFERENCE.md @@ -98,14 +98,14 @@ cgc stats /path/to/repo # Stats for specific repo ### **Query the Graph** ```bash # Find all classes -cgc cypher "MATCH (c:Class) RETURN c.name LIMIT 20" +cgc query "MATCH (c:Class) RETURN c.name LIMIT 20" # Find all functions -cgc cypher "MATCH (f:Function) RETURN f.name LIMIT 20" +cgc query "MATCH (f:Function) RETURN f.name LIMIT 20" # Search for specific code -cgc search class Flask -cgc search function render_template +cgc find name Flask +cgc find name render_template ``` --- @@ -195,7 +195,7 @@ Available Bundles - [ ] Search for a bundle: `cgc registry search ` - [ ] Download a bundle: `cgc load ` - [ ] View loaded repos: `cgc list` -- [ ] Query the graph: `cgc cypher ""` +- [ ] Query the graph: `cgc query ""` --- diff --git a/docs/TEAM_ROLES.md b/docs/TEAM_ROLES.md new file mode 100644 index 00000000..79612c5c --- /dev/null +++ b/docs/TEAM_ROLES.md @@ -0,0 +1,54 @@ +# CodeGraphContext Team Boundaries & Issue Routing Guide + +This document defines the strict boundaries for assigning GitHub issues, PRs, and Roadmap milestones to our 7 specialized teams. Maintainers must follow these rules to ensure system stability and efficient parallel development. + +--- + +## 🏗️ 1. AdminArchs (Senior Maintainers & Core Architecture) +**The Rule:** If a task introduces risk to the entire system, fundamentally changes how the engine works, or involves production deployment infrastructure, it belongs here. New contributors should **not** be assigned these tasks. +* **System-Wide Architecture:** Changing database interfaces (e.g., `GraphQueryInterface`), shifting from synchronous to true asynchronous operations, or implementing persistent job states. +* **Concurrency & Performance:** Parallelized indexing (Worker Pools), connection pooling, and multi-client transport layers (WebSockets/SSE). +* **DevOps & Deployment:** Docker Compose, Kubernetes Helm charts, and CI/CD pipeline structures (GitHub Actions). +* **Security:** Production API auth, security audits, and rate limiting. + +## 🐍 2. PythonDevs (Core Engine Developers) +**The Rule:** If it involves writing Python code to parse, query, or manage local data—but *doesn't* require a massive architectural rewrite—it belongs here. +* **CLI & Tools:** Adding new sub-commands to the Typer CLI, refactoring existing `cli/` files into smaller modules. +* **CodeFinder & Queries:** Writing new Cypher queries to extract data, or refactoring existing query files. +* **Database Adapters:** Maintaining the existing Neo4j, KuzuDB, and FalkorDB drivers (without changing the fundamental async architecture). +* **AST Toolkits:** Implementing language-specific AST patterns (e.g., Python decorator resolution, Java annotations). + +## 🧠 3. AIDevs (AI & Machine Learning Integration) +**The Rule:** If it involves LLMs, embeddings, or prompt engineering, it goes here. +* **Local LLMs:** Integrating Ollama or local model inference. +* **Graph RAG:** Generating vector embeddings, storing them in vector indices, and writing hybrid search pipelines. +* **Semantic Features:** AI-guided function summarization and natural language querying (`cgc ask`). + +## 🌐 4. WebDevs (Frontend & UI/UX) +**The Rule:** If it lives in the browser and uses React, TypeScript, or visualizations, it goes here. +* **Visualizations:** React-force-graph rendering, WebGL optimizations, D3/Mermaid integration. +* **UI Components:** Refactoring large `tsx` monoliths into smaller UI components (e.g., glassmorphic modals, sidebars). +* **Web Interfaces:** The main CodeGraphContext website and the PR Reviewer visual web dashboard. + +## 🔌 5. ExtensionDevs (IDE & Browser Integrations) +**The Rule:** If it's a plugin that connects an external IDE or browser to our core engine, it goes here. +* **VS Code:** CodeLens markers, Webview panels, definition providers, and status bar indicators. +* **Browser Extensions:** Chrome/Firefox extensions (Manifest V3) for injecting graph contexts into GitHub or web LLMs. + +## 📚 6. DocsExperts (Technical Writing & DevRel) +**The Rule:** If it involves communicating how the tool works to the outside world, it goes here. +* **Documentation:** Auto-generating MkDocs API references, maintaining installation guides. +* **Content:** Writing technical blog posts (e.g., "Graph RAG for Code") and creating demo videos. +* **Internationalization:** Translating the README into Spanish, Hindi, Portuguese, etc. + +## 🧪 7. Testers (QA & Benchmarking) +**The Rule:** If it involves ensuring the code actually works under stress and across different environments, it goes here. +* **Benchmarking:** Running the ingestion benchmark suite to track performance regressions (LOC/sec, DB latency). +* **Parity Checks:** Expanding the DB parity test suite to ensure FalkorDB, Kuzu, and Neo4j return identical results. +* **Test Suite Health:** Isolating tests, mocking DBs, and removing test flakiness. + +--- +### ⚠️ Key Exception for Maintainers: +**"Single-file refactors vs. Systemic Shifts"** +If an issue asks to refactor `CodeGraphViewer.tsx` into smaller files, assign it to **WebDevs**. If an issue asks to refactor `cli/main.py` into smaller files, assign it to **PythonDevs**. +*However*, if an issue asks to "Shift the database ingestion to use parallelized multiprocessing worker queues"—that is a systemic shift and MUST be assigned to **AdminArchs**. diff --git a/TESTING.md b/docs/TESTING.md similarity index 100% rename from TESTING.md rename to docs/TESTING.md diff --git a/docs/UPDATING_DOCS.md b/docs/UPDATING_DOCS.md index 8df1058d..5705f239 100644 --- a/docs/UPDATING_DOCS.md +++ b/docs/UPDATING_DOCS.md @@ -37,15 +37,19 @@ docs/ ├── mkdocs.yml # Configuration & navigation ├── docs/ # Markdown content │ ├── index.md -│ ├── installation.md +│ ├── getting-started/ +│ │ └── installation.md │ ├── cookbook.md +│ ├── deployment/ # Deployment guides +│ │ ├── README.md +│ │ ├── DOCKER_README.md +│ │ └── ... │ └── ... -└── deployment/ # Deployment guides (NEW!) - ├── README.md - ├── DOCKER_README.md - └── ... +└── ... ``` +**Note:** Deployment pages live under **`docs/docs/deployment/`**. They are linked from the **`Deployment`** section in **`mkdocs.yml`** `nav`; if you add a new deployment markdown file, remember to add a `nav` entry or it will not appear in the site sidebar. + ### Adding New Pages 1. **Create a markdown file** in `docs/docs/`: @@ -106,10 +110,10 @@ npm run build # Generates dist/ folder ### What We Just Did -1. ✅ Moved deployment docs from root to `docs/deployment/` -2. ✅ Updated `docs/mkdocs.yml` to include deployment section +1. ✅ Moved deployment docs from root to `docs/docs/deployment/` +2. ✅ Updated `docs/mkdocs.yml` to include a **Deployment** section in `nav` 3. ✅ Updated `website/src/components/Footer.tsx` to link to deployment docs -4. ✅ Created `docs/deployment/README.md` as navigation index +4. ✅ Created `docs/docs/deployment/README.md` as navigation index ### Next Steps to Publish @@ -146,6 +150,6 @@ npm run build # Generates dist/ folder ## 💡 Tips - **MkDocs** uses relative paths from `docs/docs/` directory -- Use `../deployment/FILE.md` to reference files outside `docs/docs/` +- Use paths under `docs/docs/deployment/` for deployment markdown; link from other pages with relative paths (e.g. `deployment/README.md` from a page in `docs/docs/`). - The React site links to GitHub for docs (see Footer.tsx) - TypeScript errors in `website/` are normal without `npm install` diff --git a/docs/custom_theme/main.html b/docs/custom_theme/main.html new file mode 100644 index 00000000..bde8371a --- /dev/null +++ b/docs/custom_theme/main.html @@ -0,0 +1,408 @@ +{% extends "base.html" %} + +{% block extrahead %} + + +{% endblock %} + +{% block site_nav %} +
+ +
+
+ + Logo + CodeGraphContext + / + Documentation +
+ +
+
+ + + + + +
+
+
+ +
+
+
+ CodeGraphContext/CodeGraphContext +
+ + + + v0.5.0 + + + 3.2k + + + + + + + 564 +
+
+
+ + + + + + + +
+
+ +
+
+ + +
+
+ Home + {% if page.ancestors %} + {% for ancestor in page.ancestors %} + + {{ ancestor.title }} + {% endfor %} + {% endif %} + + {{ page.title }} +
+ + +
+ {{ page.content }} +
+
+
+ +
+ + +
+
+{% endblock %} + +{% block scripts %} +{{ super() }} + +{% endblock %} + +{% block content %} +{% endblock %} \ No newline at end of file diff --git a/docs/docs/architecture.md b/docs/docs/architecture.md deleted file mode 100644 index cc1d9266..00000000 --- a/docs/docs/architecture.md +++ /dev/null @@ -1,74 +0,0 @@ -# System Architecture - -CodeGraphContext (CGC) is a **context-aware code intelligence engine** that bridges the gap between your source code and your AI tools. - -It operates primarily as a background service (MCP Server) backed by a graph database, with a CLI for management. Instead of relying solely on vector embeddings, CodeGraphContext leverages an exact knowledge graph for deep, deterministic insights into the codebase. - -## High-Level Architecture Diagram - -```mermaid -graph TD - Client[AI Client / CLI / Editor] - Server[MCP Server Interface] - CoreEngine[Core Engine & Tools] - ParserLayer[Tree-sitter Language Parsers] - DBLayer[Database Abstraction Layer] - KuzuDB[(KùzuDB - Embedded Default)] - Neo4j[(Neo4j - Enterprise Option)] - FS[File System] - - Client -- "1. Context Queries (MCP)" --> Server - Client -- "2. CLI Commands (Index/List)" --> CoreEngine - Server -- "Forward Requests" --> CoreEngine - CoreEngine -- "Read/Write Graph" --> DBLayer - CoreEngine -- "Trigger Full/Incremental Indexing" --> ParserLayer - ParserLayer -- "Scan Source Files" --> FS - DBLayer -- "Store nodes & edges" --> KuzuDB - DBLayer -- "Store nodes & edges" --> Neo4j -``` - -## 1. The Core (Backend) - -The primary intelligence resides within the `src/codegraphcontext` directory. The system is built purely in Python and heavily relies on abstracting operations to stay versatile. - -| Component | Responsibility | -| :--- | :--- | -| **MCP Server (`server.py`)** | Acts as the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) host. It translates JSON-RPC requests from LLM clients (like Claude, Cursor, Windsurf) into standardized graph database queries. | -| **Graph Builder (`graph_builder.py`)** | The "indexer" module. It coordinates parsing the codebase using **Tree-sitter**, extracts dependencies, and builds the knowledge graph layout in memory before flushing objects to the database. | -| **Database Abstraction Layer** | Handles the connection to the underlying graph database. By default, CGC uses **KùzuDB** for embedded, fast, local indexing, but also natively supports **Neo4j** for complex or enterprise-level deployments. | -| **Watchers & Background Jobs** | Manages long-running tasks such as full repository indexing, bundle processing, and file system monitoring (`watchdog` module) that triggers incremental updates upon file modifications without blocking the AI. | -| **Tree-sitter Parsers** | Provide accurate, robust Abstract Syntax Tree (AST) parsing across multiple supported languages to identify classes, functions, files, modules, and their inter-relationships (CALLS, IMPORTS, INHERITS, CONTAINS). | - -## 2. Front-ends and Observability - -**Important:** CodeGraphContext does **not** rely on a built-in heavy GUI application. - -The interaction is designed for low friction and automation: -1. **AI IDE Integrations:** The primary front-end is your AI IDE chat via the Model Context Protocol. You ask a question ("How does user authentication work in the `users` module?"), and the MCP server uses the graph to resolve exactly what files and functions fulfill that context. -2. **CLI (Command Line Interface):** A fully featured standard CLI (`cgc`) to initialize, index, clean, or export graph bundles from the terminal. -3. **Visualization Dashboard:** A standalone React visualization client (`cgc visualize`) that parses the raw graph DB and plots nodes dynamically using `react-force-graph` for exploratory views without needing an external desktop app. -4. **Database Browsers:** Deep advanced exploration of the actual graph connections using direct Cypher queries via the KùzuDB CLI or Neo4j Browser (`localhost:7474`). - -## 3. Data Flow - -1. **Repository Indexing:** - * `cgc index .` invokes the graph builder. - * Files are skipped/filtered based on `.cgcignore` configurations. - * Files are parsed via language-specific Tree-sitter grammars. - * Language parsers emit standardized objects (Classes, Functions, Modules). - * The Graph Builder reconciles imports (edges across different files) and saves them robustly into the active DB. -2. **Real-time querying:** - * An LLM needs context for "Who calls `authenticate_user`?". - * The MCP client forwards the tool call: `analyze_code_relationships(queryType="find_callers", target="authenticate_user")`. - * The MCP server runs strict Cypher validation against the Database Abstraction Layer. - * DB returns specific file paths, snippet boundaries, and exact structural context natively avoiding hallucination. - -## 4. Key Technologies -* **Language:** Python 3.10+ -* **Parsing:** Tree-sitter (for reliable and multi-language AST extraction) -* **Protocol:** Model Context Protocol (MCP) by Anthropic for seamless IDE interactions -* **Databases:** - * **KùzuDB:** Used as the default, lightweight embeddable C++ graph database supporting native Cypher compatibility. - * **Neo4j:** Production-level graph DB compatibility via Cypher standardizing queries. -* **CLI Interface:** `click` and `typer` frameworks. -* **Visualizations:** Next.js / Vite / `react-force-graph` within the `website` directory for rendering the 3D local graph views. diff --git a/docs/site/assets/images/favicon.png b/docs/docs/assets/logo.png similarity index 100% rename from docs/site/assets/images/favicon.png rename to docs/docs/assets/logo.png diff --git a/docs/docs/concepts/architecture.md b/docs/docs/concepts/architecture.md new file mode 100644 index 00000000..772fbdd4 --- /dev/null +++ b/docs/docs/concepts/architecture.md @@ -0,0 +1,109 @@ +# System Architecture + +CodeGraphContext (CGC) is structured as a multi-tier code intelligence pipeline. It acts as the bridge between source code parsers, local/remote graph databases, and client developer tools or AI agents. + +--- + +## High-Level Architectural Layout + +CGC consists of three primary layers: **Ingestion**, **Persistence**, and **Interface**. + +```mermaid +graph TD + subgraph Ingestion Layer + src[Raw Source Files] --> parsers[Tree-sitter / SCIP Parsers] + parsers --> builder[Graph Builder & Entity Resolver] + end + + subgraph Persistence Layer + builder --> db_api[Database Abstraction API] + db_api --> embedded[Embedded: KuzuDB / LadybugDB / FalkorDB Lite] + db_api --> server[Server: Neo4j / FalkorDB Remote] + end + + subgraph Interface Layer + embedded --> cli[CGC CLI Client] + server --> cli + embedded --> mcp[MCP Server Gateway] + server --> mcp + mcp --> agents[AI Assistant Clients: Cursor, Claude, VS Code] + end +``` + +--- + +## 1. The Ingestion Layer + +The Ingestion Layer is responsible for reading raw codebase directories, parsing code tokens, and constructing the structural property graph. + +- **File Discovery & Filtering**: Scans the directory tree. Respects standard `.gitignore` settings and custom `.cgcignore` configurations. +- **Polyglot Parsers**: Utilizes Tree-sitter libraries to generate Concrete Syntax Trees (CSTs) for supported programming languages (Python, Java, JavaScript, TypeScript, Go, C++, etc.). +- **Symbol Linker (SCIP)**: Optionally processes SCIP index data to resolve cross-file references and imported dependency symbols. +- **Reference Resolution**: Resolves target call signatures. If `Function A` in `file_1.py` calls `Function B` in `file_2.py`, this linker creates a directed `CALLS` edge between their respective function nodes. +- **Asynchronous Ingestion Workers**: Processes large codebases concurrently via multi-threaded background workers managed by a internal job controller queue. + +--- + +## 2. The Persistence Layer + +The Persistence Layer abstracts database operations so that the engine can interface with multiple graph database systems using a single unified API. + +- **Database Abstraction API**: A client layer exposing methods to write batch transactions (`write_nodes`, `write_edges`) and query graph relationships via Cypher or native bindings. +- **Embedded Engines**: + - **FalkorDB Lite (Default on Unix)**: Embedded in-memory graph engine when `falkordblite` is installed (Linux/macOS, Python 3.12+). + - **KuzuDB (Cross-platform fallback)**: In-process C++ graph engine used automatically on Windows or when FalkorDB Lite is unavailable. + - **LadybugDB**: SQL-based embedded graph engine designed for concurrent read/write transactions. +- **Networked Server Engines**: + - **FalkorDB Remote**: Remote client linking to FalkorDB instances. + - **Neo4j**: Enterprise-scale storage supporting distributed clustering and the Neo4j web browser console. + +--- + +## 3. The Interface Layer + +The Interface Layer exposes the query engine to developer workflows and automated pipelines. + +- **CLI (`cgc`)**: Compiled Python script offering utilities to index directories, show statistics (`cgc stats`), execute analysis commands (`cgc analyze`), export/import context bundles, and verify backend readiness (`cgc doctor`). +- **FastAPI HTTP Gateway**: Exposes a REST API (`cgc api start`) to query the code graph over standard HTTP and serve the interactive React visualizer. +- **Model Context Protocol (MCP) Server**: Exposes 21 standard JSON-RPC tools for tool-calling agents. + +--- + +## Core Data Flows + +### A. Repository Ingest and Index Pipeline + +```mermaid +sequenceDiagram + participant User as Developer / Watcher + participant CLI as CGC CLI / Watcher + participant Ingest as Ingestion Pipeline + participant DB as Graph Database + + User->>CLI: cgc index [path] + CLI->>Ingest: Discover and Filter Files (.cgcignore) + Ingest->>Ingest: Parse Code Syntax Trees (Tree-sitter) + Ingest->>Ingest: Resolve Call Chains & Inheritances + Ingest->>DB: Open Transaction + Ingest->>DB: Ingest Nodes (Files, Classes, Functions) + Ingest->>DB: Ingest Edges (CONTAINS, CALLS, IMPORTS) + Ingest->>DB: Commit Transaction + DB-->>CLI: Return Ingestion Stats + CLI-->>User: Display Success Summary +``` + +### B. MCP Query Pipeline + +```mermaid +sequenceDiagram + participant LLM as AI Assistant (e.g., Claude) + participant MCP as CGC MCP Server + participant DB as Graph Database + + LLM->>MCP: Call Tool: analyze_code_relationships(caller_chain) + MCP->>MCP: Translate parameters to Cypher Query + MCP->>DB: Execute Cypher Statement + DB-->>MCP: Return Node & Edge Data + MCP->>MCP: Format Result Snippets and Code Locations + MCP-->>LLM: Return Tool Output payload +``` diff --git a/docs/docs/concepts/backends.md b/docs/docs/concepts/backends.md new file mode 100644 index 00000000..1e592299 --- /dev/null +++ b/docs/docs/concepts/backends.md @@ -0,0 +1,162 @@ +# Database Backends + +CodeGraphContext (CGC) implements a pluggable database architecture. A common interface abstracts graph creation, updates, and traversals, allowing you to choose the database engine that best fits your scale, operating system, and visualization needs. + +--- + +## Backend Comparison Matrix + +| Feature / Metric | FalkorDB (Lite, Default) | KuzuDB | LadybugDB | FalkorDB (Remote) | Neo4j | +| :--- | :--- | :--- | :--- | :--- | :--- | +| **Type** | Embedded In-Memory | Embedded C++ | Embedded SQL | Remote Client | Remote Client | +| **Operating System** | Linux / macOS | Cross-Platform | Cross-Platform | Cross-Platform | Cross-Platform | +| **Setup Overhead** | None | None | None | Low (Docker) | Medium (Docker/Aura) | +| **Read Latency** | Extremely Low | Very Low | Low | Low | Medium | +| **Max Capacity** | RAM-Bounded | Large | Medium | Unlimited | Unlimited | +| **Visualization** | CLI / Custom Web UI | CLI / Custom Web UI | CLI / Custom Web UI | Neo4j Client (via Cypher) | Neo4j Browser Console | + +--- + +## 1. KuzuDB + +KuzuDB is an in-process property graph database management system. It requires zero configuration and stores graph data inside a directory on your filesystem. + +- **OLAP Optimized**: Designed for structured graph analysis and multi-hop queries. +- **Cross-Platform**: Natively supports Windows, Linux, and macOS on Python 3.10+. +- **Data Directory**: Graphs are saved inside the local `.codegraphcontext/` directory within the workspace. + +### Version Compatibility + +| Package | Declared bounds (`pyproject.toml`) | Versions | +| :--- | :--- | :--- | +| `kuzu` | Not declared | `0.10.0`, `0.11.0`, `0.11.1`, `0.11.2`, `0.11.3` | + +### Setup +Ensure the driver is installed: +```bash +pip install kuzu +``` +Select KuzuDB as the default backend: +```bash +cgc config db kuzudb +``` + +--- + +## 2. LadybugDB + +LadybugDB is an embedded graph database engine implemented over relational SQL drivers. + +- **Concurreny Safe**: Thread-safe operations suitable for concurrent watcher tasks. +- **Relational Backend**: Uses SQLite/relational queries underneath to simulate property graph operations. + +### Setup +Select LadybugDB as the default backend: +```bash +cgc config db ladybugdb +``` + +--- + +## 3. FalkorDB (Lite & Remote) + +FalkorDB is a low-latency, high-performance graph database. It supports two execution modes. + +### FalkorDB Lite +An embedded, in-memory graph engine that uses local shared memory drivers. +- **Limitation**: Unix-only (Linux and macOS) and requires Python 3.12+. +- **Speed**: Optimal traversal latency due to in-memory index layouts. +- **Content search**: `cgc find content` uses portable substring matching on `source` and `docstring` fields (no Neo4j Lucene index required). + +### FalkorDB Remote +Connects to an external Redis-compatible FalkorDB server instance running in a Docker container or network host. + +### Version Compatibility + +| Package | Declared bounds (`pyproject.toml`) | Versions | +| :--- | :--- | :--- | +| `falkordblite` | `>=0.7, <0.10` | `0.7.0`, `0.8.0`, `0.9.0` | +| `falkordb` | `>=1.0, <1.6` | `1.5.0` | +| `redis` | `>=5, <6` | `5.3.1` | + +### Setup +Install the target drivers: +```bash +# For FalkorDB Lite +pip install falkordblite + +# For FalkorDB Remote +pip install falkordb +``` +Configure FalkorDB: +```bash +# Switch default database +cgc config db falkordb + +# For Remote: configure connections +cgc config set FALKORDB_HOST 127.0.0.1 +cgc config set FALKORDB_PORT 6379 +``` + +--- + +## 4. Neo4j (Enterprise & Shared) + +Neo4j is the enterprise standard for graph database clustering, management, and analysis. + +- **Neo4j Browser**: Connect to `http://localhost:7474` to visualize and interact with your code graph using Neo4j's query visualizer. +- **Scale**: Handles repositories containing millions of lines of code. + +### Version Compatibility + +| Package | Declared bounds (`pyproject.toml`) | Versions | +| :--- | :--- | :--- | +| `neo4j` | `>=5.15.0` | `6.2.0` | + +### Setup +Start a Neo4j server (e.g., using Docker): +```bash +docker run -d --name neo4j-cgc -p 7474:7474 -p 7687:7687 -e NEO4J_AUTH=neo4j/password neo4j:latest +``` +Install the Neo4j client library: +```bash +pip install neo4j +``` +Configure CGC to connect to Neo4j: +```bash +cgc config db neo4j +cgc config set NEO4J_URI bolt://localhost:7687 +cgc config set NEO4J_USERNAME neo4j +cgc config set NEO4J_PASSWORD password +``` + +Or run the interactive wizard: `cgc neo4j setup`. + +--- + +## 5. Nornic DB + +Nornic is a Neo4j-compatible embedded graph driver. Configure it when you want Bolt/Cypher semantics without a standalone Neo4j server. + +```bash +cgc config db nornic +cgc config set NORNIC_URI bolt://localhost:7687 +cgc config set NORNIC_USERNAME nornic +cgc config set NORNIC_PASSWORD +``` + +Connection keys mirror the Neo4j section in [Configuration Reference](../reference/config.md). + +--- + +## Backend Selection Logic + +When executing commands, CGC automatically resolves the active database connection using the following precedence: + +1. **CLI Flag Override**: Explicitly set using `--database` or `-db` (e.g., `cgc index --database neo4j`). +2. **Environment Variable**: Resolves via `CGC_RUNTIME_DB_TYPE` settings. +3. **Global Config File**: Reads the value set via `cgc config db`. +4. **Fallback Auto-Detection**: + - If `FALKORDB_HOST` env is present, connects to FalkorDB Remote. + - On Unix: Tries to initialize FalkorDB Lite -> KuzuDB -> Neo4j. + - On Windows: Tries to initialize KuzuDB -> Neo4j. \ No newline at end of file diff --git a/docs/docs/concepts/graph-model.md b/docs/docs/concepts/graph-model.md new file mode 100644 index 00000000..423864fa --- /dev/null +++ b/docs/docs/concepts/graph-model.md @@ -0,0 +1,108 @@ +# The Code Graph Model + +CodeGraphContext models codebase structures as a directed, attributed **Property Graph**. By mapping files, modules, classes, and functions to distinct nodes, and their interactions to directed edges, the engine provides a semantic representation of your code. + +--- + +## 1. Node Types & Attributes + +Nodes represent physical files or structural syntax declarations. Each node has a set of attribute properties. + +### Structural Code Nodes + +#### `Repository` +The root node representing the indexed codebase workspace. +- `path`: The absolute file path to the repository directory. +- `name`: The directory name of the repository. + +#### `File` +Represents a source code file on disk. +- `path`: The path relative to the repository root. +- `language`: The resolved language parser type (e.g., `python`, `typescript`, `java`). +- `hash`: SHA-256 hash of the file contents for tracking changes. + +#### `Module` +A namespace or package boundary (e.g., a Python module, Java package, Go package). +- `name`: The full qualified import path of the module. + +#### `Class` +Object-oriented class declarations. +- `name`: The class identifier name. +- `path`: The relative file path containing the class. +- `start_line` / `end_line`: The line coordinates in the source file. + +#### `Function` +Methods, functions, or subroutines. +- `name`: The function identifier name. +- `path`: The relative file path containing the definition. +- `signature`: The full function parameters and return type signature (if declared). +- `docstring`: Extracted comments and docstrings. +- `complexity`: Computed cyclomatic complexity score. +- `start_line` / `end_line`: Source file line coordinates. + +--- + +### External Integration & Framework Nodes + +#### `SpringBean` (Java Spring Framework) +Java classes decorated with Spring stereotype annotations (e.g., `@Component`, `@Service`, `@Repository`, `@Controller`). +- `bean_name`: The resolved identifier of the bean. +- `scope`: The scope of the bean lifecycle (singleton, prototype). +- `stereotype`: The annotation label type. + +#### `SpringEndpoint` (Java Spring REST) +HTTP REST controller mappings (e.g., `@GetMapping`, `@PostMapping`). +- `path`: The mapped HTTP endpoint URI pattern. +- `method`: The HTTP method (GET, POST, PUT, DELETE). +- `controller_class`: The class containing the handler definition. + +#### `DbTable` (Datasource Schemas) +Database tables imported from SQL schemas. +- `name`: Table identifier name. +- `schema`: The parent database schema/catalog name. + +#### `DbColumn` (Datasource Schemas) +Columns inside a database table. +- `name`: Column name. +- `data_type`: SQL data type declaration. +- `is_primary_key` / `is_foreign_key`: Boolean constraints. + +#### `RedisKeyPattern` (NoSQL Schemas) +Patterns representing keys in Redis cache databases. +- `pattern`: The key naming pattern (e.g., `user:{id}:profile`). +- `data_type`: Redis data structure (string, hash, list, set). + +--- + +## 2. Directed Relationship Edges + +Edges represent structural nesting, import linkages, or execution calls. + +| Edge Type | Source Node | Target Node | Semantics / Description | +| :--- | :--- | :--- | :--- | +| **`CONTAINS`** | `Repository` / `File` / `Class` | `File` / `Class` / `Function` | Models physical nesting and scope containment (e.g., File contains Function). | +| **`IMPORTS`** | `File` | `Module` / `File` | Models dependency import linkages (e.g., `import os` or `from models import User`). | +| **`CALLS`** | `Function` | `Function` | Models execution paths: the source function invokes the target function. | +| **`INHERITS`** | `Class` | `Class` | Models object inheritance hierarchy (superclass links). | +| **`IMPLEMENTS`** | `Class` | `Class` | Models implementation of interfaces or abstract classes. | +| **`MAPS_TO`** | `SpringEndpoint` | `Function` | Links a REST endpoint URI handler to its target controller method. | +| **`READS`** | `Function` | `DbTable` / `DbColumn` / `RedisKeyPattern` | Identifies that the function queries or fetches data from the datasource node. | +| **`WRITES`** | `Function` | `DbTable` / `DbColumn` / `RedisKeyPattern` | Identifies that the function inserts, updates, or deletes data in the datasource node. | + +--- + +## 3. Polyglot Schema Example + +Below is a conceptual schema illustrating how a Java Spring Controller is parsed, linking REST requests down to database tables: + +```mermaid +graph TD + rep[Repository] -->|CONTAINS| file[UserController.java] + file -->|CONTAINS| cls[UserController Class] + cls -->|CONTAINS| fn[getUserDetails Method] + + endpoint[SpringEndpoint: GET /users/profile] -->|MAPS_TO| fn + fn -->|CALLS| repo_fn[UserRepository.findById Method] + repo_fn -->|READS| table[DbTable: users] + table -->|CONTAINS| col[DbColumn: user_id] +``` diff --git a/docs/docs/concepts/how-it-works.md b/docs/docs/concepts/how-it-works.md new file mode 100644 index 00000000..0304cf09 --- /dev/null +++ b/docs/docs/concepts/how-it-works.md @@ -0,0 +1,81 @@ +# How Ingestion Works + +This guide explains how CodeGraphContext (CGC) parses source files, maps structural relationships, updates indices incrementally, and serves queries. + +--- + +## The Ingest Pipeline Flow + +```mermaid +graph TD + A[Start: cgc index / watch] --> B[File Discovery & Hashing] + B --> C{Has File Hash Changed?} + C -->|No| D[Skip File Ingestion] + C -->|Yes| E[Execute Tree-sitter Parser] + E --> F[Extract AST Entities & Declarations] + F --> G[Parse Imports & Scope References] + G --> H[Resolve Symbols & Direct Call Targets] + H --> I[Open Database Transaction] + I --> J[Commit Graph Nodes & Edges] + J --> K[Update local .codegraphcontext/ state] +``` + +--- + +## 1. Syntax Parsing & AST Extraction + +CGC scans source code using AST parser engines: + +### Tree-sitter Grammar Parsing +By default, CGC uses **Tree-sitter** parsers. Tree-sitter generates concrete syntax trees (CSTs) for files: +- CGC registers grammar definitions for 22 target programming languages. +- Language-specific Tree-sitter query files (e.g., `queries/python/tags.scm`) scan the AST to isolate definitions: functions, method names, class structures, parameters, variables, and decorators. +- It records source coordinates (start line, start column, end line, end column) and docstrings. + +### SCIP Ingestion (Opt-in) +For large or complex codebases, you can feed a pre-built **SCIP (Sourcegraph Code Intelligence Protocol)** index file into CGC: +- SCIP provides highly accurate cross-module definition-to-reference mappings compiled by compiler front-ends. +- CGC merges Tree-sitter structural declarations with SCIP symbol resolution definitions, which improves cross-file reference accuracy. + +--- + +## 2. Cross-File Reference Resolution + +Once declarations are parsed into memory, the engine links dependencies and calls: + +- **Containment Linking**: Generates `CONTAINS` relationships mapping a `File` to its child `Class` nodes, and `Class` nodes to their `Function` methods. +- **Import Resolution**: Resolves imports (e.g., `from app.models import User`) to connect file nodes to module namespaces. +- **Call Targets Linker**: When a function contains an invocation name, the resolver searches the symbol index to locate matching target function nodes. If a match is verified, a directed `CALLS` edge is committed. +- **Inheritance Traversal**: Resolves base class relationships, creating directed `INHERITS` or `IMPLEMENTS` edges between class nodes. + +--- + +## 3. Incremental Watching & Synchronization + +Re-parsing an entire codebase on every change is slow. CGC handles file writes incrementally: + +- **State File Tracking**: CGC stores a database registry tracking every indexed file's path, modification timestamp, and SHA-256 content hash. +- **Filesystem Watcher**: The `cgc watch` command initializes a background watcher using the `watchdog` library. +- **Incremental Updates**: + 1. The watchdog detects a file modification event. + 2. The indexing scheduler recalculates the modified file's SHA-256 hash. + 3. If the hash differs, CGC opens a database write transaction. + 4. It removes the file's old node declarations and relationship edges. + 5. It parses the new file content, resolves its symbols, and commits the updated nodes and edges. + 6. The transaction is committed, ensuring the database remains in sync. + +--- + +## 4. Serving Queries via Cypher + +CGC translates CLI requests and MCP tool invocations into **Cypher Queries** that run against the active database. + +For instance, calling `cgc analyze callers calculate_total` translates into a Cypher query: + +```cypher +MATCH (caller:Function)-[:CALLS]->(callee:Function) +WHERE callee.name = 'calculate_total' +RETURN caller.name, caller.path, caller.start_line +``` + +By querying the database engine via Cypher, CGC traverses relationships in milliseconds, bypassing the need to search raw source code files in real-time. diff --git a/docs/docs/concepts/how_it_works.md b/docs/docs/concepts/how_it_works.md deleted file mode 100644 index 80d3797c..00000000 --- a/docs/docs/concepts/how_it_works.md +++ /dev/null @@ -1,23 +0,0 @@ -# How It Works - -Understanding the pipeline helps you write better queries. - -## 1. Parsing (Tree-Sitter) -We use **Tree-Sitter** to parse your source code into an Abstract Syntax Tree (AST). This allows us to support many languages (Python, JS, Go, Dart, Perl, etc.) with high accuracy. - -## 2. Graph Construction -We walk the AST and generate **Nodes** and **Edges**. - -* **Nodes:** Entities like `Class`, `Function`, `File`, `Module`. -* **Edges:** Relationships like `CALLS`, `IMPORTS`, `INHERITS`. - -## 3. Storage -These nodes and edges are written to the Graph Database (FalkorDB or Neo4j). - -## 4. Querying -When you ask "Who calls X?", we translate that into a Cypher query: - -```cypher -MATCH (caller:Function)-[:CALLS]->(callee:Function {name: 'X'}) -RETURN caller -``` diff --git a/docs/docs/concepts/modes.md b/docs/docs/concepts/modes.md deleted file mode 100644 index 50df26a6..00000000 --- a/docs/docs/concepts/modes.md +++ /dev/null @@ -1,22 +0,0 @@ -# CLI Mode vs. MCP Mode - -CodeGraphContext has a "dual personality". - -## 🛠️ Mode 1: The CLI Tool -**"I want to ask questions."** - -In this mode, YOU are the intelligent agent. You use terminal commands (`cgc analyze`) to explore the codebase. -* **Best for:** Architecture reviews, refactoring planning, exploring new codebases. -* **Interface:** Terminal. - -## 🤖 Mode 2: The MCP Server -**"I want my AI to know the answers."** - -In this mode, the AI (Cursor/Claude) is the agent. It asks the questions. -* **Best for:** Coding assistance, "Chat with Codebase", debugging help. -* **Interface:** Natural Language Chat in your IDE. - ---- - -**Crucially: They share the same database.** -You can `cgc index` a repo in your terminal, and then immediately ask your AI about it. diff --git a/docs/docs/concepts/the_graph.md b/docs/docs/concepts/the_graph.md deleted file mode 100644 index b96f4de0..00000000 --- a/docs/docs/concepts/the_graph.md +++ /dev/null @@ -1,32 +0,0 @@ -# The Graph Model - -What does "Code as a Graph" actually look like? - -## Nodes (The Nouns) - -The graph contains these primary node types: - -* **`File`**: A physical file on disk (e.g., `main.py`). -* **`Function`**: A function definition (e.g., `def process_data()`). -* **`Class`**: A class definition (e.g., `class User`). -* **`Module`**: A logical grouping (e.g., a Python package). -* **`Import`**: Represents an external dependency (e.g., `import requests`). - -## Relationships (The Verbs) - -Edges connect the nodes to describe interaction: - -* `(:Function)-[:CALLS]->(:Function)`: Function A calls Function B. -* `(:Class)-[:INHERITS]->(:Class)`: Class A extends Class B. -* `(:File)-[:CONTAINS]->(:Function)`: Where the code lives. -* `(:File)-[:IMPORTS]->(:Module)`: Dependency usage. - -## Example Query - -Using Cypher, you can query this structure: - -```cypher -// Find all classes that inherit from 'BaseModel' -MATCH (c:Class)-[:INHERITS]->(p:Class {name: 'BaseModel'}) -RETURN c.name -``` diff --git a/docs/docs/contributing.md b/docs/docs/contributing.md index e30cb22d..7e966489 100644 --- a/docs/docs/contributing.md +++ b/docs/docs/contributing.md @@ -1,47 +1,73 @@ # Contributing to CodeGraphContext -We welcome contributions! Please follow these steps: +Thank you for your interest in contributing to CodeGraphContext (CGC). We welcome contributions from the community to improve the performance, language support, and tooling capabilities of the engine. -## General Guidelines +--- -* Ensure your code adheres to the existing style and conventions of the project. -* Write clear, concise, and well-documented code. -* All new features or bug fixes should be accompanied by appropriate tests. -* Keep your pull requests focused on a single feature or bug fix. +## Development Principles -## Setting up Your Development Environment +- **Code Quality**: Adhere to PEP 8 standards for Python codebase. +- **Robust Testing**: Every bug fix, driver implementation, or parser extension must be accompanied by unit or integration tests. +- **Focused Commits**: Keep pull requests focused on a single change set. +- **Maintain Documentation**: Update references and guides if code changes alter command arguments or configurations. -1. Fork the repository. -2. Set up your development environment: `pip install -e ".[dev]"` -3. Create a new branch for your feature or bugfix (e.g., `git checkout -b feature/my-new-feature`). +--- -## Debugging +## Setting Up the Development Workspace -To enable debug mode for detailed logging, locate the `debug_mode` variable in `src/codegraphcontext/tools/graph_builder.py` and set its value to `1`. +1. **Clone the Repository**: + ```bash + git clone https://github.com/CodeGraphContext/CodeGraphContext.git + cd CodeGraphContext + ``` -```python -# src/codegraphcontext/tools/graph_builder.py -debug_mode = 1 +2. **Initialize Virtual Environment**: + Initialize an isolated python environment and install dependencies: + ```bash + python -m venv .venv + source .venv/bin/activate + pip install -e ".[dev]" + ``` + +--- + +## Development Workflows + +### Debug Logging +Enable verbose debug logs during execution by setting the environment variable: +```bash +export CGC_LOG_LEVEL=DEBUG +cgc index ``` -## Running Tests +### Running the Test Suite +The testing pipeline utilizes `pytest`. Ensure all checks pass locally before pushing changes: + +```bash +# Run all unit and integration tests +pytest -Tests are located in the `tests/` directory and are run using `pytest`. +# Test a specific driver module +pytest tests/unit/core/test_database_kuzu_compat.py -1. Navigate to the root of the `CodeGraphContext` directory. -2. Run all tests using the command: `pytest` -3. To run specific tests, you can provide the path to the test file, for example: `pytest tests/test_tools.py` -4. **Skipping Re-indexing:** To speed up test runs, especially during development, you can set the `CGC_SKIP_REINDEX` environment variable to `true`. This will prevent the test suite from re-indexing the sample project if it's already indexed. - ```bash - CGC_SKIP_REINDEX=true pytest - ``` +# Run tests bypassing re-indexing cache +CGC_SKIP_REINDEX=true pytest +``` + +*Note: Integration tests for remote databases like Neo4j require a running local database instance (refer to docker-compose.yml).* -## Submitting Changes +### Formatting & Linting +We enforce formatting and static checks via `ruff`. Run linting checks before committing: + +```bash +ruff check . +ruff format . +``` -1. Write your code and add corresponding tests in the `tests/` directory. -2. Ensure all tests pass and your code lints without errors. -3. Commit your changes with a descriptive commit message. -4. Submit a pull request to the `main` branch. +--- +## Pull Request Guidelines - +1. **Feature Branches**: Branch from `main` using descriptive naming (e.g., `feat/ladybug-concurrency` or `fix/mcp-json-rpc`). +2. **Commit Styling**: Write clear, descriptive commit logs. +3. **Submission**: Open a pull request against the `main` branch. Detail the modification, verify unit test runs, and link to open issue tickets if applicable. diff --git a/docs/docs/contributing_languages.md b/docs/docs/contributing_languages.md index de36cc5e..b8e36959 100644 --- a/docs/docs/contributing_languages.md +++ b/docs/docs/contributing_languages.md @@ -1,174 +1,139 @@ -# Contributing New Language Support to CodeGraphContext - -This document outlines the steps and best practices for adding support for a new programming language to CodeGraphContext. By following this guide, contributors can efficiently integrate new languages and leverage the Neo4j graph for verification. - -## 1. Understanding the Architecture - -CodeGraphContext uses a modular architecture for multi-language support: - -* **Generic `TreeSitterParser` (in `graph_builder.py`):** This acts as a wrapper, dispatching parsing tasks to language-specific implementations. -* **Language-Specific Parser Modules (in `src/codegraphcontext/tools/languages/`):** Each language (e.g., Python, JavaScript) has its own module (e.g., `python.py`, `javascript.py`) containing: - * Tree-sitter queries (`_QUERIES`). - * A `TreeSitterParser` class that encapsulates language-specific parsing logic. - * A `pre_scan_` function for initial symbol mapping. -* **`GraphBuilder` (in `graph_builder.py`):** Manages the overall graph building process, including file discovery, pre-scanning, and dispatching to the correct language parser. - -## 2. Steps to Add a New Language (e.g., TypeScript - `.ts`) - -### Step 2.1: Create the Language Module File - -1. Create a new file: `src/codegraphcontext/tools/languages/typescript.py`. -2. Add the necessary imports: `from pathlib import Path`, `from typing import Any, Dict, Optional, Tuple`, `import logging`, `import ast` (if needed for AST manipulation). -3. Define `TS_QUERIES` (Tree-sitter queries for TypeScript). -4. Create a `TypescriptTreeSitterParser` class. -5. Create a `pre_scan_typescript` function. - -### Step 2.2: Define Tree-sitter Queries (`TS_QUERIES`) - -This is the most critical and often iterative step. You'll need to define queries for: - -* **`functions`**: Function declarations, arrow functions, methods. -* **`classes`**: Class declarations, class expressions. -* **`imports`**: ES6 imports (`import ... from ...`), CommonJS `require()`. -* **`calls`**: Function calls, method calls. -* **`variables`**: Variable declarations (`let`, `const`, `var`). -* **`docstrings`**: (Optional) How documentation comments are identified. -* **`lambda_assignments`**: (Optional, Python-specific) If the language has similar constructs. - -**Tips for Query Writing:** -* **Consult Tree-sitter Grammars:** Find the `node-types.json` or grammar definition for your language (e.g., `tree-sitter-typescript`). -* **Use `tree-sitter parse`:** Use the `tree-sitter parse` command-line tool to inspect the AST of sample code snippets. This is invaluable for identifying correct node types and field names. -* **Start Simple:** Begin with basic queries and gradually add complexity. -* **Test Iteratively:** After each query, test it with sample code. - -### Step 2.3: Implement `TreeSitterParser` Class - -This class (e.g., `TypescriptTreeSitterParser`) will encapsulate the language-specific logic. - -1. **`__init__(self, generic_parser_wrapper)`**: - * Store `generic_parser_wrapper`, `language_name`, `language`, `parser` from the generic wrapper. - * Load `TS_QUERIES` using `self.language.query(query_str)`. -2. **Helper Methods**: - * `_get_node_text(self, node)`: Extracts text from a tree-sitter node. - * `_get_parent_context(self, node, types=...)`: (Language-specific node types for context). - * `_calculate_complexity(self, node)`: (Language-specific complexity nodes). - * `_get_docstring(self, body_node)`: (Language-specific docstring extraction). -3. **`parse(self, path: Path, is_dependency: bool = False) -> Dict`**: - * Reads the file, parses it with `self.parser`. - * Calls its own `_find_*` methods (`_find_functions`, `_find_classes`, etc.). - * Returns a standardized dictionary format (as seen in `python.py` and `javascript.py`). -4. **`_find_*` Methods**: - Implement these for each query type, extracting data from the AST and populating the standardized dictionary. - -### Step 2.4: Implement `pre_scan_` Function - -This function (e.g., `pre_scan_typescript`) will quickly scan files to build an initial `imports_map`. - -1. It takes `files: list[Path]` and `parser_wrapper` (an instance of `TreeSitterParser`). -2. Uses a simplified query (e.g., for `class_declaration` and `function_declaration`) to quickly find definitions. -3. Returns a dictionary mapping symbol names to file paths. - -### Step 2.5: Integrate into `graph_builder.py` - -1. **`GraphBuilder.__init__`**: - * Add `'.ts': TreeSitterParser('typescript')` to `self.parsers`. -2. **`TreeSitterParser.__init__`**: - * Add an `elif self.language_name == 'typescript':` block to initialize `self.language_specific_parser` with `TypescriptTreeSitterParser(self)`. -3. **`GraphBuilder._pre_scan_for_imports`**: - * Add an `elif '.ts' in files_by_lang:` block to import `pre_scan_typescript` and call it. - -## 3. Verification and Debugging using Neo4j - -After implementing support for a new language, it's crucial to verify that the graph is being built correctly. - -### Step 3.1: Prepare a Sample Project - -Create a small sample project for your new language (e.g., `tests/sample_project_typescript/`) with: -* Function declarations. -* Class declarations (including inheritance). -* Various import types (if applicable). -* Function calls. -* Variable declarations. - -### Step 3.2: Index the Sample Project - -1. **Delete existing data (if any):** - ```bash - # Replace with your sample project path - print(default_api.delete_repository(repo_path='/path/to/your/sample_project')) -2. **Index the project:** - ```bash - # Replace with your sample project path - print(default_api.add_code_to_graph(path='/path/to/your/sample_project')) -3. **Monitor job status:** - ```bash - # Use the job_id returned by add_code_to_graph - print(default_api.check_job_status(job_id='')) - -### Step 3.3: Query the Neo4j Graph - -Use Cypher queries to inspect the generated graph. - -* **Check for Files and Language Tags:** - ```cypher - MATCH (f:File) - WHERE f.path STARTS WITH '/path/to/your/sample_project' - RETURN f.name, f.path, f.lang - ``` - *Expected:* All files from your sample project should be listed with the correct `lang` tag. - -* **Check for Functions:** - ```cypher - MATCH (f:File)-[:CONTAINS]->(fn:Function) - WHERE f.path STARTS WITH '/path/to/your/sample_project' - AND fn.lang = '' - RETURN f.name AS FileName, fn.name AS FunctionName, fn.line_number AS Line - ``` - *Expected:* All functions from your sample project should be listed. - -* **Check for Classes:** - ```cypher - MATCH (f:File)-[:CONTAINS]->(c:Class) - WHERE f.path STARTS WITH '/path/to/your/sample_project' - AND c.lang = '' - RETURN f.name AS FileName, c.name AS ClassName, c.line_number AS Line - ``` - *Expected:* All classes from your sample project should be listed. - -* **Check for Imports (Module-level):** - ```cypher - MATCH (f:File)-[:IMPORTS]->(m:Module) - WHERE f.path STARTS WITH '/path/to/your/sample_project' - AND f.lang = '' - RETURN f.name AS FileName, m.name AS ImportedModule, m.full_import_name AS FullImportName - ``` - *Expected:* All module-level imports should be listed. - -* **Check for Function Calls:** - ```cypher - MATCH (caller:Function)-[:CALLS]->(callee:Function) - WHERE caller.path STARTS WITH '/path/to/your/sample_project' - AND caller.lang = '' - RETURN caller.name AS Caller, callee.name AS Callee, caller.path AS CallerFile, callee.path AS CalleeFile - ``` - *Expected:* All function calls should be correctly linked. - -* **Check for Class Inheritance:** - ```cypher - MATCH (child:Class)-[:INHERITS]->(parent:Class) - WHERE child.path STARTS WITH '/path/to/your/sample_project' - AND child.lang = '' - RETURN child.name AS ChildClass, parent.name AS ParentClass, child.path AS ChildFile, parent.path AS ParentFile - ``` - *Expected:* All inheritance relationships should be correctly linked. - -### Step 3.4: Debugging Common Issues - -* **`NameError: Invalid node type ...`**: Your tree-sitter query is using a node type that doesn't exist in the language's grammar. Use `tree-sitter parse` to inspect the AST. -* **Missing Relationships (e.g., `CALLS`, `IMPORTS`)**: - * **Check `_find_*` methods**: Ensure your `_find_*` methods are correctly extracting the necessary data. - * **Check `imports_map`**: Verify that the `pre_scan_` function is correctly populating the `imports_map`. - * **Check `local_imports` map**: Ensure the `local_imports` map (built in `_create_function_calls` and `_create_inheritance_links`) is correctly resolving symbols. -* **Incorrect `lang` tags**: Ensure `self.language_name` is correctly passed and stored. - -By following these steps, contributors can effectively add and verify new language support. +# Adding Language Support + +This guide outlines the steps required to add parsing support for a new programming language to CodeGraphContext. + +--- + +## 1. Architectural Integration + +CGC uses a modular parsing system based on Tree-sitter: + +1. **`TreeSitterParser` (`graph_builder.py`)**: The primary generic wrapper that dispatches files to specific language sub-parsers. +2. **Language Parser Modules (`src/codegraphcontext/tools/languages/`)**: Individual python modules containing: + - Tree-sitter AST tags queries (`_QUERIES`). + - A `TreeSitterParser` class inheriting from the parser interface. + - A `pre_scan_` method for rapid initial symbol caching. +3. **`GraphBuilder`**: Dispatches files to language parsers, resolves imports, and feeds nodes/relationships to the persistence drivers. + +--- + +## 2. Step-by-Step Implementation + +### Step A: Create the Language Parser Module +Create a new file under `src/codegraphcontext/tools/languages/` (e.g., `typescript.py`). + +Add standard parser imports: +```python +from pathlib import Path +from typing import Dict, Any, List +from codegraphcontext.tools.languages.base import BaseParser +``` + +### Step B: Define AST Tag Queries +AST tags are parsed using Tree-sitter query expressions. Define queries to target: +- **`functions`**: Standard functions, methods, arrow assignments. +- **`classes`**: Class and interface boundaries. +- **`imports`**: Syntax specifying external file or module dependencies. +- **`calls`**: Function or method invocations. +- **`variables`**: Variable declarations and assignments. + +*Tip: Use the CLI `tree-sitter parse` tool to inspect a sample source file's Concrete Syntax Tree (CST) and locate the correct node name keys.* + +### Step C: Implement the Parser Class +Inherit from the base parser and implement AST extraction routines: + +```python +class TypescriptTreeSitterParser(BaseParser): + def __init__(self, generic_parser): + super().__init__(generic_parser, "typescript") + self.queries = self.load_queries() + + def parse(self, path: Path, is_dependency: bool = False) -> Dict[str, Any]: + content = path.read_text() + tree = self.parser.parse(bytes(content, "utf8")) + + # Populate and return standardized AST data structures + return { + "functions": self._find_functions(tree, content), + "classes": self._find_classes(tree, content), + "calls": self._find_calls(tree, content), + "imports": self._find_imports(tree, content), + "variables": self._find_variables(tree, content), + } +``` + +### Step D: Implement the Fast Pre-Scan +Define a fast pre-scan routine to map declaration locations before linking call relationships: + +```python +def pre_scan_typescript(files: List[Path], parser_wrapper) -> Dict[str, Path]: + # Returns a dictionary mapping class/function symbol names to file paths. + ... +``` + +### Step E: Register the Parser +Map the file extension to the new parser class in `parser_factory.py`: + +```python +# Map extension inside the registry +SUPPORTED_LANGUAGES = { + ".ts": "typescript", + ".tsx": "typescript", +} +``` + +--- + +## 3. Verification & Diagnostic Queries + +Once the parser is registered, verify graph extraction using sample source files: + +1. **Index a test codebase**: + ```bash + cgc index ./tests/fixtures/sample_ts_project/ --force + ``` +2. **Execute verification queries using Cypher**: + - Verify files are parsed: + ```bash + cgc query "MATCH (f:File) RETURN f.path, f.language" + ``` + - Verify functions are identified: + ```bash + cgc query "MATCH (f:File)-[:CONTAINS]->(fn:Function) RETURN f.path, fn.name" + ``` + - Verify caller links: + ```bash + cgc query "MATCH (caller:Function)-[:CALLS]->(callee:Function) RETURN caller.name, callee.name" + ``` + +### Emacs Lisp smoke check + +Emacs Lisp support uses the `elisp` grammar already distributed by `tree-sitter-language-pack`; no external Emacs process or manual grammar compilation is required for the Tree-sitter path. + +To smoke-test the checked-in two-file fixture against an isolated Kuzu database: + +```bash +tmpdir=$(mktemp -d) +export PYTHONPATH=src +export DEFAULT_DATABASE=kuzudb +export CGC_RUNTIME_DB_TYPE=kuzudb +export CGC_RUNTIME_DB_PATH="$tmpdir/kuzu.db" + +uv run python -m codegraphcontext index tests/fixtures/sample_projects/sample_project_elisp --force + +uv run python -m codegraphcontext query "MATCH (f:File) WHERE f.path ENDS WITH '.el' RETURN f.name AS file ORDER BY file" +uv run python -m codegraphcontext query "MATCH (fn:Function) WHERE fn.lang = 'elisp' RETURN fn.name AS function ORDER BY function" +uv run python -m codegraphcontext query "MATCH (v:Variable) WHERE v.lang = 'elisp' RETURN v.name AS variable ORDER BY variable" +uv run python -m codegraphcontext query "MATCH (f:File)-[:IMPORTS]->(m:Module) RETURN f.name AS file, m.name AS module ORDER BY file, module" +uv run python -m codegraphcontext query "MATCH (caller:Function)-[:CALLS]->(callee:Function) WHERE caller.lang = 'elisp' RETURN caller.name AS caller_name, callee.name AS callee_name ORDER BY caller_name, callee_name" + +rm -rf "$tmpdir" +``` + +Expected results include `foo-core.el` and `foo-ui.el`, function nodes such as `foo-core-greet` and `foo-ui-render`, variable nodes such as `foo-core-count` and `foo-core-loud`, module nodes for `cl-lib`, `foo-core`, and `foo-ui`, and direct call edges including `foo-ui-render -> foo-core-greet` and `foo-core-greet -> foo-core-format`. + +### Emacs Lisp SCIP follow-up + +The initial Emacs Lisp implementation intentionally stays on the Tree-sitter pipeline. There is no standard `scip-elisp` indexer to register in `EXTENSION_TO_SCIP`, and the commonly used `elisp-refs` package is designed as an interactive Emacs reference finder rather than a batch indexer: it searches files recorded in the running Emacs `load-history`, renders results in a special buffer instead of emitting JSON or SCIP data, and exposes useful Lisp-2 function/variable heuristics only through internal APIs. + +A future semantic indexer could reuse those heuristics in a dedicated batch wrapper, but it would still need directory discovery, side-effect-safe loading or buffer creation, line/column conversion from character offsets, structured output, and explicit handling for macro expansion and indirect calls. Until that exists, `.el` files should continue to use Tree-sitter indexing with documented limitations around arbitrary macro semantics and dynamic dispatch. diff --git a/docs/docs/cookbook.md b/docs/docs/cookbook.md deleted file mode 100644 index 6c782fb1..00000000 --- a/docs/docs/cookbook.md +++ /dev/null @@ -1,525 +0,0 @@ -# MCP Tool Cookbook - -This cookbook provides examples of how to use the `mcp` tool to query and understand your Python codebase. The "Tool" indicates which `mcp` tool to use, and the "JSON Arguments" are what the LLM would provide to that tool. - ---- - -## Basic Queries - -### 1. Find a specific function by name -- **Natural Language:** "Where is the function `foo` defined?" -- **Tool:** `find_code` -- **JSON Arguments:** -```json -{ - "query": "foo" -} -``` - -![Query 1](images/1.png) - -### 2. Find all calls to a specific function -- **Natural Language:** "Find all calls to the `helper` function." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_callers", - "target": "helper" -} -``` - -![Query 2](images/2.png) - -### 3. Find what a function calls -- **Natural Language:** "What functions are called inside the `foo` function?" -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_callees", - "target": "foo", - "context": "/teamspace/studios/this_studio/demo/CodeGraphContext/tests/sample_project/module_a.py" -} -``` - -![Query 3](images/3.png) - -### 4. Find all imports of a specific module -- **Natural Language:** "Where is the `math` module imported?" -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_importers", - "target": "math" -} -``` - -![Query 4](images/4.png) - -### 5. Find all methods of a class -- **Natural Language:** "What are the methods of the `A` class?" -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "class_hierarchy", - "target": "A" -} -``` -- **Note:** The response for `class_hierarchy` includes a list of methods. - -![Query 5](images/5.png) - -### 6. Find all classes that inherit from a specific class -- **Natural Language:** "Show me all classes that inherit from `Base`." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "class_hierarchy", - "target": "Base" -} -``` -- **Note:** The response for `class_hierarchy` includes a list of child classes. - -![Query 6](images/6.png) - -### 7. Find all functions with a specific decorator -- **Natural Language:** "Find all functions with the `log_decorator`." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_functions_by_decorator", - "target": "log_decorator" -} -``` - -![Query 7](images/7.png) - -### 8. Find all dataclasses -- **Natural Language:** "Find all dataclasses." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Class) WHERE 'dataclass' IN c.decorators RETURN c.name, c.path" -} -``` - -![Query 8](images/8.png) - ---- - -## Code Analysis & Quality - -### 9. Find the 5 most complex functions -- **Natural Language:** "Find the 5 most complex functions." -- **Tool:** `find_most_complex_functions` -- **JSON Arguments:** -```json -{ - "limit": 5 -} -``` - -![Query 9](images/9.png) - -### 10. Calculate cyclomatic complexity of a function -- **Natural Language:** "What is the cyclomatic complexity of `try_except_finally`?" -- **Tool:** `calculate_cyclomatic_complexity` -- **JSON Arguments:** -```json -{ - "function_name": "try_except_finally" -} -``` - -### 11. Find unused code -- **Natural Language:** "Find unused code, but ignore API endpoints decorated with `@app.route`." -- **Tool:** `find_dead_code` -- **JSON Arguments:** -```json -{ - "exclude_decorated_with": ["@app.route"] -} -``` - -![Query 11](images/11.png) - -### 12. Find the call chain between two functions -- **Natural Language:** "What is the call chain from `wrapper` to `helper`?" -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "call_chain", - "target": "wrapper->helper" -} -``` - -![Query 12](images/12.png) - -### 13. Find all direct and indirect callers of a function -- **Natural Language:** "Show me all functions that eventually call the `helper` function." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_all_callers", - "target": "helper" -} -``` - -![Query 13](images/13.png) - -### 14. Find functions by argument name -- **Natural Language:** "Find all functions that take `self` as an argument." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_functions_by_argument", - "target": "self" -} -``` - -![Query 14](images/14.png) - -### 15. List all python package imports from a directory -- **Natural Language:** "List all python package imports from my project directory." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:File)-[:IMPORTS]->(m:Module) WHERE f.path ENDS WITH '.py' RETURN DISTINCT m.name" -} -``` - ---- - -## Repository Information Queries - -### 16. List all indexed projects -- **Natural Language:** "List all projects I have indexed." -- **Tool:** `list_indexed_repositories` -- **JSON Arguments:** -```json - {} -``` - -![Query 16](images/16.png) - -### 17. Check the status of an indexing job -- **Natural Language:** "What is the status of job `4cb9a60e-c1b1-43a7-9c94-c840771506bc`?" -- **Tool:** `check_job_status` -- **JSON Arguments:** -```json -{ - "job_id": "4cb9a60e-c1b1-43a7-9c94-c840771506bc" -} -``` - -### 18. List all background jobs -- **Natural Language:** "Show me all background jobs." -- **Tool:** `list_jobs` -- **JSON Arguments:** -```json - {} -``` - ---- - -## Advanced Cypher Queries - -These examples use the `execute_cypher_query` tool for more specific and complex questions. - -### 19. Find all function definitions -- **Natural Language:** "Find all function definitions in the codebase." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (n:Function) RETURN n.name, n.path, n.line_number LIMIT 50" -} -``` - -![Query 19](images/19.png) - -### 20. Find all classes -- **Natural Language:** "Show me all the classes." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (n:Class) RETURN n.name, n.path, n.line_number LIMIT 50" -} -``` - -![Query 20](images/20.png) - -### 21. Find all functions in a file -- **Natural Language:** "Find all functions in `module_a.py`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function) WHERE f.path ENDS WITH 'module_a.py' RETURN f.name" -} -``` - -![Query 21](images/21.png) - -### 22. Find all classes in a file -- **Natural Language:** "Find all classes in `advanced_classes.py`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Class) WHERE c.path ENDS WITH 'advanced_classes.py' RETURN c.name" -} -``` - -![Query 22](images/22.png) - -### 23. List all top-level functions and classes in a file -- **Natural Language:** "List all top-level functions and classes in `module_a.py`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:File)-[:CONTAINS]->(n) WHERE f.name = 'module_a.py' AND (n:Function OR n:Class) AND n.context IS NULL RETURN n.name" -} -``` - -![Query 23](images/23.png) - -### 24. Find functions in one module that call a function in another -- **Natural Language:** "Find functions in `module_a.py` that call `helper` in `module_b.py`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (caller:Function)-[:CALLS]->(callee:Function {name: 'helper'}) WHERE caller.path ENDS WITH 'module_a.py' AND callee.path ENDS WITH 'module_b.py' RETURN caller.name" -} -``` - -![Query 24](images/24.png) - -### 25. Find circular file imports -- **Natural Language:** "Are there any circular dependencies between files?" -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f1:File)-[:IMPORTS]->(m2:Module), (f2:File)-[:IMPORTS]->(m1:Module) WHERE f1.name = m1.name + '.py' AND f2.name = m2.name + '.py' RETURN f1.name, f2.name" -} -``` - -### 26. Find all functions with more than 5 arguments -- **Natural Language:** "Find all functions with a large number of arguments." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function) WHERE size(f.args) > 5 RETURN f.name, f.path, size(f.args) as arg_count" -} -``` - -![Query 26](images/26.png) - -### 27. Find all functions in a file that have a docstring -- **Natural Language:** "Find all functions in `module_a.py` that have a docstring." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function) WHERE f.path ENDS WITH 'module_a.py' AND f.docstring IS NOT NULL AND f.docstring <> '' RETURN f.name" -} -``` - -### 28. Find all classes that have a specific method -- **Natural Language:** "Find all classes that have a `greet` method." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Class)-[:CONTAINS]->(m:Function {name: 'greet'}) RETURN c.name, c.path" -} -``` - -![Query 28](images/28.png) - -### 29. Find the depth of inheritance for all classes -- **Natural Language:** "How deep are the inheritance chains for all classes?" -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Class) OPTIONAL MATCH path = (c)-[:INHERITS*]->(parent:Class) RETURN c.name, c.path, length(path) AS depth ORDER BY depth DESC" -} -``` - -![Query 29](images/29.png) - -### 30. Find all functions that have a docstring -- **Natural Language:** "Show me all functions that are documented." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function) WHERE f.docstring IS NOT NULL AND f.docstring <> '' RETURN f.name, f.path LIMIT 50" -} -``` - -![Query 30](images/30.png) - -### 31. Find all decorated methods in a class -- **Natural Language:** "Find all decorated methods in the `Child` class." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Class {name: 'Child'})-[:CONTAINS]->(m:Function) WHERE m.decorators IS NOT NULL AND size(m.decorators) > 0 RETURN m.name" -} -``` - -![Query 31](images/31.png) - -### 32. Find the number of functions in each file -- **Natural Language:** "How many functions are in each file?" -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function) RETURN f.path, count(f) AS function_count ORDER BY function_count DESC" -} -``` - -![Query 32](images/32.png) - -### 33. Find all methods that override a parent method -- **Natural Language:** "Find all methods that are overridden from a parent class." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Class)-[:INHERITS]->(p:Class), (c)-[:CONTAINS]->(m:Function), (p)-[:CONTAINS]->(m_parent:Function) WHERE m.name = m_parent.name RETURN m.name as method, c.name as child_class, p.name as parent_class" -} -``` - -![Query 33](images/33.png) - -### 34. Find all functions that call `super()` -- **Natural Language:** "Find all methods that call their parent's method via `super()`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function)-[r:CALLS]->() WHERE r.full_call_name STARTS WITH 'super(' RETURN f.name, f.path" -} -``` - -![Query 34](images/34.png) - -### 35. Find all calls to a function with a specific argument -- **Natural Language:** "Find all calls to `helper` with the argument `x`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH ()-[r:CALLS]->(f:Function {name: 'helper'}) WHERE 'x' IN r.args RETURN r.full_call_name, r.line_number, r.path" -} -``` - -![Query 35](images/35.png) - -### 36. Find all functions that are not called by any other function -- **Natural Language:** "Find all dead code (functions that are never called)." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function) WHERE NOT (()-[:CALLS]->(f)) AND f.is_dependency = false RETURN f.name, f.path" -} -``` - -![Query 36](images/36.png) - -### 37. Find all functions that are called with a specific argument -- **Natural Language:** "Find all calls to `print` with the argument `'hello'`." -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (c:Call) WHERE c.name = 'print' AND 'hello' IN c.args RETURN c.file, c.lineno" -} -``` - -### 38. Find all direct and indirect callees of a function -- **Natural Language:** "Show me all functions that are eventually called by the `foo` function." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "find_all_callees", - "target": "foo", - "context": "/teamspace/studios/this_studio/demo/CodeGraphContext/tests/sample_project/module_a.py" -} -``` - -![Query 38](images/38.png) - -### 39. Find all functions that are overridden -- **Natural Language:** "Find all functions that are overridden." -- **Tool:** `analyze_code_relationships` -- **JSON Arguments:** -```json -{ - "query_type": "overrides", - "target": "foo" -} -``` - -![Query 39](images/39.png) - -### 40. Find all modules imported by `module_a` -- **Natural Language:** "Find all modules imported by `module_a`." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:File {name: 'module_a.py'})-[:IMPORTS]->(m:Module) RETURN m.name AS imported_module_name" -} -``` - -![Query 40](images/40.png) - -### 41. Find large functions that should be refactored -- **Natural Language:** "Find functions with more than 20 lines of code that might need refactoring." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function)\n WHERE f.end_line - f.line_number > 20\n RETURN f" -} -``` - -![Query 41](images/41.png) - -### 42. Find recursive functions -- **Natural Language:** "Find all functions that call themselves (recursive functions)." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH p=(f:Function)-[:CALLS]->(f2:Function)\n WHERE f.name = f2.name AND f.path = f2.path\n RETURN p" -} -``` -![Query 42](images/42.png) - -### 43. Find most connected functions (hub functions) -- **Natural Language:** "Find the functions that are most central to the codebase (called by many and call many others)." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "MATCH (f:Function)\n OPTIONAL MATCH (f)-[:CALLS]->(callee:Function)\n OPTIONAL MATCH (caller:Function)-[:CALLS]->(f)\n WITH f, count(DISTINCT callee) AS calls_out, count(DISTINCT caller) AS calls_in\n ORDER BY (calls_out + calls_in) DESC\n LIMIT 5\n MATCH p=(f)-[*0..2]-()\n RETURN p" -} -``` -![Query 43](images/43.png) - -## Security & Sensitive Data Analysis - -### 44. Find potential security vulnerabilities (hardcoded secrets) -- **Natural Language:** "Find potential hardcoded passwords, API keys, or secrets in the codebase." -- **Tool:** `execute_cypher_query` -- **JSON Arguments:** -```json -{ - "cypher_query": "WITH [\"password\", \"api_key\", \"apikey\", \"secret_token\", \"token\", \"auth\", \"access_key\", \"private_key\", \"client_secret\", \"sessionid\", \"jwt\"] AS keywords\n MATCH (f:Function)\n WHERE ANY(word IN keywords WHERE toLower(f.source_code) CONTAINS word)\n RETURN f" -} -``` \ No newline at end of file diff --git a/docs/docs/deployment/DEPLOYMENT_CHECKLIST.md b/docs/docs/deployment/DEPLOYMENT_CHECKLIST.md deleted file mode 100644 index c3197ec3..00000000 --- a/docs/docs/deployment/DEPLOYMENT_CHECKLIST.md +++ /dev/null @@ -1,259 +0,0 @@ -# CodeGraphContext Docker Deployment Checklist - -Use this checklist to ensure a successful deployment. - -## 📋 Pre-Deployment - -### Local Testing -- [ ] Docker and Docker Compose installed -- [ ] Run `./docker-quickstart.sh` successfully -- [ ] Test basic cgc commands inside container -- [ ] Verify FalkorDB Lite works (default) -- [ ] Test Neo4j connection (if using) -- [ ] Index a sample project -- [ ] Verify data persists after container restart - -### Code Preparation -- [ ] Latest code committed to Git -- [ ] All tests passing -- [ ] Documentation updated -- [ ] `.cgcignore` configured properly -- [ ] Environment variables documented - -## 🏗️ Infrastructure Setup - -### Choose Hosting Option -- [ ] Selected hosting provider (VM/Container Platform/Kubernetes) -- [ ] Account created and billing configured -- [ ] Region/zone selected (choose closest to users) -- [ ] Resource sizing determined (see DOCKER_SUMMARY.md) - -### For Cloud VM Deployment -- [ ] VM provisioned (Ubuntu 22.04+ recommended) -- [ ] SSH access configured -- [ ] Firewall rules configured - - [ ] Port 22 (SSH) - Your IP only - - [ ] Port 7474 (Neo4j HTTP) - Optional, secure - - [ ] Port 7687 (Neo4j Bolt) - Optional, secure -- [ ] Docker installed on VM -- [ ] Docker Compose installed on VM - -### For Container Platform (GCP/AWS/Azure) -- [ ] Container registry access configured -- [ ] Image pushed to registry -- [ ] Service/task definition created -- [ ] Environment variables configured -- [ ] Persistent storage configured - -### For Kubernetes -- [ ] Cluster created and accessible -- [ ] kubectl configured -- [ ] Namespace created -- [ ] Storage class available -- [ ] Ingress controller configured (if needed) - -## 🔒 Security Configuration - -### Secrets Management -- [ ] Changed default Neo4j password -- [ ] Secrets stored securely (not in git) -- [ ] Environment variables configured -- [ ] `.env` file created (if needed) -- [ ] Credentials documented securely - -### Network Security -- [ ] Firewall rules configured -- [ ] Security groups configured (cloud) -- [ ] VPN configured (if needed) -- [ ] HTTPS/TLS configured (production) -- [ ] Rate limiting configured (if applicable) - -### Access Control -- [ ] SSH key-based authentication only -- [ ] Sudo access restricted -- [ ] Docker socket secured -- [ ] Database authentication enabled -- [ ] Backup access secured - -## 🚀 Deployment - -### Build and Deploy -- [ ] Image built successfully - ```bash - docker build -t codegraphcontext:latest . - ``` -- [ ] Image tagged properly -- [ ] Image pushed to registry (if using) -- [ ] Containers started - ```bash - docker-compose up -d - ``` -- [ ] Health checks passing -- [ ] Logs reviewed for errors - -### Database Setup -- [ ] Database service running -- [ ] Database accessible from app -- [ ] Schema created (automatic) -- [ ] Test query executed successfully -- [ ] Backup configured - -### Application Verification -- [ ] Container running - ```bash - docker-compose ps - ``` -- [ ] cgc command accessible - ```bash - docker-compose exec codegraphcontext cgc --version - ``` -- [ ] Can index test project -- [ ] Can query indexed data -- [ ] MCP server starts (if using) - -## 📊 Monitoring & Maintenance - -### Logging -- [ ] Log aggregation configured -- [ ] Log retention policy set -- [ ] Error alerting configured -- [ ] Log rotation enabled - -### Monitoring -- [ ] Resource monitoring enabled (CPU, RAM, Disk) -- [ ] Container health monitoring -- [ ] Database monitoring -- [ ] Uptime monitoring -- [ ] Alert thresholds configured - -### Backups -- [ ] Backup strategy defined -- [ ] Automated backups configured - ```bash - # Example backup script - docker run --rm -v cgc-data:/data -v $(pwd):/backup \ - alpine tar czf /backup/cgc-backup-$(date +%Y%m%d).tar.gz /data - ``` -- [ ] Backup restoration tested -- [ ] Backup retention policy set -- [ ] Off-site backup configured - -### Updates -- [ ] Update schedule defined -- [ ] Update process documented -- [ ] Rollback procedure documented -- [ ] Automated updates configured (optional) - -## 📈 Performance Optimization - -### Resource Allocation -- [ ] CPU limits set appropriately -- [ ] Memory limits set appropriately -- [ ] Storage size adequate -- [ ] Swap configured (if needed) - -### Database Optimization -- [ ] Indexes created (automatic) -- [ ] Query performance tested -- [ ] Connection pooling configured -- [ ] Cache configured (if applicable) - -### Application Optimization -- [ ] `.cgcignore` optimized -- [ ] Batch processing configured -- [ ] Concurrent processing tuned -- [ ] Resource limits tested under load - -## 🧪 Testing - -### Functional Testing -- [ ] Index operation works -- [ ] Query operations work -- [ ] Watch functionality works -- [ ] MCP server works (if using) -- [ ] All CLI commands tested - -### Performance Testing -- [ ] Indexing large codebase tested -- [ ] Query performance acceptable -- [ ] Memory usage acceptable -- [ ] Disk usage acceptable - -### Disaster Recovery -- [ ] Backup restoration tested -- [ ] Container restart tested -- [ ] Database failover tested (if applicable) -- [ ] Data persistence verified - -## 📝 Documentation - -### Deployment Documentation -- [ ] Deployment steps documented -- [ ] Configuration documented -- [ ] Credentials documented (securely) -- [ ] Architecture diagram created -- [ ] Runbook created - -### Operational Documentation -- [ ] Common operations documented -- [ ] Troubleshooting guide created -- [ ] Escalation procedures defined -- [ ] On-call procedures defined (if applicable) - -## 🎯 Post-Deployment - -### Immediate (First 24 Hours) -- [ ] Monitor logs continuously -- [ ] Check resource usage -- [ ] Verify all functionality -- [ ] Test backup/restore -- [ ] Document any issues - -### First Week -- [ ] Review performance metrics -- [ ] Optimize resource allocation -- [ ] Fine-tune monitoring alerts -- [ ] Update documentation -- [ ] Train team members - -### Ongoing -- [ ] Weekly log review -- [ ] Monthly security updates -- [ ] Quarterly disaster recovery test -- [ ] Regular performance reviews -- [ ] Continuous improvement - -## 🆘 Emergency Contacts - -Document your emergency contacts: - -- [ ] Cloud provider support: _______________ -- [ ] Database administrator: _______________ -- [ ] DevOps team: _______________ -- [ ] Security team: _______________ -- [ ] On-call rotation: _______________ - -## 📞 Support Resources - -- **Documentation:** DOCKER_DEPLOYMENT.md, DOCKER_SUMMARY.md -- **GitHub Issues:** https://github.com/CodeGraphContext/CodeGraphContext/issues -- **Discord Community:** https://discord.gg/dR4QY32uYQ -- **Website:** http://codegraphcontext.vercel.app/ - -## ✅ Final Sign-Off - -- [ ] All checklist items completed -- [ ] Deployment reviewed by team -- [ ] Stakeholders notified -- [ ] Documentation handed off -- [ ] Monitoring confirmed working -- [ ] Backup verified -- [ ] Ready for production! 🚀 - ---- - -**Deployment Date:** _______________ -**Deployed By:** _______________ -**Reviewed By:** _______________ -**Production URL:** _______________ -**Notes:** _______________ diff --git a/docs/docs/deployment/DOCKER_COMPLETE_GUIDE.md b/docs/docs/deployment/DOCKER_COMPLETE_GUIDE.md deleted file mode 100644 index f0632429..00000000 --- a/docs/docs/deployment/DOCKER_COMPLETE_GUIDE.md +++ /dev/null @@ -1,423 +0,0 @@ -# 🎉 CodeGraphContext Docker Packaging - Complete Summary - -## ✅ What Has Been Created - -I've successfully packaged **CodeGraphContext** as a production-ready Docker container with comprehensive deployment options. Here's everything that was created: - -### 📦 Core Docker Files - -1. **`Dockerfile`** - - Multi-stage build for optimized image size - - Python 3.12 base for FalkorDB Lite support - - All dependencies included - - Health checks configured - - Production-ready - -2. **`.dockerignore`** - - Excludes unnecessary files from build - - Reduces image size by ~70% - - Faster builds - -3. **`docker-compose.template.yml`** - - Complete Docker Compose configuration - - CodeGraphContext service with FalkorDB Lite (default) - - Optional Neo4j service for production - - Persistent volumes for data - - Network configuration - - Copy to `docker-compose.yml` to use - -4. **`.env.example`** - - Environment variable template - - Neo4j configuration - - Application settings - - Security best practices - -### 🚀 Deployment Scripts - -5. **`docker-quickstart.sh`** ⭐ - - **Interactive setup wizard** - - Checks Docker installation - - Database selection (FalkorDB/Neo4j) - - Automated build and start - - User-friendly prompts - - **Perfect for first-time users** - -6. **`deploy-production.sh`** ⭐ - - **Full production automation** - - System updates and Docker installation - - Firewall configuration - - Systemd service creation (auto-start on boot) - - Automatic daily backups - - Monitoring tools installation - - Security hardening - - **One-command production deployment** - -### 📚 Documentation - -7. **`DOCKER_README.md`** - Quick Reference - - Fast lookup guide - - Common commands - - Quick start options - - Troubleshooting tips - -8. **`DOCKER_SUMMARY.md`** - Complete Overview - - Comprehensive guide to all Docker files - - Quick start instructions (3 options) - - Hosting options overview - - Resource requirements - - Security checklist - - Monitoring and maintenance - -9. **`DOCKER_DEPLOYMENT.md`** - Detailed Deployment Guide - - Local development setup - - Cloud VM deployment (AWS, GCP, Azure, DigitalOcean) - - Container platforms (Cloud Run, ECS, ACI) - - Kubernetes deployment - - Security best practices - - Performance optimization - - Troubleshooting guide - - **Most comprehensive guide** - -10. **`HOSTING_COMPARISON.md`** - Hosting Options Analysis - - Detailed comparison of 15+ hosting providers - - Cost analysis ($0 to $200+/month) - - Setup difficulty ratings - - Pros and cons for each option - - Decision matrix - - Use-case recommendations - - Cost optimization tips - - **Helps you choose the right hosting** - -11. **`DEPLOYMENT_CHECKLIST.md`** - Step-by-Step Checklist - - Pre-deployment tasks - - Infrastructure setup - - Security configuration - - Deployment steps - - Monitoring setup - - Testing procedures - - Post-deployment tasks - - **Ensures nothing is missed** - -### ☸️ Kubernetes Files (k8s/) - -12. **`k8s/README.md`** - Kubernetes Quick Start -13. **`k8s/deployment.yaml`** - Main application deployment -14. **`k8s/service.yaml`** - Service configuration -15. **`k8s/pvc.yaml`** - Persistent volume claim -16. **`k8s/configmap.yaml`** - Configuration management -17. **`k8s/neo4j-deployment.yaml`** - Optional Neo4j database - -### 🔄 CI/CD - -18. **`.github/workflows/docker-publish.yml`** - - Automated Docker builds on push - - Publishes to GitHub Container Registry - - Multi-platform support (amd64, arm64) - - Automatic versioning - - Build caching for speed - -### 🎨 Visual Assets - -19. **Architecture Diagram** (generated image) - - Visual representation of deployment options - - Shows local, cloud, and Kubernetes deployments - - Data flow and persistence - -## 🎯 Quick Start Guide - -### For Local Development (Easiest) - -```bash -# Option 1: Use the quick-start script (recommended) -./docker-quickstart.sh - -# Option 2: Manual Docker Compose -cp docker-compose.template.yml docker-compose.yml -docker-compose up -d -docker-compose exec codegraphcontext bash - -# Option 3: Docker only -docker build -t codegraphcontext:latest . -docker run -it --rm -v $(pwd):/workspace codegraphcontext:latest bash -``` - -### For Production Deployment - -```bash -# On your cloud VM (Ubuntu/Debian) -# This single command does EVERYTHING: -./deploy-production.sh -``` - -The production script will: -- ✅ Install Docker and Docker Compose -- ✅ Configure firewall -- ✅ Clone repository -- ✅ Build and start containers -- ✅ Set up auto-start on boot -- ✅ Configure daily backups -- ✅ Install monitoring tools -- ✅ Apply security hardening - -## 🌐 Hosting Options Summary - -### Recommended Options by Use Case - -| Use Case | Provider | Monthly Cost | Setup Time | -|----------|----------|--------------|------------| -| **Learning/Testing** | Local Docker | Free | 5 minutes | -| **Hobby Project** | Railway.app | Free | 10 minutes | -| **Small Team** | DigitalOcean | $12-24 | 15 minutes | -| **Production** | DigitalOcean/AWS | $24-50 | 30 minutes | -| **Enterprise** | Kubernetes (GKE/EKS) | $50-200+ | 2-4 hours | -| **Budget Option** | Oracle Cloud | Free Forever | 30 minutes | - -### Top 3 Recommendations - -1. **DigitalOcean Droplet** ($12/month) ⭐ **BEST FOR MOST USERS** - - Simple, predictable pricing - - Easy setup with `deploy-production.sh` - - Good performance - - Excellent documentation - -2. **Railway.app** (Free tier) ⭐ **BEST FOR HOBBY PROJECTS** - - Generous free tier - - Extremely simple deployment - - GitHub integration - - No credit card required - -3. **Oracle Cloud** (Always Free) ⭐ **BEST FREE OPTION** - - Truly free forever - - 4 ARM cores, 24GB RAM - - Production-ready - - No time limits - -## 📊 What You Get - -### Docker Image Features -- ✅ Python 3.12 (FalkorDB Lite support) -- ✅ All dependencies pre-installed -- ✅ Multi-stage build (optimized size) -- ✅ Health checks included -- ✅ Proper volume mounts -- ✅ Environment configuration -- ✅ Production-ready - -### Database Options -- ✅ **FalkorDB Lite** (default) - Built-in, no setup -- ✅ **Neo4j** (optional) - Production-grade, scalable - -### Deployment Options -- ✅ Local Docker -- ✅ Docker Compose -- ✅ Cloud VMs (AWS, GCP, Azure, DigitalOcean) -- ✅ Container Platforms (Cloud Run, ECS, ACI) -- ✅ Kubernetes (GKE, EKS, AKS) -- ✅ PaaS (Railway, Render, Fly.io) - -### Automation & Tools -- ✅ Interactive quick-start script -- ✅ Production deployment automation -- ✅ Automatic backups -- ✅ Auto-start on boot (systemd) -- ✅ Firewall configuration -- ✅ Monitoring tools -- ✅ CI/CD pipeline (GitHub Actions) - -## 🔒 Security Features - -- ✅ Firewall configuration (UFW) -- ✅ Secure password management -- ✅ Environment variable isolation -- ✅ Network segmentation -- ✅ Health checks -- ✅ Automated backups -- ✅ Security update recommendations - -## 📈 Resource Requirements - -### Minimum (Development) -- CPU: 1 core -- RAM: 2GB -- Storage: 10GB -- Cost: Free (local) or $12/month (cloud) - -### Recommended (Production) -- CPU: 2-4 cores -- RAM: 4-8GB -- Storage: 20-50GB SSD -- Cost: $24-50/month - -### Large Scale -- CPU: 4+ cores -- RAM: 8-16GB -- Storage: 50-100GB SSD -- Cost: $50-200+/month - -## 🎓 Documentation Quality - -All documentation includes: -- ✅ Step-by-step instructions -- ✅ Code examples -- ✅ Troubleshooting sections -- ✅ Security best practices -- ✅ Cost estimates -- ✅ Real-world examples -- ✅ Common pitfalls -- ✅ Next steps - -## 🚀 Getting Started (3 Steps) - -### Step 1: Choose Your Path - -**For Learning/Testing:** -```bash -./docker-quickstart.sh -``` - -**For Production:** -```bash -# On your cloud VM -./deploy-production.sh -``` - -### Step 2: Read the Docs - -- **Quick Start:** `DOCKER_README.md` -- **Full Guide:** `DOCKER_DEPLOYMENT.md` -- **Choose Hosting:** `HOSTING_COMPARISON.md` -- **Checklist:** `DEPLOYMENT_CHECKLIST.md` - -### Step 3: Deploy! - -Follow the guide for your chosen hosting option. - -## 📞 Support & Resources - -### Documentation Files (in order of importance) -1. `DOCKER_README.md` - Start here -2. `DOCKER_SUMMARY.md` - Overview -3. `DOCKER_DEPLOYMENT.md` - Detailed guide -4. `HOSTING_COMPARISON.md` - Choose hosting -5. `DEPLOYMENT_CHECKLIST.md` - Deployment steps -6. `k8s/README.md` - Kubernetes guide - -### Community & Help -- **GitHub Issues:** https://github.com/CodeGraphContext/CodeGraphContext/issues -- **Discord:** https://discord.gg/dR4QY32uYQ -- **Website:** http://codegraphcontext.vercel.app/ -- **Docs:** https://CodeGraphContext.github.io/CodeGraphContext/ - -## 🎯 Recommended Learning Path - -1. **Day 1:** Run locally with `./docker-quickstart.sh` -2. **Day 2:** Test on Railway.app (free tier) -3. **Day 3:** Read `HOSTING_COMPARISON.md` and choose provider -4. **Day 4:** Deploy to production with `deploy-production.sh` -5. **Day 5:** Set up monitoring and backups -6. **Ongoing:** Scale as needed - -## 💡 Pro Tips - -1. **Start Small:** Begin with local Docker, then move to cloud -2. **Use Scripts:** The automation scripts save hours of work -3. **Read Comparisons:** `HOSTING_COMPARISON.md` helps you save money -4. **Follow Checklist:** `DEPLOYMENT_CHECKLIST.md` ensures nothing is missed -5. **Test Backups:** Always test restore before you need it -6. **Monitor Costs:** Set up billing alerts on cloud providers -7. **Security First:** Change default passwords immediately -8. **Document Everything:** Keep notes on your specific setup - -## 🎉 What Makes This Special - -### Comprehensive Coverage -- 19 files created -- 15+ hosting options documented -- 3 deployment methods -- Multiple database options -- Full automation scripts - -### Production-Ready -- Security hardening -- Automated backups -- Health checks -- Monitoring setup -- Auto-start on boot -- CI/CD pipeline - -### User-Friendly -- Interactive scripts -- Clear documentation -- Step-by-step guides -- Troubleshooting help -- Multiple skill levels supported - -### Cost-Conscious -- Free options documented -- Cost comparisons included -- Optimization tips -- Budget recommendations - -## 🏆 Success Metrics - -After following this guide, you will have: -- ✅ CodeGraphContext running in Docker -- ✅ Production-ready deployment -- ✅ Automated backups -- ✅ Monitoring in place -- ✅ Security configured -- ✅ Documentation for your team -- ✅ Scalable infrastructure -- ✅ Cost-optimized setup - -## 🎊 You're Ready to Deploy! - -Everything you need is here: -- **Quick start:** `./docker-quickstart.sh` -- **Production:** `./deploy-production.sh` -- **Documentation:** See files listed above -- **Support:** Discord and GitHub Issues - -**Choose your path and start deploying! 🚀** - ---- - -## 📋 File Inventory - -### Scripts (Executable) -- `docker-quickstart.sh` - Interactive local setup -- `deploy-production.sh` - Automated production deployment - -### Configuration -- `Dockerfile` - Container definition -- `docker-compose.template.yml` - Compose configuration -- `.dockerignore` - Build optimization -- `.env.example` - Environment template - -### Documentation -- `DOCKER_README.md` - Quick reference -- `DOCKER_SUMMARY.md` - Complete overview -- `DOCKER_DEPLOYMENT.md` - Detailed guide -- `HOSTING_COMPARISON.md` - Provider comparison -- `DEPLOYMENT_CHECKLIST.md` - Step-by-step checklist -- `THIS_FILE.md` - You are here! - -### Kubernetes -- `k8s/README.md` -- `k8s/deployment.yaml` -- `k8s/service.yaml` -- `k8s/pvc.yaml` -- `k8s/configmap.yaml` -- `k8s/neo4j-deployment.yaml` - -### CI/CD -- `.github/workflows/docker-publish.yml` - -### Visual -- Architecture diagram (generated image) - -**Total: 19 files + 1 image = 20 deliverables** - ---- - -**Happy deploying! If you have any questions, check the documentation or reach out on Discord! 🎉** diff --git a/docs/docs/deployment/DOCKER_DEPLOYMENT.md b/docs/docs/deployment/DOCKER_DEPLOYMENT.md deleted file mode 100644 index 5d69eff1..00000000 --- a/docs/docs/deployment/DOCKER_DEPLOYMENT.md +++ /dev/null @@ -1,355 +0,0 @@ -# Docker Deployment Guide for CodeGraphContext - -This guide explains how to build, run, and deploy CodeGraphContext using Docker. - -## 📦 Quick Start - -### Option 1: Using Docker Compose (Recommended) - -1. **Copy the template file:** - ```bash - cp docker-compose.template.yml docker-compose.yml - ``` - -2. **Start CodeGraphContext with FalkorDB Lite (default):** - ```bash - docker-compose up -d codegraphcontext - ``` - -3. **Access the container:** - ```bash - docker-compose exec codegraphcontext bash - ``` - -4. **Inside the container, use cgc commands:** - ```bash - cgc index . - cgc list - cgc analyze callers my_function - ``` - -### Option 2: Using Docker Directly - -1. **Build the image:** - ```bash - docker build -t codegraphcontext:latest . - ``` - -2. **Run the container:** - ```bash - docker run -it --rm \ - -v $(pwd):/workspace \ - -v cgc-data:/root/.codegraphcontext \ - codegraphcontext:latest bash - ``` - -3. **Use cgc commands inside the container:** - ```bash - cgc index . - cgc help - ``` - -## 🗄️ Database Options - -### FalkorDB Lite (Default - Included in Container) - -FalkorDB Lite is built into the Docker image and requires no additional setup. It's perfect for: -- Development and testing -- Single-user scenarios -- Quick analysis tasks - -**No additional configuration needed!** Just start the container and use `cgc` commands. - -### Neo4j (Optional - For Production) - -If you need a production-grade database, you can use Neo4j: - -1. **Start both CodeGraphContext and Neo4j:** - ```bash - docker-compose --profile neo4j up -d - ``` - -2. **Configure CodeGraphContext to use Neo4j:** - ```bash - docker-compose exec codegraphcontext bash - cgc neo4j setup - ``` - - When prompted, use: - - URI: `bolt://neo4j:7687` - - Username: `neo4j` - - Password: `codegraph123` - -3. **Access Neo4j Browser:** - Open http://localhost:7474 in your browser - -## 📊 Volume Mounts - -The Docker setup uses two types of volumes: - -1. **Workspace Volume** (`./:/workspace`): - - Mounts your current directory into the container - - Allows CodeGraphContext to index your code - - Changes are bidirectional - -2. **Data Volume** (`cgc-data:/root/.codegraphcontext`): - - Persists database files and configuration - - Survives container restarts - - Keeps your indexed data safe - -## 🚀 Common Usage Patterns - -### Index a Project - -```bash -# Start the container -docker-compose up -d codegraphcontext - -# Access the container -docker-compose exec codegraphcontext bash - -# Index the current directory -cgc index . - -# List indexed repositories -cgc list -``` - -### Run as MCP Server - -```bash -# Start the container with MCP server -docker-compose run --rm -p 8080:8080 codegraphcontext cgc mcp start -``` - -### Watch for Changes - -```bash -docker-compose exec codegraphcontext cgc watch /workspace -``` - -### One-off Analysis - -```bash -# Run a single command without entering the container -docker-compose run --rm codegraphcontext cgc analyze complexity --threshold 10 -``` - -## 🌐 Hosting Online - -### Option 1: Cloud VM (AWS, GCP, Azure, DigitalOcean) - -1. **Provision a VM** (Ubuntu 22.04 recommended): - - Minimum: 2 vCPUs, 4GB RAM - - Recommended: 4 vCPUs, 8GB RAM - -2. **Install Docker and Docker Compose:** - ```bash - curl -fsSL https://get.docker.com -o get-docker.sh - sudo sh get-docker.sh - sudo apt-get install docker-compose-plugin - ``` - -3. **Clone your repository:** - ```bash - git clone https://github.com/CodeGraphContext/CodeGraphContext.git - cd CodeGraphContext - ``` - -4. **Start the services:** - ```bash - cp docker-compose.template.yml docker-compose.yml - docker-compose --profile neo4j up -d - ``` - -5. **Configure firewall:** - ```bash - # For Neo4j Browser - sudo ufw allow 7474/tcp - sudo ufw allow 7687/tcp - ``` - -### Option 2: Container Platforms - -#### Google Cloud Run - -```bash -# Build and push to Google Container Registry -gcloud builds submit --tag gcr.io/YOUR_PROJECT_ID/codegraphcontext - -# Deploy to Cloud Run -gcloud run deploy codegraphcontext \ - --image gcr.io/YOUR_PROJECT_ID/codegraphcontext \ - --platform managed \ - --region us-central1 \ - --memory 2Gi -``` - -#### AWS ECS/Fargate - -1. **Push to ECR:** - ```bash - aws ecr create-repository --repository-name codegraphcontext - docker tag codegraphcontext:latest YOUR_ACCOUNT.dkr.ecr.REGION.amazonaws.com/codegraphcontext:latest - docker push YOUR_ACCOUNT.dkr.ecr.REGION.amazonaws.com/codegraphcontext:latest - ``` - -2. **Create ECS Task Definition** and deploy using AWS Console or CLI - -#### Azure Container Instances - -```bash -# Create resource group -az group create --name cgc-rg --location eastus - -# Create container instance -az container create \ - --resource-group cgc-rg \ - --name codegraphcontext \ - --image codegraphcontext:latest \ - --cpu 2 \ - --memory 4 -``` - -### Option 3: Kubernetes (For Scale) - -1. **Create deployment:** - ```yaml - apiVersion: apps/v1 - kind: Deployment - metadata: - name: codegraphcontext - spec: - replicas: 1 - selector: - matchLabels: - app: codegraphcontext - template: - metadata: - labels: - app: codegraphcontext - spec: - containers: - - name: codegraphcontext - image: codegraphcontext:latest - volumeMounts: - - name: cgc-data - mountPath: /root/.codegraphcontext - volumes: - - name: cgc-data - persistentVolumeClaim: - claimName: cgc-pvc - ``` - -2. **Apply configuration:** - ```bash - kubectl apply -f deployment.yaml - ``` - -### Option 4: Self-Hosted with Docker Swarm - -```bash -# Initialize swarm -docker swarm init - -# Deploy stack -docker stack deploy -c docker-compose.yml cgc -``` - -## 🔒 Security Considerations - -1. **Change default Neo4j password:** - Edit `docker-compose.yml` and update `NEO4J_AUTH` - -2. **Use environment variables for secrets:** - ```bash - docker-compose run --rm \ - -e NEO4J_PASSWORD=your_secure_password \ - codegraphcontext bash - ``` - -3. **Enable HTTPS** for production deployments using reverse proxy (nginx, Traefik) - -4. **Restrict network access:** - - Use firewall rules - - Configure security groups - - Use VPN for sensitive codebases - -## 🔧 Troubleshooting - -### Container won't start -```bash -# Check logs -docker-compose logs codegraphcontext - -# Rebuild image -docker-compose build --no-cache codegraphcontext -``` - -### Database connection issues -```bash -# Verify Neo4j is running -docker-compose ps - -# Check Neo4j logs -docker-compose logs neo4j - -# Test connection -docker-compose exec codegraphcontext cgc query "MATCH (n) RETURN count(n)" -``` - -### Out of memory -```bash -# Increase Docker memory limit in Docker Desktop settings -# Or for Neo4j, edit docker-compose.yml: -NEO4J_dbms_memory_heap_max__size=4G -``` - -## 📈 Performance Optimization - -1. **Use volumes for large codebases:** - ```yaml - volumes: - - type: volume - source: cgc-data - target: /root/.codegraphcontext - ``` - -2. **Allocate more resources:** - ```yaml - deploy: - resources: - limits: - cpus: '4' - memory: 8G - ``` - -3. **Use SSD storage** for database volumes in production - -## 🎯 Production Checklist - -- [ ] Change default passwords -- [ ] Configure persistent volumes -- [ ] Set up automated backups -- [ ] Configure monitoring and logging -- [ ] Enable HTTPS/TLS -- [ ] Set resource limits -- [ ] Configure health checks -- [ ] Set up CI/CD pipeline -- [ ] Document deployment process -- [ ] Test disaster recovery - -## 📚 Additional Resources - -- [Docker Documentation](https://docs.docker.com/) -- [Docker Compose Reference](https://docs.docker.com/compose/) -- [Neo4j Docker Guide](https://neo4j.com/developer/docker/) -- [CodeGraphContext Documentation](https://CodeGraphContext.github.io/CodeGraphContext/) - -## 💡 Tips - -- Use `.cgcignore` to exclude files from indexing -- Mount specific directories instead of entire filesystem -- Use Docker networks for service isolation -- Implement log rotation for long-running containers -- Consider using Docker secrets for sensitive data diff --git a/docs/docs/deployment/DOCKER_README.md b/docs/docs/deployment/DOCKER_README.md deleted file mode 100644 index 6437f5f8..00000000 --- a/docs/docs/deployment/DOCKER_README.md +++ /dev/null @@ -1,184 +0,0 @@ -# 🐳 Docker Setup - Quick Reference - -This is a quick reference guide for Docker deployment. For detailed information, see the comprehensive guides. - -## 📚 Documentation Files - -- **`DOCKER_SUMMARY.md`** - Complete overview of Docker packaging -- **`DOCKER_DEPLOYMENT.md`** - Detailed deployment instructions -- **`HOSTING_COMPARISON.md`** - Compare 15+ hosting options -- **`DEPLOYMENT_CHECKLIST.md`** - Step-by-step deployment checklist -- **`k8s/README.md`** - Kubernetes deployment guide - -## 🚀 Quick Start (3 Options) - -### Option 1: Automated Setup (Recommended) -```bash -./docker-quickstart.sh -``` - -### Option 2: Docker Compose -```bash -cp docker-compose.template.yml docker-compose.yml -docker-compose up -d -docker-compose exec codegraphcontext bash -``` - -### Option 3: Docker Only -```bash -docker build -t codegraphcontext:latest . -docker run -it --rm -v $(pwd):/workspace codegraphcontext:latest bash -``` - -## 🌐 Production Deployment - -### Automated Production Setup -```bash -# On your cloud VM (Ubuntu/Debian) -./deploy-production.sh -``` - -### Manual Production Setup -```bash -# Install Docker -curl -fsSL https://get.docker.com -o get-docker.sh -sudo sh get-docker.sh - -# Clone and deploy -git clone https://github.com/CodeGraphContext/CodeGraphContext.git -cd CodeGraphContext -./docker-quickstart.sh -``` - -## 💰 Hosting Recommendations - -| Use Case | Provider | Cost | Setup | -|----------|----------|------|-------| -| **Hobby** | Railway.app | Free | Very Easy | -| **Production** | DigitalOcean | $12-24/mo | Easy | -| **Enterprise** | Kubernetes | $50+/mo | Hard | -| **Budget** | Oracle Cloud | Free | Medium | - -See `HOSTING_COMPARISON.md` for detailed comparison of 15+ options. - -## 📦 What's Included - -✅ Multi-stage Dockerfile (optimized for size) -✅ Docker Compose with FalkorDB Lite & Neo4j -✅ Kubernetes manifests (production-ready) -✅ GitHub Actions (automated builds) -✅ Quick-start script (interactive setup) -✅ Production deployment script (full automation) -✅ Comprehensive documentation -✅ Architecture diagram - -## 🔧 Common Commands - -```bash -# Start services -docker-compose up -d - -# View logs -docker-compose logs -f codegraphcontext - -# Access container -docker-compose exec codegraphcontext bash - -# Stop services -docker-compose down - -# Rebuild -docker-compose build --no-cache - -# With Neo4j -docker-compose --profile neo4j up -d -``` - -## 🗄️ Database Options - -### FalkorDB Lite (Default) -- Built-in, no setup required -- Perfect for development -- Lightweight and fast - -### Neo4j (Production) -```bash -docker-compose --profile neo4j up -d -# Then configure: cgc neo4j setup -# URI: bolt://neo4j:7687 -# User: neo4j -# Pass: codegraph123 -``` - -## 📊 Resource Requirements - -| Environment | CPU | RAM | Storage | -|-------------|-----|-----|---------| -| Development | 1 core | 2GB | 10GB | -| Production | 2-4 cores | 4-8GB | 20-50GB | -| Large Scale | 4+ cores | 8-16GB | 50-100GB | - -## 🔒 Security Checklist - -- [ ] Change default Neo4j password -- [ ] Configure firewall rules -- [ ] Enable HTTPS/TLS -- [ ] Set up automated backups -- [ ] Use environment variables for secrets -- [ ] Regular security updates - -## 🆘 Troubleshooting - -### Container won't start -```bash -docker-compose logs codegraphcontext -docker-compose build --no-cache -``` - -### Database connection issues -```bash -docker-compose ps -docker-compose logs neo4j -``` - -### Out of memory -```bash -# Edit docker-compose.yml -deploy: - resources: - limits: - memory: 4G -``` - -## 📈 Next Steps - -1. **Local Testing:** Run `./docker-quickstart.sh` -2. **Choose Hosting:** Review `HOSTING_COMPARISON.md` -3. **Deploy:** Follow `DOCKER_DEPLOYMENT.md` -4. **Checklist:** Use `DEPLOYMENT_CHECKLIST.md` -5. **Monitor:** Set up logging and backups - -## 🎯 Recommended Path - -``` -Local Development (Docker) - ↓ -Test on Railway.app (Free) - ↓ -Production on DigitalOcean ($12/mo) - ↓ -Scale with Kubernetes (as needed) -``` - -## 📞 Support - -- **Documentation:** See files listed above -- **GitHub Issues:** https://github.com/CodeGraphContext/CodeGraphContext/issues -- **Discord:** https://discord.gg/dR4QY32uYQ -- **Website:** http://codegraphcontext.vercel.app/ - -## 🎉 You're Ready! - -Everything you need is here. Start with `./docker-quickstart.sh` and refer to the detailed guides as needed. - -**Happy deploying! 🚀** diff --git a/docs/docs/deployment/HOSTING_COMPARISON.md b/docs/docs/deployment/HOSTING_COMPARISON.md deleted file mode 100644 index bc0d080e..00000000 --- a/docs/docs/deployment/HOSTING_COMPARISON.md +++ /dev/null @@ -1,518 +0,0 @@ -# 🌐 Hosting Options Comparison Guide - -This guide compares different hosting options for CodeGraphContext to help you choose the best option for your needs. - -## 📊 Quick Comparison Table - -| Provider | Type | Monthly Cost | Setup Difficulty | Best For | Pros | Cons | -|----------|------|--------------|------------------|----------|------|------| -| **DigitalOcean Droplet** | VM | $12-24 | Easy | Small teams, production | Simple, affordable, predictable pricing | Manual scaling | -| **AWS EC2** | VM | $15-50 | Medium | Enterprise, AWS users | Flexible, integrates with AWS | Complex pricing, requires AWS knowledge | -| **Google Cloud VM** | VM | $15-45 | Medium | GCP users | Good free tier, reliable | Complex pricing | -| **Azure VM** | VM | $20-50 | Medium | Microsoft shops | Azure integration | More expensive | -| **Google Cloud Run** | Serverless | $5-20 | Easy | Variable workloads | Pay per use, auto-scaling | Cold starts, stateless | -| **AWS Fargate** | Container | $15-40 | Medium | AWS users, containers | Serverless containers | AWS lock-in | -| **Azure Container Instances** | Container | $30-60 | Easy | Azure users | Simple deployment | More expensive | -| **Railway.app** | PaaS | $0-20 | Very Easy | Hobby projects | Free tier, simple | Limited resources | -| **Render.com** | PaaS | $0-25 | Very Easy | Small projects | Free tier, easy setup | Limited free tier | -| **Fly.io** | PaaS | $0-30 | Easy | Global deployment | Edge deployment, free tier | Learning curve | -| **Oracle Cloud** | VM | $0 (Free tier) | Medium | Budget-conscious | Always free tier | ARM architecture | -| **Self-hosted** | On-premise | Hardware cost | Hard | Full control needed | Complete control | Maintenance burden | -| **Kubernetes (GKE/EKS/AKS)** | Orchestration | $50-200+ | Hard | Large scale, microservices | Scalable, resilient | Complex, expensive | - -## 🎯 Detailed Comparisons - -### 1. Cloud VMs (Recommended for Most Users) - -#### DigitalOcean Droplets ⭐ **RECOMMENDED** -``` -💰 Cost: $12/month (2GB RAM) - $24/month (4GB RAM) -⚙️ Setup: Easy -📈 Scaling: Manual -``` - -**Pros:** -- Simple, predictable pricing -- Excellent documentation -- Easy-to-use control panel -- Good performance -- Managed databases available -- Free bandwidth allowance - -**Cons:** -- Manual scaling required -- Limited to specific regions -- No serverless options - -**Best For:** -- Small to medium teams -- Production deployments -- Budget-conscious projects -- Developers new to cloud - -**Setup Steps:** -```bash -# 1. Create droplet (Ubuntu 22.04, 2-4GB RAM) -# 2. SSH into droplet -ssh root@your-droplet-ip - -# 3. Install Docker -curl -fsSL https://get.docker.com -o get-docker.sh -sudo sh get-docker.sh - -# 4. Clone and deploy -git clone https://github.com/CodeGraphContext/CodeGraphContext.git -cd CodeGraphContext -./docker-quickstart.sh -``` - -#### AWS EC2 -``` -💰 Cost: $15-50/month (t3.medium or larger) -⚙️ Setup: Medium -📈 Scaling: Auto-scaling available -``` - -**Pros:** -- Integrates with AWS ecosystem -- Auto-scaling groups -- Spot instances for cost savings -- Global presence -- Advanced networking - -**Cons:** -- Complex pricing -- Steep learning curve -- Can get expensive -- Requires IAM knowledge - -**Best For:** -- Existing AWS users -- Enterprise deployments -- Need for AWS integrations -- Advanced networking requirements - -#### Google Cloud Compute Engine -``` -💰 Cost: $15-45/month (e2-medium or larger) -⚙️ Setup: Medium -📈 Scaling: Auto-scaling available -``` - -**Pros:** -- $300 free credit for new users -- Good performance -- Preemptible VMs for savings -- Global network -- Easy integration with GCP services - -**Cons:** -- Complex pricing -- Learning curve -- Requires GCP knowledge - -**Best For:** -- GCP users -- Need for Google services integration -- Global deployment - -#### Azure Virtual Machines -``` -💰 Cost: $20-50/month (B2s or larger) -⚙️ Setup: Medium -📈 Scaling: Auto-scaling available -``` - -**Pros:** -- Azure ecosystem integration -- Good for .NET applications -- Enterprise support -- Hybrid cloud options - -**Cons:** -- Generally more expensive -- Complex portal -- Steeper learning curve - -**Best For:** -- Microsoft shops -- Enterprise with Azure commitment -- .NET integration needs - -### 2. Container Platforms - -#### Google Cloud Run ⭐ **BEST SERVERLESS OPTION** -``` -💰 Cost: $5-20/month (pay per use) -⚙️ Setup: Easy -📈 Scaling: Automatic (0 to N) -``` - -**Pros:** -- Pay only for actual usage -- Automatic scaling to zero -- No infrastructure management -- Fast deployments -- Free tier (2M requests/month) - -**Cons:** -- Cold starts (1-3 seconds) -- Stateless (need external DB) -- Request timeout limits -- Not ideal for long-running tasks - -**Best For:** -- Variable workloads -- API endpoints -- Cost optimization -- Minimal maintenance - -**Setup:** -```bash -gcloud builds submit --tag gcr.io/PROJECT_ID/codegraphcontext -gcloud run deploy codegraphcontext \ - --image gcr.io/PROJECT_ID/codegraphcontext \ - --platform managed \ - --memory 2Gi \ - --allow-unauthenticated -``` - -#### AWS Fargate -``` -💰 Cost: $15-40/month -⚙️ Setup: Medium -📈 Scaling: Automatic -``` - -**Pros:** -- Serverless containers -- No EC2 management -- Integrates with ECS/EKS -- Good for microservices - -**Cons:** -- AWS lock-in -- More expensive than EC2 -- Complex setup -- Limited customization - -**Best For:** -- AWS container users -- Microservices architecture -- No server management - -#### Azure Container Instances -``` -💰 Cost: $30-60/month -⚙️ Setup: Easy -📈 Scaling: Manual/Automatic -``` - -**Pros:** -- Simple deployment -- Fast startup -- Azure integration -- Per-second billing - -**Cons:** -- More expensive -- Limited features vs AKS -- Regional availability - -**Best For:** -- Azure users -- Simple container deployments -- Batch processing - -### 3. Platform-as-a-Service (PaaS) - -#### Railway.app ⭐ **BEST FOR HOBBY PROJECTS** -``` -💰 Cost: $0-20/month (Free tier available) -⚙️ Setup: Very Easy -📈 Scaling: Automatic -``` - -**Pros:** -- Generous free tier -- Extremely simple deployment -- GitHub integration -- Automatic HTTPS -- Built-in databases - -**Cons:** -- Limited resources on free tier -- Less control -- Smaller community - -**Best For:** -- Hobby projects -- Prototypes -- Learning/testing -- Small personal projects - -**Setup:** -```bash -# 1. Connect GitHub repo -# 2. Railway auto-detects Dockerfile -# 3. Deploy with one click -``` - -#### Render.com -``` -💰 Cost: $0-25/month (Free tier available) -⚙️ Setup: Very Easy -📈 Scaling: Automatic -``` - -**Pros:** -- Free tier for web services -- Simple deployment -- Automatic SSL -- Good documentation -- Managed databases - -**Cons:** -- Free tier spins down after inactivity -- Limited free tier resources -- Less flexible than VMs - -**Best For:** -- Small projects -- Side projects -- Testing -- Simple deployments - -#### Fly.io -``` -💰 Cost: $0-30/month (Free tier: 3 VMs) -⚙️ Setup: Easy -📈 Scaling: Global edge deployment -``` - -**Pros:** -- Free tier with 3 shared VMs -- Global edge deployment -- Low latency worldwide -- Good for distributed apps -- Persistent volumes - -**Cons:** -- Learning curve for flyctl CLI -- Smaller ecosystem -- Limited documentation - -**Best For:** -- Global applications -- Low latency requirements -- Edge computing -- Distributed systems - -### 4. Free/Budget Options - -#### Oracle Cloud Always Free ⭐ **BEST FREE OPTION** -``` -💰 Cost: $0 (Forever free tier) -⚙️ Setup: Medium -📈 Scaling: Manual -``` - -**Pros:** -- Truly free forever -- 4 ARM cores, 24GB RAM (generous!) -- 200GB storage -- No credit card required after trial -- Production-ready - -**Cons:** -- ARM architecture (may need adjustments) -- Complex interface -- Account approval can be slow -- Limited support - -**Best For:** -- Budget-conscious developers -- Long-term free hosting -- Learning cloud platforms -- Personal projects - -**Free Tier Includes:** -- 2 AMD VMs (1/8 OCPU, 1GB RAM each) -- 4 ARM VMs (1 OCPU, 6GB RAM each) -- 200GB block storage -- 10TB outbound data transfer/month - -### 5. Kubernetes (For Scale) - -#### Google Kubernetes Engine (GKE) -``` -💰 Cost: $50-200+/month -⚙️ Setup: Hard -📈 Scaling: Highly scalable -``` - -**Pros:** -- Production-grade orchestration -- Auto-scaling -- Self-healing -- Rolling updates -- Multi-zone deployment - -**Cons:** -- Complex setup -- Expensive -- Requires K8s expertise -- Overkill for small projects - -**Best For:** -- Large-scale deployments -- Microservices -- High availability needs -- Teams with K8s experience - -#### Amazon EKS -``` -💰 Cost: $75-250+/month ($0.10/hour for control plane) -⚙️ Setup: Hard -📈 Scaling: Highly scalable -``` - -**Best For:** -- AWS ecosystem users -- Enterprise Kubernetes -- Complex microservices - -#### Azure Kubernetes Service (AKS) -``` -💰 Cost: $50-200+/month (free control plane) -⚙️ Setup: Hard -📈 Scaling: Highly scalable -``` - -**Best For:** -- Azure users -- Enterprise deployments -- .NET microservices - -## 🎯 Decision Matrix - -### Choose Based on Your Needs: - -#### "I want the cheapest option" -→ **Oracle Cloud Always Free** or **Railway.app Free Tier** - -#### "I want the easiest setup" -→ **Railway.app** or **Render.com** - -#### "I need production-ready and affordable" -→ **DigitalOcean Droplet** ($12-24/month) - -#### "I'm already using AWS/GCP/Azure" -→ Use your existing cloud provider's VM or container service - -#### "I have variable/unpredictable traffic" -→ **Google Cloud Run** (pay per use) - -#### "I need global low-latency" -→ **Fly.io** (edge deployment) - -#### "I need enterprise features and scale" -→ **Kubernetes** on GKE/EKS/AKS - -#### "I'm just testing/learning" -→ **Local Docker** or **Railway.app Free Tier** - -#### "I need full control and customization" -→ **DigitalOcean** or **AWS EC2** - -## 💡 Recommendations by Use Case - -### Personal/Hobby Project -1. **Railway.app** (free tier) -2. **Oracle Cloud** (always free) -3. **Render.com** (free tier) - -### Small Team/Startup -1. **DigitalOcean** ($12-24/month) -2. **Google Cloud Run** ($5-20/month) -3. **Fly.io** ($0-30/month) - -### Medium Business -1. **DigitalOcean** ($24-48/month) -2. **AWS EC2** with auto-scaling -3. **Google Cloud VM** with managed services - -### Enterprise -1. **Kubernetes** (GKE/EKS/AKS) -2. **AWS** with full ecosystem -3. **Azure** for Microsoft shops - -### High Traffic/Scale -1. **Kubernetes** with auto-scaling -2. **AWS** with load balancers -3. **Multi-region deployment** - -## 📈 Cost Optimization Tips - -1. **Start Small:** Begin with minimal resources, scale up as needed -2. **Use Spot/Preemptible Instances:** Save 60-80% on cloud VMs -3. **Reserved Instances:** Commit for 1-3 years for 30-70% savings -4. **Right-size Resources:** Monitor and adjust CPU/RAM allocation -5. **Use Free Tiers:** Take advantage of always-free and trial credits -6. **Optimize Images:** Smaller Docker images = faster deploys, less storage -7. **Clean Up:** Delete unused resources, snapshots, and volumes -8. **Use CDN:** Offload static content to reduce server load -9. **Implement Caching:** Reduce database queries and processing -10. **Monitor Costs:** Set up billing alerts and budgets - -## 🔄 Migration Path - -**Recommended progression:** - -1. **Start:** Local Docker (free) -2. **Test:** Railway.app or Render.com (free tier) -3. **Production:** DigitalOcean Droplet ($12-24/month) -4. **Scale:** Upgrade droplet or move to Kubernetes -5. **Enterprise:** Multi-region Kubernetes with managed services - -## 📚 Additional Resources - -- **Cost Calculators:** - - [AWS Calculator](https://calculator.aws/) - - [GCP Calculator](https://cloud.google.com/products/calculator) - - [Azure Calculator](https://azure.microsoft.com/pricing/calculator/) - -- **Comparison Sites:** - - [CloudPricing.net](https://cloudpricing.net/) - - [Instances.vantage.sh](https://instances.vantage.sh/) - -- **Documentation:** - - See `DOCKER_DEPLOYMENT.md` for detailed setup guides - - See `DEPLOYMENT_CHECKLIST.md` for deployment steps - -## 🎉 Final Recommendation - -**For most users starting out:** -→ **DigitalOcean Droplet** ($12/month, 2GB RAM) - -**Why?** -- Simple and predictable pricing -- Easy to set up and manage -- Good performance -- Excellent documentation -- Can easily upgrade as you grow -- Great balance of cost, features, and ease of use - -**Quick Start:** -```bash -# 1. Create DigitalOcean account -# 2. Create Ubuntu 22.04 droplet (2GB RAM) -# 3. SSH in and run: -curl -fsSL https://get.docker.com -o get-docker.sh -sudo sh get-docker.sh -git clone https://github.com/CodeGraphContext/CodeGraphContext.git -cd CodeGraphContext -./docker-quickstart.sh -``` - -You'll be up and running in under 10 minutes! 🚀 diff --git a/docs/docs/deployment/README.md b/docs/docs/deployment/README.md deleted file mode 100644 index 639b32c4..00000000 --- a/docs/docs/deployment/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Deployment Documentation - -This directory contains comprehensive guides for deploying CodeGraphContext in various environments. - -## 📚 Available Guides - -### Docker Deployment -- **[DOCKER_README.md](./DOCKER_README.md)** - Quick start guide for Docker deployment -- **[DOCKER_COMPLETE_GUIDE.md](./DOCKER_COMPLETE_GUIDE.md)** - Comprehensive Docker deployment guide with all options -- **[DOCKER_DEPLOYMENT.md](./DOCKER_DEPLOYMENT.md)** - Production Docker deployment strategies - -### Hosting Options -- **[HOSTING_COMPARISON.md](./HOSTING_COMPARISON.md)** - Comparison of different hosting platforms and recommendations - -### Checklists -- **[DEPLOYMENT_CHECKLIST.md](./DEPLOYMENT_CHECKLIST.md)** - Pre-deployment checklist and best practices - -## 🚀 Quick Start - -For most users, we recommend starting with the [DOCKER_README.md](./DOCKER_README.md) for a quick Docker-based deployment. - -For production deployments, review the [DEPLOYMENT_CHECKLIST.md](./DEPLOYMENT_CHECKLIST.md) first, then follow the [DOCKER_COMPLETE_GUIDE.md](./DOCKER_COMPLETE_GUIDE.md). - -## 🔗 Related Resources - -- Main project README: [README.md](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/README.md) -- Docker scripts: [docker-quickstart.sh](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/docker-quickstart.sh), [deploy-production.sh](https://github.com/CodeGraphContext/CodeGraphContext/blob/main/deploy-production.sh) -- Kubernetes configs: [k8s/](https://github.com/CodeGraphContext/CodeGraphContext/tree/main/k8s) - diff --git a/docs/docs/future_work.md b/docs/docs/future_work.md deleted file mode 100644 index e719a38c..00000000 --- a/docs/docs/future_work.md +++ /dev/null @@ -1,7 +0,0 @@ -# Ongoing Concerns and Future Work - -This page outlines some of the current limitations of CodeGraphContext and areas for future development. - -## Semantic Search - -The tool is smart enough to find and analyze a function through millions of code files, but the tool is not yet smart enough to understand that a user searching for “calculate_sum” is also intending to look at the “calculate_addition” function. This level of semantic similarity needs to be researched, developed, tested and eventually implemented by our tool. diff --git a/docs/docs/getting-started/installation.md b/docs/docs/getting-started/installation.md index a8a75d55..53717bfe 100644 --- a/docs/docs/getting-started/installation.md +++ b/docs/docs/getting-started/installation.md @@ -1,84 +1,98 @@ -# Installation +# Ingesting & Installing CodeGraphContext -We have designed the installation to be as automatic as possible. +CodeGraphContext (CGC) is packaged as a standard Python utility. The CLI and server components are installed using Python package managers. -## Step 1: Install the Package +--- + +## 1. CLI Installation -Open your terminal and run: +### Method A: Execution via `uvx` (Recommended) +If you use [uv](https://github.com/astral-sh/uv), you can run the CGC CLI on demand without installing it globally: ```bash -pip install codegraphcontext +uvx codegraphcontext --help ``` -*Tip: We recommend installing this in a virtual environment (venv) or globally via `pipx`.* +### Method B: Isolated Global Installation via `pipx` +To install the CLI in an isolated Python environment and make it globally available: + +```bash +pipx install codegraphcontext +``` + +### Method C: Standard Package Installation via `pip` +To install CGC in your active Python or virtual environment: + +```bash +pip install codegraphcontext +``` --- -## Step 2: Database Setup +## 2. Database Driver Setup -CGC requires a graph database backend. Choose **ONE** path below. +CGC requires Python driver bindings for your selected database backend. On **Unix with Python 3.12+**, FalkorDB Lite is the default when `falkordblite` is installed; on **Windows** (or when FalkorDB Lite is unavailable), CGC falls back to **KuzuDB**. See [Important defaults](../reference/config.md#important-defaults-read-this-first). -=== "Option A: KùzuDB (Recommended & Default)" - - **Platforms:** Linux, macOS, Windows (WSL & Native). +### Installing KuzuDB Drivers +KuzuDB is embedded and runs directly inside the Python process. +```bash +pip install kuzu +``` - **KùzuDB** is an embedded, lightweight graph database written in C++. It is the default for CodeGraphContext because it is extremely fast and requires no external services. - * **Pros:** Requires zero configuration. Runs automatically in-memory or on-disk. No Docker needed. - * **Cons:** No built-in Interactive Browser (unlike Neo4j). Use `cgc visualize` for graph views. +### Installing FalkorDB Drivers (Optional) +If using the FalkorDB backend: +- **Embedded Lite** (Unix and Python 3.12+ only): + ```bash + pip install falkordblite + ``` +- **Remote Server Client**: + ```bash + pip install falkordb + ``` + +### Installing Neo4j Drivers (Optional) +If connecting to a standalone Neo4j instance: +```bash +pip install neo4j +``` - *This is the default out-of-the-box experience. You don't need to do anything else!* +--- -=== "Option B: Neo4j (Enterprise / Visual)" +## 3. Configuring the Default Backend - **Platforms:** Windows, macOS, Linux, Docker. +Set your preferred default database backend in the global configuration: - Neo4j is the industry-standard enterprise graph database. - * **Pros:** Powerful web-based Graph Browser (`localhost:7474`). Handles massive codebases perfectly. - * **Cons:** Heavier resource usage. Requires Docker or a separate service running in the background. +```bash +cgc config db falkordb # FalkorDB Lite (default on Unix when falkordblite is installed) +cgc config db kuzudb # KuzuDB (cross-platform fallback) +cgc config db ladybugdb # LadybugDB +cgc config db falkordb-remote # Remote FalkorDB server +cgc config db neo4j # Neo4j +cgc config db nornic # Nornic (Neo4j-compatible) +``` - 1. **Configure environment for Neo4j:** - Create a `.env` file or export `CGC_GRAPH_BACKEND=neo4j` and `NEO4J_URI=bolt://localhost:7687` along with `NEO4J_USER` and `NEO4J_PASSWORD`. - 2. **Start Neo4j via Docker:** - ```bash - docker run -d --name neo4j -p 7474:7474 -p 7687:7687 -e NEO4J_AUTH=neo4j/password neo4j:latest - ``` +For remote databases (FalkorDB Remote, Neo4j), refer to the database connection properties in the [Configuration Reference](../reference/config.md). --- -## Step 3: Verify Installation +## 4. Validating the Installation -Let's make sure everything is talking to each other. Run the "Doctor" command (coming soon) or check the CLI help: +Verify that the CLI and its database bindings are correctly loaded using the diagnostics tool: ```bash -cgc --help +# Verify the installed CLI version +cgc version + +# Run the system diagnostics check +cgc doctor ``` -You should see all the commands available for CodeGraphContext. +The `doctor` command executes self-tests on the configuration, tests database drivers, and confirms directory permissions. --- -## Step 4: Configure AI Assistant (For MCP Users) - -If you plan to use CodeGraphContext with **Cursor**, **Claude**, **Windsurf**, or **Kiro**, you must configure the MCP server. - -1. **Understand MCP Integration:** - * CodeGraphContext runs as an MCP server. This means it provides "Tools" to your LLM. - * To install it in Claude Desktop, for example, add it to your `claude_desktop_config.json`: - ```json - { - "mcpServers": { - "CodeGraphContext": { - "command": "cgc", - "args": ["mcp"] - } - } - } - ``` - -2. **Using cursor:** - * Go to Cursor Settings > Features > MCP. - * Add a new server: type `command`, name it `CodeGraphContext`, and command `cgc mcp`. - -3. **Refresh your AI Tool:** - * Restart your IDE / Claude Desktop. - * Verify that tools like `analyze_code_relationships` or `find_code` are now available for the AI to use. +## 5. Next Steps + +Once the CLI is verified, continue to index your project workspace. + +**[Proceed to Quickstart →](quickstart.md)** diff --git a/docs/docs/getting-started/mcp-setup.md b/docs/docs/getting-started/mcp-setup.md new file mode 100644 index 00000000..6b458662 --- /dev/null +++ b/docs/docs/getting-started/mcp-setup.md @@ -0,0 +1,117 @@ +# Model Context Protocol Setup + +CodeGraphContext (CGC) implements the Model Context Protocol (MCP). This enables LLM-powered applications and IDE extensions to discover and invoke tools that fetch context directly from your code graph. + +--- + +## 1. Automated Setup (Recommended) + +CGC includes an interactive wizard that detects supported IDEs and applications on your system and configures their MCP client settings automatically. + +Run the wizard from your terminal: + +```bash +cgc mcp setup +``` + +The wizard will locate configuration files for Claude Desktop, Cursor, and other compatible environments, and request permission to add CodeGraphContext as a local tool provider. + +--- + +## 2. Manual Client Configuration + +If you prefer to configure your workspace manually, refer to the client configurations below. + +### Claude Desktop + +To configure Claude Desktop to run the local CGC server, add a configuration entry to the `claude_desktop_config.json` file. + +#### Configuration File Locations: +- **Linux**: `~/.config/Claude/claude_desktop_config.json` +- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` +- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + +#### Configuration Schema: +Add the following key under the `mcpServers` object: + +```json +{ + "mcpServers": { + "codegraphcontext": { + "command": "cgc", + "args": ["mcp", "start"] + } + } +} +``` + +*Note: If you are running CGC in an isolated virtual environment or using `uvx`, adjust the command accordingly (e.g., using `uvx codegraphcontext mcp start`).* + +--- + +### Cursor IDE + +Cursor supports local MCP servers via direct process execution: + +1. Open **Cursor Settings** (Preferences / Settings -> Features -> MCP). +2. Click **+ Add New MCP Server**. +3. Fill in the fields: + - **Name**: `CodeGraphContext` + - **Type**: `command` + - **Command**: `cgc mcp start` +4. Click **Save**. + +--- + +### OpenCode + +OpenCode manages MCP in its own UI. Follow the vendor's guide at [OpenCode MCP servers](https://opencode.ai/docs/ko/mcp-servers/#_top) to register a stdio server. + +Use the following configuration details: +- **Type**: `stdio` +- **Command**: `cgc` +- **Arguments**: `mcp start` + +*Note: Ensure your database credentials and configurations match what `cgc mcp setup` generated (usually in `~/.codegraphcontext/.env`).* + +--- + +### VS Code (via Continue extension) + +If you use VS Code with the [Continue.dev](https://continue.dev) plugin: + +1. Open your Continue configuration file (`~/.continue/config.json`). +2. Add the server details inside the `contextProviders` or `mcp` settings array: + +```json +{ + "mcp": { + "codegraphcontext": { + "command": "cgc", + "args": ["mcp", "start"] + } + } +} +``` + +--- + +## 3. Verifying Tool Connectivity + +After restarting your IDE or Claude Desktop app, verify that the 21 MCP tools are active. You should see commands like: + +- `find_code` (Keyword search across symbols and file contents) +- `analyze_code_relationships` (Lookup callers, callees, and inheritance paths) +- `execute_cypher_query` (Execute direct database Cypher statements) + +You can verify it by prompting the assistant: +> "Analyze the call path between the `process_data` and `db_commit` functions in my current codebase." + +--- + +## 4. Connection Troubleshooting + +If the tools do not load: +1. **Command Resolution**: Verify that the `cgc` command is present in your system's global `PATH`. If you installed via a virtual environment, use the absolute path to the executable (e.g., `/usr/local/bin/cgc` or `/home/user/.local/bin/cgc`). +2. **Process Integrity**: Test starting the server manually in your shell by running `cgc mcp start`. It should listen on standard input/output (stdin/stdout) for JSON-RPC messages and not exit immediately. +3. **Database Selection**: Ensure your default database is configured and has indexed data. Run `cgc doctor` to verify configuration. diff --git a/docs/docs/getting-started/prerequisites.md b/docs/docs/getting-started/prerequisites.md index d24b21ef..b404b0dd 100644 --- a/docs/docs/getting-started/prerequisites.md +++ b/docs/docs/getting-started/prerequisites.md @@ -1,41 +1,49 @@ -# Prerequisites & Context +# System Prerequisites -Before installing CodeGraphContext (CGC), it helps to understand the pieces involved. CGC is a **Client-Server system**, even if you run it all on your laptop. +CodeGraphContext (CGC) is designed as a client-server architecture. To ensure a successful installation, understand the primary roles and requirements of the environment. -## The Three Components +--- + +## Architecture Components -1. **The Engine (This Tool)** - * A Python package responsible for parsing code and talking to the database. -2. **The Database** - * Where the graph lives. CGC needs a place to store "Function A calls Function B". -3. **The Client** - * **CLI:** Your terminal. - * **MCP:** Your AI Editor (Cursor, VS Code, Claude). +1. **The Ingestion Engine**: The core Python package responsible for scanning source directories, running Tree-sitter and SCIP syntax parsers, and linking references. +2. **The Graph Storage Layer**: The database backend containing nodes and edges representing code entities and their interactions. +3. **The Interface Clients**: + - **CLI (`cgc`)**: Terminal interface used for managing indices, running analytical searches, and system diagnostics. + - **MCP Server**: Gateway enabling Model Context Protocol communication for IDEs and AI assistants. --- -## 💻 System Requirements +## Hardware & OS Requirements + +| Resource | Minimum Requirement | Notes | +| :--- | :--- | :--- | +| **Operating System** | Linux, macOS, or Windows | Windows WSL is supported but native installation works via KuzuDB. | +| **Python Version** | Python 3.10 or higher | Python 3.10+ is required for the core package and KuzuDB. | +| **Memory** | 4 GB RAM | Large repositories benefit from 8 GB+ memory during initial scans. | -* **OS:** Linux, macOS, or Windows (WSL recommended). -* **Python:** 3.10 or higher. -* **Memory:** At least 4GB RAM (Graph DBs love RAM). +--- -## 🗄️ Database Options (Context) +## Database Backend Selection -You do **not** need to install a database yet. The installer will help you. But you should know your choice: +CGC supports multiple database engines. You only need to set up the engine that fits your requirements. -| Option | Best For... | Complexity | -| :--- | :--- | :--- | -| **FalkorDB Lite** | **Quick Start / Linux / macOS.** Runs inside Python. No extra setup. | ⭐ | -| **Neo4j** | **Windows / Production / Large Team.** Persistent storage. Requires Docker or Desktop App. | ⭐⭐⭐ | +| Database Backend | Setup Type | Target Platform | Use Case | +| :--- | :--- | :--- | :--- | +| **FalkorDB Lite (Default)** | In-process (Embedded) | Unix (Linux/macOS), Python 3.12+ | Default when `falkordblite` is installed. In-memory, extremely low latency. | +| **KuzuDB** | In-process (Embedded) | Cross-Platform (Linux/macOS/Windows) | Automatic fallback on Windows or when FalkorDB Lite is unavailable. Python 3.10+. | +| **LadybugDB** | In-process (Embedded) | Cross-Platform | Alternative embedded engine; `pip install ladybug`. | +| **FalkorDB Remote** | Networked Server | Cross-Platform Client | Connects to a remote FalkorDB/Redis-compatible server. | +| **Neo4j** | Networked Server | Cross-Platform Client | Enterprise clustering, Neo4j Browser, AuraDB. | +| **Nornic DB** | Embedded / Bolt client | Cross-Platform | Neo4j-compatible driver without a full Neo4j deployment. | --- -## 🤖 AI Assistant (Optional) +## Development Environment Interfaces -If you plan to use CGC with an AI, you need an **MCP-compliant client**. We officially support: +To use CodeGraphContext inside your coding workflow, ensure you have an MCP-compliant workspace interface, such as: -* [Cursor IDE](https://cursor.sh) -* [VS Code](https://code.visualstudio.com/) -* [Claude Desktop App](https://claude.ai/download) -* ...and **any other tool** relevant to Agentic Coding that supports the Model Context Protocol. +- **Cursor IDE** (Native MCP Support) +- **VS Code** (with the Continue or similar MCP extension) +- **Claude Desktop** (Native local process or SSE support) +- **Windsurf IDE / OpenCode** diff --git a/docs/docs/getting-started/quickstart.md b/docs/docs/getting-started/quickstart.md index ecacc2fe..9ca4b0a4 100644 --- a/docs/docs/getting-started/quickstart.md +++ b/docs/docs/getting-started/quickstart.md @@ -1,116 +1,73 @@ -# Quickstart (Detailed Walkthrough) +# Quickstart Guide -CodeGraphContext is a powerful tool to translate your codebase into an exact knowledge graph. Here is an in-depth, step-by-step walkthrough on how to start using it today. +This guide describes how to index a local repository and run your first code structure analysis queries. -## 1. Prepare Your Repository - -Before indexing, navigate into the directory of the codebase you want to analyze. -CodeGraphContext can index very large Mono-repos, but it's crucial to skip compiled assets, virtual environments, or logs. - -Create a `.cgcignore` file in your root folder (it follows the same syntax as `.gitignore`). - -```text -# Example .cgcignore -node_modules/ -dist/ -build/ -.venv/ -__pycache__/ -*.min.js -``` -[📄 Detailed .cgcignore configuration](../reference/cgcignore.md) +--- -## 2. Index the Codebase +## 1. Index the Repository -Run the following command to parse the codebase and store it in your local graph database (KùzuDB by default). +Navigate to the root directory of the codebase you want to index. Run the `index` command to scan the codebase and populate the code graph. ```bash -cgc index . +cd /path/to/your/repository +cgc index ``` -**What is happening during this step?** -1. **File Scanning:** CGC traverses all files in your folder, obeying `.cgcignore`. -2. **Parser Matching:** It matches the file extension (e.g., `.py`, `.ts`, `.go`) to the bundled Tree-sitter parsers. -3. **AST Extraction:** It extracts Classes, Functions, Variables, Imports, and Exports. -4. **Relationship Mapping:** It wires up the edges (e.g., `Function A` CALLS `Function B`, `File X` IMPORTS `Module Y`). -5. **Database Commit:** It saves all these nodes and edges locally. +CGC scans your files, respects your `.gitignore` and `.cgcignore` configurations, runs Tree-sitter parsers to extract code elements, and links relationships. -A progress bar will tell you how many files have been successfully ingested. +--- -## 3. Verify the Knowledge Graph +## 2. Inspect Ingestion Statistics -Once the indexing is complete, you should verify what was recorded in the database. +Verify the indexed code structure by viewing database statistics: -**List all indexed repositories:** -```bash -cgc list -``` -*This will output the repository path, time of last index, and summary statistics (number of nodes/edges).* - -**View Database Statistics:** ```bash cgc stats ``` -*This dumps a global overview of total Functions, Classes, and Files known by the database.* -## 4. Run Analytical Queries (CLI) +The command returns metrics showing: +- Total number of files parsed +- Count of code nodes (functions, classes, modules) +- Count of resolved relationships (Containment, Invocations, Imports, Variables) -You don't need an AI to get value out of CodeGraphContext! You can ask structural questions directly from your terminal. +--- -**Find where a function is called:** -```bash -cgc analyze callers authenticate_user -``` +## 3. Query Symbol Relationships -**Find everything a function calls (dependencies):** -```bash -cgc analyze callees authenticate_user -``` +Query the ingested graph relationships from the terminal. For example, to identify all callers of a function named `handle_request`: -**Find complexity outliers (Refactoring targets):** ```bash -cgc analyze complexity --top 10 +cgc analyze callers handle_request ``` -**Show the Class Inheritance Hierarchy:** +To see what other functions `handle_request` calls: + ```bash -cgc analyze hierarchy DataController +cgc analyze calls handle_request ``` -## 5. Live File Watching - -If you are actively developing and want the graph to stay up to date without manually running `cgc index .` every time you save a file: +To find a call chain/path between two functions (e.g., from `main` to `save_record`): ```bash -cgc watch . +cgc analyze chain main save_record ``` -*This spins up a background process. Whenever a `.py` or `.ts` file changes, CGC instantly re-parses just that file and updates the exact nodes in KùzuDB.* +--- -## 6. Visualizing the Graph +## 4. Enable Real-Time Watchers -Seeing your code structure can reveal architectural flaws, tangled imports, or monolithic classes. +To keep your code graph updated as you write code, start a directory watcher in the background. The watcher monitors file writes and incrementally updates the graph database. ```bash -cgc visualize +cgc watch ``` -This command will start a local React application and print a web URL (`http://localhost...`). -Opening it in your browser gives you a 3D/2D mapped interaction of your code! +To stop a watcher, use `cgc unwatch `. --- -## 7. Next Step: Integrate with AI - -The true power of this graph is handing it to your LLMs. -If you use Cursor, Windsurf, or Claude Desktop: - -1. **Start the MCP Server:** - ```bash - cgc mcp - ``` -2. **Setup:** Add `cgc mcp` as an MCP server command in your IDE/chat configuration. -3. **Prompt the AI:** Simply ask the AI, *"I need to change how authentication tokens are validated. What functions are affected?"* -4. **Magic:** The AI will natively call the graph, precisely pinpointing dependencies without hallucination! +## Next Steps -👉 **[MCP Integration Guide](../guides/mcp_guide.md)** for detailed instructions. +- **[MCP Server Setup](mcp-setup.md)**: Connect CodeGraphContext to your AI assistant. +- **[Indexing Guide](../guides/indexing.md)**: Learn about ignore files and deep scans. +- **[CLI Reference](../reference/cli.md)**: Full command reference manual. diff --git a/docs/docs/guides/bundles.md b/docs/docs/guides/bundles.md index 4cf1c09e..58e8d9b5 100644 --- a/docs/docs/guides/bundles.md +++ b/docs/docs/guides/bundles.md @@ -1,30 +1,464 @@ -# Using On-Demand Bundles +# Portable CGC Bundles & Registries -Don't index everything yourself. Use pre-built graphs for popular libraries. +CodeGraphContext (CGC) supports **Portable Graph Bundles** (`.cgc` files)—serialized snapshots of an indexed codebase. Bundles allow teams to distribute pre-parsed code structures so that other developers or CI runners can load them without re-parsing the original source code. -## What is a Bundle? -A `.cgc` bundle is a snapshot of a graph. It allows you to "import" the knowledge of `flask`, `pandas`, or `react` without parsing it yourself. +## 🎯 What are .cgc Bundles? -## How to use them +`.cgc` (CodeGraphContext Bundle) files are **portable, pre-indexed graph snapshots** that can be distributed and loaded instantly without re-indexing. Think of them as "npm packages for code knowledge graphs." + +### Key Benefits + +- ⚡ **Instant Loading** - Load in seconds instead of minutes/hours of indexing +- 🎯 **Pre-analyzed** - All code relationships already computed +- 🔍 **Query Ready** - Start using with AI assistants immediately +- 📦 **Portable** - Works across any CodeGraphContext installation +- 🌐 **Shareable** - Distribute pre-indexed knowledge easily + +--- + +## 📦 Bundle Structure + +A `.cgc` file is a ZIP archive containing: + +``` +numpy.cgc +├── metadata.json # Repository and indexing metadata +├── schema.json # Graph schema definition +├── nodes.jsonl # All nodes (one JSON per line) +├── edges.jsonl # All relationships (one JSON per line) +├── stats.json # Graph statistics +└── README.md # Human-readable description +``` + +### File Formats + +#### metadata.json +```json +{ + "cgc_version": "0.5.0", + "exported_at": "2026-01-13T22:00:00", + "repo": "numpy/numpy", + "commit": "a1b2c3d4", + "languages": ["python", "c"], + "format_version": "1.0" +} +``` + +#### nodes.jsonl (excerpt) +```jsonl +{"_id": "4:abc123", "_labels": ["Function"], "name": "array", "path": "/numpy/core/array.py", "line_number": 42} +{"_id": "4:def456", "_labels": ["Class"], "name": "ndarray", "path": "/numpy/core/multiarray.py", "line_number": 100} +``` + +#### edges.jsonl (excerpt) +```jsonl +{"from": "4:abc123", "to": "4:def456", "type": "CALLS", "properties": {}} +{"from": "4:xyz789", "to": "4:def456", "type": "INHERITS", "properties": {}} +``` + +--- + +## 🚀 Quick Start + +### Creating Bundles + +```bash +# Export current indexed repository +cgc bundle export my-project.cgc --repo /path/to/project + +# Export all indexed repositories +cgc bundle export all-repos.cgc + +# Export without statistics (faster) +cgc bundle export quick.cgc --repo /path/to/project --no-stats + +# Shortcut +cgc export my-project.cgc --repo /path/to/project +``` + +### Loading Bundles + +```bash +# Load a bundle (adds to existing graph) +cgc bundle import numpy.cgc + +# Load and clear existing data (interactive confirmation) +cgc bundle import numpy.cgc --clear + +# Non-interactive / CI: skip confirmation when clearing +cgc bundle import numpy.cgc --clear --yes +cgc bundle load numpy --clear -y + +# Shortcut +cgc load numpy.cgc +``` + +### Using Pre-indexed Bundles + +```bash +# Download from GitHub Releases +wget https://github.com/CodeGraphContext/CodeGraphContext/releases/download/bundles-20260113/numpy-1.26.4-a1b2c3d.cgc + +# Load it +cgc load numpy-1.26.4-a1b2c3d.cgc + +# Start querying immediately +cgc find name linalg +cgc analyze deps numpy.linalg +``` + +--- + +## 📚 Available Pre-indexed Bundles + +We provide weekly-updated bundles for popular repositories: + +### Tier 1 - Python Core Libraries + +| Repository | Description | Size | Download | +|------------|-------------|------|----------| +| **numpy** | Scientific computing | ~50MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | +| **pandas** | Data analysis | ~80MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | +| **fastapi** | Modern web framework | ~15MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | +| **requests** | HTTP library | ~10MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | +| **flask** | Web framework | ~12MB | [Latest](https://github.com/CodeGraphContext/CodeGraphContext/releases/latest) | + +### Coming Soon + +- **scikit-learn** - Machine learning +- **django** - Web framework +- **pytorch** - Deep learning (subset) +- **kubernetes** - Container orchestration (Go) +- **redis** - In-memory database + +--- + +## 🔧 Advanced Usage + +### Bundle Versioning + +Bundles follow this naming convention: +``` +--.cgc +``` + +Examples: +- `numpy-1.26.4-a1b2c3d.cgc` +- `pandas-2.1.0-xyz789.cgc` +- `fastapi-0.109.0-abc123.cgc` + +### Combining Bundles + +```bash +# Load multiple bundles into the same graph +cgc load numpy.cgc +cgc load pandas.cgc +cgc load scikit-learn.cgc + +# Now query across all three +cgc find name fit --type function +``` + +### Exporting Specific Repositories + +```bash +# Index multiple repos +cgc index /path/to/numpy +cgc index /path/to/pandas + +# Export each separately +cgc export numpy.cgc --repo /path/to/numpy +cgc export pandas.cgc --repo /path/to/pandas + +# Or export everything +cgc export all-my-projects.cgc +``` + +--- + +## 🏗️ Creating Your Own Bundle Registry + +### 1. Index Your Repositories + +```bash +# Clone and index +git clone https://github.com/your-org/your-repo +cd your-repo +cgc index . +``` + +### 2. Export to Bundle + +```bash +# Get commit info +COMMIT=$(git rev-parse --short HEAD) +TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "main") + +# Export with version info +cgc export "your-repo-${TAG}-${COMMIT}.cgc" --repo . +``` + +### 3. Distribute + +#### Option A: GitHub Releases + +```bash +# Create a release +gh release create bundles-$(date +%Y%m%d) \ + your-repo-*.cgc \ + --title "Pre-indexed Bundles - $(date +%Y-%m-%d)" \ + --notes "Pre-indexed code graphs for instant loading" +``` + +#### Option B: Object Storage (S3, R2, GCS) + +```bash +# Upload to S3 +aws s3 cp your-repo-*.cgc s3://your-bucket/bundles/ + +# Make public or use signed URLs +aws s3 presign s3://your-bucket/bundles/your-repo-*.cgc +``` + +#### Option C: Hugging Face Datasets + +```bash +# Install huggingface_hub +pip install huggingface_hub + +# Upload +huggingface-cli upload your-org/cgc-bundles your-repo-*.cgc +``` + +--- + +## 3. The Public Bundle Registry + +CGC hosts a remote repository of pre-indexed graph bundles for popular libraries and frameworks, allowing developers to query third-party code structures. + +### Searching the Registry +Search for public graph packages matching a specific keyword (e.g., `flask`): + +```bash +cgc registry search flask +``` + +### Loading Registry Bundles +To download and load a package from the registry directly into your local database: + +```bash +cgc bundle load flask +``` + +If the package is not found locally, the engine contacts the remote registry API, downloads the matching version, and runs the import process automatically. + +### Registry Command Suite +- **List All Available Registry Packages**: + ```bash + cgc registry list + ``` +- **Request On-Demand Generation**: If a specific library is missing, submit a request for the registry build server to generate a bundle from a public GitHub repository URL: + ```bash + cgc registry request https://github.com/pallets/click --wait + ``` + +--- + +## 🔍 Bundle Inspection + +### View Bundle Contents + +```bash +# Extract and view +unzip -l numpy.cgc + +# View metadata +unzip -p numpy.cgc metadata.json | jq + +# View statistics +unzip -p numpy.cgc stats.json | jq + +# Read README +unzip -p numpy.cgc README.md +``` + +### Validate Bundle + +```bash +# Check bundle integrity +cgc bundle validate numpy.cgc # (future feature) +``` + +--- + +## 🎓 Use Cases + +### 1. AI Assistant Context + +```bash +# AI can now query structure instantly +# "Show me all functions that use numpy.linalg" +``` + +### 2. Code Analysis Pipelines -### 1. Search the Registry ```bash -cgc registry search react +# CI/CD: Load pre-indexed dependencies +cgc load fastapi.cgc +cgc load sqlalchemy.cgc + +# Analyze your code against them +cgc index ./my-api +cgc analyze deps my_api ``` -### 2. Load a Bundle +### 3. Educational Resources + ```bash -cgc load react +# Students can explore famous codebases +cgc load django.cgc +cgc find name authenticate +cgc analyze chain authenticate ``` -*(This downloads ~5MB instead of parsing 50MB of source code).* -### 3. Query it -Now your AI knows about React's internals. -"How does `useEffect` work internally in React?" -> The AI can traverse the imported graph nodes. +### 4. Research & Documentation -## Requesting a Bundle -If a library isn't there, request it: ```bash -cgc registry request https://github.com/fastapi/fastapi +# Researchers can analyze code evolution +cgc load numpy-1.25.0.cgc +cgc load numpy-1.26.0.cgc + +# Compare structures (future feature) +cgc diff numpy-1.25.0.cgc numpy-1.26.0.cgc ``` -Our build servers will index it and make it available within minutes. + +--- + +## 🔐 Security Considerations + +### Bundle Verification + +Always verify bundles from untrusted sources: + +```bash +# Check metadata +unzip -p bundle.cgc metadata.json + +# Verify source repository +# Ensure commit hash matches official repo +``` + +### Sandboxing + +Bundles only contain graph data, not executable code. However: +- Review metadata before loading +- Use `--clear` cautiously (it deletes existing data) +- Keep backups of your graph database + +--- + +## 🛠️ Troubleshooting + +### Bundle Import Fails + +```bash +# Check bundle integrity +unzip -t bundle.cgc + +# Verify format version +unzip -p bundle.cgc metadata.json | jq .cgc_version + +# Try with --clear flag +cgc load bundle.cgc --clear +``` + +### Large Bundle Performance + +```bash +# For very large bundles, increase batch size +# (future configuration option) +export CGC_IMPORT_BATCH_SIZE=5000 +cgc load large-bundle.cgc +``` + +### Version Mismatch + +```bash +# Check your CGC version +cgc --version + +# Update if needed +pip install --upgrade codegraphcontext + +# Check bundle version +unzip -p bundle.cgc metadata.json | jq .cgc_version +``` + +--- + +## 📖 API Reference + +### Python API + +```python +from codegraphcontext.core.cgc_bundle import CGCBundle +from codegraphcontext.core.database import DatabaseManager + +# Initialize +db_manager = DatabaseManager() +bundle = CGCBundle(db_manager) + +# Export +success, message = bundle.export_to_bundle( + output_path=Path("my-bundle.cgc"), + repo_path=Path("/path/to/repo"), + include_stats=True +) + +# Import +success, message = bundle.import_from_bundle( + bundle_path=Path("my-bundle.cgc"), + clear_existing=False +) +``` + +--- + +## 🗺️ Roadmap + +### v0.2.0 - Bundle Registry +- [ ] Central bundle registry +- [ ] `cgc registry search` command +- [ ] Automatic download from registry +- [ ] Bundle versioning and updates + +### v0.3.0 - Advanced Features +- [ ] Delta bundles (incremental updates) +- [ ] Bundle compression options +- [ ] Encrypted bundles +- [ ] Bundle signing and verification + +### v0.4.0 - Collaboration +- [ ] Bundle merging +- [ ] Conflict resolution +- [ ] Multi-repository bundles +- [ ] Bundle diff and comparison + +--- + +## 🤝 Contributing + +### Creating Bundles for Popular Repos + +We welcome contributions of pre-indexed bundles! See [CONTRIBUTING.md](../docs/contributing.md) for guidelines. + +### Improving Bundle Format + +The bundle format is versioned and extensible. Propose improvements via GitHub issues. + +--- + +## 📄 License + +Bundle format specification: MIT License +Pre-indexed bundles: Subject to source repository licenses diff --git a/docs/docs/guides/ci_cd.md b/docs/docs/guides/ci_cd.md deleted file mode 100644 index 050654f3..00000000 --- a/docs/docs/guides/ci_cd.md +++ /dev/null @@ -1,33 +0,0 @@ -# CI/CD & Advanced Usage - -Integrate CodeGraphContext into your automation pipelines. - -## CI/CD Pipeline Integration - -You can use CGC to block PRs that introduce "Dead Code" or excessive complexity. - -**Example GitHub Action:** - -```yaml -name: Code Quality -on: [pull_request] -jobs: - analyze: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install CGC - run: pip install codegraphcontext - - name: Index Code - run: cgc index . - - name: Check Complexity - # Fail if any function is complexity > 20 - run: cgc analyze complexity --threshold 20 --fail-on-found -``` - -## Large Scale Indexing - -For repos with > 100,000 LOC: -1. **Use Neo4j:** FalkorDB may run out of RAM. -2. **Increase Memory:** `NEO4J_dbms_memory_heap_max_size=4G`. -3. **Exclude Tests:** Add `tests/` to `.cgcignore`. diff --git a/docs/docs/guides/contexts.md b/docs/docs/guides/contexts.md index 93144dd1..2dc8409c 100644 --- a/docs/docs/guides/contexts.md +++ b/docs/docs/guides/contexts.md @@ -1,313 +1,156 @@ -# Contexts: Managing Multiple Code Graphs +# Configuration Contexts & Workspaces -CodeGraphContext (CGC) uses a **context system** to decide _where_ your code graph lives on disk. This lets you keep separate projects in separate databases, share one big database across everything, or let each repo manage its own graph — all without changing how you use `cgc`. - -This guide walks through every mode from scratch with copy-pasteable examples. +CodeGraphContext (CGC) uses a context resolution system to determine where graph database files are stored and resolved. This allows developers to isolate codebases, use named workspaces, or share a single global database. --- -## Quick Orientation +## Workspace Directory Structure -``` -~/.codegraphcontext/ <-- global config home - config.yaml <-- mode + named context registry - .env <-- DB credentials, tuning knobs +Below is the standard directory structure under global and local scopes: + +```text +~/.codegraphcontext/ <-- Global configuration directory + config.yaml <-- Active context mode and registry + .env <-- Database credentials and tuning configurations global/ - .cgcignore <-- default ignore patterns + .cgcignore <-- Global ignore patterns db/ - falkordb/ <-- global-mode database + falkordb/ <-- Global-mode FalkorDB Lite storage (default on Unix) + kuzudb/ <-- Global-mode KuzuDB storage directory contexts/ - ProjectAB/ + ProjectA/ db/ - falkordb/ <-- named-context database - .cgcignore <-- per-context ignore patterns + kuzudb/ <-- Named-context KuzuDB storage directory + .cgcignore <-- Context-specific ignore patterns ``` -When you run `cgc index .` (or any other command), CGC resolves _which database_ to talk to by checking, in order: +--- + +## Context Resolution Precedence -1. **`--context ` flag** — always wins if provided. The name is any string you choose (e.g. `MyProject`, `client-acme`, `backend-v2`). It doesn't need to be pre-created; indexing with `--context` auto-creates the context for you. -2. **Local `.codegraphcontext/` folder** — if one exists in the current working directory (and it isn't the global `~/.codegraphcontext`), per-repo mode kicks in. -3. **`config.yaml` mode** — the system-wide mode (`global`, `per-repo`, or `named`) plus the default context name. -4. **Fallback** — global mode, default database backend. +When executing a CLI command (e.g., `cgc index`) or starting an MCP session, CGC resolves the target database location in this priority order: + +1. **Context Override Flag**: If `--context ` or `-c ` is provided, CGC routes all writes and queries to the specified named context. +2. **Local Repository Scope**: If the current directory contains a `.codegraphcontext/` folder, CGC operates in per-repo mode. +3. **Global Config Setting**: CGC reads the active mode (`global`, `per-repo`, or `named`) and default context name specified in `~/.codegraphcontext/config.yaml`. +4. **Default Fallback**: On Linux/macOS with Python 3.12+, connects to FalkorDB Lite at `~/.codegraphcontext/global/db/falkordb/`; otherwise KuzuDB at `~/.codegraphcontext/global/db/kuzudb/`. --- -## The Three Modes +## Context Modes -### 1. Global (default) +### 1. Global Mode (Default) -Every project shares a single database under `~/.codegraphcontext/global/db//`. +In Global Mode, all indexed repositories populate a single shared database. ```bash -# Check your current mode +# Verify active mode settings cgc context list -# Explicitly set global mode (this is the factory default) +# Set mode to global cgc context mode global ``` -Index anything and it all lands in the same graph: +When indexing multiple repositories, their nodes are ingested into the same graph structure, which enables cross-project relationship tracing: ```bash -cd ~/projects/backend +cd ~/projects/service-api cgc index . -cd ~/projects/frontend +cd ~/projects/service-gateway cgc index . -# Both repos are visible in one graph +# List all ingested repositories cgc list ``` -**Best for:** Solo developers working on a handful of related repos who want cross-project call tracing. - --- -### 2. Per-Repo +### 2. Per-Repo Mode -Each repository gets its own `.codegraphcontext/` folder (like `.git/`) with a private database inside. +In Per-Repo Mode, each repository maintains its own local `.codegraphcontext/` directory (similar to how Git uses `.git/`). ```bash cgc context mode per-repo ``` -Now when you index: - -```bash -cd ~/projects/backend -cgc index . -# Creates ~/projects/backend/.codegraphcontext/db/falkordb/ -``` +When indexing inside a project, a local database folder is created within the repository root: ```bash -cd ~/projects/frontend +cd ~/projects/service-api cgc index . -# Creates ~/projects/frontend/.codegraphcontext/db/falkordb/ +# Creates: ~/projects/service-api/.codegraphcontext/db/kuzudb/ ``` -The two graphs are completely isolated. `cgc list` inside `backend/` only shows the backend repo. +Graphs are completely isolated, and commands run within a repository only inspect the local database. -CGC only checks the current working directory for a `.codegraphcontext/` folder — it does not walk up parent directories. This means you always get the context of the directory you're standing in, with no surprises from a parent project. - -**Best for:** Large teams or monorepos where you want zero cross-project interference and the graph config checked into version control. - -> **Tip:** Add `.codegraphcontext/` to your `.gitignore` if you don't want the DB committed. Keep it out of `.gitignore` if you want teammates to share the same config. +When you first index in per-repo mode, CGC auto-creates `.codegraphcontext/` and seeds a local `config.yaml` from your global `DEFAULT_DATABASE`. Project-local `.codegraphcontext/.env` and `.env` files are loaded **only in this mode** (unless you set `CGC_LOAD_PROJECT_ENV=1`). In global or named mode, global `~/.codegraphcontext/.env` wins so cloned repos cannot override your credentials. --- -### 3. Named Contexts +### 3. Named Context Mode -Named contexts are like Git branches for your databases. You create a logical workspace, give it a name, and point one or more repos at it. +Named contexts act as logical workspaces. You can assign a specific name (e.g., `ClientA`, `StagingGraph`) and associate multiple codebases with it. ```bash -# Switch to named mode +# Switch to named context mode cgc context mode named -# Create a context (optional — indexing auto-creates it) -cgc context create MyProject - -# Index a repo into that context -cgc index ~/projects/api --context MyProject -cgc index ~/projects/web --context MyProject +# Create a named context +cgc context create ProjectA -# Both repos share the "MyProject" graph -cgc list --context MyProject +# Index codebases into the named context +cgc index ~/projects/api --context ProjectA +cgc index ~/projects/web --context ProjectA ``` -If the context doesn't exist when you pass `--context`, CGC creates it automatically — you don't need a separate `create` step. - -Set a default so you can stop typing `--context` every time: +Setting a default context name eliminates the need to pass the `--context` flag: ```bash -cgc context default MyProject - -# Now bare commands use MyProject -cgc list # same as: cgc list --context MyProject -cgc stats # same as: cgc stats --context MyProject -``` - -**Best for:** Consultants juggling client projects, teams with shared infra repos, or anyone who wants explicit workspace separation without scattering config folders inside repos. - ---- - -## Switching Modes - -Two equivalent ways: +cgc context default ProjectA -```bash -# Via the context subcommand -cgc context mode named - -# Via config set (alias) -cgc config set mode named +# Future commands use the default named context +cgc list +cgc stats ``` -Valid values: `global`, `per-repo`, `named`. - -Switching mode does **not** delete any data. Your old databases stay on disk; CGC just changes which one it connects to. - --- -## Working With Named Contexts - -### Create +## Managing Named Contexts via CLI +### Create a Named Context +Create a context and optionally specify its target database driver and storage path: ```bash -cgc context create mobile-app -cgc context create mobile-app --database kuzudb # use KùzuDB instead -cgc context create mobile-app --db-path /mnt/fast/cgc # custom location +cgc context create mobile-app --database kuzudb # Or use shorthand aliases: --db, -db, -d +cgc context create mobile-app --db-path /mnt/fast/cgc ``` -### List - +### List Contexts +Displays active modes, registered contexts, database backend configurations, and associated repository directories: ```bash cgc context list ``` -Output shows the current mode, default context (marked with `*`), all registered contexts with their database backend and linked repos. - -### Set Default - -```bash -cgc context default mobile-app -``` - -### Delete - +### Delete a Context +Deletes the named context from the active registry: ```bash cgc context delete mobile-app ``` - -This removes the context from the registry. The actual database files on disk are preserved — delete them manually if you want to reclaim space. - ---- - -## The `--context` Flag - -The `--context` (or `-c`) flag is accepted on data-accessing commands and always overrides the mode: - -```bash -cgc index . --context ProjectA -cgc index ./libs/shared --context ProjectA -cgc list --context ProjectA -cgc stats --context ProjectA -cgc delete ./libs/shared --context ProjectA -cgc clean --context ProjectA -cgc query "MATCH (f:Function) RETURN f.name LIMIT 5" --context ProjectA -``` - -Even if you're in `global` mode, passing `--context` forces named-context resolution for that single invocation. - ---- - -## MCP Server & IDE Integration - -When your IDE starts the MCP server (`cgc mcp start`), context resolution happens based on the **working directory the server was launched from**: - -| Mode | What the MCP server connects to | -|---|---| -| `global` | `~/.codegraphcontext/global/db//` | -| `per-repo` | `/.codegraphcontext/db//` | -| `named` | The default named context's database | - -This means your AI assistant (Cursor, Claude Desktop, etc.) automatically sees the right graph without any extra config — just make sure the MCP server starts from the correct project directory. +*Note: Deleting a context removes its registration from `config.yaml`. The underlying database files on disk are preserved to prevent data loss. You can delete the files manually if needed.* --- -## `.cgcignore` Resolution - -Each context level can have its own `.cgcignore`: - -| Mode | `.cgcignore` location | -|---|---| -| Global | `~/.codegraphcontext/global/.cgcignore` | -| Per-repo | `/.codegraphcontext/.cgcignore` | -| Named | `~/.codegraphcontext/contexts//.cgcignore` | - -On first install, CGC creates the global `.cgcignore` with sensible defaults (node_modules, venv, build dirs, etc.). You can edit it at any time. - ---- +## Ingest Ignore Configurations (`.cgcignore`) -## End-to-End Example: New User Setup +CGC filters files using `.cgcignore` config files. The location of the active ignore file depends on the context mode: -Starting from a fresh install with two projects — a Python API and a React frontend. +| Mode | Active `.cgcignore` Path | +| :--- | :--- | +| **Global** | `~/.codegraphcontext/global/.cgcignore` | +| **Per-Repo** | `/.codegraphcontext/.cgcignore` | +| **Named** | `~/.codegraphcontext/contexts//.cgcignore` | -```bash -# 1. Install CGC (if not already) -pip install codegraphcontext - -# 2. First run — CGC bootstraps config automatically -cgc index ~/projects/my-api -# -> Creates ~/.codegraphcontext/ with config.yaml, global .cgcignore -# -> Prints a one-time welcome banner explaining modes -# -> Indexes my-api into the global database - -# 3. Index another project (still in global mode, shares the same DB) -cgc index ~/projects/my-frontend - -# 4. See both repos in the same graph -cgc list -# my-api ~/projects/my-api -# my-frontend ~/projects/my-frontend - -# 5. Query across both projects -cgc query "MATCH (f:Function) RETURN f.name, f.path LIMIT 10" -``` - -### Switching to named contexts - -```bash -# 6. Decide you want isolated graphs -cgc context mode named - -# 7. Re-index into separate contexts (auto-creates them) -cgc index ~/projects/my-api --context API -cgc index ~/projects/my-frontend --context Frontend - -# 8. Work within one context -cgc context default API -cgc list # only shows my-api -cgc analyze callers authenticate_user - -# 9. Peek at the other context when needed -cgc list --context Frontend -``` - -### Setting up the MCP server for your IDE - -```bash -# 10. Start MCP from the API project directory -cd ~/projects/my-api -cgc mcp start -# The server resolves context based on CWD and connects to the right DB -``` - ---- - -## Configuration File Reference - -### `~/.codegraphcontext/config.yaml` - -```yaml -version: 1 -mode: named # global | per-repo | named -default_context: API # used when mode=named and no --context flag -contexts: - API: - database: falkordb - db_path: /home/user/.codegraphcontext/contexts/API/db/falkordb - repos: - - /home/user/projects/my-api - cgcignore_path: /home/user/.codegraphcontext/contexts/API/.cgcignore - Frontend: - database: falkordb - db_path: /home/user/.codegraphcontext/contexts/Frontend/db/falkordb - repos: - - /home/user/projects/my-frontend - cgcignore_path: /home/user/.codegraphcontext/contexts/Frontend/.cgcignore -``` - -### `~/.codegraphcontext/global/.cgcignore` +### Default Global `.cgcignore` Template ```text node_modules/ @@ -319,33 +162,5 @@ __pycache__/ *.pyc .git/ .idea/ +.vscode/ ``` - -Edit this to add project-wide ignore patterns. For per-context overrides, edit the `.cgcignore` inside the context's directory. - ---- - -## Migration From Older Versions - -If you were using CGC before the context system was added: - -- **`cgc_config.yaml`** is automatically migrated to **`config.yaml`** on first load. The old file is preserved as a backup. -- **Existing global database** at `~/.codegraphcontext/global/falkordb.db` continues to be used. New installs use the `global/db/falkordb/` layout, but CGC detects the legacy path and keeps using it. -- All changes are additive — `cgc index .` with no flags behaves identically to before. - ---- - -## Quick Reference - -| Task | Command | -|---|---| -| Check current mode | `cgc context list` | -| Switch to global mode | `cgc context mode global` | -| Switch to per-repo mode | `cgc context mode per-repo` | -| Switch to named mode | `cgc context mode named` | -| Create a named context | `cgc context create ` | -| Delete a named context | `cgc context delete ` | -| Set default context | `cgc context default ` | -| Index into a specific context | `cgc index . --context ` | -| Set mode via config | `cgc config set mode named` | -| View all config | `cgc config show` | diff --git a/docs/docs/guides/datasource-indexing.md b/docs/docs/guides/datasource-indexing.md new file mode 100644 index 00000000..964f9652 --- /dev/null +++ b/docs/docs/guides/datasource-indexing.md @@ -0,0 +1,77 @@ +# Ingesting Database & Cache Schemas + +CodeGraphContext (CGC) goes beyond parsing code syntax—it allows developers to ingest database and cache schemas. By linking code functions to database columns or cache keys, CGC maps dependencies from the API layer down to the storage tables. + +--- + +## 1. Supported Datasources + +CGC provides ingestion connectors for three primary database models: + +1. **Aurora MySQL / Relational Schemas**: Ingests tables, columns, primary/foreign keys, and SQL constraints. +2. **Apache Cassandra / Column-Family Schemas**: Ingests keyspaces, column families (tables), columns, and cluster keys. +3. **Redis / NoSQL Cache Stores**: Ingests logical key namespace patterns and cache structure types. + +--- + +## 2. Ingesting Schemas via CLI + +Use the `cgc datasource` command group to configure and ingest datasource metadata. + +### A. Ingesting MySQL Schemas +Connect to a MySQL database to extract table metadata and column datatypes: + +```bash +cgc datasource mysql --host 127.0.0.1 --port 3306 --user app_user --password secure_pass --database main_db +``` + +This populates the active context with `DbTable` and `DbColumn` nodes, linking tables to columns via `CONTAINS` edges. + +### B. Ingesting Cassandra Schemas +Connect to a Cassandra cluster to extract keyspace schemas: + +```bash +cgc datasource cassandra --host 127.0.0.1 --port 9042 --keyspace production_keyspace +``` + +This populates the context with keyspace tables and columns. + +### C. Ingesting Redis Key Patterns +Analyze active Redis databases to extract key schemas: + +```bash +cgc datasource redis --host 127.0.0.1 --port 6379 --db 0 +``` + +This command runs key scans, resolves namespaces (e.g., `user:*:profile` or `session:*`), and populates `RedisKeyPattern` nodes. + +--- + +## 3. Resolving Code-to-Database Relationships + +After ingesting both your codebase (via `cgc index`) and your database schemas (via `cgc datasource`), CGC runs static query analysis. + +It parses SQL query strings and Redis command invocations inside your code functions (e.g., `SELECT user_id FROM users` or `redis.get(f"user:{user_id}:profile")`) and resolves target nodes. + +### Resulting Edges: +- **`READS`**: Ingested when a function queries database tables, reads columns, or fetches Redis key patterns. +- **`WRITES`**: Ingested when a function writes data to tables (INSERT, UPDATE, DELETE) or modifies cache keys. + +--- + +## 4. Querying Datasource Relationships + +Once the unified graph is created, you can query relationships using Cypher: + +### Example A: Trace Functions Modifying a Table +```cypher +MATCH (fn:Function)-[:WRITES]->(table:DbTable {name: 'orders'}) +RETURN fn.name, fn.path +``` + +### Example B: Identify Functions Interfacing with Cache Patterns +```cypher +MATCH (fn:Function)-[:READS]->(cache:RedisKeyPattern) +WHERE cache.pattern CONTAINS 'session' +RETURN fn.name, cache.pattern +``` diff --git a/docs/docs/guides/indexing.md b/docs/docs/guides/indexing.md new file mode 100644 index 00000000..f170c292 --- /dev/null +++ b/docs/docs/guides/indexing.md @@ -0,0 +1,127 @@ +# Indexing Source Code + +Indexing extracts syntactic structures and links semantic relationships within a codebase to populate the graph database. CodeGraphContext (CGC) supports multiple scan strategies. + +--- + +## 1. Local Workspace Indexing + +To index the repository directory you are currently working in, navigate to the folder and run: + +```bash +cd /path/to/project +cgc index +``` + +### Ingestion Scopes +By default, the `index` command scans all supported source files in the current working directory. You can narrow the scope by specifying a target subdirectory or file: + +```bash +# Index only the core module folder +cgc index ./src/core + +# Index a single file +cgc index ./src/main.py +``` + +### Overwriting the Index +CGC tracks modification timestamps and file hashes to perform incremental scans. To force a full re-index of all files, bypass the cache with the `--force` flag: + +```bash +cgc index --force +``` + +--- + +## 2. Ingesting Third-Party Packages + +To trace references to external dependencies (e.g., standard library classes or package functions), you can manually add installed Python libraries to your active code graph. + +Use the `add-package` command: + +```bash +# Ingest requests library +cgc add-package requests python +``` + +The command resolves the package's installation path on your system, parses its definitions, and appends the nodes to your active context. + +--- + +## 3. SCIP and `.cgcignore` + +When `SCIP_INDEXER=true`, external SCIP tools run on eligible source files. SCIP ingestion **respects `.cgcignore`** the same way Tree-sitter indexing does—ignored paths are not passed to SCIP indexers. + +C and C++ SCIP requires a `compile_commands.json` in the project (or under `build/` / `cmake-build-*/`). Without it, CGC logs a warning and falls back to Tree-sitter for those files. + +--- + +## 4. Real-Time Directory Watchers + +For active development, run a filesystem watcher in the background to capture file writes and incrementally sync the graph. + +```bash +# Start watching the active workspace +cgc watch +``` + +```mermaid +sequenceDiagram + participant User + participant Watch as cgc watch + participant FileSystem + participant ParsingQueue + + User->>Watch: Start watch command + Watch->>FileSystem: Monitor directory + + FileSystem-->>Watch: File changed + Watch->>ParsingQueue: Queue file for parsing + + FileSystem-->>Watch: Another change + Watch->>ParsingQueue: Queue updated file +``` + +- **Listing Watchers**: View active file monitors with: + ```bash + cgc watching + ``` +- **Stopping Watchers**: Terminate directory monitoring using: + ```bash + cgc unwatch /path/to/project + ``` + +On startup, each watcher **reconciles** the graph with disk: files present on disk but missing from the graph are indexed, and graph entries for deleted files are removed before live monitoring begins. + +--- + +## 5. Ingest Filters (`.cgcignore`) + +To prevent compiling bloated indices or parsing build artifacts, define ignore rules in a `.cgcignore` file in the root of your repository or context directory. + +### Glob Pattern Rules: +- Lines starting with `#` are treated as comments. +- Directories should terminate with a trailing slash `/`. +- Supports wildcards (`*`) and recursive matches (`**/`). + +### Typical `.cgcignore` Configuration: +```text +# Exclude build and compiled outputs +build/ +dist/ +*.egg-info/ +__pycache__/ +*.pyc + +# Exclude dependency libraries +node_modules/ +.venv/ +venv/ +env/ + +# Exclude IDE configurations +.git/ +.vscode/ +.idea/ +.project +``` diff --git a/docs/docs/guides/mcp_guide.md b/docs/docs/guides/mcp_guide.md deleted file mode 100644 index 8323fc90..00000000 --- a/docs/docs/guides/mcp_guide.md +++ /dev/null @@ -1,40 +0,0 @@ -# Using with AI (MCP Guide) - -The Model Context Protocol (MCP) allows AI coding assistants to talk directly to tools like CodeGraphContext. - -## 1. Run the MCP Setup Wizard - -We provide an interactive tool to configure your editors automatically. - -```bash -cgc mcp setup -``` - -**What happens here:** - -1. The tool looks for configuration files (e.g., `~/Library/Application Support/Cursor/User/globalStorage/mcp.json`). -2. It injects the `CodeGraphContext` server details. -3. It ensures the server knows how to find your database. - -## 2. Supported Clients - -| Client | Setup Method | Notes | -| :--- | :--- | :--- | -| **Cursor** | Automatic | Requires "MCP" feature enabled in settings. | -| **Claude Desktop** | Automatic | Works with the Claude 3.5 Sonnet model. | -| **VS Code** | Semi-Automatic | Requires the **"Continue"** extension or similar MCP client. | - -## 3. How to Use It (Once Connected) - -Open your AI Chat and talk naturally. The AI now has a "tool" it can call. - -**Example Prompts:** - -* "Please index the current directory." -> *AI calls `add_code_to_graph`* -* "Who calls the `process_payment` function?" -> *AI calls `analyze_callers`* -* "Find all dead code in `utils.py`." -> *AI calls `find_dead_code`* - -## 4. Troubleshooting - -* **"Component not found":** This usually means the MCP server didn't start. Check the logs in your AI editor. -* **"Database error":** Ensure your Neo4j container is running (`docker ps`) or that your Python environment is active. diff --git a/docs/docs/guides/onboarding-codebase.md b/docs/docs/guides/onboarding-codebase.md new file mode 100644 index 00000000..8e09a0ce --- /dev/null +++ b/docs/docs/guides/onboarding-codebase.md @@ -0,0 +1,75 @@ +# Developer Onboarding & Code Tour + +Welcome to the CodeGraphContext (CGC) developer portal. This guide details the structural layout of the repository to help new contributors navigate the codebase, understand the interactions between components, and locate files when debugging or extending features. + +--- + +## Repository Directory Layout + +The root workspace contains the following directories: + +```text +CodeGraphContext/ +├── src/ <-- Core Python application source code +│ └── codegraphcontext/ <-- Primary package namespace +├── website/ <-- React-based force-directed graph visualizer UI +├── docs/ <-- MkDocs documentation source files and themes +├── tests/ <-- Unit, integration, and parser test suites +├── scripts/ <-- Maintainer scripts, build helpers, and language updates +├── k8s/ <-- Kubernetes deployment descriptors and manifests +└── organizer/ <-- Research drafts, roadmaps, and feature experiments +``` + +--- + +## Codebase Component Tour + +### 1. The Core Engine (`src/codegraphcontext/`) + +This directory houses the engine execution layers: + +- **`cli/`**: Contains the Typer-based command-line definition files. Subcommands like `cgc index`, `cgc watch`, and `cgc analyze` map their arguments here. +- **`core/`**: Houses the database abstraction layers. Database files like `database_kuzu.py`, `database_ladybug.py`, `database_falkor.py`, and `database_neo4j.py` inherit from a unified database driver interface class. +- **`tools/languages/`**: Standardizes language parsing classes. Contains Tree-sitter tag query files (e.g., `queries/python/tags.scm`) and logic to parse classes, functions, and inheritances. +- **`tools/handlers/`**: Implements individual handler logics for each Model Context Protocol (MCP) tool. The main server file (`server.py`) delegates incoming JSON-RPC calls to these specialized handler modules. +- **`core/watcher.py`**: Integrates the `watchdog` monitoring library to schedule incremental index re-scans. +- **`graph_builder.py`**: Coordinates multi-threaded ingestion, links call references, and batches insertions to the active database backend. + +### 2. The Interactive Visualizer UI (`website/`) + +A self-contained React project that runs the graphical visualization console. + +- **`src/components/CodeGraphViewer.tsx`**: Uses `react-force-graph` to render nodes and relationships in a 2D/3D interface. +- **`api/`**: Connection layers to retrieve graph data from the FastAPI backend served by the `cgc api start` process. + +### 3. Verification Test Suite (`tests/`) + +The test suite ensures reliability across backends and language parsers: + +- **`unit/`**: Validates isolated logic blocks, such as specific regex matches, configuration expansions, or parser AST collections. +- **`integration/`**: Verifies graph operations against actual database instances (KuzuDB, Neo4j, FalkorDB). +- **`fixtures/`**: Minimal test codebases (e.g., mock Python classes or Javascript files) used by integration tests to check parser outputs. + +### 4. Enterprise Deployments (`k8s/`) + +Contains Kubernetes descriptors: +- **`deployment.yaml` & `service.yaml`**: Manifests to deploy the FastAPI gateway and MCP server in cluster environments. +- **`neo4j-deployment.yaml`**: Persistent volume claims and stateful sets for Neo4j database containers. + +--- + +## Entry Points for Extension + +### Adding Support for a New Language +1. Create a language module under `src/codegraphcontext/tools/languages/` inheriting from the base parser. +2. Define AST query patterns in `queries//tags.scm`. +3. Add the parser registration in `parser_factory.py`. +4. Run language tests via `scripts/test_all_parsers.py`. + +### Implementing a New MCP Tool +1. Register the tool schema definition in `src/codegraphcontext/tool_definitions.py`. +2. Add a matching tool handler module in `src/codegraphcontext/tools/handlers/`. +3. Map the tool handler execution path inside `src/codegraphcontext/server.py`. + +### Debugging Database Drivers +- Database implementations are isolated in `src/codegraphcontext/core/`. Modify queries or connection parameters inside the respective driver wrapper file. diff --git a/docs/docs/guides/onboarding.md b/docs/docs/guides/onboarding.md deleted file mode 100644 index 7567cf63..00000000 --- a/docs/docs/guides/onboarding.md +++ /dev/null @@ -1,71 +0,0 @@ -# Onboarding Guide - -Welcome to the CodeGraphContext source code! This guide is designed to help new contributors and maintainers understand exactly what each folder and file in the repository does. This detailed breakdown ensures you know where to look when debugging or adding new features to the core engine, CLI, or UI. - -## Root Directory Structure - -The root directory contains important configuration files for packaging, testing, and Docker: - -- `.cgcignore` - The internal tool exclusion file (acts like `.gitignore`) which skips un-indexable items. -- `.env.example` - Template for environment variables (like Neo4j credentials, debug modes). -- `CONTRIBUTING.md` - Guidelines for how to contribute to the repository. -- `docker-compose.yml` - Sets up Neo4j for testing and debugging. -- `Dockerfile` - Builds the primary containerized application image. -- `pyproject.toml` - The primary Python packaging and configuration file, defining dependencies, tools, and the CLI entry point (`cgc`). -- `cgc_entry.py` - Root entry point script mapped within pyproject.toml to execute CLI commands. - ---- - -## Detailed Directory Breakdown - -### `src/codegraphcontext/` -This is the **Core Engine** containing all the Python logic to index, watch, and query code contexts. -- **`cli/`**: Directory housing the command-line interface logic using `click` or `typer` frameworks. All commands like `cgc index`, `cgc list`, and `cgc clean` live here. -- **`db/`**: The Database Abstraction Layer. Contains drivers and query execution logic for connecting to **KùzuDB** (our embedded default) and **Neo4j** (for enterprise-scale usages). -- **`parsers/`**: Collection of language-specific Tree-sitter implementations. Each script corresponds to a language (e.g., `python.py`, `javascript.py`) responsible for translating syntax ASTs into standard nodes/edges. -- **`utils/`**: Shared helpers across the project (logging, environment validation). -- **`graph_builder.py`**: The heavy-lifter file. Coordinates parsing across files, batches nodes and edges, and commits them to the DB. -- **`server.py`**: The main Model Context Protocol (MCP) server. Used by tools like Cursor or Claude Desktop to natively talk to this tool. -- **`watchdog/`**: Code that leverages `watchdog` to continuously monitor files for changes and instantly trigger an incremental index. - -### `docs/` -Contains the knowledge base you are currently reading! -- **`mkdocs.yml`**: Structure configuration for the static site generator. -- **`docs/`**: Source markdown assets for all guides, reference manuals, and cookbooks. -- **`docs/images/`**: Assets for architectural flowcharts and interface snapshots. - -### `tests/` -The testing suite. It ensures no PR breaks the engine. -- **`unit/`**: Isolated testing for small functions (such as specific parsers returning the right class name). -- **`integration/`**: Ensuring connections to local KùzuDB/Neo4j successfully write and read complex relationships. -- **`fixtures/`**: Mocked codebase folders (small fake python or typescript projects) used to validate parsers. - -### `k8s/` -Kubernetes manifests for enterprise, scaled-out deployments. -- **`deployment.yaml` & `service.yaml`**: The descriptors that orchestrate running CGC within a Kubernetes cluster. -- **`neo4j-deployment.yaml`**: Standalone persistence layer setups. - -### `website/` -A self-contained React project responsible for generating the visual, exploratory graph view. -- **`src/components/CodeGraphViewer.tsx`**: The main frontend react component utilizing `react-force-graph` to draw 3D/2D nodes dynamically. -- **`api/`**: Interaction endpoints allowing the UI to ping a running `cgc visualize` host. - -### `scripts/` -A suite of bash and python automations for maintainers. -- **`create-bundle.sh`**: Scripts to pre-package indexes for known large repositories (like Django or React) so users can download them ready to go. -- **`update_language_parsers.py`**: A maintainer script to pull down new tree-sitter libraries. -- **`test_all_parsers.py`**: Validation scripts testing new Tree-sitter bindings. - -### `organizer/` -A directory used for tracking the core team's research goals, roadmap notes, and experimental drafts before they are formalized into documentation or core repository features. - ---- - -## Where to start modifying? - -- **Want to add a new Language?** Look in `src/codegraphcontext/parsers/`. You'll need to define a new parser class and write Tree-sitter queries. -- **Want to add a new MCP Action?** Head to `src/codegraphcontext/server.py`. The `@app.tool` decorators expose new commands to Claude/Cursor. -- **Want to change Database connections?** Modify `src/codegraphcontext/db/`. -- **UI Tweaks on the Visualizer?** Change `website/src/components/CodeGraphViewer.tsx` and run `npm run dev` in that folder. - -Enjoy contributing to CodeGraphContext! diff --git a/docs/docs/guides/setup_deep_dive.md b/docs/docs/guides/setup_deep_dive.md deleted file mode 100644 index 445dc67d..00000000 --- a/docs/docs/guides/setup_deep_dive.md +++ /dev/null @@ -1,25 +0,0 @@ -# Setup Deep Dive: Neo4j Wizard - -This guide explains exactly what the `cgc neo4j setup` wizard does behind the scenes. - -## 🪄 `cgc neo4j setup` - -**Purpose:** Configures the *Storage* backend. - -**What it does (Docker Mode):** - -1. Checks if `docker` is available. -2. Runs `docker pull neo4j:latest`. -3. Runs a container mapping ports `7474` (HTTP) and `7687` (Bolt). -4. Sets a default password (`codegraphcontext`). - -**What it does (Native Mode):** - -1. Checks for `apt` (Debian/Ubuntu). -2. Adds the Neo4j repository keys. -3. Runs `apt install neo4j`. - -**Failure Modes:** - -* **Port Conflict:** If port 7687 is already used, the container will exit. -* **No Docker:** Steps will fail immediately. diff --git a/docs/docs/guides/visualization.md b/docs/docs/guides/visualization.md index 29f71993..712430a4 100644 --- a/docs/docs/guides/visualization.md +++ b/docs/docs/guides/visualization.md @@ -1,25 +1,56 @@ -# Visualizing the Graph +# Interactive Graph Visualization -Sometimes a table of text isn't enough. You need to see the map. +Visualizing your code graph helps identify complex call paths, cyclical dependencies, and architectural anomalies. CodeGraphContext includes a built-in React-based interactive force-directed graph visualizer. -## 1. Browser link (`cgc visualize`) +--- -The easiest way involves the `cgc visualize` command (alias `cgc v`). +## 1. Running the Local Visualizer Server + +Start the local visualization server using the `visualize` command: ```bash cgc visualize ``` -This will print a URL (e.g., `http://localhost:7474/browser?cmd=edit&arg=MATCH...`). -Clicking this links opens the **Neo4j Browser** with a pre-filled query to show the immediate neighborhood of your code. +By default, this command: +1. Resolves the active database context. +2. Launches a FastAPI web server on port **8000**. +3. Opens your default web browser to `http://localhost:8000`. + +### Custom Port & Repo Overrides +Specify a custom port or target repository path when starting the server: + +```bash +# Run server on port 9000 for a specific repository +cgc visualize --repo ~/projects/my-api --port 9000 + +# Use a specific named context database +cgc visualize --context StagingGraph +``` + +--- + +## 2. Using the Interactive UI + +The browser interface serves a force-directed graph showing your codebase structures: -## 2. Using Neo4j Bloom (Advanced) +- **Node Interactions**: Click on any node (file, class, function) to view its code details, extracted signatures, cyclomatic complexity scores, and docstrings in the detail pane. +- **Dynamic Search**: Use the search filter to highlight specific symbols. +- **Relationship Filters**: Toggle visibility of relationship edges (e.g., hiding `IMPORTS` to focus exclusively on execution `CALLS` flow). +- **Navigation Controls**: Zoom, pan, and drag nodes to isolate call loops and modules. -If you are using Neo4j Desktop, you can use **Bloom**. -* Bloom allows "Google Maps" style zooming. -* Type logical phrases like "Show me callers of X". +--- -## 3. Interactive Web View (Coming Soon) +## 3. Neo4j Browser Visualizations (Neo4j Backend Only) -We are building a lightweight React-based visualizer that runs directly from `cgc analyze --viz`. -* [View Roadmap](../roadmap.md) +If you are using Neo4j as your active database backend, you can leverage the native **Neo4j Browser Console** for complex Cypher queries. + +1. Open your browser and navigate to the Neo4j Console (typically `http://localhost:7474`). +2. Log in using your configured credentials. +3. Execute a Cypher query to retrieve and render graph structures: + +```cypher +// Visualize all functions called by the "process_payment" function +MATCH (f1:Function {name: 'process_payment'})-[r:CALLS]->(f2:Function) +RETURN f1, r, f2 +``` diff --git a/docs/docs/images/cgcIcon.png b/docs/docs/images/cgcIcon.png new file mode 100644 index 00000000..1f56c0a6 Binary files /dev/null and b/docs/docs/images/cgcIcon.png differ diff --git a/docs/docs/images/favicon.png b/docs/docs/images/favicon.png new file mode 100644 index 00000000..e8230cae Binary files /dev/null and b/docs/docs/images/favicon.png differ diff --git a/docs/docs/images/logo.png b/docs/docs/images/logo.png new file mode 100644 index 00000000..b66d81d4 Binary files /dev/null and b/docs/docs/images/logo.png differ diff --git a/docs/docs/index.md b/docs/docs/index.md index 3f1a1c2d..e36447fb 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -1,59 +1,45 @@ -# Welcome to CodeGraphContext +# Introduction to CodeGraphContext -CodeGraphContext (CGC) is a **Code Intelligence Engine**. It indexes your codebase into a graph database to provide deep semantic understanding for both developers (via CLI) and AI agents (via MCP). +CodeGraphContext (CGC) is a high-performance, developer-focused **Code Intelligence Engine** that transforms source repositories into semantic, queryable property graphs. Tree-sitter and optional SCIP indexers extract symbols; CGC resolves calls, imports, and inheritance into a graph you can query from the CLI, MCP tools, or the HTTP API gateway. -### :material-console: For Developers (CLI) -Use powerful terminal commands to query call chains, find dependencies, and visual architectural graphs. -**[Explore CLI Reference](reference/cli_indexing.md)** +**Current release: 0.5.0** -### :material-robot: For AI Agents (MCP) -Connect your codebase to Cursor, Claude, or VS Code. Let AI assist you with full context awareness. -**[Setup AI Assistant](guides/mcp_guide.md)** & -**[Explore Natural Language Queries](reference/mcp_master.md)** - -### ⚙️ Configuration -Customize databases, file limits, and more. -**[View Configuration Guide](reference/configuration.md)** +Recent improvements in the 0.5.0 line include cross-language call-graph resolution fixes (Perl, Ruby, Lua, Haskell, Rust, TypeScript, C#, Dart, C), structural edge persistence (`PARTIAL_OF`, `IMPLEMENTS`, `METACLASS`, etc.), and average CALLS audit accuracy rising from ~84% to ~98%. --- -## 🏗️ Architecture - -CGC sits between your code and your tools. It's not just a script; it's a persistent system. - -![CodeGraphContext Architecture](images/architecture.png) +## Core Capabilities -## 🚀 Why CodeGraphContext? - -* **Cut Debugging Time:** Stop manually grepping. Find the exact root cause in seconds, not hours. -* **Onboard Faster:** Help new developers understand complex logic without interrupting senior engineers. -* **Reduce Technical Debt:** Identify dead code, circular dependencies, and "god objects" instantly. +- **Semantic AST Extraction**: Utilizes tree-sitter for syntax analysis and SCIP (Sourcegraph Code Intelligence Protocol) for static symbol resolution across multiple directories. +- **Model Context Protocol (MCP) Integration**: Built-in MCP server support allows AI models and IDE agents (Cursor, Claude, VS Code, Windsurf) to query the codebase context dynamically. +- **Pluggable Database Architecture**: FalkorDB Lite on Unix (Python 3.12+), KuzuDB as the cross-platform fallback, plus LadybugDB, FalkorDB Remote, Nornic, and Neo4j. See [configuration defaults](reference/config.md#important-defaults-read-this-first). +- **Filesystem Synchronization**: Integrated directory watchers monitor file updates and update the graph incrementally. +- **Portable Code Graphs**: Supports exporting and importing serialized graph representations as `.cgc` bundles for offline sharing and registry integration. --- -## 🗺️ How to Read These Docs +## Architectural Layout -We have organized the documentation to match your journey: +CGC acts as the translation layer between source code parsing engines, graph datastores, and consumer clients. -1. **[Getting Started](getting-started/quickstart.md):** The linear path to installation. -2. **[Usage](use_cases_detailed.md):** Real-world ROI scenarios and user stories. -3. **[Guides](guides/mcp_guide.md):** Task-based tutorials (Setup, Visualization, CI/CD). -4. **[Core Concepts](concepts/how_it_works.md):** Understand the "magic" (Architecture, Graph Model). -5. **[CLI Reference](reference/cli_master.md):** Complete terminal command dictionary. -6. **[MCP Reference](reference/mcp_master.md):** AI agent tools and queries. -7. **[Project Info](contributing.md):** Roadmap, contributing, and configuration. +```mermaid +graph TD + src[Source Code] --> parse[Tree-sitter & SCIP Ingestion] + parse --> builder[Graph Builder & Linker] + builder --> db[Graph Storage: KuzuDB, FalkorDB, Neo4j] + db --> cli[CGC CLI Client] + db --> mcp[MCP Server Gateway] + mcp --> ai[AI Assistant Interfaces] +``` --- -### Ready to Start? -**[Install CodeGraphContext](getting-started/installation.md)** - -### Learn the Concepts -**[How Indexing Works](concepts/how_it_works.md)** - -### Project Links +## Documentation Roadmap -* **GitHub Repository**: [CodeGraphContext/CodeGraphContext](https://github.com/CodeGraphContext/CodeGraphContext) -* **Website**: [codegraphcontext.vercel.app](https://codegraphcontext.vercel.app) -* **Maintainer**: Shashank +To get started with CodeGraphContext, follow the structured sections below: +1. **[Getting Started](getting-started/prerequisites.md)**: Explore prerequisites, installation steps, quickstart tutorials, and MCP setup. +2. **[Core Concepts](concepts/architecture.md)**: Deep dive into the architecture, graph model schemas, database backends, and parser designs. +3. **[User Guides](guides/indexing.md)**: Learn indexing strategies, workspaces contexts, bundles distribution, custom visualizers, and database schema mappings. +4. **[Reference Manual](reference/cli.md)**: CLI commands, [HTTP API](reference/api.md), MCP tool schemas, and configuration variables. +5. **[Community Portal](contributing.md)**: Guidelines for contributing code, extending languages support, and the project roadmap. diff --git a/docs/docs/integration_guide.md b/docs/docs/integration_guide.md deleted file mode 100644 index 3afcb87c..00000000 --- a/docs/docs/integration_guide.md +++ /dev/null @@ -1,760 +0,0 @@ -# Integration Guide - CodeGraphContext - -This document explains how CodeGraphContext **integrates seamlessly** into typical developer workflows, rather than requiring "step-outs" or context switching. - ---- - -## Table of Contents - -1. [Philosophy: Integration vs Step-Out](#philosophy-integration-vs-step-out) -2. [IDE Integration (Primary Workflow)](#ide-integration-primary-workflow) -3. [CI/CD Integration](#cicd-integration) -4. [Git Workflow Integration](#git-workflow-integration) -5. [Code Review Integration](#code-review-integration) -6. [Documentation Integration](#documentation-integration) -7. [Team Collaboration Integration](#team-collaboration-integration) -8. [Comparison: CGC vs Alternatives](#comparison-cgc-vs-alternatives) - ---- - -## Philosophy: Integration vs Step-Out - -### ❌ Step-Out Workflow (What We Avoid) - -**Traditional code analysis tools require context switching:** - -``` -Developer Workflow (Broken): -1. Writing code in IDE -2. ❌ STOP - Open terminal -3. ❌ Run analysis tool -4. ❌ Read output in terminal -5. ❌ Switch back to IDE -6. ❌ Remember what you were doing -7. Continue coding (context lost) -``` - -**Problems:** -- Context switching kills productivity -- Interrupts flow state -- Requires remembering to use the tool -- Separate from where you work - -### ✅ Integrated Workflow (What CGC Provides) - -**CGC integrates into your existing tools:** - -``` -Developer Workflow (Seamless): -1. Writing code in IDE -2. ✅ Ask AI assistant (already in IDE) -3. ✅ AI uses CGC automatically (invisible) -4. ✅ Get answer in same context -5. Continue coding (flow maintained) -``` - -**Benefits:** -- No context switching -- Natural language interface -- Automatic, not manual -- Integrated where you already work - ---- - -## IDE Integration (Primary Workflow) - -### Cursor IDE (Recommended) - -#### Setup (One-Time, 5 minutes) - -```bash -# 1. Install CGC -pip install codegraphcontext - -# 2. Index your project -cd ~/projects/my-project -cgc index . - -# 3. Setup MCP -cgc mcp setup -# Select: Cursor - -# 4. Start MCP server (auto-start recommended) -cgc mcp start & - -# 5. Restart Cursor -``` - -#### Daily Workflow (Zero Extra Steps) - -**Scenario 1: Understanding Code** - -``` -You're reading unfamiliar code: - -// You see this function call -processPayment(order, user); - -// You wonder: "What does this do?" - -Traditional Approach: -1. ❌ Search for function definition -2. ❌ Open file -3. ❌ Read code -4. ❌ Find what IT calls -5. ❌ Repeat... - -CGC Integrated Approach: -1. ✅ Highlight "processPayment" -2. ✅ Ask AI: "What does this function do?" -3. ✅ AI uses CGC, shows: - - Function definition - - What it calls - - Who calls it - - Full execution flow -4. ✅ Continue reading (no context switch) -``` - -**Scenario 2: Before Refactoring** - -``` -You want to rename a function: - -Traditional Approach: -1. ❌ Open terminal -2. ❌ Run: grep -r "oldFunction" . -3. ❌ Read 100+ lines of output -4. ❌ Manually filter false positives -5. ❌ Switch back to IDE -6. ❌ Start refactoring (hope you didn't miss anything) - -CGC Integrated Approach: -1. ✅ Right-click function name -2. ✅ Ask AI: "What will break if I rename this?" -3. ✅ AI uses CGC, shows: - - All 23 callers - - Files affected - - Test coverage -4. ✅ IDE refactor tool with confidence -5. ✅ No terminal, no context switch -``` - -**Scenario 3: Code Review** - -``` -You're reviewing a PR: - -Traditional Approach: -1. ❌ Read PR diff -2. ❌ Open terminal -3. ❌ Run analysis commands -4. ❌ Switch back to GitHub -5. ❌ Write review -6. ❌ Repeat for each file - -CGC Integrated Approach: -1. ✅ Open PR in Cursor -2. ✅ Ask AI: "Analyze the impact of this PR" -3. ✅ AI uses CGC, provides: - - Functions affected - - Test coverage - - Risk assessment -4. ✅ Write review in same window -5. ✅ No context switch -``` - -### VS Code (with Continue.dev) - -#### Setup - -```bash -# 1-3: Same as Cursor - -# 4. Setup MCP for Continue -cgc mcp setup -# Select: VS Code (Continue.dev) - -# 5. Restart VS Code -``` - -#### Integration Points - -**1. Inline Chat (Cmd+I)** -``` -While editing code: -1. Press Cmd+I (inline chat) -2. Ask: "Who calls this function?" -3. Get answer inline -4. Continue editing -``` - -**2. Sidebar Chat** -``` -While exploring codebase: -1. Open Continue sidebar -2. Ask: "Show me the architecture of the auth module" -3. Get visualization -4. Click through to files -``` - -**3. Code Actions** -``` -Right-click on function: -1. "Ask Continue: Find callers" -2. "Ask Continue: Show call chain" -3. "Ask Continue: Check complexity" -``` - -### JetBrains IDEs (IntelliJ, PyCharm, etc.) - -#### Setup - -```bash -# 1-3: Same as above - -# 4. Setup MCP for JetBrains -cgc mcp setup -# Select: JetBrains (AI Assistant) - -# 5. Restart IDE -``` - -#### Integration Points - -**1. AI Assistant Panel** -``` -While coding: -1. Open AI Assistant (Alt+Enter) -2. Ask questions about code -3. AI uses CGC automatically -``` - -**2. Quick Actions** -``` -Right-click on code: -1. "AI Assistant: Analyze Impact" -2. "AI Assistant: Find Usages (Deep)" -3. "AI Assistant: Show Architecture" -``` - ---- - -## CI/CD Integration - -### GitHub Actions - -**Automatic Code Quality Checks** - -```yaml -# .github/workflows/code-quality.yml -name: Code Quality - -on: [pull_request] - -jobs: - analyze: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - - name: Install CodeGraphContext - run: pip install codegraphcontext - - - name: Index codebase - run: cgc index . - - - name: Find dead code - run: | - cgc analyze dead-code --exclude-decorated @api.route > dead-code.txt - if [ -s dead-code.txt ]; then - echo "⚠️ Dead code found:" - cat dead-code.txt - echo "::warning::Dead code detected. Consider cleanup." - fi - - - name: Check complexity - run: | - cgc analyze complexity --limit 10 --threshold 15 > complex.txt - if [ -s complex.txt ]; then - echo "⚠️ Complex functions found:" - cat complex.txt - echo "::warning::High complexity detected. Consider refactoring." - fi - - - name: Find circular dependencies - run: | - cgc query " - MATCH (m1:Module)-[:IMPORTS]->(m2:Module)-[:IMPORTS]->(m1) - RETURN m1.name, m2.name - " > circular.txt - if [ -s circular.txt ]; then - echo "❌ Circular dependencies found:" - cat circular.txt - echo "::error::Circular dependencies detected!" - exit 1 - fi - - - name: Comment on PR - uses: actions/github-script@v6 - with: - script: | - const fs = require('fs'); - const deadCode = fs.readFileSync('dead-code.txt', 'utf8'); - const complex = fs.readFileSync('complex.txt', 'utf8'); - - let comment = '## Code Quality Report\n\n'; - - if (deadCode) { - comment += '### ⚠️ Dead Code\n```\n' + deadCode + '\n```\n\n'; - } - - if (complex) { - comment += '### ⚠️ Complex Functions\n```\n' + complex + '\n```\n\n'; - } - - if (!deadCode && !complex) { - comment += '✅ No issues found!\n'; - } - - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: comment - }); -``` - -**Result**: Every PR automatically gets code quality feedback - -### GitLab CI - -```yaml -# .gitlab-ci.yml -code_quality: - stage: test - image: python:3.10 - script: - - pip install codegraphcontext - - cgc index . - - cgc analyze dead-code > dead-code.txt || true - - cgc analyze complexity --limit 10 > complexity.txt || true - artifacts: - reports: - codequality: - - dead-code.txt - - complexity.txt - only: - - merge_requests -``` - ---- - -## Git Workflow Integration - -### Pre-Commit Hooks - -**Automatic checks before every commit** - -```bash -# .git/hooks/pre-commit -#!/bin/bash - -# Index changed files only (fast) -cgc reindex - -# Check if commit introduces dead code -DEAD_CODE=$(cgc analyze dead-code --changed-only) -if [ -n "$DEAD_CODE" ]; then - echo "⚠️ Warning: This commit may introduce dead code:" - echo "$DEAD_CODE" - read -p "Continue anyway? (y/n) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 - fi -fi - -# Check if commit introduces high complexity -COMPLEX=$(cgc analyze complexity --changed-only --threshold 15) -if [ -n "$COMPLEX" ]; then - echo "⚠️ Warning: This commit introduces complex functions:" - echo "$COMPLEX" - read -p "Continue anyway? (y/n) " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - exit 1 - fi -fi - -exit 0 -``` - -**Make it executable:** -```bash -chmod +x .git/hooks/pre-commit -``` - -### Git Aliases - -**Add CGC commands to git workflow** - -```bash -# ~/.gitconfig -[alias] - # Show impact of current changes - impact = !cgc analyze callers $(git diff --name-only | xargs cgc find --files) - - # Find dead code in current branch - dead = !cgc analyze dead-code --branch $(git branch --show-current) - - # Show architecture diff between branches - arch-diff = !cgc visualize --branch main && cgc visualize --branch $(git branch --show-current) && echo "Compare: main_arch.html vs current_arch.html" - - # Check complexity of changed functions - complexity = !cgc analyze complexity --changed-only -``` - -**Usage:** -```bash -git impact # See what your changes affect -git dead # Find dead code in your branch -git arch-diff # Compare architecture -git complexity # Check if you're adding complex code -``` - ---- - -## Code Review Integration - -### GitHub PR Template - -**Automatic CGC analysis in PR description** - -```markdown - -## Description - - -## Impact Analysis - - -**Functions Modified:** -- [ ] `function_name` - X callers, Y files affected - -**Test Coverage:** -- [ ] All modified functions have tests -- [ ] No new dead code introduced -- [ ] Complexity within acceptable limits - -## CGC Checks - -- [ ] No circular dependencies -- [ ] No high-complexity functions (>15) -- [ ] No dead code introduced - -## Checklist -- [ ] Code follows style guidelines -- [ ] Self-review completed -- [ ] Comments added for complex logic -- [ ] Documentation updated -- [ ] Tests pass locally -- [ ] CGC analysis reviewed -``` - -### Review Checklist Script - -```bash -#!/bin/bash -# scripts/review-pr.sh - -PR_BRANCH=$1 - -echo "🔍 Analyzing PR: $PR_BRANCH" -echo - -# Get changed files -CHANGED_FILES=$(git diff main...$PR_BRANCH --name-only --diff-filter=M | grep '\.py$') - -echo "📝 Changed Files:" -echo "$CHANGED_FILES" -echo - -# For each changed file, find modified functions -for file in $CHANGED_FILES; do - echo "📄 Analyzing: $file" - - # Find functions in this file - FUNCTIONS=$(cgc find --file $file --type function) - - # For each function, check impact - echo "$FUNCTIONS" | while read func; do - echo " Function: $func" - CALLERS=$(cgc analyze callers $func --count-only) - echo " Callers: $CALLERS" - done - echo -done - -# Check for new dead code -echo "🧹 Checking for dead code..." -cgc analyze dead-code --branch $PR_BRANCH - -# Check complexity -echo "📊 Checking complexity..." -cgc analyze complexity --branch $PR_BRANCH --threshold 15 - -# Check for circular dependencies -echo "🔄 Checking for circular dependencies..." -cgc query " -MATCH (m1:Module)-[:IMPORTS]->(m2:Module)-[:IMPORTS]->(m1) -RETURN m1.name, m2.name -" - -echo -echo "✅ Review complete!" -``` - -**Usage:** -```bash -./scripts/review-pr.sh feature-branch -``` - ---- - -## Documentation Integration - -### Auto-Generated Architecture Docs - -**Keep docs in sync with code** - -```bash -# scripts/update-docs.sh -#!/bin/bash - -echo "📚 Updating documentation..." - -# Generate architecture diagram -cgc visualize --output docs/architecture.html - -# Generate module dependency graph -cgc analyze deps --all --output docs/dependencies.md - -# Generate complexity report -cgc analyze complexity --limit 20 --output docs/complexity.md - -# Generate API surface area -cgc find --type function --decorated @api.route --output docs/api-endpoints.md - -echo "✅ Documentation updated!" -``` - -**Add to CI:** -```yaml -# .github/workflows/docs.yml -name: Update Docs - -on: - push: - branches: [main] - -jobs: - docs: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - run: pip install codegraphcontext - - run: cgc index . - - run: ./scripts/update-docs.sh - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - commit_message: "docs: auto-update architecture docs" - file_pattern: docs/* -``` - -**Result**: Documentation automatically updates with every commit - ---- - -## Team Collaboration Integration - -### Slack Integration - -**Share CGC insights in Slack** - -```python -# scripts/slack-bot.py -import os -from slack_sdk import WebClient -from slack_sdk.errors import SlackApiError -import subprocess - -client = WebClient(token=os.environ['SLACK_BOT_TOKEN']) - -def handle_cgc_command(channel, command): - """Handle /cgc slash command in Slack""" - - # Run CGC command - result = subprocess.run( - ['cgc'] + command.split(), - capture_output=True, - text=True - ) - - # Post result to Slack - try: - client.chat_postMessage( - channel=channel, - text=f"```\n{result.stdout}\n```" - ) - except SlackApiError as e: - print(f"Error: {e}") - -# Example usage: -# /cgc analyze callers processPayment -# /cgc find "authentication" --type function -# /cgc analyze dead-code -``` - -### Shared Graph Database - -**Team shares one graph (optional)** - -```bash -# Setup shared Neo4j instance -cgc neo4j setup --host team-neo4j.company.com - -# All team members connect to same graph -cgc config set db.host team-neo4j.company.com -cgc config set db.user team -cgc config set db.password - -# Now everyone sees the same graph -# Updates from one developer visible to all -``` - -**Benefits:** -- Consistent view of codebase -- No duplicate indexing -- Faster onboarding (graph already built) - ---- - -## Comparison: CGC vs Alternatives - -### CGC vs IDE Built-in Tools - -| Feature | IDE Built-in | CodeGraphContext | -|---------|-------------|------------------| -| **Find References** | ✅ Direct only | ✅ Direct + Indirect | -| **Call Hierarchy** | ✅ Limited | ✅ Complete graph | -| **Cross-language** | ❌ No | ✅ Yes | -| **Dead Code** | ❌ No | ✅ Yes | -| **Complexity** | ❌ No | ✅ Yes | -| **Architecture** | ❌ No | ✅ Yes | -| **AI Integration** | ❌ No | ✅ Yes (MCP) | -| **CI/CD** | ❌ No | ✅ Yes | - -**Verdict**: CGC complements IDE tools, doesn't replace them - -### CGC vs SourceGraph - -| Feature | SourceGraph | CodeGraphContext | -|---------|------------|------------------| -| **Code Search** | ✅ Excellent | ✅ Good | -| **Graph Analysis** | ⚠️ Limited | ✅ Complete | -| **Local Use** | ❌ No | ✅ Yes | -| **AI Integration** | ❌ No | ✅ Yes (MCP) | -| **Open Source** | ⚠️ Limited | ✅ Yes | -| **Cost** | 💰 $$ | ✅ Free | -| **GitHub Only** | ✅ Yes | ✅ Any repo | - -**Verdict**: CGC better for local, AI-integrated workflows - -### CGC vs Context7 (MCP) - -| Feature | Context7 | CodeGraphContext | -|---------|----------|------------------| -| **Context Type** | Docstrings only | Full graph | -| **Relationships** | ❌ No | ✅ Yes | -| **Call Chains** | ❌ No | ✅ Yes | -| **Dead Code** | ❌ No | ✅ Yes | -| **Architecture** | ❌ No | ✅ Yes | -| **Accuracy** | ⚠️ Depends on docs | ✅ Code-based | - -**Verdict**: CGC provides structural intelligence, not just text - ---- - -## Integration Best Practices - -### 1. Start Small -```bash -# Day 1: Just index -cgc index . - -# Day 2: Add MCP -cgc mcp setup -cgc mcp start & - -# Week 1: Add to git hooks -# Week 2: Add to CI/CD -# Month 1: Team adoption -``` - -### 2. Make it Automatic -```bash -# Auto-start MCP server -echo 'cgc mcp start &' >> ~/.bashrc - -# Auto-reindex on file changes -cgc watch . - -# Auto-update docs -# Add to CI/CD pipeline -``` - -### 3. Integrate Where You Work -- ✅ Use MCP in IDE (primary) -- ✅ Add to git workflow (secondary) -- ✅ Add to CI/CD (automated) -- ❌ Don't require manual terminal commands - -### 4. Provide Value Immediately -- First use should solve a real problem -- Show time savings concretely -- Integrate into existing workflows -- Don't require behavior change - ---- - -## Summary: Integration Philosophy - -### ❌ What CGC is NOT: -- Not a separate tool you "switch to" -- Not a manual process you "remember to run" -- Not a terminal-only tool -- Not a replacement for your IDE - -### ✅ What CGC IS: -- **Invisible enhancement** to your AI assistant -- **Automatic analysis** in your CI/CD -- **Integrated intelligence** in your IDE -- **Seamless addition** to your workflow - -### The Goal: -**You shouldn't think about CGC. You should just get better answers, faster reviews, and safer refactorings—automatically.** - ---- - -## Next Steps - -- **Setup MCP integration** → [SETUP_WORKFLOWS.md](./setup_workflows.md) -- **See it in action** → [USER_JOURNEYS.md](./user_journeys.md) -- **Detailed use cases** → [USE_CASES_DETAILED.md](./use_cases_detailed.md) -- **CLI reference** → [CLI Reference](reference/cli_master.md) -- **MCP reference** → [MCP Reference](reference/mcp_master.md) diff --git a/docs/docs/javascripts/mermaid-init.js b/docs/docs/javascripts/mermaid-init.js new file mode 100644 index 00000000..1d334751 --- /dev/null +++ b/docs/docs/javascripts/mermaid-init.js @@ -0,0 +1,46 @@ +// docs/docs/javascripts/mermaid-init.js + +document.addEventListener("DOMContentLoaded", function() { + if (typeof mermaid !== "undefined") { + // 1. Find and transform all code-wrapped mermaid blocks into plain div.mermaid tags + const blocks = document.querySelectorAll('pre.mermaid, pre code.language-mermaid'); + const processed = new Set(); + + blocks.forEach(el => { + let targetEl = el; + // If we are looking at the code block inside a pre, target the pre + if (el.tagName.toLowerCase() === 'code' && el.parentNode.tagName.toLowerCase() === 'pre') { + targetEl = el.parentNode; + } + + if (processed.has(targetEl)) return; + processed.add(targetEl); + + const codeEl = targetEl.querySelector('code') || targetEl; + const diagramText = codeEl.textContent.trim(); + + const container = document.createElement('div'); + container.className = 'mermaid'; + container.textContent = diagramText; + + targetEl.parentNode.replaceChild(container, targetEl); + }); + + // 2. Initialize and run Mermaid + const isDarkMode = document.documentElement.getAttribute('data-theme') === 'dark' || + document.body.getAttribute('data-md-color-scheme') === 'dark'; + + mermaid.initialize({ + startOnLoad: false, + theme: isDarkMode ? 'dark' : 'default', + securityLevel: 'loose', + themeVariables: { + fontSize: '13px', + fontFamily: 'Inter, Outfit, sans-serif' + } + }); + + // Run the parser on our normalized div elements + mermaid.run(); + } +}); diff --git a/docs/docs/reference/api.md b/docs/docs/reference/api.md new file mode 100644 index 00000000..d8a12738 --- /dev/null +++ b/docs/docs/reference/api.md @@ -0,0 +1,70 @@ +# HTTP API Reference + +CodeGraphContext ships a **CGC Gateway** HTTP server for ChatGPT Actions, Claude connectors, and custom web frontends. It wraps the same MCP tool surface exposed by `cgc mcp start`. + +--- + +## Starting the Server + +```bash +cgc api start +cgc api start --host 127.0.0.1 --port 8080 +cgc api start --reload # development only +``` + +The server loads credentials from the same configuration chain as the CLI (`~/.codegraphcontext/.env`, context resolution, etc.). + +--- + +## Endpoints + +### Health & Status + +| Method | Path | Description | +| :--- | :--- | :--- | +| `GET` | `/health` | Liveness probe. Returns `{"status": "ok"}`. Suitable for load balancers and Kubernetes. | +| `GET` | `/` | Simple HTML landing page with links to OpenAPI docs. | +| `GET` | `/api/v1/status` | Database connectivity and active backend name. | + +### MCP-over-SSE + +| Method | Path | Description | +| :--- | :--- | :--- | +| `GET` | `/api/v1/mcp/sse` | Server-Sent Events stream for MCP clients. | +| `POST` | `/api/v1/mcp/messages` | MCP message ingress for SSE transport. | + +### REST Tool Bridge + +| Method | Path | Description | +| :--- | :--- | :--- | +| `GET` | `/api/v1/tools` | Lists registered MCP tools and schemas. | +| `POST` | `/api/v1/tools/call` | Invokes a tool by name with JSON arguments. | +| `POST` | `/api/v1/index` | Triggers repository indexing (background job). | +| `POST` | `/api/v1/query` | Executes a read-only Cypher query. | +| `GET` | `/api/v1/repositories` | Lists indexed repositories in the active context. | + +Interactive OpenAPI documentation is available at `/docs` while the server is running. + +--- + +## Example: Health Check + +```bash +curl -s http://localhost:8000/health +# {"status":"ok"} +``` + +```bash +curl -s http://localhost:8000/api/v1/status +``` + +--- + +## Relationship to MCP + +| Interface | Transport | Typical use | +| :--- | :--- | :--- | +| `cgc mcp start` | stdio | Cursor, Claude Desktop, VS Code | +| `cgc api start` | HTTP / SSE | Web apps, ChatGPT Actions, remote agents | + +Both paths share the same graph database and tool implementations. diff --git a/docs/docs/reference/cgcignore.md b/docs/docs/reference/cgcignore.md deleted file mode 100644 index 76a2fb65..00000000 --- a/docs/docs/reference/cgcignore.md +++ /dev/null @@ -1,47 +0,0 @@ -# .cgcignore Guide - -The `.cgcignore` file tells CodeGraphContext which files or folders to skip during indexing. It functions exactly like `.gitignore`. - -## Why use it? -* **Performance:** Skipping large directories (like `node_modules` or `vendor`) makes indexing significantly faster. -* **Relevance:** Excluding tests, build artifacts, or generated code keeps your knowledge graph focused on source code. -* **Security:** Ensure sensitive configuration files aren't indexed (though everything remains local). - -## File Specification - -* **Filename:** `.cgcignore` -* **Location:** Root of your project (where you run `cgc index`). -* **Syntax:** Standard glob patterns (same as git). - -## Example - -Create a file named `.cgcignore` in your project root with the following content: - -```text -# Dependency directories -node_modules/ -venv/ -.venv/ -__pycache__/ - -# Build artifacts -dist/ -build/ -target/ -*.egg-info/ - -# Tests (Optional - if you only want source code) -tests/ -spec/ -**/*_test.py -**/*.test.js - -# Sensitive files -.env -config.js -secrets.json - -# Documentation -docs/ -*.md -``` diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md new file mode 100644 index 00000000..07d965ef --- /dev/null +++ b/docs/docs/reference/cli.md @@ -0,0 +1,247 @@ +# CLI Command Reference + +The `cgc` command-line interface is the entry point for indexing code, running graph queries, managing contexts, and administering database backends. + +Run `cgc --help` or `cgc help` for the live command tree on your installed version. + +--- + +## Global Options + +These flags apply to most subcommands: + +| Option | Shorthand | Description | +| :--- | :--- | :--- | +| `--database` | `--db`, `-db` | Override the active backend for this invocation (`neo4j`, `falkordb`, `falkordb-remote`, `kuzudb`, `nornic`, `ladybugdb`). | +| `--db-path` | | Override the on-disk storage directory for embedded engines. | +| `--context` | `-c` | Target a named context workspace. | +| `--visual` | `--viz`, `-V` | Open results in the interactive graph visualization UI. | +| `--version` | `-v` | Print package version and exit. | +| `--help` | `-h` | Show help and exit. | + +Use `cgc version` (or `cgc --version`) to print the installed release (currently **0.5.0**). + +--- + +## Core Index & Lifecycle + +| Command | Usage | Notes | +| :--- | :--- | :--- | +| **`index`** | `cgc index [PATH] [--force] [--dependency]` | Shortcut: `cgc i`. Incremental by default; `--force` rebuilds from scratch. | +| **`clean`** | `cgc clean` | Purges orphaned nodes and dangling relationships. | +| **`stats`** | `cgc stats` | Repository and node counts for the active context. | +| **`delete`** | `cgc delete ` | Shortcut: `cgc rm`. Removes one indexed repository. | +| **`list`** | `cgc list` | Shortcut: `cgc ls`. Lists indexed repositories. | +| **`add-package`** | `cgc add-package ` | Indexes an installed third-party package as a dependency graph. | + +--- + +## Search (`find`) + +```bash +cgc find [args] [options] +``` + +| Subcommand | Description | +| :--- | :--- | +| `find name ` | Search by symbol name. Options: `--type function\|class\|file\|module`, `--fuzzy` / `--no-fuzzy`. | +| `find pattern ` | Regex search across indexed source. | +| `find type ` | List nodes of a given label (e.g. `Function`, `Class`). | +| `find content ` | Full-text / substring search in source and docstrings. Neo4j uses Lucene; embedded backends use portable substring matching. | +| `find decorator ` | Functions with a given decorator. | +| `find argument ` | Functions declaring a parameter name. | +| `find variable ` | Variable references and assignments. | + +--- + +## Analysis (`analyze`) + +```bash +cgc analyze [args] [options] +``` + +| Subcommand | Description | +| :--- | :--- | +| `analyze callers ` | Direct callers of a function. | +| `analyze calls ` | Direct callees of a function. | +| `analyze chain ` | Shortest call path between two symbols. | +| `analyze deps ` | Module import dependencies. | +| `analyze tree ` | Class inheritance tree. | +| `analyze complexity ` | Cyclomatic complexity for one function. | +| `analyze dead-code` | Unreferenced functions/files (with optional filters). | +| `analyze overrides ` | Methods overridden in subclasses. | +| `analyze variable ` | Variable scope and modification sites. | +| `analyze kotlin-call-audit` | Kotlin-specific call resolution audit. | + +--- + +## Querying & Reports + +#### `query` +Execute a read-only Cypher query. + +```bash +cgc query "MATCH (f:Function) RETURN f.name LIMIT 10" +cgc query "MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 50" --visual +``` + +`cgc cypher` still works as a hidden alias but prints a deprecation warning—prefer `cgc query`. + +#### `report` +Generate `CGC_REPORT.md` with god-node, complexity, and coupling metrics. + +```bash +cgc report [--include-java] +``` + +#### `visualize` +Launch the React force-directed graph UI (shortcut: `cgc v`). + +```bash +cgc visualize [--repo ] [--port 8000] +``` + +--- + +## Context Workspaces (`context`) + +Manage isolation modes and named workspaces. See [Configuration Contexts](../guides/contexts.md). + +```bash +cgc context list +cgc context mode +cgc context create [--database kuzudb] [--db-path /path] +cgc context delete +cgc context default +``` + +--- + +## Configuration (`config`) + +```bash +cgc config show +cgc config set +cgc config db +cgc config reset +``` + +Valid backends: `kuzudb`, `ladybugdb`, `falkordb`, `falkordb-remote`, `neo4j`, `nornic`. See [Configuration Reference](config.md). + +--- + +## MCP & Neo4j Setup + +```bash +cgc mcp setup # Interactive IDE wizard (shortcut: cgc m) +cgc mcp start # Start stdio MCP server +cgc mcp tools # List registered MCP tools + +cgc neo4j setup # Neo4j connection wizard (shortcut: cgc n) +``` + +--- + +## Portable Bundles (`bundle`) + +```bash +cgc bundle export [--repo PATH] [--no-stats] [--context NAME] +cgc bundle import [--clear] [--yes|-y] [--context NAME] +cgc bundle load [--clear] [--yes|-y] + +# Shortcuts +cgc export my-project.cgc --repo /path/to/project +cgc load numpy +``` + +Use `--yes` / `-y` with `--clear` to skip the destructive-import confirmation (required in CI/non-interactive shells). + +#### `registry` +Browse and download pre-indexed bundles. + +```bash +cgc registry list [--verbose] [--unique] +cgc registry search +cgc registry download [--output DIR] [--load] +cgc registry request +``` + +--- + +## Real-Time Watchers + +```bash +cgc watch [PATH] # Shortcut: cgc w +cgc unwatch +cgc watching +``` + +On startup, watchers reconcile the graph with the filesystem (add missing files, remove deleted paths) before monitoring changes. + +--- + +## Git Hooks (`hook`) + +Keep the graph in sync on commit: + +```bash +cgc hook install [PATH] [--force] +cgc hook uninstall [PATH] +cgc hook status [PATH] +``` + +--- + +## HTTP API Gateway (`api`) + +```bash +cgc api start [--host 0.0.0.0] [--port 8000] [--reload] +``` + +Exposes REST endpoints under `/api/v1` and a liveness probe at `GET /health`. See [HTTP API Reference](api.md). + +--- + +## External Datasources (`datasource`) + +```bash +cgc datasource mysql +cgc datasource cassandra +cgc datasource redis +``` + +--- + +## SCIP Setup + +```bash +cgc setup-scip +``` + +Installs or verifies external SCIP indexers when `SCIP_INDEXER=true`. C/C++ require `compile_commands.json`; see the README SCIP section. + +--- + +## System Diagnostics + +```bash +cgc doctor +``` + +Checks configuration, database connectivity, Tree-sitter parsers, dependencies, and file permissions. + +--- + +## Command Shortcuts + +| Shortcut | Full command | +| :--- | :--- | +| `cgc i` | `cgc index` | +| `cgc ls` | `cgc list` | +| `cgc rm` | `cgc delete` | +| `cgc v` | `cgc visualize` | +| `cgc w` | `cgc watch` | +| `cgc m` | `cgc mcp` | +| `cgc n` | `cgc neo4j` | +| `cgc export` | `cgc bundle export` | +| `cgc load` | `cgc bundle load` | diff --git a/docs/docs/reference/cli_analysis.md b/docs/docs/reference/cli_analysis.md deleted file mode 100644 index 8571c4c2..00000000 --- a/docs/docs/reference/cli_analysis.md +++ /dev/null @@ -1,141 +0,0 @@ -# CLI: Analysis & Querying - -These commands allow you to extract insights from your indexed code. - -## Code Analysis - -### `analyze callers` -Finds every function that calls a specific function. Essential for "Impact Analysis" before refactoring. - -**Usage:** -```bash -cgc analyze callers -``` - -### `analyze calls` -The reverse of `callers`. Shows what a specific function calls. - -**Usage:** -```bash -cgc analyze calls -``` - -### `analyze chain` -Connects the dots. Finds the path of execution between two functions. - -**Usage:** -```bash -cgc analyze chain --depth 5 -``` - -### `analyze deps` -Shows dependencies and imports for a specific module. - -**Usage:** -```bash -cgc analyze deps -``` - -### `analyze tree` -Visualizes the Class Inheritance hierarchy for a given class. - -**Usage:** -```bash -cgc analyze tree -``` - -### `analyze complexity` -Finds functions that are difficult to maintain (Cyclomatic Complexity). - -**Usage:** -```bash -cgc analyze complexity --threshold 10 -``` - -### `analyze dead-code` -Finds potentially unused functions (0 callers). - -**Usage:** -```bash -cgc analyze dead-code --exclude "@route" -``` - -### `analyze overrides` -Shows methods that override parent class methods. - -**Usage:** -```bash -cgc analyze overrides -``` - -### `analyze variable` -Analyzes where a variable is defined and used. - -**Usage:** -```bash -cgc analyze variable -``` - ---- - -## Discovery & Search - -### `find pattern` -Fuzzy search for code elements. Use this when you don't know the exact name. - -**Usage:** -```bash -cgc find pattern -``` - -### `find name` -Finds code elements (Class, Function) by their **exact** name. - -**Usage:** -```bash -cgc find name -``` - -### `find type` -List all nodes of a specific type. - -**Usage:** -```bash -cgc find type -``` - -**Supported Types:** - -* `class`: Find all classes. -* `function`: Find all functions/methods. -* `module`: Find all indexed files/modules. - -**Example:** -```bash -# Find all classes in the codebase -cgc find type class -``` - -### `find content` -Full-text search across your source code and docstrings. - -**Usage:** -```bash -cgc find content "search term" -``` - -### `find decorator` -Finds all functions that are decorated with a specific decorator. - -**Usage:** -```bash -cgc find decorator @app.route -``` - -### `find argument` -Finds all functions that define a specific argument name. - -**Usage:** -```bash -cgc find argument user_id -``` diff --git a/docs/docs/reference/cli_indexing.md b/docs/docs/reference/cli_indexing.md deleted file mode 100644 index 635ed7e5..00000000 --- a/docs/docs/reference/cli_indexing.md +++ /dev/null @@ -1,116 +0,0 @@ -# CLI: Indexing & Management - -These commands are the foundation of CodeGraphContext. They allow you to add, remove, and monitor the code repositories in your graph. - -## `cgc index` - -Adds a code repository to the graph database. This is the first step for any project. - -!!! info "Excluding Files (.cgcignore)" - Want to skip specific files or folders? CodeGraphContext supports a `.cgcignore` file. - **[📄 Read the .cgcignore Guide](cgcignore.md)** - -**Usage:** -```bash -cgc index [path] [options] -``` - -**Common Options:** - -* `path`: The folder to index (default: current directory). -* `--force`: Re-index from scratch, even if it looks unchanged. - -**Example:** -```bash -# Index the current folder -$ cgc index . - -# Index a specific project -$ cgc index /home/user/projects/backend-api -``` - ---- - -## `cgc list` - -Shows all repositories currently stored in your graph database. - -**Usage:** -```bash -cgc list -``` - -**Example Output:** -```text -Indexed Repositories: -1. /home/user/projects/backend-api (Nodes: 1205) -2. /home/user/projects/frontend-ui (Nodes: 850) -``` - ---- - -## `cgc watch` - -Starts a real-time monitor. If you edit a file, the graph updates instantly. - -!!! warning "Foreground Process" - This command runs in the foreground. Open a new terminal tab to keep it running. - -**Usage:** -```bash -cgc watch [path] -``` - -**Example:** -```bash -$ cgc watch . -[INFO] Watching /home/user/projects/backend-api for changes... -[INFO] Detected change in users/models.py. Re-indexing... -``` - ---- - -## `cgc delete` - -Removes a repository from the database. This does *not* delete your actual files, only the graph index. - -**Usage:** -```bash -cgc delete [path] [options] -``` - -**Common Options:** - -* `--all`: Dangerous. Wipes the entire database. - ---- - -## `cgc bundle` Commands - -Tools for managing portable graph snapshots (`.cgc` files). - -### `cgc bundle export` -Save your graph to a file. Useful for sharing context with team members or loading into a production read-only instance. -```bash -cgc bundle export my-graph.cgc --repo /path/to/repo -``` - -### `cgc bundle load` -Download and install a popular library bundle from our registry. -*(Alias: `cgc load`)* - -```bash -cgc load flask -``` - -### `cgc registry` -Search for available pre-indexed bundles in the cloud registry. -**Usage:** `cgc registry [query]` - -```bash -# List top bundles -cgc registry - -# Search for a specific package -cgc registry pandas -``` diff --git a/docs/docs/reference/cli_master.md b/docs/docs/reference/cli_master.md deleted file mode 100644 index d4acb74b..00000000 --- a/docs/docs/reference/cli_master.md +++ /dev/null @@ -1,52 +0,0 @@ -# Comprehensive CLI Reference - -This page lists **every single command** available in CodeGraphContext. - -## Indexing & Management - -| Command | Description | Full Details | -| :--- | :--- | :--- | -| **`cgc index`** | Adds a directory to the code graph. | [details](cli_indexing.md#cgc-index) | -| **`cgc list`** | Lists all indexed repositories. | [details](cli_indexing.md#cgc-list) | -| **`cgc delete`** | Removes a repository from the graph. | [details](cli_indexing.md#cgc-delete) | -| **`cgc watch`** | Monitors a directory for real-time updates. | [details](cli_indexing.md#cgc-watch) | -| **`cgc clean`** | Removes orphaned nodes from the DB. | - | -| **`cgc stats`** | Show node count statistics. | - | - -## Code Analysis - -| Command | Description | Full Details | -| :--- | :--- | :--- | -| **`cgc analyze callers`** | Show what functions call X. | [details](cli_analysis.md#analyze-callers) | -| **`cgc analyze calls`** | Show what functions X calls (callees). | [details](cli_analysis.md#analyze-calls) | -| **`cgc analyze chain`** | Show path between function A and B. | [details](cli_analysis.md#analyze-chain) | -| **`cgc analyze deps`** | Show imports/dependencies for a module. | [details](cli_analysis.md#analyze-deps) | -| **`cgc analyze tree`** | Show class inheritance hierarchy. | [details](cli_analysis.md#analyze-tree) | -| **`cgc analyze complexity`** | Find complex functions (Cyclomatic). | [details](cli_analysis.md#analyze-complexity) | -| **`cgc analyze dead-code`** | Find unused functions. | [details](cli_analysis.md#analyze-dead-code) | -| **`cgc analyze overrides`** | Find method overrides in subclasses. | [details](cli_analysis.md#analyze-overrides) | -| **`cgc analyze variable`** | Find variable usage across files. | [details](cli_analysis.md#analyze-variable) | - -## Discovery & Search - -| Command | Description | Full Details | -| :--- | :--- | :--- | -| **`cgc find name`** | Find element by exact name. | [details](cli_analysis.md#find-name) | -| **`cgc find pattern`** | Fuzzy search (substring). | [details](cli_analysis.md#find-pattern) | -| **`cgc find type`** | List all Class/Function nodes. | [details](cli_analysis.md#find-type) | -| **`cgc find variable`** | Find variables by name. | [details](cli_analysis.md#analyze-variable) | -| **`cgc find content`** | Full-text search in source code. | [details](cli_analysis.md#find-content) | -| **`cgc find decorator`** | Find functions with `@decorator`. | [details](cli_analysis.md#find-decorator) | -| **`cgc find argument`** | Find functions with specific arg. | [details](cli_analysis.md#find-argument) | - -## System & Configuration - -| Command | Description | Full Details | -| :--- | :--- | :--- | -| **`cgc doctor`** | Run system health check. | [details](cli_system.md#cgc-doctor) | -| **`cgc mcp setup`** | Configure AI clients. | [details](cli_system.md#cgc-mcp-setup) | -| **`cgc neo4j setup`** | Configure Neo4j database. | [details](cli_system.md#cgc-neo4j-setup) | -| **`cgc config`** | View or modify settings. | [details](configuration.md) | -| **`cgc bundle export`** | Save graph to `.cgc` file. | [details](cli_indexing.md#cgc-bundle-commands) | -| **`cgc bundle load`** | Load graph from file/registry. | [details](cli_indexing.md#cgc-bundle-commands) | -| **`cgc registry`** | Browse cloud bundles. | [details](cli_indexing.md#cgc-registry) | diff --git a/docs/docs/reference/cli_system.md b/docs/docs/reference/cli_system.md deleted file mode 100644 index 1a3f280a..00000000 --- a/docs/docs/reference/cli_system.md +++ /dev/null @@ -1,64 +0,0 @@ -# CLI: System & Configuration - -Commands to manage the CodeGraphContext engine itself. - -## `cgc doctor` - -Self-diagnostic tool. Runs a health check on your installation. - -**Checks performed:** - -* Database connectivity (Neo4j / FalkorDB). -* Python version compatibility. -* Required dependencies. - -**Usage:** -```bash -cgc doctor -``` - ---- - -## `cgc mcp setup` - -The interactive wizard for configuring AI clients. - -**What it does:** - -1. Detects installed AI Clients (Cursor, VS Code, Claude). -2. Creates the necessary config files (e.g., `mcp.json`). -3. Generates a `.env` file with database credentials. - -**Usage:** -```bash -cgc mcp setup -``` - ---- - -## `cgc neo4j setup` - -The interactive wizard for configuring the graph database backend. - -**What it does:** - -* **Docker:** Pulls and runs the official Neo4j image. -* **Local:** Helps locate a local installation. -* **Remote:** Configures credentials for AuraDB. - -**Usage:** -```bash -cgc neo4j setup -``` - ---- - -## `cgc config` Commands - -Directly modify settings without editing text files. - -* `cgc config show`: Print current configuration. -* `cgc config set `: Update a setting. - * Example: `cgc config set DEFAULT_DATABASE neo4j` -* `cgc config db `: Switch backends (shortcut). - * Example: `cgc config db falkordb` diff --git a/docs/docs/reference/config.md b/docs/docs/reference/config.md new file mode 100644 index 00000000..dc9e8141 --- /dev/null +++ b/docs/docs/reference/config.md @@ -0,0 +1,181 @@ +# Configuration Reference + +CodeGraphContext (CGC) is configured using environment variables, local configuration files, and the CLI. + +--- + +## Important defaults (read this first) + +These three points are the most common sources of confusion in older docs and issue reports: + +### Default database backend + +| Platform | What CGC uses by default | +| :--- | :--- | +| **Unix (Linux/macOS), Python 3.12+** | **FalkorDB Lite** when `falkordblite` is installed (`DEFAULT_DATABASE=falkordb`) | +| **Windows**, or FalkorDB Lite unavailable | **KuzuDB** as the automatic fallback | +| **Any platform** | Override anytime with `cgc config db ` | + +KuzuDB is **not** the universal default—it is the cross-platform fallback when FalkorDB Lite cannot run. + +### Neo4j username key + +Use **`NEO4J_USERNAME`**, not `NEO4J_USER`. The latter is not a valid `cgc config set` key. + +```bash +cgc config set NEO4J_USERNAME neo4j +``` + +### Project-local `.env` files + +Repository `.codegraphcontext/.env` and `.env` files are loaded **only in per-repo context mode** (or when `CGC_LOAD_PROJECT_ENV=1`). In **global** or **named** mode, `~/.codegraphcontext/.env` wins so cloned repos cannot override your credentials. Set `CGC_IGNORE_PROJECT_ENV=1` to force-skip project env. + +See [Project-Local Environment Files](#project-local-environment-files) below for the full precedence table. + +--- + +## The `cgc config` CLI Utility + +Use the `config` command group to inspect and modify settings from your terminal. + +### 1. Inspect Effective Settings +Prints the merged configuration values (resolving defaults, global `.env`, and local workspaces): + +```bash +cgc config show +``` + +### 2. Set Configuration Values +Persists key-value settings to the global environment configuration file: + +```bash +# Set default database engine +cgc config set DEFAULT_DATABASE falkordb + +# Change file size threshold (in MB) +cgc config set MAX_FILE_SIZE_MB 25 +``` + +`DEFAULT_DATABASE` is the supported configuration key for selecting the database backend. `DEFAULT_BACKEND` is not a valid `cgc config` key. + +### 3. Database Selection Shortcut +Quickly updates the `DEFAULT_DATABASE` key: + +```bash +cgc config db falkordb +``` + +Valid database backend identifiers: `kuzudb`, `ladybugdb`, `falkordb` (Lite/embedded), `falkordb-remote`, `neo4j`, and `nornic`. + +### 4. Reset to Defaults +Restores all keys to factory configurations: + +```bash +cgc config reset +``` + +--- + +## Configuration Variable Reference + +### Core Engine Settings + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`DEFAULT_DATABASE`** | `falkordb` | Active database engine. Options: `kuzudb`, `ladybugdb`, `falkordb`, `falkordb-remote`, `neo4j`. | +| **`ENABLE_AUTO_WATCH`** | `false` | When `true`, indexing a project automatically initializes a directory watcher. | +| **`PARALLEL_WORKERS`** | `4` | Max thread pool size for parsing code files concurrently. | +| **`CACHE_ENABLED`** | `true` | Caches file hashes to support fast incremental scans. | + +### Indexing Scope Configurations + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`MAX_FILE_SIZE_MB`** | `10` | Skips source files exceeding this size limit (in Megabytes). | +| **`IGNORE_TEST_FILES`** | `false` | When `true`, skips files containing test keywords or directories like `tests/`. | +| **`IGNORE_HIDDEN_FILES`** | `true` | When `true`, skips dotfiles and hidden folders (e.g., `.github/`). | +| **`INDEX_VARIABLES`** | `true` | Extracts variable assignments into the graph. Set to `false` for smaller graph database sizes. | +| **`INDEX_SOURCE`** | `true` | Stores raw source snippets in node attributes. Set to `false` for a lighter graph. | +| **`SKIP_EXTERNAL_RESOLUTION`** | `false` | Skips looking up external Java dependencies. | + +### Optional SCIP Indexer Configurations + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`SCIP_INDEXER`** | `false` | When `true`, enables SCIP-based symbol resolution. | +| **`SCIP_LANGUAGES`** | `python,typescript,go,rust,java` | List of target languages to process via SCIP. | + +--- + +## Database Connection Configurations + +### Neo4j Connection Properties +Required when `DEFAULT_DATABASE` is set to `neo4j`. + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`NEO4J_URI`** | `bolt://localhost:7687` | Server connection URI. | +| **`NEO4J_USERNAME`** | `neo4j` | Database user name. | +| **`NEO4J_PASSWORD`** | None | Database connection password. | +| **`NEO4J_DATABASE`** | `neo4j` | Logical database partition name. | + +### Nornic Connection Properties +Required when `DEFAULT_DATABASE` is set to `nornic`. + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`NORNIC_URI`** | `bolt://localhost:7687` | Server connection URI (supports `nornic://` and `bolt://` schemes). | +| **`NORNIC_USERNAME`** | `nornic` | Database user name. | +| **`NORNIC_PASSWORD`** | None | Database connection password. | +| **`NORNIC_DATABASE`** | None | Logical database partition name. | + +### FalkorDB Remote Connection Properties +Required when `DEFAULT_DATABASE` is set to `falkordb-remote`. + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`FALKORDB_HOST`** | `127.0.0.1` | Remote host address. | +| **`FALKORDB_PORT`** | `6379` | TCP Port. | +| **`FALKORDB_PASSWORD`** | None | Authentication password. | +| **`FALKORDB_SSL`** | `false` | Enables SSL/TLS connection socket. | +| **`FALKORDB_GRAPH_NAME`** | `codegraph` | Target graph namespace. | + +### Embedded Database Directories (KuzuDB / LadybugDB / FalkorDB Lite) +Local embedded database instances are stored on disk. Use the settings below to redirect them: + +| Config Key | Default | Description | +| :--- | :--- | :--- | +| **`KUZUDB_PATH`** | `~/.codegraphcontext/global/db/kuzudb/` | Root storage directory for KuzuDB files. | +| **`LADYBUGDB_PATH`** | `~/.codegraphcontext/global/db/ladybugdb/` | Root storage directory for LadybugDB files. | +| **`FALKORDB_PATH`** | `~/.codegraphcontext/global/db/falkordb/` | Storage path for FalkorDB Lite database. | + +--- + +## Project-Local Environment Files + +Repository-level env files are **not** loaded in every mode: + +| File | When it applies | +| :--- | :--- | +| `~/.codegraphcontext/.env` | Always (global defaults) | +| `/.codegraphcontext/.env` | **Per-repo mode only** (or when `CGC_LOAD_PROJECT_ENV=1`) | +| `/.env` | **Per-repo mode only** (searched up to 5 parent directories) | + +In **global** or **named** context mode, a checked-in `.codegraphcontext/.env` inside a clone does **not** override your user config—this prevents accidental hijacking when indexing third-party repos. + +Override flags: + +- `CGC_IGNORE_PROJECT_ENV=1` — never load project-local env. +- `CGC_LOAD_PROJECT_ENV=1` — always load project-local env (even outside per-repo mode). + +--- + +## Settings Precedence Levels + +CGC resolves configuration keys in the following hierarchical priority (highest level overrides lower levels): + +1. **CLI flag parameters** — e.g. `cgc index --db neo4j`. +2. **Runtime environment variables** — shell/CI exports and `CGC_RUNTIME_DB_TYPE`. +3. **Project-local env** — `.codegraphcontext/.env` / `.env` (per-repo mode only; see above). +4. **User global settings** — `~/.codegraphcontext/.env` (including values from `cgc config set`). +5. **System defaults** — hardcoded fallbacks in the package. diff --git a/docs/docs/reference/configuration.md b/docs/docs/reference/configuration.md deleted file mode 100644 index 9a30fceb..00000000 --- a/docs/docs/reference/configuration.md +++ /dev/null @@ -1,84 +0,0 @@ -# Configuration & Settings - -CodeGraphContext is highly configurable through environment files and the CLI. - -## `cgc config` Command - -View and modify settings directly from the terminal. - -### 1. View Settings -Shows the current effective configuration (merged from defaults and `.env`). - -```bash -cgc config show -``` - -### 2. Set a Value -Update a setting permanently. This writes to `~/.codegraphcontext/.env`. - -**Syntax:** `cgc config set ` - -```bash -# Switch to Neo4j backend -cgc config set DEFAULT_BACKEND neo4j - -# Increase max file size to index (MB) -cgc config set MAX_FILE_SIZE_MB 20 - -# Enable automatic watching after index -cgc config set ENABLE_AUTO_WATCH true -``` - -### 3. Quick Switch Database -A shortcut to toggle between `falkordb` and `neo4j`. - -```bash -cgc config db neo4j -``` - ---- - -## Configuration Reference - -Here are the available settings you can configure. - -### Core Settings - -| Key | Default | Description | -| :--- | :--- | :--- | -| **`DEFAULT_BACKEND`** | `falkordb` | The database engine to use (`neo4j` or `falkordb`). | -| **`ENABLE_AUTO_WATCH`** | `false` | If `true`, `cgc index` will automatically start watching for changes. | -| **`PARALLEL_WORKERS`** | `4` | Number of parallel threads to use during indexing. | -| **`CACHE_ENABLED`** | `true` | Caches file hashes to speed up re-indexing. | - -### Indexing Scope - -| Key | Default | Description | -| :--- | :--- | :--- | -| **`MAX_FILE_SIZE_MB`** | `5` | Files larger than this (in MB) are skipped. | -| **`IGNORE_TESTS`** | `false` | If `true`, skips folders named `tests` or `spec`. | -| **`IGNORE_HIDDEN`** | `true` | Skips hidden files (`.git`, `.vscode`). | -| **`INDEX_VARIABLES`** | `true` | Creates nodes for variables. Set to `false` for a smaller graph. | - -### Database Connection (Neo4j) - -| Key | Description | -| :--- | :--- | -| **`NEO4J_URI`** | Connection URI (e.g., `bolt://localhost:7687`). | -| **`NEO4J_USERNAME`** | Database user (default: `neo4j`). | -| **`NEO4J_PASSWORD`** | Database password. | - ---- - -## Configuration Files - -CodeGraphContext uses the following hierarchy: - -1. **Project Level:** `.cgcignore` in your project root (files to exclude). -2. **User Level:** `~/.codegraphcontext/.env` (global settings). -3. **Defaults:** Built-in application defaults. - -To reset everything to defaults: -```bash -cgc config reset -``` diff --git a/docs/docs/reference/mcp.md b/docs/docs/reference/mcp.md new file mode 100644 index 00000000..6eba82c5 --- /dev/null +++ b/docs/docs/reference/mcp.md @@ -0,0 +1,193 @@ +# MCP Tool Reference + +When running the CodeGraphContext (CGC) MCP server, it registers a suite of **25 JSON-RPC tools** that AI assistants can use to analyze and query the code graph. + +--- + +## Code Ingestion & System Control + +### `add_code_to_graph` +Indexes a local directory or file into the active database context. +- **Parameters**: + - `path` (string, required): Absolute filesystem path. + - `is_dependency` (boolean, optional): Marks the code as an external library. + +### `add_package_to_graph` +Discovers, downloads (if needed), and indexes a third-party package. +- **Parameters**: + - `package_name` (string, required): Name of the package (e.g., `requests`, `express`). + - `language` (string, required): Language syntax parser (`python`, `javascript`, `typescript`, `java`, `c`, `go`, `ruby`, `php`, `cpp`). + - `is_dependency` (boolean, optional): Marks the package as an external library (default: true). + +### `watch_directory` +Launches a directory watcher for incremental updates. +- **Parameters**: + - `path` (string, required): Directory path to watch. + +### `unwatch_directory` +Stops file monitoring on a folder path. +- **Parameters**: + - `path` (string, required): Directory path. + +### `list_watched_paths` +Lists all directories currently monitored by watchers. + +### `delete_repository` +Removes a repository's code structures from the graph. +- **Parameters**: + - `repo_path` (string, required): Repository path. + +### `list_indexed_repositories` +Returns a list of all repositories stored in the active database. + +### `get_repository_stats` +Retrieves ingestion metrics (counts of files, functions, classes, modules). +- **Parameters**: + - `repo_path` (string, optional): Restricts stats to a specific repository. + +--- + +## Background Job Controller + +Some operations (like indexing large codebases) execute as background tasks. Use these tools to monitor task states: + +### `list_jobs` +Lists all background jobs and their execution states. + +### `check_job_status` +Queries progress and logs for a specific job. +- **Parameters**: + - `job_id` (string, required): Target job ID. + +--- + +## Code Search & Relationship Analysis + +### `find_code` +Searches symbol definitions, file names, or source code for keyword matches. +- **Parameters**: + - `query` (string, required): Target keyword or pattern. + - `fuzzy_search` (boolean, optional): Enables fuzzy matching. + - `edit_distance` (number, optional): Levenshtein distance limit (0-2). + - `repo_path` (string, optional): Restricts search scope. + +### `analyze_code_relationships` +The primary tool for traversing structural relationships in the graph. +- **Parameters**: + - `query_type` (string, required): The traversal type. Must be one of: + - `find_callers`: Find immediate caller functions. + - `find_callees`: Find immediate functions called by target. + - `find_all_callers`: Deep search up the invocation chain. + - `find_all_callees`: Deep search down the execution path. + - `find_importers`: Find files importing the target symbol/module. + - `who_modifies`: Trace variables or structures written to. + - `class_hierarchy`: Resolves superclass and subclass trees. + - `overrides`: Finds functions overriding parent methods. + - `dead_code`: Scans context for unused subroutines. + - `call_chain`: Traces invocation chains between source and destination. + - `module_deps`: Identifies dependencies between modules. + - `variable_scope`: Tracks variable bindings. + - `find_complexity`: Returns cyclomatic complexity score. + - `find_functions_by_argument`: Searches for functions declaring target parameter. + - `find_functions_by_decorator`: Searches for functions decorated with target. + - `target` (string, required): The identifier name to analyze. + - `context` (string, optional): Specific file path to resolve target namespace conflicts. + - `repo_path` (string, optional): Restricts search scope. + +### `calculate_cyclomatic_complexity` +Computes the complexity score of a function. +- **Parameters**: + - `function_name` (string, required): Function identifier. + - `path` (string, optional): File path containing definition. + - `repo_path` (string, optional): Restricts search scope. + +### `find_most_complex_functions` +Returns methods with the highest cyclomatic complexity scores. +- **Parameters**: + - `limit` (integer, optional): Maximum rows to return (default: 10). + - `repo_path` (string, optional): Restricts search scope. + +### `find_dead_code` +Scans for unreferenced code declarations. +- **Parameters**: + - `exclude_decorated_with` (array of strings, optional): Excludes functions carrying specified decorator annotations (e.g., `@app.route`). + - `repo_path` (string, optional): Restricts search scope. + +--- + +## Workspace Context Management + +### `discover_codegraph_contexts` +Scans subdirectories for existing `.codegraphcontext/` directories. +- **Parameters**: + - `path` (string, optional): Scan root directory. + - `max_depth` (integer, optional): Folder depth limit (default: 1). + +### `switch_context` +Reconnects the MCP session to a different graph database. +- **Parameters**: + - `context_path` (string, required): Path to the target repository root containing `.codegraphcontext/`. + - `save` (boolean, optional): Persists configuration mapping (default: true). + +--- + +## Advanced Querying & Reporting + +### `execute_cypher_query` +Executes raw Cypher queries directly against the graph database. +- **Parameters**: + - `cypher_query` (string, required): Cypher statement. + +### `visualize_graph_query` +Generates a Neo4j visualization link. +- **Parameters**: + - `cypher_query` (string, required): Cypher statement to render. + +### `generate_report` +Compiles a markdown quality report (`CGC_REPORT.md`). +- **Parameters**: + - `output_path` (string, optional): Output report path. + - `include_java` (boolean, optional): Appends Spring endpoints and bean tables. + - `god_node_limit` (integer, optional): Limit for high fan-in symbol rows. + - `complexity_limit` (integer, optional): Limit for complex method rows. + - `cross_module_limit` (integer, optional): Limit for module coupling rows. + +--- + +## Portability & Registries + +### `load_bundle` +Imports a portable `.cgc` file, downloading it from the registry if needed. +- **Parameters**: + - `bundle_name` (string, required): Package name (e.g., `requests`) or file name. + - `clear_existing` (boolean, optional): Purges active context before loading. + +### `search_registry_bundles` +Searches the public CGC bundle server database. +- **Parameters**: + - `query` (string, optional): Name or description keywords. + - `unique_only` (boolean, optional): Returns only the latest version of packages. + +--- + +## Frameworks & Datasource Extensions + +### `find_java_spring_endpoints` +Searches Spring controller REST mappings. +- **Parameters**: + - `http_method` (string, optional): Method filter (`GET`, `POST`, etc.). + - `path_pattern` (string, optional): URL path substring. + - `repo_path` (string, optional): Restricts search scope. + +### `find_java_spring_beans` +Returns registered Spring stereotype beans. +- **Parameters**: + - `stereotype` (string, optional): Stereotype filter (`SERVICE`, `REPOSITORY`, etc.). + - `repo_path` (string, optional): Restricts search scope. + +### `find_datasource_nodes` +Returns ingested MySQL, Cassandra, or Redis schema nodes. +- **Parameters**: + - `kind` (string, optional): Datasource kind (`mysql`, `cassandra`, `redis`). + - `name` (string, optional): Substring filter for names. + - `include_columns` (boolean, optional): Appends table columns/key patterns (default: false). diff --git a/docs/docs/reference/mcp_master.md b/docs/docs/reference/mcp_master.md deleted file mode 100644 index ccd8f06e..00000000 --- a/docs/docs/reference/mcp_master.md +++ /dev/null @@ -1,67 +0,0 @@ -# MCP Reference & Natural Language Queries - -This page lists all available **MCP Tools** that your AI assistant (Cursor, Claude, VS Code) can use. - -When you ask a question in natural language, the AI selects one of these tools behind the scenes. - -!!! tip "File Exclusion" - You can control what gets indexed using `.cgcignore`. - [**📄 Read the Guide**](cgcignore.md) - -## Core Analysis Tools - -These are the most commonly used tools for understanding code. - -| Tool Name | Description | Natural Language Example | -| :--- | :--- | :--- | -| **`find_code`** | Search for code by name or fuzzy text. | "Where is the `User` class defined?" | -| **`analyze_code_relationships`** | The swiss-army knife for call graphs and dependencies. | "Find all callers of `process_payment`." | -| **`calculate_cyclomatic_complexity`** | Measure function complexity. | "What is the complexity of `main`?" | -| **`find_most_complex_functions`** | List the hardest-to-maintain functions. | "Show me the 5 most complex functions." | -| **`find_dead_code`** | Identify unused functions. | "Find dead code, but ignore `@route`." | - -## System & Management - -Tools for managing the graph and background jobs. - -| Tool Name | Description | Natural Language Example | -| :--- | :--- | :--- | -| **`monitor_directory`** | Start monitoring a folder (Alias: `watch_directory`)| "Watch the `src` folder." | -| **`list_watched_paths`** | See what is being monitored. | "What directories are being watched?" | -| **`unwatch_directory`** | Stop monitoring a folder. | "Stop watching `src`." | -| **`list_indexed_repositories`** | Show what projects are currently indexed. | "What repos are indexed?" | -| **`get_repository_stats`** | Show counts of files, classes, LOC. | "Show stats for the backend repo." | -| **`delete_repository`** | Remove a repo from the graph. | "Remove the frontend repo." | -| **`add_code_to_graph`** | Manually add a specific path. | "Add the `lib` folder." | -| **`add_package_to_graph`** | Index an external library/package. | "Add the `requests` library." | - -## Job Control - -| Tool Name | Description | Natural Language Example | -| :--- | :--- | :--- | -| **`list_jobs`** | View all background tasks. | "Show me active jobs." | -| **`check_job_status`** | Check if a specific job is done. | "Is job `xyz` finished?" | - -## Bundles & Registry - -| Tool Name | Description | Natural Language Example | -| :--- | :--- | :--- | -| **`search_registry_bundles`** | Find shared graphs in the cloud. | "Search for a `flask` bundle." | -| **`load_bundle`** | Install a graph bundle. | "Load the `flask` bundle." | - -## Advanced Querying - -For complex questions that standard tools can't answer. - -| Tool Name | Description | Natural Language Example | -| :--- | :--- | :--- | -| **`execute_cypher_query`** | Run a raw read-only database query. | "Find all recursive functions." | -| **`visualize_graph_query`** | Generate a Neo4j Browser link for a query. | "Visualize the class hierarchy of `BaseModel`." | - ---- - -## Example Queries (Cookbook) - -For a deep dive into exactly how to phrase questions and what JSON arguments look like, check out the Cookbook. - -[📖 View the MCP Cookbook](../cookbook.md) diff --git a/docs/docs/reference/troubleshooting.md b/docs/docs/reference/troubleshooting.md index 745c7bdb..26969f34 100644 --- a/docs/docs/reference/troubleshooting.md +++ b/docs/docs/reference/troubleshooting.md @@ -1,26 +1,93 @@ -# Troubleshooting +# Troubleshooting Manual -## Common Issues +This guide detail procedures for identifying, diagnosing, and resolving issues when setting up or executing CodeGraphContext. -### 1. "cgc: command not found" -**Cause:** The folder where `pip` installs scripts is not in your system PATH. -**Fix:** -* **Linux/Mac:** Add `export PATH="$HOME/.local/bin:$PATH"` to your `.bashrc` or `.zshrc`. -* **Windows:** Reinstall Python and check "Add to PATH". +--- -### 2. "Connection Refused" (Neo4j) -**Cause:** The Neo4j container is not running. -**Fix:** -```bash -docker start cgc-neo4j -``` +## 1. Engine Installation & Compilation Issues + +### KuzuDB Installation Errors (C++ Compiler Required) +KuzuDB relies on a compiled C++ engine core. If `pip install kuzu` fails: +- **Reason**: The pre-compiled wheel is not available for your system architecture/Python version, forcing a compile from source without build tools. +- **Resolution**: + - **Linux**: Install build essentials: `sudo apt-get install build-essential python3-dev` + - **macOS**: Install developer CLI tools: `xcode-select --install` + - **Windows**: Install [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) via Visual Studio Installer. + +### FalkorDB Lite Unix Dependencies +FalkorDB Lite only runs on Linux/macOS and requires **Python 3.12+**. +- **Reason**: Underlying shared libraries are not compiled for Windows or older Python interpreter versions. +- **Resolution**: Switch the active database context backend to `kuzudb` which is fully cross-platform. + +--- + +## 2. Database Connection Failures + +### "No database backend available" +- **Reason**: CGC is looking for KuzuDB, FalkorDB, or Neo4j, but the respective Python client packages are missing from the current virtual environment. +- **Resolution**: Verify package installations: + ```bash + pip install kuzu neo4j falkordb + ``` + +### Neo4j Connection Refused / Auth Failures +- **Reason**: Connection parameters in configuration do not match your running Neo4j Instance. +- **Resolution**: Run `cgc config show` to check host bindings and credentials. Verify that the Neo4j instance is up and accepting TCP connections (e.g., using `telnet localhost 7687` or via Docker logs). + +--- + +## 3. MCP Server & Daemon Failures -### 3. "Import Error: FalkorDB" -**Cause:** You are trying to use FalkorDB on Windows, or heavily outdated Python. -**Fix:** Switch to Neo4j, or upgrade to Python 3.12+ (WSL). +### IDE Assistant Fails to Load Tools +If Claude Desktop or Cursor does not show CGC tools: +- **Step 1: Process Check**: Test the server execution by running the launch command directly in your shell: + ```bash + cgc mcp start + ``` + The server should wait for input on stdin/stdout. If it immediately crashes or exits, inspect the stack trace. +- **Step 2: Absolute Executable Paths**: IDEs often run in isolated shell contexts that do not inherit your user shell's `PATH`. Replace the `cgc` command with the absolute path in your IDE configuration files: + - Find the absolute path using: `which cgc` (Linux/macOS) or `where cgc` (Windows). + - Update `command` in JSON (e.g., `/home/username/.local/bin/cgc`). +- **Step 3: Logs Inspection**: Review the server log files. MCP server logs are written to: + `~/.codegraphcontext/logs/mcp.log` --- -## Getting Help +## 4. Indexing & Filesystem Watcher Failures + +### Indexing is Slow or Out of Memory +- **Reason**: CGC is attempting to index massive build folders, dependencies, or compiled files (e.g., `.git/`, `node_modules/`, `venv/`). +- **Resolution**: Ensure a `.cgcignore` file is present in the repository root containing appropriate ignore rules (refer to the [Indexing Guide](../guides/indexing.md)). + +### Directory Watcher Fails to Update +- **Reason**: The watchdog monitor has run out of system file handles (common on Linux with large repositories). +- **Resolution**: Increase the max user watches value: + ```bash + echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p + ``` + +--- + +## 5. HTTP API Gateway + +### Gateway does not respond +- **Check**: Confirm the process is running: `cgc api start --port 8000`. +- **Health probe**: `curl http://localhost:8000/health` should return `{"status":"ok"}`. +- **Database errors on `/api/v1/status`**: Run `cgc doctor`—the gateway uses the same database configuration as the CLI. + +--- + +## 6. System Health Check (`doctor`) + +To execute a comprehensive diagnostic test of the active environment, run: + +```bash +cgc doctor +``` -If these didn't solve it, please open an issue on [GitHub](https://github.com/CodeGraphContext/CodeGraphContext/issues) with the output of `cgc doctor`. +The diagnostics engine performs the following tests: +1. **Python Version**: Confirms interpreter meets version requirements. +2. **Configuration Integrity**: Checks for syntax errors in `config.yaml`. +3. **Database Driver Availability**: Checks imports for Kuzu, FalkorDB, and Neo4j. +4. **Active Connection Health**: Attempts connection transactions to the configured database. +5. **Permissions Audit**: Verifies write capability to target log and database storage directories. \ No newline at end of file diff --git a/docs/docs/roadmap.md b/docs/docs/roadmap.md index 0294888a..fff29034 100644 --- a/docs/docs/roadmap.md +++ b/docs/docs/roadmap.md @@ -1,36 +1,430 @@ -# 🗺️ Project Roadmap +# CodeGraphContext: 6-Month Evolution & Feature Roadmap -CodeGraphContext is an evolving tool. We believe in transparency about where we are and where we are going. +CodeGraphContext (CGC) is a polyglot code intelligence tool that maps static codebases into queryable graph databases, exposing this context to AI assistants and developers. This document provides a complete inventory of CGC's existing features, details its current limitations, and outlines a comprehensive 6-month evolution path split into 50 distinct milestones. -## 🟢 Currently Supported (Stable) +--- + +## 1. Inventory of Current CGC Capabilities + +Below is the inventory of features currently implemented, shipped, and operational within the CodeGraphContext codebase: + +### 1.1 Ingestion & Parsing +- **Polyglot Tree-Sitter Parsers**: Parser classes for 20 target languages: + - Python (with Jupyter Notebook/`.ipynb` cells support via `nbformat`/`nbconvert`) + - JavaScript, TypeScript, TSX + - Go, Rust, C, C++ + - Java, Kotlin, Scala + - Ruby, C#, PHP, Swift + - Dart, Perl, Haskell, Elixir, Lua +- **SCIP Parsing Pipeline**: Optional protobuf-based precise indexer leveraging external `scip-*` language tools for deep AST symbol extraction. +- **Dependency Resolvers**: Package path resolution logic for 9 languages to link external dependencies into the graph representation. +- **Incremental Watcher**: Multi-threaded `watchdog` system for automatic file modification, creation, and deletion detection with debounced graph writes. +- **`.cgcignore` Filter**: Custom pattern matcher adhering to `.gitignore`-style rules to prevent noise (vendor folders, binaries) from entering the graph database. + +### 1.2 Graph Persistence & Schemas +- **Database Adapters**: Consistent connection wrappers for 5 backend drivers: + - **FalkorDB Lite**: Local embedded UNIX DB via Redislite. + - **FalkorDB Remote**: Remote FalkorDB client. + - **KuzuDB**: Local embedded relational-graph DB (default for Windows). + - **Neo4j**: Server-side graph database (AuraDB/Docker compatible). + - **Nornic DB**: Neo4j-compatible embedded database driver. +- **Graph Schema**: Schema contracts enforcing 17 node labels (e.g., `Repository`, `File`, `Function`, `Class`, `Variable`, `Interface`, `Enum`, `Parameter`) and 7 relationship types (`CONTAINS`, `CALLS`, `IMPORTS`, `INHERITS`, `IMPLEMENTS`, `HAS_PARAMETER`, `INCLUDES`). + +### 1.3 Query & Analysis (CodeFinder) +- **Fuzzy Search**: In-memory Levenshtein distance fallback search for classes and functions across all DB backends. +- **Ast & Structural Queries**: Out-of-the-box Cypher queries for: + - Transitive call chains and callers/callees. + - Class inheritance and C# interface implementations. + - Complexity analysis (Cyclomatic complexity calculations). + - Dead code analysis (unreferenced files and function declarations). + - Variable scope tracking and usage analysis. +- **Named Contexts**: Local configuration profiles allowing switching between global contexts, per-repository contexts, or shared workspace contexts. + +### 1.4 Interfaces & Client Integration +- **MCP Server**: JSON-RPC over `stdio` implementing 20 tools for tools/list and tools/call, allowing cursor/claude to interact with the database. +- **CLI Commands**: Over 55 interactive and command-line scripts for configuration, index wizardry, health checks (`cgc doctor`), and query execution. +- **Viz Server & Website**: + - FastAPI server serving static visual assets locally. + - React SPA with force-directed graphs (2D, 3D, 3D City visual structures, and Mermaid flowchart SVG exports). + - In-browser parsing worker utilizing `web-tree-sitter` WASM files to parse local uploads or cloned GitHub repositories without Python dependencies. +- **Bundles & Registry**: + - `.cgc` archive format for exporting/importing graph snapshots. + - GitHub-backed registry search and on-demand trigger mechanism via GitHub Actions dispatch. + +### 1.5 VS Code Extension +- **Early-stage vsix extension** (`extensions/vscode`): + - Setup wizard commands and activity bar viewer stubs. + - Config management matching core CLI options. + - Interactive menus and control panel webview. + +--- + +## 2. Current Known Bugs & Technical Limitations + +| Code | Type | Limitation / Bug | Severity | Impact | +|------|------|------------------|----------|--------| +| **L1** | Arch | **Single-process MCP Server** | Medium | The standard stdio JSON-RPC transport limits the server to one IDE wrapper instance at a time; no concurrent shared connections. | +| **L2** | Arch | **Sync-over-Async Handlers** | Low | Handlers run in threads (`asyncio.to_thread`). True non-blocking asynchronous drivers for Neo4j/Kuzu are not utilized. | +| **L3** | Arch | **In-Memory Job Manager** | Medium | Background indexing job states are lost on server restart, leading to broken job polling. | +| **L4** | Arch | **Monolithic `cli/main.py`** | Medium | CLI commands are structured in a single 2386-line file, increasing maintenance overhead and making testing difficult. | +| **L5** | Arch | **Monolithic `CodeGraphViewer.tsx`** | High | Renders layout, handles Cytoscape/Force-graph state, and processes files in a single 1579-line file. | +| **L6** | DB | **FalkorDB UNIX Restriction** | Medium | FalkorDB Lite is blocked on Windows due to redislite binaries, causing silent fallbacks. | +| **L7** | DB | **KuzuDB Cypher Dialect Discrepancies** | High | Specific Cypher queries (e.g. `UNWIND`, aggregations) behave differently between Kuzu and Neo4j, resulting in query failures. | +| **L8** | Parse | **Syntactic Boundary** | Medium | Tree-sitter has no type solver; dynamic imports or duplicate class names across folders can result in false connections in the call graph. | +| **L9** | Parse | **Stubbed Advanced Toolkits** | High | All 16 language `*Toolkit` classes in `query_tool_languages/` raise `NotImplementedError` when advanced queries are invoked. | +| **L10** | Test | **Flaky Integration Tests** | Medium | `test_cgcignore_patterns.py` requires a fully installed workspace and a live DB, leading to CI failures. | +| **L11** | Test | **Ruby Mixins and C++ Duplicate Tests** | Low | Stubbed test fixtures like `test_mixins.py` refer to invalid fixtures, and C++ tests are duplicated. | + +--- + +## 3. The 6-Month Evolutionary Roadmap (50 Milestones) + +Here is the week-by-week and month-by-month execution plan to address the constraints, scale the architecture, and implement the planned integrations (Ollama, cloud LLMs, browser extensions, benchmarking, and VS Code upgrades). + +### Month 1: Architectural Refactoring, Testing Isolation & Benchmarking (Milestones 1–9) + +> **Focus**: De-monolithing the CLI and frontend, isolating tests, and implementing a real indexing performance bench. + +#### Milestone 1: Deconstruct `cli/main.py` Monolith +- **Difficulty**: Medium +- **Knowledge Needed**: Typer CLI, Python Package structures. +- **Deliverable**: Split `cli/main.py` into separate sub-command modules under `codegraphcontext/cli/commands/` (e.g., `index.py`, `find.py`, `analyze.py`, `bundle.py`). +- **Behavioral Improvement**: Developer maintenance increases; CLI startup overhead drops because only required commands are imported. + +#### Milestone 2: Refactor `CodeGraphViewer.tsx` +- **Difficulty**: Hard +- **Knowledge Needed**: React, TypeScript, state synchronization. +- **Deliverable**: Split the React viewer into subcomponents (`GraphCanvas`, `CodeViewerSidebar`, `SearchAndFilter`, `VisualSettingsPanel`). +- **Behavioral Improvement**: Frontend codebase becomes modular, making it easier to fix rendering bugs and add custom layout managers. + +#### Milestone 3: Database Query Interface Protocol (R4) +- **Difficulty**: Hard +- **Knowledge Needed**: Cypher dialects (Kuzu vs Neo4j vs FalkorDB), Abstract Base Classes. +- **Deliverable**: Extract database queries from `CodeFinder` into a dedicated translation layer (`GraphQueryInterface`), with subclassed providers for KuzuDB and Neo4j. +- **Behavioral Improvement**: Eliminates Cypher dialect differences; KuzuDB queries no longer crash on unsupported Cypher syntax. + +#### Milestone 4: Clean and Isolate Test Suite (L11, L10) +- **Difficulty**: Medium +- **Knowledge Needed**: pytest, mocks, CI environment configurations. +- **Deliverable**: Remove the dead `test_mixins.py` Ruby fixture; deduplicate C++ tests; isolate `test_cgcignore_patterns.py` by mocking the database connection. +- **Behavioral Improvement**: CI run succeeds on every commit without needing local DB servers or pre-installed environment binaries. + +#### Milestone 5: Standardized Error Schema and Handler Layer (R10) +- **Difficulty**: Easy +- **Knowledge Needed**: MCP protocol, error-handling conventions. +- **Deliverable**: Establish structured error codes and messages for the MCP response payloads (e.g., `INDEX_NOT_FOUND`, `DB_CONNECTION_LOST`). +- **Behavioral Improvement**: AI assistants understand why a tool call failed and can recover gracefully (e.g., prompting the user to run an indexer). + +#### Milestone 6: Bundle Schema Versioning & Validation (R12, R13) +- **Difficulty**: Medium +- **Knowledge Needed**: ZIP archiving, JSON schema validation, version parsing. +- **Deliverable**: Add a version header inside the `.cgc` bundle `metadata.json` and create the `cgc bundle validate ` CLI command. +- **Behavioral Improvement**: Prevents older versions of CGC from loading newer, incompatible database structures, alerting the user with clear instructions. + +#### Milestone 7: Build Real-World Ingestion Benchmarking Suite (R9) +- **Difficulty**: Medium +- **Knowledge Needed**: Benchmarking methodologies, performance telemetry. +- **Deliverable**: Create `scripts/run_benchmarks.py` using a standard corpus of target repositories (e.g., a 100k LOC Python/Go codebase). Track parsing throughput (LOC/sec) and database insertion latencies. +- **Behavioral Improvement**: Provides quantitative metrics on indexing speed, preventing regressions during parser upgrades. + +#### Milestone 8: Establish Query Latency Profiling +- **Difficulty**: Easy +- **Knowledge Needed**: Python timing utilities, Cypher EXPLAIN. +- **Deliverable**: Include Cypher query execution time metrics in debug logs and `cgc` CLI output. +- **Behavioral Improvement**: Developers can identify slow queries and optimize database constraints/indexes accordingly. + +#### Milestone 9: Persistent Job Manager (L3, R6) +- **Difficulty**: Medium +- **Knowledge Needed**: SQLite, async job states. +- **Deliverable**: Replace the in-memory dict in `JobManager` with a lightweight, embedded SQLite table (`jobs.db`) under `.codegraphcontext/`. +- **Behavioral Improvement**: Long-running index jobs resume or report correct failed/completed states if the IDE or MCP server restarts. + +--- + +### Month 2: Core Database Optimization & Advanced Language Toolkits (Milestones 10–18) + +> **Focus**: True asynchronous driver interfaces, query optimizations, and implementing the stubbed programming language query toolkits. + +#### Milestone 10: Implement Core Python `*Toolkit` Queries (L9) +- **Difficulty**: Medium +- **Knowledge Needed**: Python Tree-sitter AST, import hooks. +- **Deliverable**: Implement `PythonToolkit` queries for advanced tasks (e.g., identifying decorators, resolving dynamic import boundaries). +- **Behavioral Improvement**: The AI assistant can execute target queries tailored specifically to Pythonic patterns instead of generic text searches. + +#### Milestone 11: Implement JS/TS and TSX `*Toolkit` Queries +- **Difficulty**: Medium +- **Knowledge Needed**: JS/TS AST structures. +- **Deliverable**: Fill in the JS/TS and TSX toolkit query stubs to handle class overrides, export patterns, and React hook dependencies. +- **Behavioral Improvement**: Yields accurate query results for JS/TS codebases. + +#### Milestone 12: Implement Go and Rust `*Toolkit` Queries +- **Difficulty**: Medium +- **Knowledge Needed**: Go and Rust syntax structures (traits, interfaces, structs, impl blocks). +- **Deliverable**: Implement toolkit stubs for Go (struct composition) and Rust (trait implementations, lifetimes). +- **Behavioral Improvement**: Allows the AI to query traits and interface compositions accurately. + +#### Milestone 13: Implement Java and C# `*Toolkit` Queries +- **Difficulty**: Medium +- **Knowledge Needed**: JVM and .NET syntax patterns. +- **Deliverable**: Implement toolkit stubs for Java and C# to support generic parameter constraints, annotations, and properties. +- **Behavioral Improvement**: Provides accurate class inheritance hierarchies and interface implementations. + +#### Milestone 14: Implement C and C++ `*Toolkit` Queries +- **Difficulty**: Hard +- **Knowledge Needed**: C/C++ AST, preprocessor patterns. +- **Deliverable**: Implement toolkits to track macro expansions and header inclusion graphs. +- **Behavioral Improvement**: AI can trace complex C++ macro chains and header-source relationships. + +#### Milestone 15: Non-Blocking Asynchronous Database Drivers (L2) +- **Difficulty**: Hard +- **Knowledge Needed**: Python `asyncio`, asynchronous DB drivers (`neo4j.AsyncDriver`, `kuzu` async routines). +- **Deliverable**: Refactor the database connection layer to use async calls, eliminating thread pools (`asyncio.to_thread`) for database operations. +- **Behavioral Improvement**: Enhances server throughput and reduces thread overhead under heavy concurrent MCP tool calls. + +#### Milestone 16: DB Connection Pooling (L11) +- **Difficulty**: Medium +- **Knowledge Needed**: Database connection pooling. +- **Deliverable**: Implement connection pooling for Neo4j and KuzuDB adapters. +- **Behavioral Improvement**: Eliminates connection handshake overhead for consecutive tool calls, reducing query latency. + +#### Milestone 17: Query Result Streaming (L8) +- **Difficulty**: Medium +- **Knowledge Needed**: Python generators, streaming JSON serialization. +- **Deliverable**: Implement a generator-based streaming query pipeline for large Cypher query results. +- **Behavioral Improvement**: Eliminates out-of-memory crashes when querying large graphs. + +#### Milestone 18: KuzuDB Dialect Compatibility Layer +- **Difficulty**: Medium +- **Knowledge Needed**: KuzuDB Cypher constraints. +- **Deliverable**: Implement a query rewriter that converts standard Neo4j Cypher functions into KuzuDB-compatible Cypher. +- **Behavioral Improvement**: Standardizes Cypher features across all backends. + +--- + +### Month 3: Deep AST Parsing & Semantic Ingestion Upgrades (Milestones 19–27) + +> **Focus**: Enhancing parsers, supporting non-code assets, improving incremental ingestion, and adding type inference patterns. -These features are live and battle-tested in version `0.2.1+`. +#### Milestone 19: C++ Header Parser Disambiguation (L16) +- **Difficulty**: Easy +- **Knowledge Needed**: Tree-sitter C vs C++ ASTs. +- **Deliverable**: Check for pure C markers in `.h` files to select either the C or C++ parser. +- **Behavioral Improvement**: Reduces parse errors for pure C libraries. -* **Core Indexing:** Python, JavaScript, TypeScript, Go, Java, C++, Ruby, PHP. -* **Database Backends:** - * FalkorDB Lite (In-memory, default for Unix). - * Neo4j (Docker/Native, default for Production). -* **MCP Server:** Full support for Cursor, Claude Desktop, Windsurf, VS Code. -* **Live Watching:** Real-time updates via `cgc watch`. -* **Bundles:** Export/Import indexed graphs. +#### Milestone 20: HTML and CSS Tree-Sitter Parsers (L17) +- **Difficulty**: Medium +- **Knowledge Needed**: HTML/CSS syntax trees. +- **Deliverable**: Add parsers for HTML tags and CSS class declarations. +- **Behavioral Improvement**: Bridges the gap between frontend templates and backend logic by connecting component classes to styles. -## 🚧 In Progress (Beta / Active Dev) +#### Milestone 21: SQL, Shell & YAML Parsers (L17) +- **Difficulty**: Medium +- **Knowledge Needed**: SQL dialects, Bash syntax, Tree-sitter. +- **Deliverable**: Extract database queries from source code and link them to parsed SQL schemas. +- **Behavioral Improvement**: Extends the dependency graph to cover database interactions and configuration files. -We are actively writing code for these right now. +#### Milestone 22: Incremental Ingestion Concurrency Tuning +- **Difficulty**: Medium +- **Knowledge Needed**: Multi-processing, file lock queues. +- **Deliverable**: Implement worker pools using Python's `multiprocessing` for parsing, while serializing writes to the database. +- **Behavioral Improvement**: Speeds up initial parsing on multi-core machines. -* **Interactive Visualizer:** A new web-based UI to explore the graph without Neo4j Browser. -* **Better C++ Support:** improved parsing for header/implementation linkage. -* **Configuration UI:** A TUI (Text User Interface) for managing `mcp.json` configs. +#### Milestone 23: Type Inference & Symbol Reference Resolution +- **Difficulty**: Hard +- **Knowledge Needed**: AST scope analysis, basic type inference. +- **Deliverable**: Implement a cross-file reference resolver to link function call parameters to class instantiations. +- **Behavioral Improvement**: Improves the accuracy of the call graph by reducing ambiguous function name links. -## 🔮 Planned (Future) +#### Milestone 24: Incremental SCIP Ingestion (L15) +- **Difficulty**: Hard +- **Knowledge Needed**: SCIP protocol specifications, git diffs. +- **Deliverable**: Implement incremental SCIP indexing based on git diffs. +- **Behavioral Improvement**: Reduces indexing times for large projects when using SCIP. -Concepts we are researching for the next major versions. +#### Milestone 25: Automated SCIP Installer Script (L14) +- **Difficulty**: Easy +- **Knowledge Needed**: Shell scripting, platform binaries. +- **Deliverable**: Create `cgc index setup-scip` to download and install language-specific SCIP binaries. +- **Behavioral Improvement**: Reduces setup friction for SCIP indexing. -* **Cloud Sync:** Option to share private implementation graphs with team members. -* **CI/CD Action:** GitHub Action to auto-index PRs and comment with impact analysis. -* **Natural Language Query to Graph:** Improved "Text-to-Cypher" translation for the CLI. +#### Milestone 26: AST Cognitive Complexity Calculations +- **Difficulty**: Medium +- **Knowledge Needed**: Static analysis metrics. +- **Deliverable**: Implement cognitive complexity parsing alongside cyclomatic complexity. +- **Behavioral Improvement**: AI can identify hard-to-maintain code blocks, not just branch-heavy ones. + +#### Milestone 27: Workspace Index Size Estimation Utility +- **Difficulty**: Easy +- **Knowledge Needed**: CLI user interface design. +- **Deliverable**: Create an indexing pre-flight check command showing estimated node count and DB disk usage. +- **Behavioral Improvement**: Helps users budget disk space before indexing large codebases. --- -!!! info "Request a Feature" - Have an idea? Open an issue on our [GitHub Repository](https://github.com/CodeGraphContext/CodeGraphContext/issues). +### Month 4: VS Code Extension Upgrades (Milestones 28–35) + +> **Focus**: Turning the VS Code extension into a fully featured visual and analytical assistant. + +#### Milestone 28: Interactive Webview Control Dashboard +- **Difficulty**: Medium +- **Knowledge Needed**: VS Code Extension API, React build integration. +- **Deliverable**: Embed the local React dashboard within a VS Code webview panel. +- **Behavioral Improvement**: Users can view the codebase graph directly inside the IDE. + +#### Milestone 29: CodeLens Complexity & Dependency Markers +- **Difficulty**: Medium +- **Knowledge Needed**: VS Code CodeLens API, CGC CLI queries. +- **Deliverable**: Overlay cyclomatic complexity and class hierarchies above code declarations. +- **Behavioral Improvement**: Developers see code metrics contextually while writing code. + +#### Milestone 30: VS Code Inline Cypher Console +- **Difficulty**: Medium +- **Knowledge Needed**: VS Code Webview panels, Cypher execution. +- **Deliverable**: Implement an inline Cypher query editor with syntax highlighting and table previews. +- **Behavioral Improvement**: Power users can query the graph without leaving the IDE. + +#### Milestone 31: Automatic Watcher Lifecycle Integration (L18) +- **Difficulty**: Easy +- **Knowledge Needed**: VS Code Workspace Event listeners. +- **Deliverable**: Automatically start the file watcher thread when a workspace with `.codegraphcontext/` is opened. +- **Behavioral Improvement**: Code modifications are indexed in the background without manual CLI intervention. + +#### Milestone 32: Diagnostics Provider for Dead Code +- **Difficulty**: Medium +- **Knowledge Needed**: VS Code DiagnosticCollection API. +- **Deliverable**: Expose dead code detections as warnings in the VS Code "Problems" tab. +- **Behavioral Improvement**: Warns developers about unused parameters and dead functions in real-time. + +#### Milestone 33: Context-Aware Navigation (Go to Definition) +- **Difficulty**: Hard +- **Knowledge Needed**: VS Code DefinitionProvider. +- **Deliverable**: Implement a definition provider powered by the CGC database graph. +- **Behavioral Improvement**: Accelerates navigation in dynamic languages where standard VS Code definitions fail. + +#### Milestone 34: Graph-Guided Refactoring Previews +- **Difficulty**: Hard +- **Knowledge Needed**: VS Code WorkspaceEdit API. +- **Deliverable**: Show a refactoring preview panel listing files that will be impacted by renaming a symbol. +- **Behavioral Improvement**: Reduces regression risks during large refactors. + +#### Milestone 35: One-Click Bundle Export UI +- **Difficulty**: Easy +- **Knowledge Needed**: VS Code extension commands. +- **Deliverable**: Add a button to export `.cgc` bundles directly from the sidebar. +- **Behavioral Improvement**: Simplifies sharing indexed codebase contexts with team members. + +--- + +### Month 5: ChatGPT Web & External LLM Integration (Milestones 36–43) + +> **Focus**: Supporting remote connections, writing browser extensions, and improving the website. + +#### Milestone 36: WebSocket & SSE MCP Transport Protocol (L1) +- **Difficulty**: Hard +- **Knowledge Needed**: WebSockets, Server-Sent Events, JSON-RPC. +- **Deliverable**: Add WebSocket and SSE servers to the MCP server process (`cgc mcp start --transport ws`). +- **Behavioral Improvement**: Multiple clients and IDEs can connect to a single, shared CGC database concurrently. + +#### Milestone 37: Web LLM Browser Extension (Chrome & Firefox) +- **Difficulty**: Hard +- **Knowledge Needed**: Web Extensions API, Content Scripts, IPC. +- **Deliverable**: Build a browser extension that securely connects ChatGPT, Claude, and Gemini web interfaces to the local CGC MCP daemon. +- **Behavioral Improvement**: Web-based LLMs can run code queries against local codebases securely. + +#### Milestone 38: Web Extension Workspace Matcher +- **Difficulty**: Medium +- **Knowledge Needed**: Chrome Tab APIs, Local storage. +- **Deliverable**: Detect the GitHub URL or active tab project name and select the matching local database context automatically. +- **Behavioral Improvement**: Standardizes LLM responses without manual context switching. + +#### Milestone 39: In-Browser Worker Parsing Optimizations +- **Difficulty**: Hard +- **Knowledge Needed**: Web Workers, WASM memory structures, Tree-sitter WASM. +- **Deliverable**: Optimize `parser.worker.ts` with streaming uploads and file chunking. +- **Behavioral Improvement**: Allows the browser explorer to parse large repositories without browser tab freezes. + +#### Milestone 40: Multi-Engine Web Visualizer Upgrades +- **Difficulty**: Medium +- **Knowledge Needed**: React-force-graph, WebGL rendering. +- **Deliverable**: Update `CodeGraphViewer.tsx` to support WebGL for rendering large graphs. +- **Behavioral Improvement**: Renders repositories exceeding 10,000 files smoothly. + +#### Milestone 41: Browser-Based Cypher Builder +- **Difficulty**: Medium +- **Knowledge Needed**: React, visual query builders. +- **Deliverable**: Add a drag-and-drop visual Cypher query builder to the website's explore tab. +- **Behavioral Improvement**: Simplifies querying the graph for users unfamiliar with Cypher syntax. + +#### Milestone 42: Web-Based Bundle Comparison Panel +- **Difficulty**: Medium +- **Knowledge Needed**: React diff libraries. +- **Deliverable**: Build a visual dashboard to compare two `.cgc` bundles and highlight structural changes. +- **Behavioral Improvement**: Simplifies tracking structural changes across commits. + +#### Milestone 43: Secure Origin Policy Configuration +- **Difficulty**: Easy +- **Knowledge Needed**: Web security, CORS headers. +- **Deliverable**: Add strict origin validation filters to CLI configs for external connections. +- **Behavioral Improvement**: Protects local database ports from unauthorized web requests. + +--- + +### Month 6: LLM API & Local Ollama Integrations (Milestones 44–50) + +> **Focus**: Adding AI-guided summarization, local vector embeddings, and creating documentation tutorials. + +#### Milestone 44: LLM API Key Configuration CLI +- **Difficulty**: Easy +- **Knowledge Needed**: CLI inputs, config file management. +- **Deliverable**: Create the `cgc config set-key` command to securely store OpenAI, Anthropic, and Gemini API keys. +- **Behavioral Improvement**: Provides a unified interface for cloud LLM integrations. + +#### Milestone 45: Local Ollama Model Integration +- **Difficulty**: Medium +- **Knowledge Needed**: Ollama HTTP API, local LLM configurations. +- **Deliverable**: Add an Ollama adapter supporting models like `qwen2.5-coder` or `llama3`. +- **Behavioral Improvement**: Enables offline code analysis and semantic summarization. + +#### Milestone 46: AI-Guided Semantic Summarizer +- **Difficulty**: Hard +- **Knowledge Needed**: LLM prompts, batch processing. +- **Deliverable**: Build an ingestion pipeline stage that uses LLMs to generate summaries of functions and classes, saving them as properties in the graph. +- **Behavioral Improvement**: Allows AI assistants to search the graph using natural language concepts. + +#### Milestone 47: Graph RAG Vector Embedding Ingestion +- **Difficulty**: Hard +- **Knowledge Needed**: Vector embeddings, Kuzu/Neo4j vector indices. +- **Deliverable**: Generate embeddings of code summaries and store them in the graph database. +- **Behavioral Improvement**: Combines keyword search with structural graph queries for more accurate results. + +#### Milestone 48: High-Level Architecture Blogs +- **Difficulty**: Easy +- **Knowledge Needed**: Technical writing, blogging structure. +- **Deliverable**: Publish a blog series detailing CGC's design (e.g., Tree-sitter parsers, database adapters, and MCP servers). +- **Behavioral Improvement**: Enhances community engagement and adoption. + +#### Milestone 50: Interactive Walkthrough and Demos +- **Difficulty**: Easy +- **Knowledge Needed**: Video editing, documentation design. +- **Deliverable**: Produce video tutorials demonstrating VS Code integrations, browser extensions, and CLI commands. +- **Behavioral Improvement**: Lowers the barrier to entry for new users. + +#### Milestone 50: Production-Ready Release (v1.0.0) +- **Difficulty**: Medium +- **Knowledge Needed**: PyPI workflows, release lifecycle management. +- **Deliverable**: Stabilize the API, verify all tests, and publish v1.0.0 to PyPI. +- **Behavioral Improvement**: Delivers a production-ready code intelligence tool. + +--- + +## 4. Roadmap Implementation Summary + +This roadmap prioritizes foundational stability and codebase cleanup in the first month before introducing advanced semantic and AI integrations. + +``` + Month 1 Month 2 Month 3 Month 4 Month 5 Month 6 + +----------------+ +----------------+ +----------------+ +----------------+ +----------------+ +----------------+ + | Refactoring | -----> | DB Optimization| -----> | Semantic Parse | -----> | VS Code Engine | -----> | ChatGPT Web | -----> | Ollama & RAG | + | & Benchmarks | | & Language Stubs| | & Incremental | | & Integrations | | Integrations | | Releases v1.0 | + +----------------+ +----------------+ +----------------+ +----------------+ +----------------+ +----------------+ +``` diff --git a/docs/docs/setup_workflows.md b/docs/docs/setup_workflows.md deleted file mode 100644 index a5814e32..00000000 --- a/docs/docs/setup_workflows.md +++ /dev/null @@ -1,801 +0,0 @@ -# Setup Workflows - CodeGraphContext - -This document provides **exact, step-by-step workflows** for setting up and using CodeGraphContext, both for first-time setup and everyday use. - ---- - -## Table of Contents - -1. [Prerequisites](#prerequisites) -2. [First-Time Setup: CLI Users](#first-time-setup-cli-users) -3. [First-Time Setup: MCP Users](#first-time-setup-mcp-users) -4. [Every-Time Workflow: CLI Users](#every-time-workflow-cli-users) -5. [Every-Time Workflow: MCP Users](#every-time-workflow-mcp-users) -6. [Troubleshooting](#troubleshooting) - ---- - -## Prerequisites - -### Required -- **Python 3.8+** installed on your system -- **pip** package manager -- **Git** (for cloning repositories) - -### Optional (for advanced features) -- **Neo4j** (for large-scale repositories > 100k LOC) -- **Docker** (for containerized deployment) - -### Check Prerequisites - -```bash -# Check Python version -python --version -# Expected: Python 3.8.0 or higher - -# Check pip -pip --version -# Expected: pip 20.0.0 or higher - -# Check git -git --version -# Expected: git version 2.0.0 or higher -``` - ---- - -## First-Time Setup: CLI Users - -**Time Required**: 5-10 minutes -**Frequency**: Once per machine - -### Step 1: Install CodeGraphContext - -```bash -# Install via pip -pip install codegraphcontext - -# Verify installation -cgc --version - -# Expected output: -# CodeGraphContext version 1.0.0 -``` - -**Troubleshooting**: If `cgc` command not found, add Python scripts to PATH: -```bash -# Linux/Mac -export PATH="$HOME/.local/bin:$PATH" - -# Windows -# Add %APPDATA%\Python\Scripts to PATH -``` - -### Step 2: Choose Database Backend (Optional) - -CodeGraphContext automatically uses **FalkorDB** (embedded, no setup needed) for most use cases. - -**When to use Neo4j instead:** -- Repository > 100,000 lines of code -- Need to share graph across team members -- Want persistent storage across machines - -```bash -# Option A: Use FalkorDB (default, recommended for most users) -# No action needed - works out of the box - -# Option B: Setup Neo4j (for large repositories) -cgc neo4j setup - -# This will: -# 1. Check if Neo4j is installed -# 2. If not, provide installation instructions -# 3. Configure connection settings -# 4. Test the connection -``` - -**Neo4j Setup Output:** -``` -Checking for Neo4j installation... -✗ Neo4j not found - -Would you like to install Neo4j? (y/n): y - -Installing Neo4j... -✓ Neo4j 5.x installed -✓ Started Neo4j service -✓ Default credentials: neo4j/neo4j - -Please change the default password: -New password: ******** -Confirm password: ******** - -✓ Password updated -✓ Connection tested successfully - -Configuration saved to: ~/.cgc/config.yml -``` - -### Step 3: Index Your First Repository - -```bash -# Navigate to your project -cd ~/projects/my-project - -# Index the repository -cgc index . - -# Expected output: -# Indexing repository: /home/user/projects/my-project -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Scanning files... ✓ 1,247 files found -# Parsing Python files... ✓ 892 files (71%) -# Parsing JavaScript... ✓ 234 files (19%) -# Parsing TypeScript... ✓ 121 files (10%) -# -# Building graph... -# ✓ Created 3,421 function nodes -# ✓ Created 892 class nodes -# ✓ Created 234 module nodes -# ✓ Created 15,234 relationships -# -# Database: FalkorDB (embedded) -# Indexing completed in 23.4 seconds -``` - -### Step 4: Verify Setup - -```bash -# Check repository statistics -cgc stats - -# Expected output: -# Repository Statistics -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Repository: my-project -# Path: /home/user/projects/my-project -# -# Code Elements: -# Files: 1,247 -# Functions: 3,421 -# Classes: 892 -# Modules: 234 -# -# Relationships: -# Function calls: 8,234 -# Imports: 2,456 -# Inheritance: 234 -# Total: 15,234 -# -# Database: FalkorDB -# Last indexed: 2026-01-30 01:15:23 -``` - -```bash -# Try a simple query -cgc find "main" --type function - -# Expected output: -# Found 5 functions matching 'main': -# -# 1. main -# File: src/app.py:123 -# -# 2. main_loop -# File: src/core/event_loop.py:45 -# -# ... (3 more) -``` - -### Step 5: Explore Available Commands - -```bash -# See all available commands -cgc help - -# Expected output: -# CodeGraphContext CLI -# -# Usage: cgc [COMMAND] [OPTIONS] -# -# Project Management: -# index [PATH] Index a repository -# reindex Re-index current repository -# stats Show repository statistics -# clean Clear the database -# -# Code Discovery: -# find Search for code elements -# visualize Generate architecture visualization -# -# Code Analysis: -# analyze callers Find who calls a function -# analyze callees Find what a function calls -# analyze chain Show call chain between functions -# analyze deps Show module dependencies -# analyze dead-code Find unused functions -# analyze complexity Find complex functions -# -# ... (more commands) -``` - -**✅ Setup Complete!** You're ready to use CodeGraphContext. - ---- - -## First-Time Setup: MCP Users - -**Time Required**: 10-15 minutes -**Frequency**: Once per machine - -### Step 1-3: Same as CLI Users - -Follow Steps 1-3 from [First-Time Setup: CLI Users](#first-time-setup-cli-users) - -### Step 4: Configure MCP Integration - -```bash -# Setup MCP configuration -cgc mcp setup - -# Interactive prompts: -# -# Select your IDE/Editor: -# 1. Cursor -# 2. VS Code (with Continue.dev) -# 3. Claude Desktop -# 4. Custom (manual configuration) -# -# Choice: 1 -``` - -**For Cursor:** -``` -Configuring MCP for Cursor... - -✓ Found Cursor config at: ~/.config/cursor/mcp.json -✓ Added CodeGraphContext MCP server -✓ Server command: cgc mcp start - -Configuration: -{ - "mcpServers": { - "codegraphcontext": { - "command": "cgc", - "args": ["mcp", "start"], - "env": { - "CGC_DB": "falkordb" - } - } - } -} - -✓ Configuration saved - -Next steps: -1. Restart Cursor -2. Open Command Palette (Cmd/Ctrl + Shift + P) -3. Run: "MCP: Reload Servers" -4. Verify CodeGraphContext appears in MCP server list -``` - -**For VS Code (Continue.dev):** -``` -Configuring MCP for VS Code (Continue.dev)... - -✓ Found Continue config at: ~/.continue/config.json -✓ Added CodeGraphContext MCP server - -Next steps: -1. Restart VS Code -2. Open Continue sidebar -3. Verify CodeGraphContext tools are available -``` - -**For Claude Desktop:** -``` -Configuring MCP for Claude Desktop... - -✓ Created config at: ~/Library/Application Support/Claude/claude_desktop_config.json - -Next steps: -1. Restart Claude Desktop -2. Start a new conversation -3. Ask: "What MCP tools do you have access to?" -4. Verify CodeGraphContext tools are listed -``` - -### Step 5: Start MCP Server - -```bash -# Start the MCP server -cgc mcp start - -# Expected output: -# CodeGraphContext MCP Server -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# ✓ Server started -# ✓ Protocol: Model Context Protocol (MCP) -# ✓ Transport: stdio -# ✓ Database: FalkorDB connected -# ✓ Repository: /home/user/projects/my-project -# ✓ Watching for file changes: enabled -# -# Available tools: 18 -# - find_code -# - analyze_code_relationships -# - find_dead_code -# - execute_cypher_query -# ... (14 more) -# -# Server ready. Listening for requests... -``` - -**Note**: Keep this terminal open, or run in background: - -```bash -# Run in background (Linux/Mac) -cgc mcp start & - -# Or use tmux/screen for persistent session -tmux new -s cgc -cgc mcp start -# Press Ctrl+B, then D to detach -``` - -### Step 6: Verify MCP Integration - -**In Cursor/VS Code:** - -1. Open your IDE -2. Start a new chat with AI assistant -3. Ask: "What CodeGraphContext tools do you have?" - -**Expected AI Response:** -> "I have access to 18 CodeGraphContext tools: -> -> **Code Discovery:** -> - find_code - Search for functions, classes, files -> - list_indexed_repositories - Show indexed projects -> -> **Code Analysis:** -> - analyze_code_relationships - Find callers, callees, call chains -> - find_dead_code - Find unused functions -> - find_most_complex_functions - Find complex code -> - calculate_cyclomatic_complexity - Measure function complexity -> -> **Graph Queries:** -> - execute_cypher_query - Run custom graph queries -> - visualize_graph_query - Generate graph visualizations -> -> ... (10 more tools) -> -> How can I help you explore your codebase?" - -**Test with a real query:** - -Ask AI: "Find all functions that call `authenticate`" - -**Expected AI Response:** -> "I found 12 functions that call `authenticate`: -> -> **Direct callers (3):** -> 1. `login` in api/auth.py:45 -> 2. `verify_token` in middleware/auth.py:89 -> 3. `refresh_session` in api/session.py:123 -> -> **Indirect callers (9 more):** -> ... (AI lists them) -> -> Would you like me to show the call chain for any of these?" - -**✅ MCP Setup Complete!** Your AI assistant now has code intelligence. - ---- - -## Every-Time Workflow: CLI Users - -**Time Required**: 2-5 minutes per session -**Frequency**: Daily/as needed - -### Typical Daily Workflow - -#### **Morning: Start of Day** - -```bash -# 1. Navigate to your project -cd ~/projects/my-project - -# 2. Pull latest changes -git pull origin main - -# 3. Update the code graph (if files changed) -cgc reindex - -# Output: -# Detecting changes... -# ✓ 12 files modified -# ✓ 3 files added -# ✓ 1 file deleted -# -# Updating graph... -# ✓ Updated 45 nodes -# ✓ Updated 123 relationships -# -# Reindexing completed in 3.2 seconds -``` - -**Alternative**: Use auto-watch mode (set once, forget it) - -```bash -# Enable auto-watch for this repository -cgc watch . - -# Output: -# ✓ Watching: /home/user/projects/my-project -# ✓ Auto-reindex on file changes: enabled -# -# The graph will automatically update when files change. -# Press Ctrl+C to stop watching. -``` - -#### **During Development: Common Tasks** - -**Task 1: Understanding a new feature** - -```bash -# Find the entry point -cgc find "handle_payment" --type function - -# See what it calls -cgc analyze callees handle_payment - -# See the full execution flow -cgc analyze chain handle_payment process_payment -``` - -**Task 2: Before refactoring** - -```bash -# Find all usages of a function -cgc analyze callers calculate_total - -# Check complexity -cgc analyze complexity calculate_total - -# Find dead code to remove first -cgc analyze dead-code -``` - -**Task 3: Code review** - -```bash -# Check what a PR changes -git diff main...feature-branch --name-only > changed_files.txt - -# For each changed file, check impact -cgc analyze callers MyChangedClass - -# Verify test coverage -cgc find "test_my_changed_class" --type function -``` - -**Task 4: Debugging** - -```bash -# Find where a variable is modified -cgc analyze modifies user_session - -# Trace execution path -cgc analyze chain api_endpoint database_query - -# Find similar code patterns -cgc find "try.*except.*pass" --regex -``` - -#### **End of Day: Cleanup** - -```bash -# Optional: Clear cache if needed -cgc clean --cache-only - -# Optional: Export graph for documentation -cgc visualize --output daily_architecture.html -``` - -### Quick Reference: Most Used Commands - -```bash -# Search -cgc find # Find code elements -cgc find --type function # Find only functions -cgc find --type class # Find only classes - -# Analysis -cgc analyze callers # Who calls this? -cgc analyze callees # What does this call? -cgc analyze chain # Call path from A to B -cgc analyze dead-code # Find unused code -cgc analyze complexity # Find complex functions - -# Visualization -cgc visualize # Generate architecture diagram -cgc stats # Show repository statistics - -# Maintenance -cgc reindex # Update after code changes -cgc watch . # Auto-update on file changes -``` - ---- - -## Every-Time Workflow: MCP Users - -**Time Required**: 1-2 minutes per session -**Frequency**: Daily/as needed - -### Typical Daily Workflow - -#### **Morning: Start of Day** - -```bash -# Option 1: Start MCP server manually -cgc mcp start - -# Option 2: Auto-start with system (set once) -# Add to ~/.bashrc or ~/.zshrc: -# cgc mcp start & - -# Option 3: Use tmux/screen for persistent session -tmux attach -t cgc || tmux new -s cgc "cgc mcp start" -``` - -**Verify server is running:** - -```bash -# Check if MCP server is running -ps aux | grep "cgc mcp" - -# Expected output: -# user 12345 0.1 0.5 cgc mcp start -``` - -#### **During Development: Natural Language Queries** - -Open your IDE and ask your AI assistant: - -**Understanding Code:** -- "What does the `process_payment` function do?" -- "Show me all functions that call `authenticate`" -- "What's the call chain from the API endpoint to the database?" - -**Before Refactoring:** -- "What will break if I change `calculate_total`?" -- "Find all dead code in the auth module" -- "Show me the most complex functions" - -**Code Review:** -- "What's the impact of changing `UserService`?" -- "Are there any circular dependencies?" -- "Find all functions that access the database" - -**Debugging:** -- "Trace the execution from `handle_request` to `send_email`" -- "Who modifies the `session_state` variable?" -- "Find all error handling code" - -#### **End of Day: Cleanup** - -```bash -# Optional: Stop MCP server if running manually -# Press Ctrl+C in the terminal running cgc mcp start - -# Or kill the process -pkill -f "cgc mcp start" -``` - -### Integration with AI Workflows - -**Example 1: Feature Development** - -1. **Ask AI**: "I need to add a new payment method. Show me how existing payment methods are implemented." -2. **AI uses CGC**: Finds `PaymentMethod` class, shows all implementations -3. **AI generates**: Boilerplate code following existing patterns -4. **You review**: AI-generated code with full context - -**Example 2: Bug Investigation** - -1. **Ask AI**: "Users report checkout fails for international orders. Help me debug." -2. **AI uses CGC**: Traces call chain from checkout to payment processing -3. **AI identifies**: Missing email notification call (like in User Journey #2) -4. **AI suggests**: Exact fix with line numbers - -**Example 3: Refactoring** - -1. **Ask AI**: "I want to refactor `OldAuthService` to `NewAuthService`. Create a migration plan." -2. **AI uses CGC**: Finds all 47 usages, analyzes complexity -3. **AI generates**: Step-by-step migration plan with risk assessment -4. **You execute**: Safe, incremental migration - ---- - -## Troubleshooting - -### Common Issues - -#### **Issue 1: `cgc` command not found** - -**Symptoms:** -```bash -cgc --version -# bash: cgc: command not found -``` - -**Solution:** -```bash -# Find where pip installed cgc -pip show codegraphcontext | grep Location - -# Add to PATH -export PATH="$HOME/.local/bin:$PATH" - -# Make permanent (add to ~/.bashrc or ~/.zshrc) -echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc -source ~/.bashrc -``` - -#### **Issue 2: Indexing fails with "Permission denied"** - -**Symptoms:** -```bash -cgc index . -# Error: Permission denied: /some/file.py -``` - -**Solution:** -```bash -# Check file permissions -ls -la /some/file.py - -# Option 1: Fix permissions -chmod +r /some/file.py - -# Option 2: Skip problematic files -cgc index . --ignore-errors -``` - -#### **Issue 3: MCP server not connecting** - -**Symptoms:** -- AI assistant says "CodeGraphContext tools not available" -- MCP server starts but IDE doesn't see it - -**Solution:** - -```bash -# 1. Verify server is running -cgc mcp start -# Should show "Server ready. Listening for requests..." - -# 2. Check MCP configuration -cat ~/.config/cursor/mcp.json -# Should contain "codegraphcontext" entry - -# 3. Restart IDE completely -# Not just reload - full quit and reopen - -# 4. Check IDE logs -# Cursor: Help > Toggle Developer Tools > Console -# Look for MCP connection errors - -# 5. Test with manual MCP call -echo '{"method":"tools/list"}' | cgc mcp start -# Should return list of 18 tools -``` - -#### **Issue 4: Slow indexing on large repositories** - -**Symptoms:** -```bash -cgc index . -# Takes > 5 minutes on 100k+ LOC repository -``` - -**Solution:** - -```bash -# Option 1: Use Neo4j instead of FalkorDB -cgc neo4j setup -cgc --database neo4j index . - -# Option 2: Exclude test files and dependencies -cgc config set IGNORE_DIRS "tests,node_modules,venv,.venv" -cgc index . - -# Option 3: Index in parallel -cgc config set PARALLEL_WORKERS 4 -cgc index . - -# Option 4: Use pre-built bundle if available -cgc load -``` - -#### **Issue 5: Graph out of sync with code** - -**Symptoms:** -- CGC shows old function names -- Missing new files - -**Solution:** - -```bash -# Full reindex -cgc reindex --force - -# Or use watch mode to auto-sync -cgc watch . -``` - -### Getting Help - -```bash -# Built-in help -cgc help -cgc help - -# Check version -cgc --version - -# Enable debug logging -cgc --debug index . - -# Report issues -# GitHub: https://github.com/CodeGraphContext/CodeGraphContext/issues -``` - ---- - -## Next Steps - -- **Learn by example** → [USER_JOURNEYS.md](./user_journeys.md) -- **Detailed use cases** → [USE_CASES_DETAILED.md](./use_cases_detailed.md) -- **Integration patterns** → [INTEGRATION_GUIDE.md](./integration_guide.md) -- **CLI reference** → [CLI Reference](reference/cli_master.md) -- **MCP reference** → [MCP Reference](reference/mcp_master.md) - ---- - -## Summary: Quick Start Cheat Sheet - -### First Time (CLI) -```bash -pip install codegraphcontext -cd ~/my-project -cgc index . -cgc stats -``` - -### First Time (MCP) -```bash -pip install codegraphcontext -cd ~/my-project -cgc index . -cgc mcp setup -cgc mcp start -# Restart IDE -``` - -### Every Time (CLI) -```bash -cd ~/my-project -cgc reindex # or cgc watch . -cgc analyze callers -``` - -### Every Time (MCP) -```bash -cgc mcp start # or auto-start -# Ask AI questions in natural language -``` - -**That's it! You're ready to use CodeGraphContext.** 🚀 diff --git a/docs/docs/stylesheets/redwood.css b/docs/docs/stylesheets/redwood.css new file mode 100644 index 00000000..f518b90d --- /dev/null +++ b/docs/docs/stylesheets/redwood.css @@ -0,0 +1,1091 @@ +/* ============================================================ + CodeGraphContext — Oracle Redwood Design System + ============================================================ */ + +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Space+Mono:wght@400;700&display=swap'); + +/* --- Design Tokens ----------------------------------------- */ +:root { + /* Primary Palette */ + --color-accent: #C74634; + --color-accent-hover: #a93a2b; + --color-info: #185FA5; + + /* Surfaces — Light */ + --color-background-primary: #ffffff; + --color-background-secondary: #f5f5f5; + --color-background-tertiary: #eaeaea; + + /* Text — Light */ + --color-text-primary: #1a1a1a; + --color-text-secondary: #555555; + --color-text-tertiary: #888888; + + /* Borders — Light */ + --color-border: #e0e0e0; + --color-border-light: #eeeeee; + + /* Code pill backgrounds */ + --color-cmd-bg: #FEF3F1; + --color-cmd-text: #C74634; + --color-flag-bg: #E6F1FB; + --color-flag-text: #185FA5; + + /* Callout */ + --color-callout-bg: #FEF3F1; + --color-callout-border: #C74634; + + /* Typography */ + --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-heading: 'Outfit', var(--font-sans); + --font-mono: 'Fira Code', "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + + /* Layout */ + --sidebar-width: 200px; + --masthead-height: 44px; +} + +/* --- Dark Mode --------------------------------------------- */ +[data-theme="dark"] { + --color-background-primary: #1a1a1a; + --color-background-secondary: #242424; + --color-background-tertiary: #2e2e2e; + + --color-text-primary: #e8e8e8; + --color-text-secondary: #b0b0b0; + --color-text-tertiary: #777777; + + --color-border: #333333; + --color-border-light: #2a2a2a; + + --color-cmd-bg: #2b1c19; + --color-cmd-text: #e6735e; + --color-flag-bg: #162840; + --color-flag-text: #6dafe6; + + --color-callout-bg: #2b1c19; + --color-callout-border: #C74634; +} + +@media (prefers-color-scheme: dark) { + :root:not([data-theme="light"]) { + --color-background-primary: #1a1a1a; + --color-background-secondary: #242424; + --color-background-tertiary: #2e2e2e; + + --color-text-primary: #e8e8e8; + --color-text-secondary: #b0b0b0; + --color-text-tertiary: #777777; + + --color-border: #333333; + --color-border-light: #2a2a2a; + + --color-cmd-bg: #2b1c19; + --color-cmd-text: #e6735e; + --color-flag-bg: #162840; + --color-flag-text: #6dafe6; + + --color-callout-bg: #2b1c19; + --color-callout-border: #C74634; + } +} + +/* --- Reset & Base ------------------------------------------ */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { + font-size: 18px; + line-height: 1.6; + -webkit-font-smoothing: antialiased; +} + +body { + font-family: var(--font-sans); + color: var(--color-text-primary); + background: var(--color-background-secondary); + min-height: 100vh; + display: flex; + justify-content: center; + padding: 0; +} + + +/* --- App Container ----------------------------------------- */ +.rw-app-container { + width: 100%; + max-width: 100%; + background: var(--color-background-primary); + border-radius: 0; + box-shadow: none; + border: none; + border-bottom: 1px solid var(--color-border); + overflow: hidden; + display: flex; + flex-direction: column; + position: relative; + min-height: 100vh; +} + +/* --- Masthead ---------------------------------------------- */ +.rw-masthead { + position: relative; + height: var(--masthead-height); + background: #C74634; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 20px; + z-index: 100; +} + +.rw-masthead__left { + display: flex; + align-items: center; + gap: 6px; + color: #ffffff; + font-size: 14px; + font-weight: 600; +} + +.rw-masthead__logo-img { + width: 24px; + height: 24px; + border-radius: 4px; + flex-shrink: 0; +} + +.rw-masthead__sep { + margin: 0 4px; + opacity: 0.6; + font-weight: 300; +} + +.rw-masthead__label { + font-weight: 400; + opacity: 0.9; +} + +.rw-masthead__right { + display: flex; + align-items: center; + gap: 12px; +} + +.rw-theme-toggle { + background: none; + border: none; + color: #ffffff; + cursor: pointer; + padding: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.2s; + opacity: 0.8; +} +.rw-theme-toggle:hover { + opacity: 1; +} + +.rw-masthead__github { + color: #ffffff; + display: flex; + align-items: center; + justify-content: center; + padding: 4px; + transition: opacity 0.2s; + opacity: 0.8; +} +.rw-masthead__github:hover { + opacity: 1; + text-decoration: none; +} + +/* --- Doc Shell (sidebar + content) ------------------------- */ +.rw-shell { + display: flex; + margin-top: 0; + min-height: calc(100vh - 80px - var(--masthead-height)); +} + +/* --- Sidebar ----------------------------------------------- */ +.rw-sidebar { + width: var(--sidebar-width); + flex-shrink: 0; + background: var(--color-background-primary); + border-right: 1px solid var(--color-border); + padding: 16px 0; + position: relative; + overflow-y: auto; +} + +.rw-sidebar__group { + margin-bottom: 16px; +} + +.rw-sidebar__heading { + font-family: var(--font-heading); + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--color-text-tertiary); + background: var(--color-background-secondary); /* Background difference for categories */ + padding: 8px 16px; + margin-bottom: 6px; + border-top: 1px solid var(--color-border-light); + border-bottom: 1px solid var(--color-border-light); +} + +.rw-sidebar__group:first-child .rw-sidebar__heading { + border-top: none; +} + +.rw-sidebar__link { + display: block; + padding: 6px 16px; + font-size: 15px; + color: var(--color-text-secondary); + text-decoration: none; + border-left: 3px solid transparent; + transition: background 0.12s, color 0.12s; +} +.rw-sidebar__link:hover { + background: var(--color-background-secondary); + color: var(--color-text-primary); + text-decoration: none; +} +.rw-sidebar__link--active { + border-left-color: #C74634; + background: #f5f5f5; + color: #C74634; + font-weight: 500; +} +[data-theme="dark"] .rw-sidebar__link--active { + background: #2b1c19; + color: #e6735e; +} +@keyframes rw-slide-down { + from { max-height: 0; opacity: 0; transform: translateY(-8px); } + to { max-height: 2000px; opacity: 1; transform: translateY(0); } +} + +.rw-sidebar__subgroup { + margin-top: 4px; + margin-bottom: 8px; + max-height: 0; + overflow: hidden; + opacity: 0; + transition: max-height 0.6s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.4s ease; +} + +.rw-sidebar__link--active + .rw-sidebar__subgroup { + max-height: 2000px; + opacity: 1; + animation: rw-slide-down 0.7s cubic-bezier(0.4, 0, 0.2, 1) forwards; +} +.rw-sidebar__sublink { + display: block; + padding: 5px 16px 5px 32px; + font-size: 13px; + font-style: italic; + color: var(--color-text-tertiary); + text-decoration: none; + transition: color 0.12s, opacity 0.5s ease, transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); + opacity: 0; + transform: translateX(-10px); +} + +.rw-sidebar__link--active + .rw-sidebar__subgroup .rw-sidebar__sublink { + opacity: 0.85; + transform: translateX(0); +} + +.rw-sidebar__sublink:hover { + color: var(--color-text-primary); + opacity: 1 !important; + transform: translateX(2px); +} + +.rw-sidebar__sublink:nth-child(1) { transition-delay: 0.1s; } +.rw-sidebar__sublink:nth-child(2) { transition-delay: 0.15s; } +.rw-sidebar__sublink:nth-child(3) { transition-delay: 0.2s; } +.rw-sidebar__sublink:nth-child(4) { transition-delay: 0.25s; } +.rw-sidebar__sublink:nth-child(5) { transition-delay: 0.3s; } +.rw-sidebar__sublink:nth-child(6) { transition-delay: 0.35s; } +.rw-sidebar__sublink:nth-child(7) { transition-delay: 0.4s; } +.rw-sidebar__sublink:nth-child(8) { transition-delay: 0.45s; } + +.rw-sidebar__sublink--h3 { + padding-left: 44px; + font-size: 12px; +} + +/* --- Content Area ------------------------------------------ */ +.rw-content { + margin-left: 0; /* Fixed the massive gap between sidebar and content */ + flex: 1; + min-width: 0; + padding: 40px; + max-width: 900px; + margin-right: auto; +} + +/* --- Breadcrumb -------------------------------------------- */ +.rw-breadcrumb { + font-family: 'Space Mono', monospace; + font-size: 11px; + margin-bottom: 20px; + color: var(--color-text-tertiary); + letter-spacing: 0.02em; +} +.rw-breadcrumb a { + color: var(--color-accent); +} +.rw-breadcrumb__sep { + margin: 0 4px; + color: var(--color-text-tertiary); +} + +/* --- Page Title -------------------------------------------- */ +.rw-title { + font-family: var(--font-heading); + font-size: 24px; + font-weight: 600; + color: var(--color-text-primary); + margin-bottom: 8px; + letter-spacing: -0.01em; +} + +.rw-subtitle { + font-size: 14px; + color: var(--color-text-secondary); + line-height: 1.6; + margin-bottom: 28px; +} + +/* --- Section Headings -------------------------------------- */ +.md-typeset h1, +.md-typeset h2, +.md-typeset h3, +.md-typeset h4, +.md-typeset h5, +.md-typeset h6 { + color: var(--color-text-primary) !important; + font-family: var(--font-heading) !important; +} + +.rw-heading { + font-size: 16px; + font-weight: 500; + color: var(--color-text-primary); + margin: 28px 0 12px; + padding-bottom: 4px; + display: inline-block; + border-bottom: 2px solid #C74634; +} + +.rw-subheading { + font-size: 13px; + font-weight: 600; + color: var(--color-text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + margin: 24px 0 10px; +} + +/* --- Body Text --------------------------------------------- */ +.rw-text { + font-size: 14.5px; + color: var(--color-text-secondary); + line-height: 1.6; + margin-bottom: 14px; +} + +.rw-text strong { + color: var(--color-text-primary); + font-weight: 500; +} + +/* --- Lists ------------------------------------------------- */ +.rw-list { + font-size: 14.5px; + color: var(--color-text-secondary); + line-height: 1.6; + margin: 0 0 16px 20px; +} +.rw-list li { + margin-bottom: 6px; +} +.rw-list li strong { + color: var(--color-text-primary); + font-weight: 500; +} +.rw-list--ordered { + list-style: decimal; +} + +/* --- Pills (inline code tokens) ---------------------------- */ +.rw-pill-cmd { + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-cmd-text); + background: var(--color-cmd-bg); + padding: 2px 7px; + border-radius: 4px; + white-space: nowrap; +} + +.rw-pill-flag { + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-flag-text); + background: var(--color-flag-bg); + padding: 2px 7px; + border-radius: 4px; + white-space: nowrap; +} + +.rw-usage { + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-text-primary); +} + +/* --- Tables ------------------------------------------------ */ +.rw-table, .md-typeset table:not([class]) { + width: 100%; + border-collapse: collapse; + margin: 12px 0 20px; + background-color: var(--color-background-primary) !important; + color: var(--color-text-secondary) !important; + box-shadow: none !important; + border: 1px solid var(--color-border) !important; +} + +.rw-table thead th, .md-typeset table:not([class]) th { + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-text-secondary) !important; + background: var(--color-background-secondary) !important; + padding: 8px 12px; + text-align: left; + font-weight: 500; + border-bottom: 1px solid var(--color-border) !important; +} + +.rw-table tbody td, .md-typeset table:not([class]) td { + font-size: 13px; + padding: 10px 12px; + border-bottom: 0.5px solid var(--color-border-light) !important; + vertical-align: top; + color: var(--color-text-secondary) !important; +} + +.rw-table tbody tr:hover, .md-typeset table:not([class]) tbody tr:hover { + background: var(--color-background-secondary) !important; +} + +.rw-table tbody tr:last-child td, .md-typeset table:not([class]) tbody tr:last-child td { + border-bottom: none !important; +} + +/* --- Code Blocks ------------------------------------------- */ +.rw-code-container { + position: relative; + margin: 10px 0 18px; +} + +.rw-code-block { + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + border-radius: 6px; + padding: 14px 16px; + overflow-x: auto; + font-family: var(--font-mono); + font-size: 13px; + line-height: 1.5; + color: var(--color-text-primary); + margin: 0; +} + +.rw-code-copy-btn { + position: absolute; + top: 8px; + right: 8px; + background: var(--color-background-primary); + border: 1px solid var(--color-border); + color: var(--color-text-secondary); + border-radius: 4px; + padding: 4px 6px; + cursor: pointer; + opacity: 0; + transition: opacity 0.2s, background 0.1s, color 0.1s; + display: flex; + align-items: center; + justify-content: center; + z-index: 10; +} + +pre:hover .rw-code-copy-btn { + opacity: 1; +} + +.rw-code-copy-btn:hover { + background: var(--color-background-tertiary); + color: var(--color-text-primary); +} + +.rw-code-block code { + background: none; + padding: 0; +} + +.rw-code-label { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--color-text-tertiary); + margin-bottom: 6px; + display: block; +} + +/* Inline code */ +.rw-text code, .rw-list code, .rw-subtitle code { + font-family: var(--font-mono); + font-size: 12px; + background: var(--color-background-secondary); + padding: 1px 5px; + border-radius: 3px; + color: var(--color-text-primary); +} + +/* --- Callout / Note Box ------------------------------------ */ +.rw-callout { + border-left: 3px solid var(--color-callout-border); + border-radius: 0 8px 8px 0; + background: var(--color-callout-bg); + padding: 12px 16px; + margin: 18px 0; +} + +.rw-callout__label { + font-size: 11px; + text-transform: uppercase; + color: #C74634; + font-weight: 500; + margin-bottom: 4px; +} + +.rw-callout__body { + font-size: 13px; + color: var(--color-text-secondary); + line-height: 1.5; +} + +.rw-callout__body a { + color: var(--color-accent); +} + +/* Tip callout (blue variant) */ +.rw-callout--tip { + border-left-color: var(--color-info); + background: var(--color-flag-bg); +} +.rw-callout--tip .rw-callout__label { + color: var(--color-info); +} + +/* --- Horizontal Rule --------------------------------------- */ +.rw-hr { + border: none; + border-top: 1px solid var(--color-border); + margin: 24px 0; +} + +/* --- Footer ------------------------------------------------ */ +.rw-footer { + margin-left: var(--sidebar-width); + border-top: 1px solid var(--color-border); + padding: 12px 28px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.rw-footer__left { + font-family: 'Space Mono', monospace; + font-size: 11px; + color: var(--color-text-tertiary); +} + +.rw-footer__right { + display: flex; + align-items: center; + gap: 12px; +} + +.rw-footer__right a { + color: var(--color-text-tertiary); + display: flex; + align-items: center; + transition: color 0.2s; +} + +.rw-footer__right a:hover { + color: var(--color-accent); +} + +/* ============================================================= + LANDING PAGE — Hero Design + ============================================================= */ +.rw-landing-masthead { + position: relative; + height: var(--masthead-height); + background: #C74634; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 28px; + z-index: 100; +} + +.rw-landing-body { + margin-top: 0; +} + +/* Hero */ +.rw-hero { + background: var(--color-background-secondary); + border-bottom: 1px solid var(--color-border); + padding: 60px 28px 48px; + text-align: center; +} + +.rw-hero__badge { + display: inline-block; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-accent); + background: var(--color-cmd-bg); + padding: 4px 12px; + border-radius: 12px; + margin-bottom: 16px; + font-weight: 500; +} + +.rw-hero__title { + font-size: 32px; + font-weight: 600; + color: var(--color-text-primary); + margin-bottom: 12px; + letter-spacing: -0.5px; +} + +.rw-hero__desc { + font-size: 15px; + color: var(--color-text-secondary); + max-width: 600px; + margin: 0 auto 24px; + line-height: 1.6; +} + +.rw-hero__actions { + display: flex; + gap: 12px; + justify-content: center; + flex-wrap: wrap; +} + +.rw-btn { + display: inline-flex; + align-items: center; + gap: 6px; + font-family: var(--font-sans); + font-size: 13px; + font-weight: 500; + padding: 9px 20px; + border-radius: 6px; + text-decoration: none; + cursor: pointer; + transition: background 0.15s; + border: none; +} +.rw-btn:hover { text-decoration: none; } + +.rw-btn--primary { + background: #C74634; + color: #ffffff; +} +.rw-btn--primary:hover { + background: #a93a2b; +} + +.rw-btn--secondary { + background: var(--color-background-primary); + color: var(--color-text-primary); + border: 1px solid var(--color-border); +} +.rw-btn--secondary:hover { + background: var(--color-background-secondary); +} + +/* Feature Cards */ +.rw-features { + padding: 40px 28px; + max-width: 900px; + margin: 0 auto; +} + +.rw-features__title { + font-size: 18px; + font-weight: 500; + color: var(--color-text-primary); + text-align: center; + margin-bottom: 24px; +} + +.rw-features__grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 16px; +} + +.rw-feature-card { + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 20px; +} + +.rw-feature-card__icon { + font-size: 20px; + margin-bottom: 8px; +} + +.rw-feature-card__title { + font-size: 14px; + font-weight: 500; + color: var(--color-text-primary); + margin-bottom: 6px; +} + +.rw-feature-card__desc { + font-size: 13px; + color: var(--color-text-secondary); + line-height: 1.5; +} + +/* Getting Started Steps */ +.rw-steps { + padding: 32px 28px 40px; + max-width: 900px; + margin: 0 auto; + border-top: 1px solid var(--color-border); +} + +.rw-steps__title { + font-size: 18px; + font-weight: 500; + color: var(--color-text-primary); + text-align: center; + margin-bottom: 20px; +} + +.rw-steps__list { + display: flex; + gap: 16px; + flex-wrap: wrap; + justify-content: center; +} + +.rw-step { + flex: 1; + min-width: 200px; + max-width: 280px; + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 18px; +} + +.rw-step__number { + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 50%; + background: #C74634; + color: #fff; + font-size: 12px; + font-weight: 600; + margin-bottom: 8px; +} + +.rw-step__title { + font-size: 14px; + font-weight: 500; + color: var(--color-text-primary); + margin-bottom: 4px; +} + +.rw-step__desc { + font-size: 13px; + color: var(--color-text-secondary); + line-height: 1.5; +} + +.rw-step__desc a { color: var(--color-accent); } + +/* Architecture section */ +.rw-arch { + padding: 32px 28px; + max-width: 900px; + margin: 0 auto; + border-top: 1px solid var(--color-border); +} + +.rw-arch__title { + font-size: 18px; + font-weight: 500; + color: var(--color-text-primary); + text-align: center; + margin-bottom: 8px; +} + +.rw-arch__desc { + font-size: 13px; + color: var(--color-text-secondary); + text-align: center; + margin-bottom: 20px; +} + +.rw-arch__diagram { + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 24px; + font-family: var(--font-mono); + font-size: 12px; + color: var(--color-text-primary); + line-height: 1.8; + text-align: center; + overflow-x: auto; +} + +/* Why section */ +.rw-why { + padding: 32px 28px 40px; + max-width: 900px; + margin: 0 auto; + border-top: 1px solid var(--color-border); +} + +.rw-why__title { + font-size: 18px; + font-weight: 500; + color: var(--color-text-primary); + text-align: center; + margin-bottom: 20px; +} + +.rw-why__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; +} + +.rw-why__item { + text-align: center; + padding: 16px; +} + +.rw-why__item-title { + font-size: 14px; + font-weight: 500; + color: var(--color-accent); + margin-bottom: 6px; +} + +.rw-why__item-desc { + font-size: 13px; + color: var(--color-text-secondary); + line-height: 1.5; +} + +/* Landing footer */ +.rw-landing-footer { + border-top: 1px solid var(--color-border); + padding: 14px 28px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.rw-landing-footer__left { + font-size: 12px; + color: var(--color-text-tertiary); +} + +.rw-landing-footer__right { + display: flex; + gap: 16px; +} + +.rw-landing-footer__right a { + font-size: 12px; + color: var(--color-accent); +} + +/* ============================================================= + Responsive + ============================================================= */ +@media (max-width: 768px) { + .rw-sidebar { + display: none; + } + .rw-content, + .rw-footer { + margin-left: 0; + } + .rw-why__grid { + grid-template-columns: 1fr; + } + .rw-steps__list { + flex-direction: column; + align-items: center; + } +} + +/* --- Mobile Responsive ------------------------------------- */ +.rw-masthead__hamburger { + display: none; + background: none; + border: none; + color: white; + font-size: 24px; + line-height: 1; + cursor: pointer; + padding: 0 12px 0 0; +} + +@media (max-width: 768px) { + .rw-masthead__hamburger { + display: block; + } + + .rw-masthead__label { + display: none; + } + .rw-masthead__sep { + display: none; + } + .rw-masthead__github { + display: none; + } + + .rw-shell { + position: static; + } + + .rw-sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 260px; + height: 100vh; + z-index: 1000; + transform: translateX(-100%); + transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 4px 0 24px rgba(0,0,0,0.15); + background: var(--color-background-primary); + } + + .rw-sidebar.rw-sidebar--open { + transform: translateX(0); + } + + .rw-content { + padding: 24px 16px; + width: 100%; + } + + .rw-sidebar-overlay { + display: none; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.5); + z-index: 999; + opacity: 0; + transition: opacity 0.3s ease; + } + + .rw-sidebar-overlay.rw-sidebar-overlay--active { + display: block; + opacity: 1; + } +} + +/* --- License & Prose ------------------------------------- */ +.rw-license-card { + background: var(--color-background-secondary); + border: 1px solid var(--color-border); + padding: 48px; + margin-top: 32px; + font-family: var(--font-mono); + font-size: 14px; + line-height: 1.8; + color: var(--color-text-secondary); + max-width: 850px; + position: relative; + overflow: hidden; +} + +.rw-license-card::before { + content: ""; + position: absolute; + top: 0; left: 0; width: 4px; height: 100%; + background: var(--color-accent); +} + +.rw-license-header-group { + margin-bottom: 40px; + border-bottom: 1px solid var(--color-border); + padding-bottom: 24px; +} + +.rw-license-type { + font-family: var(--font-mono); + text-transform: uppercase; + letter-spacing: 2px; + font-size: 12px; + color: var(--color-accent); + margin-bottom: 8px; + display: block; +} + +.rw-license-title { + font-family: var(--font-sans); + font-weight: 800; + font-size: 32px; + color: var(--color-text-primary); + margin: 0; +} + +.rw-license-meta { + font-family: var(--font-mono); + font-size: 13px; + color: var(--color-text-tertiary); + margin-top: 8px; +} + +.rw-license-body { + white-space: pre-wrap; +} diff --git a/docs/docs/troubleshooting.md b/docs/docs/troubleshooting.md deleted file mode 100644 index c8b3de6e..00000000 --- a/docs/docs/troubleshooting.md +++ /dev/null @@ -1,123 +0,0 @@ -# CodeGraphContext Troubleshooting Guide - -Use this checklist whenever `cgc mcp setup` or `cgc mcp start` doesn’t behave as expected. It keeps the happy path short, but includes the fallback steps when something goes wrong. - -## 1. Prerequisites at a glance - -- **Windows + PowerShell** commands below assume the `py` launcher. Adapt to `python3` if you're on macOS/Linux. -- **Python 3.12+** (recommended for FalkorDB Lite support). Run `py -3.12 --version` to confirm. -- **Database Options:** - - **FalkorDB Lite** (default for Unix/Linux/macOS, Python 3.12+): No setup required, works out of the box. - - **Neo4j** (required for Windows, optional for others): Requires Docker, WSL, or native installation. Setup via `cgc neo4j setup`. - -## 2. Create and activate a virtual environment - -From the repository root (`CodeGraphContext/`): - -```powershell -py -3.11 -m venv venv -.\venv\Scripts\python.exe -m pip install --upgrade pip -``` - -- On Windows, Neo4j driver 6.x can crash with `AttributeError: socket.EAI_ADDRFAMILY`. If you see that, run: - ```powershell - .\venv\Scripts\python.exe -m pip install "neo4j<6" - ``` - -## 3. Run the Neo4j setup wizard (optional for Unix, required for Windows) - -**Note:** If you're on Unix/Linux/macOS with Python 3.12+, FalkorDB Lite is already your default database. You can skip this step unless you prefer Neo4j. - -**For Windows users or those preferring Neo4j**, launch the wizard: - -```powershell -.\venv\Scripts\cgc.exe neo4j setup -``` - -> **Tip:** If you want the wizard to spin up a local Neo4j instance for you, make sure **Docker Desktop** is installed and running before you launch `cgc neo4j setup`. If Docker isn't running, the setup wizard will fail when it tries to install Neo4j locally. - -What happens next: - -- The wizard checks for Docker. If it's running, it can auto-provision a local Neo4j instance for you. -- Alternatively, you can supply credentials for an existing Neo4j AuraDB database. -- At the end, it generates: - - `mcp.json` in your project directory (stores the MCP server command + env vars). - - `~/.codegraphcontext/.env` containing `NEO4J_URI`, `NEO4J_USERNAME`, `NEO4J_PASSWORD`. - -Make sure the Docker container (or remote Neo4j) is still running before you start the server. - -## 4. Start the MCP server - -Once the wizard completes successfully: - -```powershell -.\venv\Scripts\cgc.exe mcp start -``` - -Expected output includes: - -```text -Starting CodeGraphContext Server... -... -MCP Server is running. Waiting for requests... -``` - -If you instead see: - -```text -Configuration Error: Neo4j credentials must be set via environment variables -``` - -then either no credentials were saved, or the wizard was skipped—see the manual alternative below. - -## 5. Manual credential setup (fallback) - -If you prefer not to use the wizard or need to fix a broken configuration: - -1. Create a `mcp.json` (or edit the one that exists) in the repository root: - - ```json - { - "mcpServers": { - "CodeGraphContext": { - "command": "cgc", - "args": ["mcp", "start"], - "env": { - "NEO4J_URI": "neo4j+s://YOUR-HOSTNAME:7687", - "NEO4J_USERNAME": "neo4j", - "NEO4J_PASSWORD": "super-secret-password" - } - } - } - } - ``` - -2. (Optional) Also create `%USERPROFILE%\.codegraphcontext\.env` with the same key/value pairs. The CLI loads that file automatically. - -3. Re-run: - - ```powershell - .\venv\Scripts\cgc.exe mcp start - ``` - -## 6. Common issues & fixes - -| Symptom | Likely Cause | Fix | -| --- | --- | --- | -| `Configuration Error: Neo4j credentials must be set…` | `mcp.json`/`.env` missing or empty | Run `cgc neo4j setup` again **with Docker running**, or create the files manually (section 5). | -| `AttributeError: socket.EAI_ADDRFAMILY` | Neo4j 6.x bug on Windows | Install the 5.x driver: `.\venv\Scripts\python.exe -m pip install "neo4j<6"` and retry. | -| Setup wizard fails while pulling Docker image | Docker Desktop not running or Docker permissions missing | Start Docker Desktop, wait for it to report “Running”, then rerun `cgc neo4j setup`. | -| Server exits immediately with no log | Neo4j instance is offline | Check Docker container status or AuraDB dashboard; restart Neo4j and call `cgc mcp start` again. | - -## 7. After the server is running - -- Keep the virtual environment active whenever you run `cgc` commands. -- Use `pytest` from the same env to run tests: - - ```powershell - .\venv\Scripts\pytest - ``` - -- Front-end website lives under `website/` if you need to run `npm run dev`. - -When in doubt, re-run the wizard with Docker active—it regenerates the configuration files without touching your code. Let me know if any section needs clarifying! :) \ No newline at end of file diff --git a/docs/docs/use_cases.md b/docs/docs/use_cases.md deleted file mode 100644 index a3a30e37..00000000 --- a/docs/docs/use_cases.md +++ /dev/null @@ -1,66 +0,0 @@ -# Real World Use Cases - -CodeGraphContext can be a powerful ally in your daily coding journey. Here are some real-world scenarios where it can significantly boost your productivity and understanding of a codebase: - -1. **Onboarding New Developers:** A new team member can quickly get up to speed by asking questions like: - - "Where is the authentication logic handled?" - - "Show me the main entry point of the application." - -2. **Impact Analysis:** Before making a change, assess the potential ripple effects: - - "What other parts of the code will be affected if I change the `calculate_total` function?" - -3. **Code Review:** Gain context on pull requests faster: - - "Show me the callers of this new function." - - "Does this change introduce any new dependencies?" - -4. **Debugging:** Trace execution flows to pinpoint the source of a bug: - - "Show me the call chain from `handle_request` to `process_payment`." - -5. **Refactoring:** Identify and plan large-scale code changes: - - "Find all instances of the deprecated `OldApiClass`." - - "List all functions that use the `urllib` library so I can replace them with `requests`." - -6. **Identifying Code Smells:** Proactively find areas that need improvement: - - "Find the 10 most complex functions in the codebase." - - "Show me functions with more than 5 arguments." - -7. **Security Audits:** Search for potentially vulnerable code patterns: - - "Find all functions that use the `eval` function." - - "Show me where raw SQL queries are being executed." - -8. **Automated Documentation:** Use the tool's understanding of the code to generate documentation. (This is what this agent is doing!) - -9. **Dependency Management:** Understand how your project uses its dependencies: - - "Which files import the `requests` library?" - - "Are there any circular dependencies between modules?" - -10. **Cleaning Up Unused Code:** Keep your codebase lean and maintainable: - - "Is there any dead or unused code in this project?" - -11. **Exploring a Large Codebase:** Navigate large, unfamiliar projects with ease: - - "List all the classes in the `core` module." - - "What are the top-level functions in the `utils` directory?" - -12. **Enforcing Coding Standards:** Check for adherence to team conventions: - - "Find all functions that are not decorated with `@log_execution`." - - "Show me all public methods that don't have a docstring." - -13. **Discovering API Usage:** Find examples of how to use internal or external APIs: - - "Show me how other parts of the code use the `UserService`." - -14. **Improving Test Coverage:** Identify areas that may lack test coverage: - - "Find all functions that are not called by any test files." - -15. **Visualizing Code Structure:** Get a bird's-eye view of your project's architecture: - - "Generate a graph visualization of the `auth` module and its dependencies." - -16. **Learning a New Framework:** Understand how a new framework operates by exploring its source code. - -17. **Code Archeology:** Investigate legacy code to understand its history and purpose. - -18. **Planning a Migration:** Identify all points of contact when migrating a library or framework: - - "Find all the places where the `old_payment_gateway` is used." - -19. **Knowledge Sharing:** Use the code graph as a centralized, always-up-to-date knowledge base for your team. - -20. **Automated Code Reviews:** Integrate CodeGraphContext into your CI/CD pipeline to automatically flag potential issues in pull requests. diff --git a/docs/docs/use_cases_detailed.md b/docs/docs/use_cases_detailed.md deleted file mode 100644 index 054242ff..00000000 --- a/docs/docs/use_cases_detailed.md +++ /dev/null @@ -1,279 +0,0 @@ -# Real-World Use Cases & ROI - -This guide demonstrates **10 concrete scenarios** where CodeGraphContext delivers immediate value. - -Each case compares the **Traditional Manual Approach** vs. the **CodeGraphContext Automated Approach**, highlighting time and cost savings. - ---- - -## Use Case 1: Safe Refactoring -**Scenario**: Renaming a critical function `execute_cypher_query` used throughout usage. - -| Context | Problem | -| :--- | :--- | -| **Role** | Senior Engineer ($75/hr) | -| **Task** | Rename widely used function | -| **Risk** | Breaking indirect callers | - -**The Manual Way** -1. **Grep**: `grep -r "execute_cypher_query" .` (Finds 150+ lines, many false positives). -2. **Filter**: Manually check each line to ignore strings/comments. -3. **Trace Indirect**: Grep for callers of callers (The "Rabbit Hole"). -4. **Fix & Pray**: Rename and hope tests catch everything. - -**Result**: 3 hours wasted. High risk of regression. - -**The Automated Way** -**Command**: -```bash -cgc analyze callers execute_cypher_query --all -``` - -**Output**: - -* Returns **exact list** of 50 functions. -* Shows **indirect impact** (who calls the functions that call this?). -* **0 False Positives** (ignores comments/strings). - -**Result**: Done in **20 minutes**. 100% confidence. - -!!! success "ROI Impact" - * **Time Saved**: 2.5 Hours - * **Cost Saved**: $200 - * **ROI**: 800% - ---- - -## Use Case 2: Code Cleanup -**Scenario**: identifying dead code before a major release. - -| Context | Problem | -| :--- | :--- | -| **Role** | Mid-level Engineer ($60/hr) | -| **Task** | Delete unused legacy features | -| **Risk** | Deleting something that is actually used dynamically | - -**The Manual Way** -1. **List Candidates**: Guess which functions look unused. -2. **Search**: Grep for each function name. -3. **Validate**: Manually verify if usages are real or just tests. -4. **Hesitate**: "I better keep it just in case." - -**Result**: 8 hours. Codebase remains bloated. - -**The Automated Way** -**Command**: -```bash -cgc analyze dead-code --exclude-decorated @app.route -``` - -**Output**: - -* Lists **50 functions** with **0 callers**. -* Automatically excludes API endpoints (via decorators). -* Provides **Risk Score** for each deletion. - -**Result**: Cleanup completed in **45 minutes**. - -!!! success "ROI Impact" - * **Time Saved**: 7 Hours - * **Cost Saved**: $420 - * **Lines Removed**: 2,345 - * **ROI**: 933% - ---- - -## Use Case 3: Technical Debt Assessment -**Scenario**: Prioritizing refactoring for next quarter. - -**The Manual Way** -* "I feel like `billing.py` is messy." -* "Let's refactor the User class." -* **Result**: Refactoring code that nobody uses. Wasted effort. - -**The Automated Way** -**Command**: -```bash -cgc analyze complexity --limit 20 --with-callers -``` - -**Output (Matrix)**: - -| Function | Complexity | Usage (Callers) | Priority | -| :--- | :--- | :--- | :--- | -| `processPayment` | 45 (High) | 34 (High) | **CRITICAL** | -| `old_export` | 40 (High) | 1 (Low) | **LOW** | - -**Result**: Focus engineering time where it matters (High Complexity + High Usage). - -!!! success "ROI Impact" - * **Time Saved**: 3.5 Hours (Analysis) - * **Value**: Prevented weeks of wasted refactoring on low-impact code. - ---- - -## Use Case 4: Onboarding New Developers -**Scenario**: Junior dev needs to fix a bug in Auth. - -**The Manual Way** -1. Read README (outdated). -2. Try to trace code manually. -3. Get stuck. -4. **Interrupt Senior Dev**: "Hey, how does auth flow work?" (30 min distraction). - -**The Automated Way** -**Command**: -```bash -cgc visualize --focus auth -``` -**Action**: - -* Developer explores interactive map. -* Sees `Login` -> `Validate` -> `DB`. -* Understands flow **independently**. - -!!! success "ROI Impact" - * **Total Savings**: $237.50 - * **Senior Dev Time Saved**: 30 mins (Most expensive resource). - * **Onboarding Speed**: Days -> Hours. - ---- - -## Use Case 5: Bug Investigation -**Scenario**: "International emails not sending." - -**The Manual Way** -1. Add print statements. -2. Reproduce locally (hard). -3. Step through debugger for hours. - -**The Automated Way** -**Command**: -```bash -cgc analyze callees processInternationalPayment -``` -**Output**: - -* List: `validateCurrency`, `chargeCard`, `logTransaction`. -* **Missing**: `sendEmail` is NOT in the list! - -**Diagnosis**: "Ah, we forgot to call `sendEmail` in the international flow." -**Time**: 5 seconds. - -!!! success "ROI Impact" - * **Time Saved**: 3.5 Hours - * **Cost Saved**: $228 - * **ROI**: 700% - ---- - -## Use Case 6: API Deprecation -**Scenario**: Deprecating `add_package_v1`. - -**The Manual Way** -* Grep and hope. -* Miss one usage in a utility script. -* **Production Incident**: Script fails after upgrade. - -**The Automated Way** -**Command**: -```bash -cgc analyze callers add_package_v1 --all -``` -**Output**: - -* Detailed report of **every single usage**, including tests and scripts. -* Migration Plan generated automatically. - ---- - -## Use Case 7: Architecture Review -**Scenario**: Preparing for SOC 2 Audit. - -**The Manual Way** -* Manually draw diagrams in Lucidchart. -* Diagrams are outdated the next day. - -**The Automated Way** -**Command**: -```bash -cgc visualize --mode architecture --output soc2_report.html -``` -**Output**: - -* Live, always-up-to-date architecture diagram. -* Highlights **Circular Dependencies** automatically. - ---- - -## Use Case 8: Security Audit (HIPAA) -**Scenario**: "List all places where we access patient data." - -**The Manual Way** -* Manual code audit (Weeks unique). - -**The Automated Way** -**Query**: -```cypher -MATCH (f:Function)-[:CALLS]->(db:Database) -RETURN f.name -``` -**Output**: - -* Exact list of all 20 data-access functions. -* Confidence: 100%. - -!!! success "ROI Impact" - * **Time Saved**: 4.5 Hours - * **Cost Saved**: $883 - * **Benefit**: Passed Audit. - ---- - -## Use Case 9: Performance Optimization -**Scenario**: Indexing is slow. - -**The Manual Way** -* Profiling everywhere. - -**The Automated Way** -**Analysis**: - -* Found `create_node` called inside a double loop via call graph analysis. -* Refactored to batch mode. -* **Result**: 10x Speedup. - ---- - -## Use Case 10: Code Review Impact -**Scenario**: Reviewing a PR with changes to `graph_builder.py`. - -**The Manual Way** -* Read diff. Look at changed lines. -* "Looks good to me." (Misses that this function is called by 50 other modules). - -**The Automated Way** -**Command**: -```bash -cgc analyze callers process_file -``` -**Output**: - -* "Warning: `process_file` is used by 15 modules. Only 2 are covered by tests." - -**Action**: Request changes. "Please add integration tests." - -!!! success "ROI Impact" - * **Time Saved**: 2 Hours - * **Value**: Prevented Production Incident. - ---- - -## Summary ROI - -| Metric | Savings | -| :--- | :--- | -| **Monthly Time Saved** | 36 Hours | -| **Monthly Cost Saved** | ~$3,100 | -| **Risk Reduction** | ~86% | -| **Break-even Point** | < 1 Day | diff --git a/docs/docs/user_journeys.md b/docs/docs/user_journeys.md deleted file mode 100644 index 2f0d07b4..00000000 --- a/docs/docs/user_journeys.md +++ /dev/null @@ -1,626 +0,0 @@ -# User Journeys - CodeGraphContext - -This document provides detailed, step-by-step user journeys for different personas using CodeGraphContext. Each journey includes **concrete examples** with actual commands and expected outputs. - ---- - -## Table of Contents - -1. [Persona 1: AI-Assisted Developer (MCP User)](#persona-1-ai-assisted-developer-mcp-user) -2. [Persona 2: New Developer/Contributor (CLI User)](#persona-2-new-developercontributor-cli-user) -3. [Persona 3: Experienced Developer - Refactoring (CLI + MCP User)](#persona-3-experienced-developer-refactoring-cli-mcp-user) - ---- - -## Persona 1: AI-Assisted Developer (MCP User) - -### Profile -- **Name**: Sarah, Senior Full-Stack Developer -- **Tools**: Uses Cursor IDE with Claude/GPT-4 for daily coding -- **Context**: Working on a large e-commerce platform (50k+ lines of code) -- **Pain Point**: AI assistant often hallucinates about code relationships or provides incorrect context - -### Concrete Example Scenario - -Sarah needs to refactor the `calculateShippingCost` function in the checkout module. She wants to know what will break before making changes. - -### Step-by-Step Journey - -#### **Step 1: One-Time Setup** (5 minutes) - -```bash -# Install CodeGraphContext -pip install codegraphcontext - -# Index the repository -cd ~/projects/ecommerce-platform -cgc index . - -# Output: -# ✓ Indexed 1,247 files -# ✓ Found 3,421 functions -# ✓ Found 892 classes -# ✓ Created 15,234 relationships -# ✓ Database: FalkorDB (auto-selected) -``` - -```bash -# Setup MCP integration -cgc mcp setup - -# Output: -# ✓ MCP configuration created at ~/.config/cursor/mcp.json -# ✓ Server command: cgc mcp start -# ✓ Restart Cursor to activate -``` - -#### **Step 2: Start MCP Server** (Every time, or run in background) - -```bash -# Start the MCP server -cgc mcp start - -# Output: -# ✓ CodeGraphContext MCP Server started -# ✓ Listening on stdio -# ✓ Database: FalkorDB connected -# ✓ Watching: /home/sarah/projects/ecommerce-platform -``` - -**Alternative**: Add to shell startup script for auto-start - -#### **Step 3: Use AI Assistant with CGC Context** - -Sarah opens Cursor and asks her AI assistant: - -**Sarah's Question:** -> "I want to refactor the calculateShippingCost function. What will be affected?" - -**Behind the Scenes:** -1. Cursor AI recognizes this needs code relationship data -2. Calls CGC MCP tool: `find_all_callers` with target `calculateShippingCost` -3. CGC returns structured data - -**CGC Response (via MCP):** -```json -{ - "function": "calculateShippingCost", - "file": "src/checkout/shipping.py", - "direct_callers": 3, - "total_callers": 12, - "callers": [ - { - "function": "processCheckout", - "file": "src/checkout/checkout_flow.py", - "line": 145 - }, - { - "function": "estimateTotal", - "file": "src/cart/cart_service.py", - "line": 89 - }, - { - "function": "generateInvoice", - "file": "src/billing/invoice_generator.py", - "line": 234 - } - ], - "indirect_callers": [ - "handleCheckoutAPI", - "updateCartTotal", - "previewOrder", - // ... 9 more - ] -} -``` - -**AI Assistant's Response to Sarah:** -> "The `calculateShippingCost` function is called by 12 functions across 3 modules: -> -> **Direct callers (3):** -> 1. `processCheckout` in checkout_flow.py - Main checkout flow -> 2. `estimateTotal` in cart_service.py - Cart total calculation -> 3. `generateInvoice` in invoice_generator.py - Invoice generation -> -> **Indirect impact (9 more functions):** -> - API endpoints: `handleCheckoutAPI`, `previewOrder` -> - Background jobs: `sendOrderConfirmation` -> -> **Recommendation:** -> - If you change the function signature, you'll need to update 3 direct callers -> - If you change the return value format, 12 functions are affected -> - Consider adding unit tests for the 3 direct callers before refactoring" - -#### **Step 4: Deep Dive with Follow-up Questions** - -**Sarah asks:** -> "Show me the call chain from the API endpoint to calculateShippingCost" - -**CGC MCP Call:** `analyze_code_relationships(query_type="call_chain", target="calculateShippingCost")` - -**AI Response:** -> "Here's the execution path: -> ``` -> handleCheckoutAPI (api/checkout.py:45) -> └─> processCheckout (checkout/checkout_flow.py:145) -> └─> calculateShippingCost (checkout/shipping.py:78) -> └─> getShippingRates (integrations/fedex_api.py:23) -> ``` -> -> This means changes to `calculateShippingCost` affect the public API endpoint." - -#### **Step 5: Make Informed Changes** - -Sarah now refactors with confidence, knowing: -- ✅ Exactly what will break -- ✅ Which tests to update -- ✅ What documentation to change -- ✅ The full impact radius - -### Time Saved -- **Without CGC**: 2-3 hours of manual code searching, likely missing some dependencies -- **With CGC**: 10 minutes with complete, accurate information -- **Savings**: ~2.5 hours per refactoring task - ---- - -## Persona 2: New Developer/Contributor (CLI User) - -### Profile -- **Name**: Alex, Junior Developer (3 months experience) -- **Context**: Just joined a team maintaining a legacy Python project -- **Task**: Fix a bug in the payment processing module -- **Pain Point**: Doesn't understand the codebase architecture, doesn't know where to start - -### Concrete Example Scenario - -Alex's first task: "Fix bug #1234 - Payment confirmation emails not sending for international orders" - -### Step-by-Step Journey - -#### **Step 1: Setup** (First time only) - -```bash -# Clone the repository -git clone https://github.com/company/payment-system.git -cd payment-system - -# Install CGC -pip install codegraphcontext - -# Index the codebase -cgc index . - -# Output: -# ✓ Indexed 847 files -# ✓ Found 2,156 functions -# ✓ Found 423 classes -# ✓ Database: FalkorDB -``` - -#### **Step 2: Understand the Architecture** (10 minutes) - -```bash -# Get an overview of the codebase structure -cgc stats - -# Output: -# Repository Statistics: -# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -# Files: 847 -# Functions: 2,156 -# Classes: 423 -# Modules: 67 -# -# Top Modules by Size: -# 1. payment_processing (234 functions) -# 2. email_service (156 functions) -# 3. order_management (189 functions) -``` - -```bash -# Visualize the architecture -cgc visualize --output architecture.html - -# Output: -# ✓ Generated visualization: architecture.html -# ✓ Open in browser to explore -``` - -**Alex opens `architecture.html` and sees:** -- Module dependency graph -- Payment processing module connects to email_service -- Clear separation between domestic and international payment flows - -#### **Step 3: Find Relevant Code** (5 minutes) - -```bash -# Search for email-related code -cgc find "email" --type function - -# Output: -# Found 23 functions matching 'email': -# -# 1. sendPaymentConfirmation -# File: src/email_service/notifications.py:45 -# -# 2. sendInternationalPaymentEmail -# File: src/email_service/international.py:12 -# -# 3. queueEmailJob -# File: src/workers/email_queue.py:78 -# -# ... (20 more) -``` - -```bash -# Find international payment handling -cgc find "international" --type function - -# Output: -# Found 8 functions matching 'international': -# -# 1. processInternationalPayment -# File: src/payment_processing/international.py:34 -# -# 2. sendInternationalPaymentEmail -# File: src/email_service/international.py:12 -``` - -#### **Step 4: Trace the Bug** (15 minutes) - -```bash -# See what calls the international payment function -cgc analyze callers processInternationalPayment - -# Output: -# Callers of 'processInternationalPayment': -# -# Direct Callers (2): -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ File │ Line │ -# ├────────────────────────────────────────────────────────────┤ -# │ handlePayment │ api/payment_api.py │ 123 │ -# │ retryFailedPayment │ workers/retry_worker.py │ 56 │ -# └────────────────────────────────────────────────────────────┘ -``` - -```bash -# Check what processInternationalPayment calls -cgc analyze callees processInternationalPayment - -# Output: -# Functions called by 'processInternationalPayment': -# -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ File │ Line │ -# ├────────────────────────────────────────────────────────────┤ -# │ validateCurrency │ utils/currency.py │ 23 │ -# │ chargeCard │ integrations/stripe.py │ 89 │ -# │ logTransaction │ logging/audit.py │ 45 │ -# │ updateOrderStatus │ orders/order_service.py │ 167 │ -# └────────────────────────────────────────────────────────────┘ -``` - -**Alex notices:** `sendInternationalPaymentEmail` is NOT in the callees list! This is the bug! - -#### **Step 5: Verify the Fix Location** (5 minutes) - -```bash -# Check how domestic payments work -cgc analyze callees processDomesticPayment - -# Output: -# Functions called by 'processDomesticPayment': -# -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ File │ Line │ -# ├────────────────────────────────────────────────────────────┤ -# │ validateCurrency │ utils/currency.py │ 23 │ -# │ chargeCard │ integrations/stripe.py │ 89 │ -# │ logTransaction │ logging/audit.py │ 45 │ -# │ updateOrderStatus │ orders/order_service.py │ 167 │ -# │ sendPaymentConfirmation │ email_service/notifications.py │ 45 │ -# └────────────────────────────────────────────────────────────┘ -``` - -**Aha!** Domestic payments call `sendPaymentConfirmation`, but international payments don't call `sendInternationalPaymentEmail`! - -#### **Step 6: Make the Fix** - -Alex adds the missing email call to `processInternationalPayment` function. - -#### **Step 7: Verify No Side Effects** (5 minutes) - -```bash -# Check if the change affects anything else -cgc analyze callers sendInternationalPaymentEmail - -# Output: -# Callers of 'sendInternationalPaymentEmail': -# -# No callers found. -# -# ⚠️ This function appears to be unused (dead code) -``` - -Perfect! Adding this call won't break anything. - -### Time Saved -- **Without CGC**: 4-6 hours of reading code, asking senior developers, trial and error -- **With CGC**: 40 minutes to understand the bug and fix it -- **Savings**: ~5 hours, plus reduced senior developer interruptions - -### Learning Outcome -Alex now understands: -- ✅ The payment processing architecture -- ✅ How domestic vs international payments differ -- ✅ The email notification system -- ✅ How to navigate the codebase independently - ---- - -## Persona 3: Experienced Developer - Refactoring (CLI + MCP User) - -### Profile -- **Name**: Marcus, Tech Lead -- **Context**: Leading a major refactoring of a 5-year-old Django application -- **Task**: Remove deprecated `OldAuthService` and migrate to `NewAuthService` -- **Pain Point**: Needs to ensure zero downtime, no broken functionality - -### Concrete Example Scenario - -The team is migrating from a custom authentication system to OAuth2. Marcus needs to: -1. Find all usages of the old auth system -2. Identify dead code that can be removed -3. Plan the migration in safe, incremental steps - -### Step-by-Step Journey - -#### **Step 1: Initial Assessment** (10 minutes) - -```bash -# Index the current codebase -cgc index . - -# Find all references to old auth -cgc find "OldAuthService" --type class - -# Output: -# Found 1 class matching 'OldAuthService': -# -# Class: OldAuthService -# File: src/auth/legacy_auth.py:23 -# Methods: 12 -``` - -```bash -# Find all functions that use it -cgc analyze callers OldAuthService - -# Output: -# Callers of 'OldAuthService': -# -# Direct Callers (15): -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ File │ Line │ -# ├────────────────────────────────────────────────────────────┤ -# │ login │ api/auth_api.py │ 34 │ -# │ logout │ api/auth_api.py │ 67 │ -# │ validateToken │ middleware/auth.py │ 89 │ -# │ refreshSession │ api/session_api.py │ 123 │ -# ... (11 more) -# └────────────────────────────────────────────────────────────┘ -# -# Total: 15 direct callers, 47 indirect callers -``` - -**Marcus realizes:** This affects 47 functions! Need a careful migration plan. - -#### **Step 2: Find Dead Code to Remove First** (5 minutes) - -```bash -# Find unused code in the auth module -cgc analyze dead-code --exclude-decorated @api_endpoint @celery_task - -# Output: -# Potentially Unused Functions: -# -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ File │ Complexity│ -# ├────────────────────────────────────────────────────────────┤ -# │ _legacyHashPassword │ auth/legacy_auth.py │ 8 │ -# │ _migrateUserSession │ auth/migration.py │ 12 │ -# │ validateLegacyToken │ auth/legacy_auth.py │ 15 │ -# └────────────────────────────────────────────────────────────┘ -# -# Total: 3 unused functions (can be safely removed) -``` - -**Marcus notes:** Remove these 3 functions first to reduce complexity. - -#### **Step 3: Identify High-Risk Areas** (10 minutes) - -```bash -# Find the most complex functions that use OldAuthService -cgc analyze complexity --filter-calls OldAuthService - -# Output: -# Complex Functions Using 'OldAuthService': -# -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ Complexity │ Callers │ Priority │ -# ├────────────────────────────────────────────────────────────┤ -# │ validateToken │ 23 │ 34 │ CRITICAL │ -# │ refreshSession │ 18 │ 12 │ HIGH │ -# │ login │ 15 │ 8 │ HIGH │ -# │ logout │ 7 │ 6 │ MEDIUM │ -# └────────────────────────────────────────────────────────────┘ -``` - -**Marcus's migration plan:** -1. Start with low-complexity, low-usage functions (logout) -2. End with high-complexity, high-usage functions (validateToken) -3. Add comprehensive tests for each before migrating - -#### **Step 4: Create Migration Checklist** (15 minutes) - -```bash -# For each function, see its full call chain -cgc analyze chain login OldAuthService - -# Output: -# Call Chain from 'login' to 'OldAuthService': -# -# login (api/auth_api.py:34) -# └─> OldAuthService.authenticate (auth/legacy_auth.py:45) -# └─> _hashPassword (auth/legacy_auth.py:123) -# └─> _validateCredentials (auth/legacy_auth.py:156) -# └─> database.query (core/db.py:89) -``` - -Marcus repeats this for all 15 direct callers, creating a detailed migration map. - -#### **Step 5: Use MCP for AI-Assisted Migration** (Ongoing) - -Marcus uses Cursor with CGC MCP to help with the migration: - -**Marcus asks AI:** -> "Generate a migration plan to replace OldAuthService with NewAuthService in the login function" - -**AI (using CGC MCP):** -1. Calls `find_callees` on `login` to see what it does -2. Calls `find_callers` on `login` to see impact -3. Analyzes the code structure - -**AI Response:** -> "Migration Plan for `login` function: -> -> **Current Flow:** -> ```python -> def login(username, password): -> auth = OldAuthService() -> user = auth.authenticate(username, password) -> session = auth.createSession(user) -> return session.token -> ``` -> -> **Proposed New Flow:** -> ```python -> def login(username, password): -> auth = NewAuthService() -> user = auth.authenticate(username, password) # Compatible API -> session = auth.createSession(user) -> return session.token -> ``` -> -> **Impact Analysis:** -> - `login` is called by 8 functions -> - Return value format is the same (session token string) -> - No breaking changes expected -> -> **Testing Checklist:** -> - [ ] Unit test: test_login_success -> - [ ] Unit test: test_login_invalid_credentials -> - [ ] Integration test: test_login_api_endpoint -> - [ ] Load test: Verify performance is similar -> -> **Rollout Strategy:** -> 1. Deploy with feature flag `use_new_auth_service` -> 2. Enable for 10% of traffic -> 3. Monitor error rates -> 4. Gradually increase to 100% -> 5. Remove OldAuthService after 2 weeks" - -#### **Step 6: Execute Migration** (Over 2 weeks) - -Marcus follows the plan: -- Week 1: Migrate 5 low-risk functions -- Week 2: Migrate 10 medium-risk functions -- After each migration, uses CGC to verify: - -```bash -# Verify old service usage is decreasing -cgc analyze callers OldAuthService - -# Week 1: 15 callers → 10 callers -# Week 2: 10 callers → 0 callers -``` - -```bash -# Final verification: Is OldAuthService still used? -cgc analyze callers OldAuthService - -# Output: -# Callers of 'OldAuthService': -# -# No callers found. -# -# ✓ Safe to remove OldAuthService -``` - -#### **Step 7: Cleanup** (30 minutes) - -```bash -# Find all dead code after migration -cgc analyze dead-code - -# Output: -# Potentially Unused Functions: -# -# ┌────────────────────────────────────────────────────────────┐ -# │ Function │ File │ LOC │ -# ├────────────────────────────────────────────────────────────┤ -# │ OldAuthService.* │ auth/legacy_auth.py │ 456 │ -# │ _legacyHashPassword │ auth/legacy_auth.py │ 23 │ -# │ _migrateUserSession │ auth/migration.py │ 34 │ -# └────────────────────────────────────────────────────────────┘ -# -# Total: 513 lines of code can be safely removed -``` - -Marcus deletes the old code, reducing technical debt by 513 lines. - -### Time Saved -- **Without CGC**: 3-4 weeks of risky, manual migration with likely bugs -- **With CGC**: 2 weeks of safe, incremental migration with zero bugs -- **Savings**: 1-2 weeks, plus avoided production incidents - -### Business Impact -- ✅ Zero downtime migration -- ✅ No customer-facing bugs -- ✅ 513 lines of dead code removed -- ✅ Improved security (OAuth2) -- ✅ Team confidence in future refactorings - ---- - -## Summary: Key Takeaways - -### For MCP Users (AI-Assisted Development) -- **Setup**: One-time 5-minute setup, then seamless integration -- **Workflow**: Ask questions in natural language, get precise answers -- **Value**: AI provides accurate context instead of hallucinations -- **Time Saved**: 2-3 hours per refactoring task - -### For CLI Users (Direct Tool Usage) -- **Setup**: One-time `cgc index` command -- **Workflow**: Use specific commands for specific tasks -- **Value**: Self-service code exploration, no senior dev needed -- **Time Saved**: 4-6 hours for onboarding, 1-2 hours per bug investigation - -### For Both -- **Integration**: Works alongside existing tools (IDE, git, CI/CD) -- **Learning Curve**: Minimal - intuitive commands and natural language -- **ROI**: Pays for itself in the first week of use -- **Scalability**: Works on codebases from 1k to 1M+ lines - ---- - -## Next Steps - -- **New to CGC?** → Start with [SETUP_WORKFLOWS.md](./setup_workflows.md) -- **Want specific examples?** → See [USE_CASES_DETAILED.md](./use_cases_detailed.md) -- **Need integration help?** → Read [INTEGRATION_GUIDE.md](./integration_guide.md) -- **CLI Reference** → See [CLI Reference](reference/cli_master.md) -- **MCP Reference** → See [MCP Reference](reference/mcp_master.md) diff --git a/docs/docs/watching.md b/docs/docs/watching.md deleted file mode 100644 index 7bca1ba5..00000000 --- a/docs/docs/watching.md +++ /dev/null @@ -1,221 +0,0 @@ -# Live File Watching - -CodeGraphContext can automatically monitor your codebase for changes and update the code graph in real-time as you develop. - -## Quick Start - -Start watching your project directory: - -```bash -cgc watch . -``` - -You'll see: -``` -🔍 Watching /path/to/your/project for changes... -✓ Already indexed (no initial scan needed) -👀 Monitoring for file changes... (Press Ctrl+C to stop) - -💡 Tip: Open a new terminal window to continue working -``` - -## How It Works - -The watcher uses file system events to detect when you: - -- Create new files -- Modify existing files -- Delete files -- Move/rename files - -When changes are detected, CodeGraphContext automatically: - -1. Re-parses the affected files -2. Updates the code graph -3. Maintains all relationships and dependencies - -## Commands - -### `cgc watch [path]` - -Start watching a directory for changes. - -**Examples:** -```bash -cgc watch . # Watch current directory -cgc watch /path/to/project # Watch specific directory -cgc w . # Shortcut alias -``` - -**Behavior:** -- Runs in the foreground (blocking mode) -- Performs initial scan if directory is not yet indexed -- Monitors for all file system changes -- Uses 2-second debouncing to batch rapid changes -- Press `Ctrl+C` to stop - -### `cgc watching` - -List all directories currently being watched. - -```bash -cgc watching -``` - -**Note:** This command is primarily for MCP server mode. For CLI watch mode, check the terminal where you ran `cgc watch`. - -### `cgc unwatch ` - -Stop watching a directory. - -```bash -cgc unwatch /path/to/project -``` - -**Note:** This command is primarily for MCP server mode. For CLI watch mode, simply press `Ctrl+C` in the watch terminal. - -## Best Practices - -### Development Workflow - -1. Start watching at the beginning of your coding session -2. Open a new terminal tab/window for your actual work -3. Code normally - changes are automatically tracked -4. Stop watching (Ctrl+C) when you're done - -### Performance Tips - -- The watcher uses debouncing (2-second delay) to avoid excessive re-indexing -- Only modified files and their dependencies are re-processed -- Large projects may take a moment to process changes - -### When to Use Watch Mode - -✅ **Good for:** - -- Active development sessions -- Refactoring work -- Keeping AI assistants up-to-date with latest code -- Live code analysis during development - -❌ **Not needed for:** - -- One-time indexing -- CI/CD pipelines -- Read-only code analysis -- Batch processing - -## Example Workflow - -Here's a typical development session using watch mode: - -```bash -# Terminal 1: Start the watcher -$ cd ~/my-project -$ cgc watch . -🔍 Watching /home/user/my-project for changes... -✓ Already indexed (no initial scan needed) -👀 Monitoring for file changes... (Press Ctrl+C to stop) - -💡 Tip: Open a new terminal window to continue working - -# ... watcher runs and shows updates as you code ... -[21:15:32] 📝 Modified: src/utils.py (re-indexing...) -[21:15:32] ✓ Updated graph (3 nodes, 2 relationships) -[21:16:45] 📝 Created: src/new_feature.py (re-indexing...) -[21:16:45] ✓ Updated graph (8 nodes, 5 relationships) -``` - -```bash -# Terminal 2: Do your development work -$ cd ~/my-project -$ code . # Open your editor -$ git checkout -b new-feature # Work normally -$ # ... make changes, save files ... -$ # The watcher in Terminal 1 automatically picks up changes! -``` - -## Troubleshooting - -### Watcher not detecting changes - -- Ensure the path is correct and accessible -- Check file permissions -- Some editors use atomic writes which may not trigger events -- Try restarting the watcher - -### High CPU usage - -- The watcher may be processing too many files -- Consider watching a smaller directory -- Check for file loops or symlinks -- Verify you're not watching `node_modules` or similar large directories - -### Changes not appearing - -- Wait for the debounce interval (2 seconds) -- Check the watcher output for errors -- Verify the file type is supported (Python, JavaScript, TypeScript, etc.) -- Ensure the file is within the watched directory - -### "Already watching" message - -If you see this message, it means the directory is already being watched. This can happen if: - -- You're running multiple watch commands -- The MCP server is already watching this directory -- A previous watch session didn't terminate cleanly - -**Solution:** Stop all watch processes and start fresh. - -## MCP Server vs CLI Watch Mode - -CodeGraphContext supports two watch modes: - -### CLI Watch Mode (This Guide) - -- **Command:** `cgc watch .` -- **Runs:** In foreground (blocking) -- **Use case:** Active development sessions -- **Control:** Press `Ctrl+C` to stop -- **Best for:** Single project, focused development - -### MCP Server Watch Mode - -- **Command:** Via MCP tools (`watch_directory`, `unwatch_directory`, `list_watched_paths`) -- **Runs:** In background (as part of MCP server) -- **Use case:** IDE integration, multiple projects -- **Control:** MCP tool calls -- **Best for:** AI assistant integration, persistent watching - -## Technical Details - -- **Library**: Uses `watchdog` for cross-platform file monitoring -- **Debouncing**: 2-second delay to batch rapid changes -- **Scope**: Watches recursively, respects `.gitignore` -- **Performance**: Only re-indexes changed files and affected relationships -- **Thread-safe**: Uses background threads for file monitoring -- **Graceful shutdown**: Properly cleans up on `Ctrl+C` - -## Integration with AI Assistants - -Watch mode is particularly powerful when combined with AI coding assistants: - -1. **Start watching your project:** - ```bash - cgc watch . - ``` - -2. **Configure your AI assistant** to use the CodeGraphContext MCP server - -3. **Code normally** - your AI assistant always has the latest code context - -4. **Ask questions** about your code, and the AI will have up-to-date information - -This creates a seamless development experience where your AI assistant stays synchronized with your codebase in real-time! - -## See Also - -- [CLI Reference](reference/cli_master.md) - Complete list of CLI commands -- [MCP Tools](reference/mcp_master.md) - MCP server tools including watch functionality -- [Installation](getting-started/installation.md) - Getting started with CodeGraphContext diff --git a/docs/docs/windows_setup.md b/docs/docs/windows_setup.md deleted file mode 100644 index d7530028..00000000 --- a/docs/docs/windows_setup.md +++ /dev/null @@ -1,260 +0,0 @@ -# CodeGraphContext – Windows Setup Guide - -This guide explains how to set up **CodeGraphContext on Windows**. - -⚠️ **Important Note for Windows Users** -- **FalkorDB Lite (redislite) is not supported natively on Windows** -- Recommended approach: **Use WSL (Windows Subsystem for Linux)** -- Alternative: Use **Neo4j** as the database backend - ---- - -## Option 1 (Recommended): Setup Using WSL - -WSL allows you to run CodeGraphContext in a Linux environment on Windows, enabling **FalkorDB Lite** and ensuring the smoothest setup experience. - ---- - -### Step 1: Install WSL - -Open **PowerShell as Administrator** and run: - -```powershell -wsl --install -```` - -Restart your system if prompted. - -Verify WSL installation: - -```powershell -wsl --version -``` - ---- - -### Step 2: Open Ubuntu (WSL Terminal) - -After restart: - -* Open the Start Menu -* Search for **Ubuntu** -* Open it - -Update packages: - -```bash -sudo apt update -sudo apt upgrade -y -``` - ---- - -### Step 3: Install Python (3.12+ Recommended) - -Inside the WSL terminal: - -```bash -sudo apt install python3.12 python3-pip -y -``` - -Verify installation: - -```bash -python3 --version -pip3 --version -``` - -> CodeGraphContext supports **Python 3.10 – 3.14**. -> **FalkorDB Lite works best with Python 3.12+**. - ---- - -### Step 4: Install CodeGraphContext - -```bash -pip install codegraphcontext -``` - -Verify installation: - -```bash -cgc --help -``` - ---- - -### Step 5: Index a Project - -Navigate to your project directory. - -Example (accessing Windows files from WSL): - -```bash -cd /mnt/c/Users//Desktop/ -``` - -Index the codebase: - -```bash -cgc index . -``` - ---- - -### Step 6 (Optional): Live Watch Mode - -To automatically update the graph when files change: - -```bash -cgc watch . -``` - ---- - -### Step 7 (Optional): MCP Setup (AI Integration) - -To use CodeGraphContext as an MCP server for AI assistants/IDEs: - -```bash -cgc mcp setup -``` - -Start the MCP server: - -```bash -cgc mcp start -``` - ---- - -## Option 2: Native Windows Setup Using Neo4j - -If you do not want WSL, CodeGraphContext can be configured to use **Neo4j** as its database backend. - ---- - -### Step 1: Install Python (Windows) - -Download Python from: -[https://www.python.org/downloads/](https://www.python.org/downloads/) - -During installation, ensure: -✅ **Add Python to PATH** - -Verify: - -```powershell -python --version -pip --version -``` - ---- - -### Step 2: Install CodeGraphContext - -```powershell -pip install codegraphcontext -``` - -Verify: - -```powershell -cgc --help -``` - ---- - -### Step 3: Install & Run Neo4j - -Neo4j can be installed using one of the following: - -* Neo4j Desktop (GUI) -* Neo4j via Docker (recommended) -* Neo4j AuraDB (cloud) - -Ensure Neo4j is running and note: - -* URI (example: `bolt://localhost:7687`) -* Username -* Password - ---- - -### Step 4: Configure Neo4j in CodeGraphContext - -Run: - -```bash -cgc neo4j setup -``` - -Follow the prompts to enter your Neo4j credentials. - ---- - -### Step 5: Index a Project - -Navigate to your project: - -```powershell -cd path\to\your\project -``` - -Index the codebase: - -```powershell -cgc index . -``` - ---- - -## Ignoring Files (`.cgcignore`) - -To ignore files and folders while indexing, create a `.cgcignore` file in your project root. - -Example `.cgcignore`: - -```txt -/build/ -/dist/ -/node_modules/ -/vendor/ -*.log -``` - ---- - -## Common Windows Issues - -### `'cgc' command not found` - -* Restart your terminal after installation -* Ensure Python Scripts directory is in PATH -* Try: `python -m pip install codegraphcontext` - -### WSL path confusion - -Windows drives can be accessed from WSL using: - -```bash -cd /mnt/c/ -``` - -### Neo4j connection issues - -* Ensure Neo4j is running -* Check URI format: `bolt://localhost:7687` -* Double-check username/password - ---- - -## Verify Installation - -Run: - -```bash -cgc list -``` -If no errors are shown, setup is complete - diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index eb696534..ff80853c 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -3,65 +3,56 @@ repo_url: https://github.com/CodeGraphContext/CodeGraphContext repo_name: CodeGraphContext/CodeGraphContext theme: name: material + custom_dir: custom_theme + logo: assets/logo.png + favicon: assets/logo.png features: - navigation.tabs - navigation.sections - navigation.expand - content.code.copy - toc.follow - palette: - - scheme: default - primary: teal - accent: purple - toggle: - icon: material/weather-night - name: Switch to dark mode - - scheme: slate - primary: teal - accent: lime - toggle: - icon: material/weather-sunny - name: Switch to light mode + +extra_css: + - stylesheets/redwood.css + +extra_javascript: + - https://unpkg.com/mermaid@10/dist/mermaid.min.js + - javascripts/mermaid-init.js nav: - - Home: index.md + - Introduction: index.md - Getting Started: - - Prerequisites & Context: getting-started/prerequisites.md + - Prerequisites: getting-started/prerequisites.md - Installation: getting-started/installation.md - - Quickstart (5 min): getting-started/quickstart.md - - Usage: - - "Real-World Scenarios (ROI)": use_cases_detailed.md - - "User Journeys (Stories)": user_journeys.md - - "Integration & Workflows": integration_guide.md - - Guides: - - Onboarding Guide: guides/onboarding.md - - Neo4j Setup Wizard : guides/setup_deep_dive.md - - MCP Setup Wizard : guides/mcp_guide.md - - Visualizing the Graph: guides/visualization.md - - Using On-Demand Bundles: guides/bundles.md - - CI/CD & Advanced: guides/ci_cd.md + - Quickstart: getting-started/quickstart.md + - MCP Setup: getting-started/mcp-setup.md - Core Concepts: - - How it Works: concepts/how_it_works.md - - "System Architecture": architecture.md - - The Graph Model: concepts/the_graph.md - - CLI vs MCP Modes: concepts/modes.md - - CLI Reference: - - "All Commands (Master List)": reference/cli_master.md - - "Indexing & Management": reference/cli_indexing.md - - "Analysis & Querying": reference/cli_analysis.md - - "System & Config": reference/cli_system.md - - MCP Reference: - - "All Tools (Master List)": reference/mcp_master.md - - "Example Cookbook": cookbook.md - - Project Info: - - Roadmap: roadmap.md + - System Architecture: concepts/architecture.md + - The Code Graph Model: concepts/graph-model.md + - Database Backends: concepts/backends.md + - How Ingestion Works: concepts/how-it-works.md + - User Guides: + - Indexing Codebases: guides/indexing.md + - Configuration Contexts: guides/contexts.md + - Portable CGC Bundles: guides/bundles.md + - Interactive Visualization: guides/visualization.md + - Datasource Indexing: guides/datasource-indexing.md + - Developer Onboarding: guides/onboarding-codebase.md + - Reference: + - CLI Reference: reference/cli.md + - HTTP API Reference: reference/api.md + - MCP Tool Reference: reference/mcp.md + - Configuration: reference/config.md + - Troubleshooting: reference/troubleshooting.md + - Community: - Contributing: contributing.md + - Contributing Languages: contributing_languages.md + - Roadmap: roadmap.md - License: license.md - - Configuration: reference/configuration.md - - ".cgcignore Guide": reference/cgcignore.md - - Troubleshooting: reference/troubleshooting.md markdown_extensions: + - tables - admonition - pymdownx.details - pymdownx.superfences: diff --git a/docs/site/404.html b/docs/site/404.html deleted file mode 100644 index c5318f8e..00000000 --- a/docs/site/404.html +++ /dev/null @@ -1,1723 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - CodeGraphContext - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
-
- -
- - - - -
- -
- - - - - - - - - -
-
- - - -
-
-
- - - - - - - -
-
-
- - - -
-
-
- - - -
-
-
- - - -
- -
- -

404 - Not found

- -
-
- - - -
- -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/site/architecture/index.html b/docs/site/architecture/index.html deleted file mode 100644 index 7e4682cd..00000000 --- a/docs/site/architecture/index.html +++ /dev/null @@ -1,2014 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - System Architecture - CodeGraphContext - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - -
- - -
- -
- - - - - - - - - -
-
- - - -
-
-
- - - - - - - -
-
-
- - - - - - - -
- -
- - - - - - - - -

System Architecture

-

CodeGraphContext (CGC) is a context-aware code intelligence engine that bridges the gap between your source code and your AI tools.

-

It operates primarily as a background service (MCP Server) backed by a graph database, with a CLI for management. Instead of relying solely on vector embeddings, CodeGraphContext leverages an exact knowledge graph for deep, deterministic insights into the codebase.

-

High-Level Architecture Diagram

-
graph TD
-    Client[AI Client / CLI / Editor]
-    Server[MCP Server Interface]
-    CoreEngine[Core Engine & Tools]
-    ParserLayer[Tree-sitter Language Parsers]
-    DBLayer[Database Abstraction Layer]
-    KuzuDB[(KùzuDB - Embedded Default)]
-    Neo4j[(Neo4j - Enterprise Option)]
-    FS[File System]
-
-    Client -- "1. Context Queries (MCP)" --> Server
-    Client -- "2. CLI Commands (Index/List)" --> CoreEngine
-    Server -- "Forward Requests" --> CoreEngine
-    CoreEngine -- "Read/Write Graph" --> DBLayer
-    CoreEngine -- "Trigger Full/Incremental Indexing" --> ParserLayer
-    ParserLayer -- "Scan Source Files" --> FS
-    DBLayer -- "Store nodes & edges" --> KuzuDB
-    DBLayer -- "Store nodes & edges" --> Neo4j
-

1. The Core (Backend)

-

The primary intelligence resides within the src/codegraphcontext directory. The system is built purely in Python and heavily relies on abstracting operations to stay versatile.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ComponentResponsibility
MCP Server (server.py)Acts as the Model Context Protocol (MCP) host. It translates JSON-RPC requests from LLM clients (like Claude, Cursor, Windsurf) into standardized graph database queries.
Graph Builder (graph_builder.py)The "indexer" module. It coordinates parsing the codebase using Tree-sitter, extracts dependencies, and builds the knowledge graph layout in memory before flushing objects to the database.
Database Abstraction LayerHandles the connection to the underlying graph database. By default, CGC uses KùzuDB for embedded, fast, local indexing, but also natively supports Neo4j for complex or enterprise-level deployments.
Watchers & Background JobsManages long-running tasks such as full repository indexing, bundle processing, and file system monitoring (watchdog module) that triggers incremental updates upon file modifications without blocking the AI.
Tree-sitter ParsersProvide accurate, robust Abstract Syntax Tree (AST) parsing across multiple supported languages to identify classes, functions, files, modules, and their inter-relationships (CALLS, IMPORTS, INHERITS, CONTAINS).
-

2. Front-ends and Observability

-

Important: CodeGraphContext does not rely on a built-in heavy GUI application.

-

The interaction is designed for low friction and automation: -1. AI IDE Integrations: The primary front-end is your AI IDE chat via the Model Context Protocol. You ask a question ("How does user authentication work in the users module?"), and the MCP server uses the graph to resolve exactly what files and functions fulfill that context. -2. CLI (Command Line Interface): A fully featured standard CLI (cgc) to initialize, index, clean, or export graph bundles from the terminal. -3. Visualization Dashboard: A standalone React visualization client (cgc visualize) that parses the raw graph DB and plots nodes dynamically using react-force-graph for exploratory views without needing an external desktop app. -4. Database Browsers: Deep advanced exploration of the actual graph connections using direct Cypher queries via the KùzuDB CLI or Neo4j Browser (localhost:7474).

-

3. Data Flow

-
    -
  1. Repository Indexing:
      -
    • cgc index . invokes the graph builder.
    • -
    • Files are skipped/filtered based on .cgcignore configurations.
    • -
    • Files are parsed via language-specific Tree-sitter grammars.
    • -
    • Language parsers emit standardized objects (Classes, Functions, Modules).
    • -
    • The Graph Builder reconciles imports (edges across different files) and saves them robustly into the active DB.
    • -
    -
  2. -
  3. Real-time querying:
      -
    • An LLM needs context for "Who calls authenticate_user?".
    • -
    • The MCP client forwards the tool call: analyze_code_relationships(queryType="find_callers", target="authenticate_user").
    • -
    • The MCP server runs strict Cypher validation against the Database Abstraction Layer.
    • -
    • DB returns specific file paths, snippet boundaries, and exact structural context natively avoiding hallucination.
    • -
    -
  4. -
-

4. Key Technologies

-
    -
  • Language: Python 3.10+
  • -
  • Parsing: Tree-sitter (for reliable and multi-language AST extraction)
  • -
  • Protocol: Model Context Protocol (MCP) by Anthropic for seamless IDE interactions
  • -
  • Databases:
      -
    • KùzuDB: Used as the default, lightweight embeddable C++ graph database supporting native Cypher compatibility.
    • -
    • Neo4j: Production-level graph DB compatibility via Cypher standardizing queries.
    • -
    -
  • -
  • CLI Interface: click and typer frameworks.
  • -
  • Visualizations: Next.js / Vite / react-force-graph within the website directory for rendering the 3D local graph views.
  • -
- - - - - - - - - - - - - -
-
- - - -
- -
- - - -
-
-
-
- - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/site/assets/javascripts/bundle.79ae519e.min.js b/docs/site/assets/javascripts/bundle.79ae519e.min.js deleted file mode 100644 index 3df3e5e6..00000000 --- a/docs/site/assets/javascripts/bundle.79ae519e.min.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: -`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` - `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{a.type==="ArrowRight"&&e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText)}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); -//# sourceMappingURL=bundle.79ae519e.min.js.map - diff --git a/docs/site/assets/javascripts/bundle.79ae519e.min.js.map b/docs/site/assets/javascripts/bundle.79ae519e.min.js.map deleted file mode 100644 index 5cf02892..00000000 --- a/docs/site/assets/javascripts/bundle.79ae519e.min.js.map +++ /dev/null @@ -1,7 +0,0 @@ -{ - "version": 3, - "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], - "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an
- - -