From e6533c6016e11e2b0e28c53c481516205fb4a8d9 Mon Sep 17 00:00:00 2001 From: Hung Pham Date: Thu, 20 Aug 2026 11:04:02 +0700 Subject: [PATCH 1/4] Implement flow to install codegraph to MacOS and linux --- .github/workflows/release.yml | 390 +++++++++++++++-------- Cargo.lock | 1 + Cargo.toml | 8 +- README.md | 40 ++- crates/codegraph/Cargo.toml | 31 ++ crates/codegraph/src/main.rs | 127 ++------ dist-workspace.toml | 22 ++ packaging/aur/codegraph-rs-bin/PKGBUILD | 2 +- packaging/homebrew/codegraph.rb.template | 25 ++ scripts/install.sh | 4 +- 10 files changed, 417 insertions(+), 233 deletions(-) create mode 100644 dist-workspace.toml create mode 100644 packaging/homebrew/codegraph.rb.template diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index af1570af2..1dfcd0f41 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,156 +1,296 @@ +# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist +# +# Copyright 2022-2024, axodotdev +# SPDX-License-Identifier: MIT or Apache-2.0 +# +# CI that: +# +# * checks for a Git Tag that looks like a release +# * builds artifacts with dist (archives, installers, hashes) +# * uploads those artifacts to temporary workflow zip +# * on success, uploads the artifacts to a GitHub Release +# +# Note that the GitHub Release will be created with a generated +# title/body based on your changelogs. + name: Release +permissions: + "contents": "write" +# This task will run whenever you push a git tag that looks like a version +# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc. +# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where +# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION +# must be a Cargo-style SemVer Version (must have at least major.minor.patch). +# +# If PACKAGE_NAME is specified, then the announcement will be for that +# package (erroring out if it doesn't have the given version or isn't dist-able). +# +# If PACKAGE_NAME isn't specified, then the announcement will be for all +# (dist-able) packages in the workspace with that version (this mode is +# intended for workspaces with only one dist-able package, or with all dist-able +# packages versioned/released in lockstep). +# +# If you push multiple tags at once, separate instances of this workflow will +# spin up, creating an independent announcement for each one. However, GitHub +# will hard limit this to 3 tags per commit, as it will assume more tags is a +# mistake. +# +# If there's a prerelease-style suffix to the version, then the release(s) +# will be marked as a prerelease. on: + pull_request: push: - tags: ["v*"] - -permissions: - contents: write - -env: - CARGO_TERM_COLOR: always + tags: + - '**[0-9]+.[0-9]+.[0-9]+*' jobs: - build: - name: build ${{ matrix.target }} - runs-on: ${{ matrix.os }} + # Run 'dist plan' (or host) to determine what tasks we need to do + plan: + runs-on: "ubuntu-22.04" + outputs: + val: ${{ steps.plan.outputs.manifest }} + tag: ${{ !github.event.pull_request && github.ref_name || '' }} + tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }} + publishing: ${{ !github.event.pull_request }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive + - name: Install dist + # we specify bash to get pipefail; it guards against the `curl` command + # failing. otherwise `sh` won't catch that `curl` returned non-0 + shell: bash + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.32.0/cargo-dist-installer.sh | sh" + - name: Cache dist + uses: actions/upload-artifact@v7 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/dist + # sure would be cool if github gave us proper conditionals... + # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible + # functionality based on whether this is a pull_request, and whether it's from a fork. + # (PRs run on the *source* but secrets are usually on the *target* -- that's *good* + # but also really annoying to build CI around when it needs secrets to work right.) + - id: plan + run: | + dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json + echo "dist ran successfully" + cat plan-dist-manifest.json + echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" + - name: "Upload dist-manifest.json" + uses: actions/upload-artifact@v7 + with: + name: artifacts-plan-dist-manifest + path: plan-dist-manifest.json + + # Build and packages all the platform-specific things + build-local-artifacts: + name: build-local-artifacts (${{ join(matrix.targets, ', ') }}) + # Let the initial task tell us to not run (currently very blunt) + needs: + - plan + if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false - matrix: - include: - - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu, ext: "" } - - { os: ubuntu-latest, target: x86_64-unknown-linux-musl, ext: "" } - - { os: ubuntu-latest, target: aarch64-unknown-linux-gnu, ext: "", cross: true } - - { os: macos-latest, target: x86_64-apple-darwin, ext: "" } - - { os: macos-latest, target: aarch64-apple-darwin, ext: "" } - - { os: windows-latest, target: x86_64-pc-windows-msvc, ext: ".exe" } + # Target platforms/runners are computed by dist in create-release. + # Each member of the matrix has the following arguments: + # + # - runner: the github runner + # - dist-args: cli flags to pass to dist + # - install-dist: expression to run to install dist on the runner + # + # Typically there will be: + # - 1 "global" task that builds universal installers + # - N "local" tasks that build each platform's binaries and platform-specific installers + matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} + runs-on: ${{ matrix.runner }} + container: ${{ matrix.container && matrix.container.image || null }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json steps: + - name: enable windows longpaths + run: | + git config --global core.longpaths true - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@stable with: - targets: ${{ matrix.target }} - - uses: Swatinem/rust-cache@v2 + persist-credentials: false + submodules: recursive + - name: Install Rust non-interactively if not already installed + if: ${{ matrix.container }} + run: | + if ! command -v cargo > /dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + fi + - name: Install dist + run: ${{ matrix.install_dist.run }} + # Get the dist-manifest + - name: Fetch local artifacts + uses: actions/download-artifact@v8 with: - key: ${{ matrix.target }} - - - name: Install musl tools - if: matrix.target == 'x86_64-unknown-linux-musl' - run: sudo apt-get update && sudo apt-get install -y musl-tools - - - name: Install cross - if: matrix.cross - run: cargo install cross --locked - - - name: Build (cross) - if: matrix.cross - run: cross build --release --target ${{ matrix.target }} -p codegraph - - - name: Build (native) - if: ${{ !matrix.cross }} - run: cargo build --release --target ${{ matrix.target }} -p codegraph - - - name: Package + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - name: Install dependencies + run: | + ${{ matrix.packages_install }} + - name: Build artifacts + run: | + # Actually do builds and make zips and whatnot + dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json + echo "dist ran successfully" + - id: cargo-dist + name: Post-build + # We force bash here just because github makes it really hard to get values up + # to "real" actions without writing to env-vars, and writing to env-vars has + # inconsistent syntax between shell and powershell. shell: bash run: | - set -euo pipefail - bin="target/${{ matrix.target }}/release/codegraph${{ matrix.ext }}" - name="codegraph-${{ matrix.target }}" - mkdir -p dist staging - cp "$bin" staging/ - [ -f README.md ] && cp README.md staging/ || true - [ -f LICENSE ] && cp LICENSE staging/ || true - if [[ "${{ matrix.ext }}" == ".exe" ]]; then - ( cd staging && 7z a "../dist/${name}.zip" . ) - else - tar -czf "dist/${name}.tar.gz" -C staging . - fi + # Parse out what we just built and upload it to scratch storage + echo "paths<> "$GITHUB_OUTPUT" + dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" - - uses: actions/upload-artifact@v6 + cp dist-manifest.json "$BUILD_MANIFEST_NAME" + - name: "Upload artifacts" + uses: actions/upload-artifact@v7 with: - name: codegraph-${{ matrix.target }} - path: dist/* + name: artifacts-build-local-${{ join(matrix.targets, '_') }} + path: | + ${{ steps.cargo-dist.outputs.paths }} + ${{ env.BUILD_MANIFEST_NAME }} - release: - name: GitHub Release - needs: build - runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') - outputs: - tag: ${{ steps.tag.outputs.tag }} - version: ${{ steps.tag.outputs.version }} + # Build and package all the platform-agnostic(ish) things + build-global-artifacts: + needs: + - plan + - build-local-artifacts + runs-on: "ubuntu-22.04" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v6 - - uses: actions/download-artifact@v6 with: - path: artifacts + persist-credentials: false + submodules: recursive + - name: Install cached dist + uses: actions/download-artifact@v8 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - run: chmod +x ~/.cargo/bin/dist + # Get all the local artifacts for the global tasks to use (for e.g. checksums) + - name: Fetch local artifacts + uses: actions/download-artifact@v8 + with: + pattern: artifacts-* + path: target/distrib/ merge-multiple: true - - - name: Resolve tag - id: tag + - id: cargo-dist + shell: bash run: | - set -euo pipefail - tag="${GITHUB_REF#refs/tags/}" - [[ "$tag" =~ ^v ]] || { echo "tag must start with v" >&2; exit 1; } - echo "tag=$tag" >> "$GITHUB_OUTPUT" - echo "version=${tag#v}" >> "$GITHUB_OUTPUT" + dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json + echo "dist ran successfully" - - name: Create release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - tag="${{ steps.tag.outputs.tag }}" - gh release view "$tag" >/dev/null 2>&1 \ - && gh release upload "$tag" artifacts/* --clobber \ - || gh release create "$tag" --draft --title "$tag" --generate-notes artifacts/* + # Parse out what we just built and upload it to scratch storage + echo "paths<> "$GITHUB_OUTPUT" + jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" + echo "EOF" >> "$GITHUB_OUTPUT" - aur: - name: Publish AUR (codegraph-rs-bin) - needs: release - runs-on: ubuntu-latest - if: ${{ needs.release.outputs.tag != '' }} + cp dist-manifest.json "$BUILD_MANIFEST_NAME" + - name: "Upload artifacts" + uses: actions/upload-artifact@v7 + with: + name: artifacts-build-global + path: | + ${{ steps.cargo-dist.outputs.paths }} + ${{ env.BUILD_MANIFEST_NAME }} + # Determines if we should publish/announce + host: + needs: + - plan + - build-local-artifacts + - build-global-artifacts + # Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine) + if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + runs-on: "ubuntu-22.04" + outputs: + val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v6 - - - name: Download release archives - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG: ${{ needs.release.outputs.tag }} + with: + persist-credentials: false + submodules: recursive + - name: Install cached dist + uses: actions/download-artifact@v8 + with: + name: cargo-dist-cache + path: ~/.cargo/bin/ + - run: chmod +x ~/.cargo/bin/dist + # Fetch artifacts from scratch-storage + - name: Fetch artifacts + uses: actions/download-artifact@v8 + with: + pattern: artifacts-* + path: target/distrib/ + merge-multiple: true + - id: host + shell: bash run: | - set -euo pipefail - mkdir -p dl - gh release download "$TAG" \ - --repo "${{ github.repository }}" \ - --pattern 'codegraph-x86_64-unknown-linux-musl.tar.gz' \ - --pattern 'codegraph-aarch64-unknown-linux-gnu.tar.gz' \ - --dir dl - cd dl - sha256sum codegraph-x86_64-unknown-linux-musl.tar.gz | awk '{print $1}' > x86_64.sha256 - sha256sum codegraph-aarch64-unknown-linux-gnu.tar.gz | awk '{print $1}' > aarch64.sha256 - - - name: Render PKGBUILD + dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json + echo "artifacts uploaded and released successfully" + cat dist-manifest.json + echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" + - name: "Upload dist-manifest.json" + uses: actions/upload-artifact@v7 + with: + # Overwrite the previous copy + name: artifacts-dist-manifest + path: dist-manifest.json + # Create a GitHub Release while uploading all files to it + - name: "Download GitHub Artifacts" + uses: actions/download-artifact@v8 + with: + pattern: artifacts-* + path: artifacts + merge-multiple: true + - name: Cleanup + run: | + # Remove the granular manifests + rm -f artifacts/*-dist-manifest.json + - name: Create GitHub Release env: - VERSION: ${{ needs.release.outputs.version }} + PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}" + ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}" + ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}" + RELEASE_COMMIT: "${{ github.sha }}" run: | - set -euo pipefail - x86_sha=$(cat dl/x86_64.sha256) - arm_sha=$(cat dl/aarch64.sha256) - cd packaging/aur/codegraph-rs-bin + # Write and read notes from a file to avoid quoting breaking things + echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt - sed -i \ - -e "s/^pkgver=.*/pkgver=$VERSION/" \ - -e "s/^pkgrel=.*/pkgrel=1/" \ - -e "s/^sha256sums_x86_64=.*/sha256sums_x86_64=('$x86_sha')/" \ - -e "s/^sha256sums_aarch64=.*/sha256sums_aarch64=('$arm_sha')/" \ - PKGBUILD - echo "--- PKGBUILD ---"; cat PKGBUILD + gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* - - name: Publish to AUR - uses: KSXGitHub/github-actions-deploy-aur@v4.1.3 - with: - pkgname: codegraph-rs-bin - pkgbuild: packaging/aur/codegraph-rs-bin/PKGBUILD - commit_username: ${{ secrets.AUR_USERNAME }} - commit_email: ${{ secrets.AUR_EMAIL }} - ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} - commit_message: "Update to ${{ needs.release.outputs.tag }}" - ssh_keyscan_types: rsa,ecdsa,ed25519 + announce: + needs: + - plan + - host + # use "always() && ..." to allow us to wait for all publish jobs while + # still allowing individual publish jobs to skip themselves (for prereleases). + # "host" however must run to completion, no skipping allowed! + if: ${{ always() && needs.host.result == 'success' }} + runs-on: "ubuntu-22.04" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: recursive diff --git a/Cargo.lock b/Cargo.lock index 2ca701f71..897dea08c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -719,6 +719,7 @@ dependencies = [ "codegraph-extract", "codegraph-graph", "codegraph-graphql", + "codegraph-installer", "codegraph-mcp", "ignore", "indicatif", diff --git a/Cargo.toml b/Cargo.toml index 399dcec47..760dcc41c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,8 @@ version = "1.2.0" edition = "2021" rust-version = "1.80" license = "MIT" -repository = "https://github.com/cleboost/codegraph" +repository = "https://github.com/hungpham10/codegraph-rs" +homepage = "https://github.com/hungpham10/codegraph-rs" authors = ["Cleboost "] [workspace.dependencies] @@ -109,3 +110,8 @@ panic = "abort" [profile.release-small] inherits = "release" opt-level = "z" + +# The profile that 'dist' will build with +[profile.dist] +inherits = "release" +lto = "thin" diff --git a/README.md b/README.md index ac9b1c59e..5d08153d0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # CodeGraph -[![CI](https://github.com/hungpham10/codegraph-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/Cleboost/codegraph-rs/actions/workflows/ci.yml) +[![CI](https://github.com/hungpham10/codegraph-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/hungpham10/codegraph-rs/actions/workflows/ci.yml) [![CodSpeed Badge](https://img.shields.io/endpoint?url=https://app.codspeed.io//badge.json)](https://app.codspeed.io//hungpham10/codegraph-rs?utm_source=badge) [![codecov](https://codecov.io/gh/hungpham10/codegraph-rs/graph/badge.svg?token=PUSMFF0CM8)](https://codecov.io/gh/hungpham10/codegraph-rs) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) @@ -52,6 +52,30 @@ Installs to `%LOCALAPPDATA%\codegraph\bin` and adds it to the user PATH. yay -S codegraph-rs-bin ``` +**macOS (Homebrew)** + +```sh +brew install hungpham10/codegraph/codegraph +``` + +**Debian / Ubuntu (.deb)** + +Download the `.deb` for your architecture from the +[latest release](https://github.com/hungpham10/codegraph-rs/releases/latest), then: + +```sh +sudo apt install ./codegraph_*.deb +``` + +**Fedora / RHEL (.rpm)** + +Download the `.rpm` for your architecture from the +[latest release](https://github.com/hungpham10/codegraph-rs/releases/latest), then: + +```sh +sudo dnf install ./codegraph-*.rpm +``` +
@@ -91,6 +115,20 @@ cargo install --git https://github.com/hungpham10/codegraph-rs codegraph
+### Set up as an MCP server for your agent + +After installing, register `codegraph` as an MCP server for your AI agent so it +can launch `codegraph serve --mcp` for your workspace: + +```sh +codegraph install --target claude # project-local (~/.claude/settings.local.json) +codegraph install --target claude --global # user-wide (~/.claude/settings.json) +``` + +Other targets: `cursor`, `codex`, `opencode`, `hermes`, `antigravity`, or `all`. +`--global` registers the (e.g. Homebrew-installed) binary at user level; without +it the registration is scoped to the current project directory. + ## Quick start ```sh diff --git a/crates/codegraph/Cargo.toml b/crates/codegraph/Cargo.toml index 611234dde..1403c1f80 100644 --- a/crates/codegraph/Cargo.toml +++ b/crates/codegraph/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true edition.workspace = true license.workspace = true repository.workspace = true +homepage.workspace = true description = "Local-first code intelligence: tree-sitter knowledge graph + MCP server." [[bin]] @@ -15,6 +16,7 @@ codegraph-graph = { path = "../codegraph-graph", features = ["sqlite", "lmdb codegraph-extract = { path = "../codegraph-extract" } codegraph-mcp = { path = "../codegraph-mcp", features = ["http"] } codegraph-graphql = { path = "../codegraph-graphql" } +codegraph-installer = { path = "../codegraph-installer" } clap = { workspace = true } tokio = { workspace = true } notify = { workspace = true } @@ -41,3 +43,32 @@ fastembed = ["codegraph-graph/fastembed"] # feature này sẽ lỗi (ort coreml chỉ compile trên macOS). Metal EP chưa được # expose bởi bản ort hiện tại → "metal" config cũng map sang CoreML. apple-accel = ["codegraph-graph/apple-accel"] + +# --- Native Linux packaging manifests (cargo-deb / cargo-rpm) --- + +[package.metadata.deb] +maintainer = "Cleboost " +copyright = "2024, Cleboost " +extended-description = """\ +codegraph is a local-first code intelligence engine: it builds a tree-sitter \ +knowledge graph of your workspace and exposes it through an MCP server and a \ +GraphQL API for AI agents (Claude Code, Cursor, Codex, …).""" +section = "devel" +priority = "optional" +assets = [ + ["../../README.md", "usr/share/doc/codegraph/README.md", "644"], + ["../../LICENSE", "usr/share/doc/codegraph/LICENSE", "644"], +] + +[package.metadata.rpm] +package = "codegraph" +license = "MIT" +summary = "Local-first code intelligence: tree-sitter knowledge graph + MCP server" +description = """\ +codegraph is a local-first code intelligence engine: it builds a tree-sitter \ +knowledge graph of your workspace and exposes it through an MCP server and a \ +GraphQL API for AI agents (Claude Code, Cursor, Codex, …).""" +assets = [ + ["../../README.md", "/usr/share/doc/codegraph/README.md", "644"], + ["../../LICENSE", "/usr/share/doc/codegraph/LICENSE", "644"], +] diff --git a/crates/codegraph/src/main.rs b/crates/codegraph/src/main.rs index 97fd921d8..016378553 100644 --- a/crates/codegraph/src/main.rs +++ b/crates/codegraph/src/main.rs @@ -4,6 +4,7 @@ use clap::{ArgAction, Parser, Subcommand}; use codegraph_extract::{ExtractStats, Orchestrator}; use codegraph_graph::{GraphIndex, SharedGraphIndex}; use codegraph_mcp::CodegraphServer; +use std::sync::Arc; #[cfg(feature = "fastembed")] use codegraph_graph::embeddings::warm_model_cache; @@ -49,6 +50,28 @@ enum Cmd { }, /// Remove the .codegraph/ directory. Deinit, + /// Register codegraph as an MCP server for an AI agent (e.g. Claude Code), + /// so the agent can launch `codegraph serve --mcp`. Writes the agent's config + /// (e.g. `~/.claude/settings.json`). After a Homebrew install, this points + /// the agent at the brew-installed `codegraph`. + Install { + /// Target agent: claude (default), cursor, codex, opencode, hermes, + /// antigravity, or `all`. + #[arg(long, default_value = "claude")] + target: String, + /// Install globally (user home) instead of project-local. + #[arg(long, default_value_t = false)] + global: bool, + }, + /// Remove codegraph's MCP server registration from an AI agent. + Uninstall { + /// Target agent (same values as `install`). + #[arg(long, default_value = "claude")] + target: String, + /// Remove the global (user-home) registration instead of project-local. + #[arg(long, default_value_t = false)] + global: bool, + }, /// Diagnose the environment: OS, codegraph version, whether the workspace is /// initialized, index stats, and external tools (git/tar) on PATH. Doctor, @@ -154,7 +177,7 @@ async fn main() -> Result<()> { match cmd { Cmd::Init { no_index, progress } => cmd_init(&root, !no_index, progress).await, Cmd::Deinit => cmd_deinit(&root), - Cmd::Doctor => cmd_doctor(&root).await, + #[cfg(feature = "fastembed")] Cmd::Embed { model, cache_dir } => cmd_embed(&model, cache_dir.as_deref()).await, Cmd::Serve { @@ -278,109 +301,7 @@ fn cmd_deinit(root: &Utf8Path) -> Result<()> { Ok(()) } -/// `codegraph doctor`: kiểm tra môi trường cơ bản và in báo cáo human-readable -/// với status `[OK]` / `[WARN]` / `[FAIL]`. Exit code ≠ 0 nếu có bất kỳ `[FAIL]`. -async fn cmd_doctor(root: &Utf8Path) -> Result<()> { - let mut ok = 0u32; - let mut warn = 0u32; - let mut fail = 0u32; - - // 1. Binary / version — luôn OK (đang chạy). - println!( - "[OK] codegraph {} ({} / {})", - env!("CARGO_PKG_VERSION"), - std::env::consts::OS, - std::env::consts::ARCH - ); - ok += 1; - // 2. Workspace root. - println!("[OK] workspace: {root}"); - ok += 1; - - // 3. Đã init chưa (thư mục `.codegraph/` tồn tại). - let initialized = is_initialized(root); - if initialized { - println!("[OK] initialized: .codegraph/ present"); - ok += 1; - } else { - println!("[WARN] not initialized: run `codegraph init`"); - warn += 1; - } - - // 4. Index stats (chỉ khi đã init) — đọc `sg_stats` từ đĩa O(1). - if initialized { - match codegraph_extract::ExtractConfig::load(root).storage_route(root) { - Some(route) => match SharedGraphIndex::open_route(Some(route)).await { - Ok(idx) => match idx.stats_cached().await { - Some(s) => { - println!( - "[OK] index: {} symbols, {} chains, {} edges, {} files", - s.symbols, s.chains, s.edges, s.files - ); - ok += 1; - } - None => { - println!("[WARN] index empty: run `codegraph init`"); - warn += 1; - } - }, - Err(e) => { - println!("[FAIL] cannot open index: {e}"); - fail += 1; - } - }, - // Backend in-memory: không có index local để inspect. - None => { - println!("[OK] index: in-memory backend (no local index to inspect)"); - ok += 1; - } - } - } - - // 5. External tools: git & tar (Windows: Git for Windows + tar.exe tích hợp). - for tool in ["git", "tar"] { - match check_tool_version(tool) { - Some(v) => { - println!("[OK] {tool}: {v}"); - ok += 1; - } - None => { - println!("[WARN] {tool} not found on PATH (needed for codegraph_diff_simulate)"); - warn += 1; - } - } - } - - println!("---"); - println!("{ok} OK, {warn} WARN, {fail} FAIL"); - if fail > 0 { - std::process::exit(1); - } - Ok(()) -} - -/// Trả version string của external tool nếu chạy được `--version`, ngược lại -/// `None` (tool không có trên PATH hoặc thoát lỗi). -fn check_tool_version(tool: &str) -> Option { - let out = std::process::Command::new(tool) - .arg("--version") - .output() - .ok()?; - if !out.status.success() { - return None; - } - let stdout = String::from_utf8_lossy(&out.stdout).trim().to_string(); - if stdout.is_empty() { - // Một số bản tool in version ra stderr. - let stderr = String::from_utf8_lossy(&out.stderr).trim().to_string(); - if stderr.is_empty() { - return Some("(present)".to_string()); - } - Some(stderr) - } else { - Some(stdout) - } } /// `codegraph embed --model `: pre-download model vào global cache để diff --git a/dist-workspace.toml b/dist-workspace.toml new file mode 100644 index 000000000..40ef9dff5 --- /dev/null +++ b/dist-workspace.toml @@ -0,0 +1,22 @@ +[workspace] +members = ["cargo:."] + +# Config for 'dist' +[dist] +# The preferred dist version to use in CI (Cargo.toml SemVer syntax) +cargo-dist-version = "0.32.0" +# CI backends to support +ci = "github" +# cargo-dist builds cross-compiled archives and the GitHub Release. +# Homebrew (.rb) and Linux (.deb/.rpm) packages are produced by dedicated +# jobs in .github/workflows/release.yml from these archives. +installers = [] +# Target platforms to build apps for (Rust target-triple syntax) +targets = [ + "x86_64-apple-darwin", + "aarch64-apple-darwin", + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64-pc-windows-msvc", +] diff --git a/packaging/aur/codegraph-rs-bin/PKGBUILD b/packaging/aur/codegraph-rs-bin/PKGBUILD index fb78b3d2f..b3c516295 100644 --- a/packaging/aur/codegraph-rs-bin/PKGBUILD +++ b/packaging/aur/codegraph-rs-bin/PKGBUILD @@ -4,7 +4,7 @@ pkgver=0.0.0 pkgrel=1 pkgdesc="Local-first code intelligence: tree-sitter knowledge graph + MCP server (prebuilt binary)" arch=('x86_64' 'aarch64') -url="https://github.com/Cleboost/codegraph-rs" +url="https://github.com/hungpham10/codegraph-rs" license=('MIT') provides=('codegraph') conflicts=('codegraph' 'codegraph-bin') diff --git a/packaging/homebrew/codegraph.rb.template b/packaging/homebrew/codegraph.rb.template new file mode 100644 index 000000000..455d1d4e8 --- /dev/null +++ b/packaging/homebrew/codegraph.rb.template @@ -0,0 +1,25 @@ +class Codegraph < Formula + desc "Local-first code intelligence: tree-sitter knowledge graph + MCP server" + homepage "https://github.com/hungpham10/codegraph-rs" + version "@@VERSION@@" + license "MIT" + + on_macos do + on_arm do + url "https://github.com/hungpham10/codegraph-rs/releases/download/@@TAG@@/codegraph-aarch64-apple-darwin.tar.gz" + sha256 "@@ARM_SHA@@" + end + on_intel do + url "https://github.com/hungpham10/codegraph-rs/releases/download/@@TAG@@/codegraph-x86_64-apple-darwin.tar.gz" + sha256 "@@X86_SHA@@" + end + end + + def install + bin.install "codegraph" + end + + test do + assert_match "codegraph", shell_output("#{bin}/codegraph --version") + end +end diff --git a/scripts/install.sh b/scripts/install.sh index 4e6c1e360..2aa1c1736 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1,10 +1,10 @@ #!/bin/sh # codegraph install script -# Usage: curl -fsSL https://raw.githubusercontent.com/cleboost/codegraph/main/scripts/install.sh | sh +# Usage: curl -fsSL https://raw.githubusercontent.com/hungpham10/codegraph-rs/main/scripts/install.sh | sh set -eu -REPO="cleboost/codegraph" +REPO="hungpham10/codegraph-rs" BIN_NAME="codegraph" INSTALL_DIR="${CODEGRAPH_INSTALL_DIR:-$HOME/.local/bin}" From 527f4c217a0915ecb7029ff1e327b2ca859cfb1c Mon Sep 17 00:00:00 2001 From: Hung Pham Date: Thu, 20 Aug 2026 11:04:51 +0700 Subject: [PATCH 2/4] Add missing pipeline to support releasing --- .github/workflows/release-packages.yml | 187 +++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 .github/workflows/release-packages.yml diff --git a/.github/workflows/release-packages.yml b/.github/workflows/release-packages.yml new file mode 100644 index 000000000..f23d36e64 --- /dev/null +++ b/.github/workflows/release-packages.yml @@ -0,0 +1,187 @@ +# Builds native Linux packages (.deb / .rpm) and publishes the Homebrew +# formula + AUR package, using the artifacts cargo-dist uploaded to the +# GitHub Release. +# +# Runs after cargo-dist's `release.yml` publishes the release, so all source +# archives are already available for download. + +name: Release Packages + +on: + release: + types: [published] + +permissions: + contents: write + +jobs: + # Build .deb packages for Debian/Ubuntu (and derivatives) from the gnu targets. + deb: + name: deb (${{ matrix.target }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-gnu + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - name: Install cross linker (aarch64) + if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }} + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV" + - name: Install cargo-deb + run: cargo install cargo-deb --locked + - name: Build + package .deb + run: cargo deb -p codegraph --target ${{ matrix.target }} + - name: Upload .deb to release + run: | + set -euo pipefail + deb=$(find target -name '*.deb' | head -n1) + gh release upload "${{ github.event.release.tag_name }}" "$deb" + + # Build .rpm packages for Fedora/RHEL (and derivatives) from the gnu targets. + rpm: + name: rpm (${{ matrix.target }}) + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - aarch64-unknown-linux-gnu + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + - name: Install rpm tooling + cross linker (aarch64) + if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }} + run: | + sudo apt-get update + sudo apt-get install -y gcc-aarch64-linux-gnu rpm + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc" >> "$GITHUB_ENV" + - name: Install rpm tooling (x86_64) + if: ${{ matrix.target == 'x86_64-unknown-linux-gnu' }} + run: | + sudo apt-get update + sudo apt-get install -y rpm + - name: Install cargo-rpm + run: cargo install cargo-rpm --locked + - name: Build + package .rpm + run: cargo rpm build -p codegraph --target ${{ matrix.target }} + - name: Upload .rpm to release + run: | + set -euo pipefail + rpm=$(find target -name '*.rpm' | head -n1) + gh release upload "${{ github.event.release.tag_name }}" "$rpm" + + # Render the Homebrew formula from the macOS archives and push it to the tap. + # Prereq: create the `hungpham10/homebrew-codegraph` tap repo and set the + # HOMEBREW_TAP_GITHUB_TOKEN secret (a PAT with write access to the tap). + homebrew: + name: Publish Homebrew formula + runs-on: ubuntu-22.04 + env: + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v6 + - name: Download macOS archives + compute sha256 + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: | + set -euo pipefail + mkdir -p dl + gh release download "$TAG" --repo "${{ github.repository }}" \ + --pattern 'codegraph-x86_64-apple-darwin.tar.gz' \ + --pattern 'codegraph-aarch64-apple-darwin.tar.gz' \ + --dir dl + x86=$(sha256sum dl/codegraph-x86_64-apple-darwin.tar.gz | awk '{print $1}') + arm=$(sha256sum dl/codegraph-aarch64-apple-darwin.tar.gz | awk '{print $1}') + echo "X86_SHA=$x86" >> "$GITHUB_ENV" + echo "ARM_SHA=$arm" >> "$GITHUB_ENV" + - name: Render formula + env: + TAG: ${{ github.event.release.tag_name }} + TEMPLATE: ${{ github.workspace }}/packaging/homebrew/codegraph.rb.template + run: | + set -euo pipefail + ver="${TAG#v}" + sed -e "s/@@VERSION@@/$ver/" \ + -e "s/@@TAG@@/$TAG/" \ + -e "s/@@X86_SHA@@/$X86_SHA/" \ + -e "s/@@ARM_SHA@@/$ARM_SHA/" \ + "$TEMPLATE" > codegraph.rb + echo "--- codegraph.rb ---"; cat codegraph.rb + - name: Push formula to tap + run: | + set -euo pipefail + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git clone "https://x-access-token:${HOMEBREW_TAP_GITHUB_TOKEN}@github.com/hungpham10/homebrew-codegraph.git" tap + mkdir -p tap/Formula + cp codegraph.rb tap/Formula/codegraph.rb + cd tap + git add -A + git commit -m "codegraph ${{ github.event.release.tag_name }}" || echo "no changes" + git push + + # Publish the prebuilt-binary AUR package (codegraph-rs-bin). + aur: + name: Publish AUR (codegraph-rs-bin) + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v6 + - name: Download release archives + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: | + set -euo pipefail + mkdir -p dl + gh release download "$TAG" \ + --repo "${{ github.repository }}" \ + --pattern 'codegraph-x86_64-unknown-linux-musl.tar.gz' \ + --pattern 'codegraph-aarch64-unknown-linux-gnu.tar.gz' \ + --dir dl + x86_sha=$(sha256sum dl/codegraph-x86_64-unknown-linux-musl.tar.gz | awk '{print $1}') + arm_sha=$(sha256sum dl/codegraph-aarch64-unknown-linux-gnu.tar.gz | awk '{print $1}') + echo "X86_SHA=$x86_sha" >> "$GITHUB_ENV" + echo "ARM_SHA=$arm_sha" >> "$GITHUB_ENV" + - name: Render PKGBUILD + env: + VERSION: ${{ github.event.release.tag_name }} + run: | + set -euo pipefail + ver="${VERSION#v}" + cd packaging/aur/codegraph-rs-bin + sed -i \ + -e "s/^pkgver=.*/pkgver=$ver/" \ + -e "s/^pkgrel=.*/pkgrel=1/" \ + -e "s/^sha256sums_x86_64=.*/sha256sums_x86_64=('$X86_SHA')/" \ + -e "s/^sha256sums_aarch64=.*/sha256sums_aarch64=('$ARM_SHA')/" \ + PKGBUILD + echo "--- PKGBUILD ---"; cat PKGBUILD + - name: Publish to AUR + uses: KSXGitHub/github-actions-deploy-aur@v4.1.3 + with: + pkgname: codegraph-rs-bin + pkgbuild: packaging/aur/codegraph-rs-bin/PKGBUILD + commit_username: ${{ secrets.AUR_USERNAME }} + commit_email: ${{ secrets.AUR_EMAIL }} + ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + commit_message: "Update to ${{ github.event.release.tag_name }}" + ssh_keyscan_types: rsa,ecdsa,ed25519 From b2ce5a48543f804f142def4d6b6a1306da1da0ca Mon Sep 17 00:00:00 2001 From: Hung Pham Date: Thu, 20 Aug 2026 11:27:26 +0700 Subject: [PATCH 3/4] Fix issue with doctor --- crates/codegraph/Cargo.toml | 4 +- crates/codegraph/src/main.rs | 212 +++++++++++++++++++- packaging/aur/codegraph-rs-bin/PKGBUILD | 2 +- packaging/aur/codegraph-rs-git/PKGBUILD | 4 +- packaging/choco/codegraph.nuspec | 6 +- packaging/choco/tools/chocolateyinstall.ps1 | 2 +- packaging/winget/codegraph.yaml | 12 +- 7 files changed, 226 insertions(+), 16 deletions(-) diff --git a/crates/codegraph/Cargo.toml b/crates/codegraph/Cargo.toml index 1403c1f80..94710bad8 100644 --- a/crates/codegraph/Cargo.toml +++ b/crates/codegraph/Cargo.toml @@ -47,8 +47,8 @@ apple-accel = ["codegraph-graph/apple-accel"] # --- Native Linux packaging manifests (cargo-deb / cargo-rpm) --- [package.metadata.deb] -maintainer = "Cleboost " -copyright = "2024, Cleboost " +maintainer = "Hung Pham " +copyright = "2024, Hung Pham " extended-description = """\ codegraph is a local-first code intelligence engine: it builds a tree-sitter \ knowledge graph of your workspace and exposes it through an MCP server and a \ diff --git a/crates/codegraph/src/main.rs b/crates/codegraph/src/main.rs index 016378553..8d4897fa9 100644 --- a/crates/codegraph/src/main.rs +++ b/crates/codegraph/src/main.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use camino::{Utf8Path, Utf8PathBuf}; use clap::{ArgAction, Parser, Subcommand}; use codegraph_extract::{ExtractStats, Orchestrator}; -use codegraph_graph::{GraphIndex, SharedGraphIndex}; +use codegraph_graph::GraphIndex; use codegraph_mcp::CodegraphServer; use std::sync::Arc; @@ -177,6 +177,9 @@ async fn main() -> Result<()> { match cmd { Cmd::Init { no_index, progress } => cmd_init(&root, !no_index, progress).await, Cmd::Deinit => cmd_deinit(&root), + Cmd::Doctor => cmd_doctor(&root).await, + Cmd::Install { target, global } => cmd_install(&root, &target, global), + Cmd::Uninstall { target, global } => cmd_uninstall(&root, &target, global), #[cfg(feature = "fastembed")] Cmd::Embed { model, cache_dir } => cmd_embed(&model, cache_dir.as_deref()).await, @@ -301,7 +304,214 @@ fn cmd_deinit(root: &Utf8Path) -> Result<()> { Ok(()) } +/// `codegraph doctor`: in báo cáo chẩn đoán môi trường để người dùng (và agent) +/// biết trạng thái hiện tại — đặc biệt hữu ích sau khi merge hỗ trợ Windows, vì +/// codegraph giờ chạy cross-platform và có thể register cho nhiều agent (Claude, +/// Cursor, Codex, …) với config path khác nhau trên mỗi OS. +async fn cmd_doctor(root: &Utf8Path) -> Result<()> { + use std::env::consts::{ARCH, OS}; + let binary = current_exe_path().unwrap_or_else(|_| Utf8PathBuf::from("codegraph")); + let initialized = is_initialized(root); + + println!("codegraph doctor"); + println!("================"); + println!("Platform : {OS} / {ARCH}"); + println!("Version : {}", env!("CARGO_PKG_VERSION")); + println!("Executable : {binary}"); + println!( + "Workspace : {}", + if initialized { + root.as_str().to_string() + } else { + "".to_string() + } + ); + + if initialized { + match open_index(root).await { + Ok(idx) => { + let s = idx.stats(); + println!( + "Index stats : {} files, {} symbols, {} chains, {} edges", + s.files, s.symbols, s.chains, s.edges + ); + } + Err(e) => println!("Index stats : "), + } + } + + // External tools codegraph relies on. On Windows, native package managers + // matter for install paths, so surface them too. + #[cfg(target_os = "windows")] + let tools: Vec<&str> = vec!["git", "tar", "winget", "choco", "scoop"]; + #[cfg(not(target_os = "windows"))] + let tools: Vec<&str> = vec!["git", "tar"]; + println!("Tools on PATH :"); + for t in tools { + let ok = std::process::Command::new(t) + .arg("--version") + .status() + .map(|s| s.success()) + .unwrap_or(false); + println!(" - {t:<8} : {}", if ok { "ok" } else { "missing" }); + } + + // MCP agent setup status: which agents are installed and whether they are + // already wired to discover codegraph's tools. This is the cross-platform + // "is my tool registered" check. + println!("MCP agents :"); + for t in codegraph_installer::registry() { + for (scope, global) in [("global", true), ("project", false)] { + let opts = codegraph_installer::InstallOpts { + project_root: if global { + None + } else { + Some(Utf8PathBuf::from(root)) + }, + global, + binary_path: binary.clone(), + home_dir: None, + }; + match t.detect(&opts) { + codegraph_installer::DetectStatus::NotFound => continue, + codegraph_installer::DetectStatus::AlreadyConfigured => { + println!(" - {} [{}]: configured ✓", t.label(), scope); + } + codegraph_installer::DetectStatus::Found => { + println!( + " - {} [{}]: agent present, codegraph NOT registered (run: codegraph install --target {} {})", + t.label(), + scope, + t.id(), + if global { "--global" } else { "" } + ); + } + } + } + } + + Ok(()) +} + +/// Đường dẫn tuyệt đối tới binary `codegraph` đang chạy — dùng làm `command` +/// trong config MCP của agent (Claude/Cursor/…). +fn current_exe_path() -> Result { + Utf8PathBuf::from_path_buf(std::env::current_exe()?) + .map_err(|p| anyhow!("non-UTF8 exe path: {}", p.display())) +} + +/// Chọn target agent theo `--target` (`all` = mọi target trong registry tương +/// ứng với scope global/project). +fn select_targets( + target: &str, + global: bool, +) -> Vec> { + let all = if global { + codegraph_installer::registry() + } else { + codegraph_installer::project_registry() + }; + if target.eq_ignore_ascii_case("all") { + return all; + } + all.into_iter().filter(|t| t.id() == target).collect() +} + +/// Danh sách id target hợp lệ (dùng trong thông báo lỗi). +fn known_targets(global: bool) -> String { + let all = if global { + codegraph_installer::registry() + } else { + codegraph_installer::project_registry() + }; + let mut ids: Vec<&str> = all.iter().map(|t| t.id()).collect(); + ids.push("all"); + ids.join(", ") +} + +/// `codegraph install --target [--global]`: register codegraph làm MCP +/// server cho agent đã chọn, trỏ `command` vào binary hiện tại. +fn cmd_install(root: &Utf8Path, target: &str, global: bool) -> Result<()> { + let binary_path = current_exe_path()?; + let opts = codegraph_installer::InstallOpts { + project_root: if global { + None + } else { + Some(Utf8PathBuf::from(root)) + }, + global, + binary_path, + home_dir: None, + }; + let targets = select_targets(target, global); + if targets.is_empty() { + anyhow::bail!("unknown target '{target}' (known: {})", known_targets(global)); + } + for t in targets { + match t.install(&opts)? { + codegraph_installer::InstallReport::Installed(paths) => { + eprintln!( + "✓ {}: installed → {}", + t.label(), + paths.iter().map(|p| p.to_string()).collect::>().join(", ") + ); + } + codegraph_installer::InstallReport::Updated(paths) => { + eprintln!( + "✓ {}: updated → {}", + t.label(), + paths.iter().map(|p| p.to_string()).collect::>().join(", ") + ); + } + codegraph_installer::InstallReport::Unchanged => { + eprintln!("• {}: already configured", t.label()); + } + codegraph_installer::InstallReport::Skipped(reason) => { + eprintln!("• {}: skipped ({reason})", t.label()); + } + } + } + Ok(()) +} + +/// `codegraph uninstall --target [--global]`: gỡ registration MCP của +/// codegraph khỏi agent đã chọn. +fn cmd_uninstall(root: &Utf8Path, target: &str, global: bool) -> Result<()> { + let binary_path = current_exe_path()?; + let opts = codegraph_installer::InstallOpts { + project_root: if global { + None + } else { + Some(Utf8PathBuf::from(root)) + }, + global, + binary_path, + home_dir: None, + }; + let targets = select_targets(target, global); + if targets.is_empty() { + anyhow::bail!("unknown target '{target}' (known: {})", known_targets(global)); + } + for t in targets { + match t.uninstall(&opts)? { + codegraph_installer::InstallReport::Updated(paths) => { + eprintln!( + "✓ {}: removed → {}", + t.label(), + paths.iter().map(|p| p.to_string()).collect::>().join(", ") + ); + } + codegraph_installer::InstallReport::Unchanged => { + eprintln!("• {}: not configured", t.label()); + } + codegraph_installer::InstallReport::Skipped(reason) => { + eprintln!("• {}: skipped ({reason})", t.label()); + } + codegraph_installer::InstallReport::Installed(_) => unreachable!(), + } + } + Ok(()) } /// `codegraph embed --model `: pre-download model vào global cache để diff --git a/packaging/aur/codegraph-rs-bin/PKGBUILD b/packaging/aur/codegraph-rs-bin/PKGBUILD index b3c516295..06c667da1 100644 --- a/packaging/aur/codegraph-rs-bin/PKGBUILD +++ b/packaging/aur/codegraph-rs-bin/PKGBUILD @@ -1,4 +1,4 @@ -# Maintainer: Cleboost +# Maintainer: Hung Pham pkgname=codegraph-rs-bin pkgver=0.0.0 pkgrel=1 diff --git a/packaging/aur/codegraph-rs-git/PKGBUILD b/packaging/aur/codegraph-rs-git/PKGBUILD index 4d9053f2d..5c2d47e13 100644 --- a/packaging/aur/codegraph-rs-git/PKGBUILD +++ b/packaging/aur/codegraph-rs-git/PKGBUILD @@ -1,10 +1,10 @@ -# Maintainer: Cleboost +# Maintainer: Hung Pham pkgname=codegraph-rs-git pkgver=r350.g5c59daf pkgrel=1 pkgdesc="Local-first code intelligence: tree-sitter knowledge graph + MCP server (git)" arch=('x86_64' 'aarch64') -url="https://github.com/Cleboost/codegraph-rs" +url="https://github.com/hungpham10/codegraph-rs" license=('MIT') depends=('gcc-libs' 'sqlite') makedepends=('rust' 'cargo' 'git') diff --git a/packaging/choco/codegraph.nuspec b/packaging/choco/codegraph.nuspec index a6cde89b7..d1af79ca0 100644 --- a/packaging/choco/codegraph.nuspec +++ b/packaging/choco/codegraph.nuspec @@ -4,9 +4,9 @@ codegraph 1.2.0 codegraph - Cleboost - https://github.com/Cleboost/codegraph-rs - https://github.com/Cleboost/codegraph-rs/blob/main/LICENSE + Hung Pham + https://github.com/hungpham10/codegraph-rs + https://github.com/hungpham10/codegraph-rs/blob/main/LICENSE false Local-first code intelligence: tree-sitter knowledge graph + MCP server. Indexes a codebase locally and exposes it over an MCP server and GraphQL API. Local-first code intelligence (MCP server). diff --git a/packaging/choco/tools/chocolateyinstall.ps1 b/packaging/choco/tools/chocolateyinstall.ps1 index 106f379d0..ee095f318 100644 --- a/packaging/choco/tools/chocolateyinstall.ps1 +++ b/packaging/choco/tools/chocolateyinstall.ps1 @@ -2,7 +2,7 @@ $ErrorActionPreference = 'Stop' $toolsDir = Split-Path -Parent $MyInvocation.MyCommand.Definition $version = $env:ChocolateyPackageVersion -$url = "https://github.com/Cleboost/codegraph-rs/releases/download/v$version/codegraph-x86_64-pc-windows-msvc.zip" +$url = "https://github.com/hungpham10/codegraph-rs/releases/download/v$version/codegraph-x86_64-pc-windows-msvc.zip" $zip = Join-Path $toolsDir "codegraph-$version.zip" Get-ChocolateyWebFile -PackageName 'codegraph' -FileFullPath $zip -Url $url diff --git a/packaging/winget/codegraph.yaml b/packaging/winget/codegraph.yaml index d9337c50e..b08a56b6d 100644 --- a/packaging/winget/codegraph.yaml +++ b/packaging/winget/codegraph.yaml @@ -5,20 +5,20 @@ # The release workflow (`release.yml`) produces that zip; fill the hash at # release time (or automate it in the release pipeline before submitting to # microsoft/winget-pkgs). -PackageIdentifier: Cleboost.codegraph +PackageIdentifier: hungpham10.codegraph PackageVersion: 1.2.0 PackageName: codegraph -Publisher: Cleboost -PublisherUrl: https://github.com/Cleboost/codegraph-rs +Publisher: Hung Pham +PublisherUrl: https://github.com/hungpham10/codegraph-rs License: MIT -LicenseUrl: https://github.com/Cleboost/codegraph-rs/blob/main/LICENSE +LicenseUrl: https://github.com/hungpham10/codegraph-rs/blob/main/LICENSE ShortDescription: Local-first code intelligence (tree-sitter knowledge graph + MCP server) Description: codegraph indexes a codebase into a local-first knowledge graph and exposes it over an MCP server and GraphQL API. -PackageUrl: https://github.com/Cleboost/codegraph-rs +PackageUrl: https://github.com/hungpham10/codegraph-rs InstallerType: zip Installers: - Architecture: x64 - InstallerUrl: https://github.com/Cleboost/codegraph-rs/releases/download/v1.2.0/codegraph-x86_64-pc-windows-msvc.zip + InstallerUrl: https://github.com/hungpham10/codegraph-rs/releases/download/v1.2.0/codegraph-x86_64-pc-windows-msvc.zip InstallerSha256: 0000000000000000000000000000000000000000000000000000000000000000 InstallerType: zip ManifestType: singleton From 43a568553aebaf6983dcccfc442f15a81990d403 Mon Sep 17 00:00:00 2001 From: hungpham10 <136320753+hungpham10@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:27:46 +0000 Subject: [PATCH 4/4] style: apply rustfmt --- crates/codegraph/src/main.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/crates/codegraph/src/main.rs b/crates/codegraph/src/main.rs index 8d4897fa9..e0a4c17e2 100644 --- a/crates/codegraph/src/main.rs +++ b/crates/codegraph/src/main.rs @@ -403,10 +403,7 @@ fn current_exe_path() -> Result { /// Chọn target agent theo `--target` (`all` = mọi target trong registry tương /// ứng với scope global/project). -fn select_targets( - target: &str, - global: bool, -) -> Vec> { +fn select_targets(target: &str, global: bool) -> Vec> { let all = if global { codegraph_installer::registry() } else { @@ -446,7 +443,10 @@ fn cmd_install(root: &Utf8Path, target: &str, global: bool) -> Result<()> { }; let targets = select_targets(target, global); if targets.is_empty() { - anyhow::bail!("unknown target '{target}' (known: {})", known_targets(global)); + anyhow::bail!( + "unknown target '{target}' (known: {})", + known_targets(global) + ); } for t in targets { match t.install(&opts)? { @@ -454,14 +454,22 @@ fn cmd_install(root: &Utf8Path, target: &str, global: bool) -> Result<()> { eprintln!( "✓ {}: installed → {}", t.label(), - paths.iter().map(|p| p.to_string()).collect::>().join(", ") + paths + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", ") ); } codegraph_installer::InstallReport::Updated(paths) => { eprintln!( "✓ {}: updated → {}", t.label(), - paths.iter().map(|p| p.to_string()).collect::>().join(", ") + paths + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", ") ); } codegraph_installer::InstallReport::Unchanged => { @@ -491,7 +499,10 @@ fn cmd_uninstall(root: &Utf8Path, target: &str, global: bool) -> Result<()> { }; let targets = select_targets(target, global); if targets.is_empty() { - anyhow::bail!("unknown target '{target}' (known: {})", known_targets(global)); + anyhow::bail!( + "unknown target '{target}' (known: {})", + known_targets(global) + ); } for t in targets { match t.uninstall(&opts)? { @@ -499,7 +510,11 @@ fn cmd_uninstall(root: &Utf8Path, target: &str, global: bool) -> Result<()> { eprintln!( "✓ {}: removed → {}", t.label(), - paths.iter().map(|p| p.to_string()).collect::>().join(", ") + paths + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", ") ); } codegraph_installer::InstallReport::Unchanged => {