Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 158 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
name: Release

# Pushing a tag such as v0.4.0 builds the `repository-intelligence` CLI on native runners,
# packages it and publishes a GitHub release. Running the workflow by hand
# (workflow_dispatch) builds and uploads the archives as workflow artifacts
# but never publishes. See docs/releasing.md.

on:
push:
tags: [ "v*" ]
workflow_dispatch:

env:
CARGO_TERM_COLOR: always

jobs:
prepare:
name: Check version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4

- name: Read workspace version and compare with the tag
id: version
run: |
version="$(cargo metadata --no-deps --format-version 1 --locked \
| jq -r '.packages[] | select(.name == "repository-intelligence") | .version')"
if [ -z "$version" ] || [ "$version" = "null" ]; then
echo "::error::could not read the repository-intelligence version from Cargo.toml"
exit 1
fi
if [ "$GITHUB_REF_TYPE" = "tag" ] && [ "$GITHUB_REF_NAME" != "v$version" ]; then
echo "::error::tag $GITHUB_REF_NAME does not match the workspace version $version (expected v$version). Fix the version in Cargo.toml or push the right tag."
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"

build:
name: Build ${{ matrix.target }}
needs: prepare
runs-on: ${{ matrix.os }}
strategy:
fail-fast: true
matrix:
include:
# Linux builds run on the oldest hosted images so the binaries link
# against an older glibc (2.35) and run on most current distributions.
- { target: x86_64-unknown-linux-gnu, os: ubuntu-22.04 }
- { target: aarch64-unknown-linux-gnu, os: ubuntu-22.04-arm }
- { target: x86_64-apple-darwin, os: macos-15-intel }
- { target: aarch64-apple-darwin, os: macos-latest }
- { target: x86_64-pc-windows-msvc, os: windows-latest }
env:
VERSION: ${{ needs.prepare.outputs.version }}
TARGET: ${{ matrix.target }}
steps:
- uses: actions/checkout@v4

- name: Set up Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: ${{ matrix.target }}

- name: Build
run: cargo build --release --locked -p repository-intelligence --target ${{ matrix.target }}

- name: Package (unix)
if: runner.os != 'Windows'
shell: bash
run: |
set -euo pipefail
name="repository-intelligence-${VERSION}-${TARGET}"
mkdir -p "dist/${name}"
cp "target/${TARGET}/release/repository-intelligence" LICENSE "dist/${name}/"
cp packaging/README.txt "dist/${name}/README.txt"
tar -C dist -czf "dist/${name}.tar.gz" "${name}"
cd dist
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "${name}.tar.gz" > "${name}.tar.gz.sha256"
else
shasum -a 256 "${name}.tar.gz" > "${name}.tar.gz.sha256"
fi
cat "${name}.tar.gz.sha256"

- name: Package (windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$name = "repository-intelligence-$env:VERSION-$env:TARGET"
New-Item -ItemType Directory -Force -Path "dist/$name" | Out-Null
Copy-Item "target/$env:TARGET/release/repository-intelligence.exe" "dist/$name/"
Copy-Item LICENSE "dist/$name/"
Copy-Item packaging/README.txt "dist/$name/README.txt"
Compress-Archive -Path "dist/$name" -DestinationPath "dist/$name.zip"
$hash = (Get-FileHash "dist/$name.zip" -Algorithm SHA256).Hash.ToLower()
"$hash $name.zip" | Set-Content -Path "dist/$name.zip.sha256" -Encoding ascii
Get-Content "dist/$name.zip.sha256"

- name: Smoke test the binary
shell: bash
run: |
set -euo pipefail
exe="target/${TARGET}/release/repository-intelligence"
if [ "$RUNNER_OS" = "Windows" ]; then exe="${exe}.exe"; fi
out="$("$exe" --version)"
echo "$out"
case "$out" in
*"${VERSION}"*) ;;
*) echo "::error::binary reports '$out', expected version ${VERSION}"; exit 1 ;;
esac

- name: Upload archives
uses: actions/upload-artifact@v4
with:
name: repository-intelligence-${{ matrix.target }}
path: |
dist/*.tar.gz
dist/*.zip
dist/*.sha256
if-no-files-found: error

release:
name: Publish release
# Only for pushed tags; a manual run stops after the build job.
if: github.ref_type == 'tag'
needs: [prepare, build]
runs-on: ubuntu-latest
permissions:
contents: write
env:
VERSION: ${{ needs.prepare.outputs.version }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/download-artifact@v4
with:
path: dist
pattern: repository-intelligence-*
merge-multiple: true

- name: List files
run: ls -l dist

- name: Create the GitHub release
run: |
set -euo pipefail
flags=""
case "$VERSION" in *-*) flags="--prerelease" ;; esac
gh release create "$GITHUB_REF_NAME" \
--repo "$GITHUB_REPOSITORY" \
--verify-tag \
--title "repository-intelligence $VERSION" \
--generate-notes \
$flags \
dist/*.tar.gz dist/*.zip dist/*.sha256
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "repository-intelligence"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
rust-version = "1.85"
license = "MIT"
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Repository Intelligence searches local code and returns verbatim source lines fo
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

> [!NOTE]
> **Status:** Experimental research prototype (v0.1.0) for local code inspection.
> **Status:** Experimental research prototype (v0.1.1) for local code inspection.

![An offline search printing the top three matching source lines](docs/demo/offline-search.gif)

Expand All @@ -20,6 +20,22 @@ Repository Intelligence searches local code and returns verbatim source lines fo

## Quick start

### Try the released CLI (macOS or Linux)

The prebuilt CLI does not require Rust. The installer downloads a release, checks its SHA-256 checksum, and installs to `~/.local/bin`:

```bash
curl -fsSL https://raw.githubusercontent.com/rustfuture/repository-intelligence/main/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
repository-intelligence --version
# Search the repository you are in, offline, and print the three best source lines
repository-intelligence --hybrid . "main function" --top 3
```

Prebuilt Linux binaries require glibc; unsupported platforms can build from source. On Windows, use the [PowerShell installer](install.ps1).

### Build from source

You need Git and Rust 1.85+ with Cargo ([rustup](https://rustup.rs/)). It has no third-party crate dependencies. Offline commands use a deterministic `HashEmbedding` provider without external services. Neural embeddings and model-assisted answers use a local Ollama service (`nomic-embed-text` and `qwen2.5-coder:1.5b`) or the AGY adapter (the Google Antigravity command-line client).

```sh
Expand Down
129 changes: 129 additions & 0 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Installer for the `repository-intelligence` CLI (https://github.com/rustfuture/repository-intelligence).
#
# irm https://raw.githubusercontent.com/rustfuture/repository-intelligence/main/install.ps1 | iex
#
# Downloads the prebuilt Windows release archive, checks its SHA-256 checksum and
# copies repository-intelligence.exe into an install directory. It needs no administrator rights.
# It does NOT change your PATH: if the install directory is not on it, the script
# prints the command to add it.
#
# Environment variables (all optional):
# RI_VERSION Version to install, e.g. 0.3.0 or v0.3.0 (default: latest release)
# RI_INSTALL_DIR Where to put repository-intelligence.exe (default: $env:LOCALAPPDATA\repository-intelligence\bin)
# RI_DOWNLOAD_BASE Base URL, local directory or file:// URL holding the archive
# and its .sha256 file, instead of the GitHub release. Needs
# RI_VERSION. Meant for mirrors and testing.
# RI_REPO GitHub repository (default: rustfuture/repository-intelligence)
#
# Everything runs inside a function and reports problems with `throw`, never `exit`,
# so piping this script to `iex` cannot close your terminal.

function Install-Tool {
Set-StrictMode -Version 2
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue' # the progress bar makes Windows PowerShell 5.1 downloads very slow

$repo = if ($env:RI_REPO) { $env:RI_REPO } else { 'rustfuture/repository-intelligence' }
$fromSource = "cargo install --git https://github.com/$repo repository-intelligence"

# Windows PowerShell 5.1 does not enable TLS 1.2 by default.
try { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 } catch { }

# --- platform -------------------------------------------------------------
if ($PSVersionTable.PSVersion.Major -ge 6 -and -not $IsWindows) {
throw "This installer is for Windows. On macOS or Linux use: curl -fsSL https://raw.githubusercontent.com/$repo/main/install.sh | sh"
}
$osArch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
switch ($osArch) {
'X64' { }
'Arm64' { Write-Host 'Windows on ARM: installing the x64 build, which runs under emulation.' }
default { throw "Unsupported CPU architecture: $osArch. No prebuilt binary is available; if you have Rust (1.85 or newer), build from source: $fromSource" }
}
$target = 'x86_64-pc-windows-msvc'

# --- version --------------------------------------------------------------
if ($env:RI_VERSION) {
$version = $env:RI_VERSION.TrimStart('v')
} elseif ($env:RI_DOWNLOAD_BASE) {
throw 'RI_DOWNLOAD_BASE is set, so also set RI_VERSION (for example $env:RI_VERSION = ''0.3.0'').'
} else {
Write-Host 'Looking up the latest release...'
try {
$release = Invoke-RestMethod -UseBasicParsing -Uri "https://api.github.com/repos/$repo/releases/latest" -Headers @{ 'User-Agent' = 'repository-intelligence-installer' }
} catch {
throw 'Could not look up the latest release (no release published yet, no network, or the GitHub API rate limit). Set $env:RI_VERSION, e.g. $env:RI_VERSION = ''0.3.0'''
}
$version = ([string]$release.tag_name).TrimStart('v')
}
if ($version -notmatch '^[0-9A-Za-z.+-]+$') { throw "Invalid version: $version" }

$installDir = if ($env:RI_INSTALL_DIR) { $env:RI_INSTALL_DIR } else { Join-Path $env:LOCALAPPDATA 'repository-intelligence\bin' }
$name = "repository-intelligence-$version-$target"
$archive = "$name.zip"
$base = if ($env:RI_DOWNLOAD_BASE) { $env:RI_DOWNLOAD_BASE } else { "https://github.com/$repo/releases/download/v$version" }
$base = $base.TrimEnd('/', '\')

$tmp = Join-Path ([IO.Path]::GetTempPath()) ("repository-intelligence-install-" + [Guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $tmp | Out-Null
try {
Write-Host "Installing repository-intelligence $version for $target"

# --- download ---------------------------------------------------------
foreach ($file in @($archive, "$archive.sha256")) {
$dest = Join-Path $tmp $file
try {
if ($base -match '^https?://') {
Invoke-WebRequest -UseBasicParsing -Uri "$base/$file" -OutFile $dest
} else {
$localBase = if ($base -match '^file://') { ([Uri]$base).LocalPath } else { $base }
Copy-Item -LiteralPath (Join-Path $localBase $file) -Destination $dest
}
} catch {
throw "Could not download $base/$file (is $version a published version for $target?)"
}
}

# --- verify -----------------------------------------------------------
$sumLine = (Get-Content -LiteralPath (Join-Path $tmp "$archive.sha256") -TotalCount 1)
$expected = ($sumLine -split '\s+')[0].ToLower()
if ($expected -notmatch '^[0-9a-f]{64}$') { throw "The checksum file $archive.sha256 is not valid." }
$actual = (Get-FileHash -LiteralPath (Join-Path $tmp $archive) -Algorithm SHA256).Hash.ToLower()
if ($expected -ne $actual) {
Write-Error "Checksum mismatch for $archive`n expected: $expected`n actual: $actual" -ErrorAction Continue
throw 'Refusing to install a file that does not match its checksum.'
}
Write-Host 'Checksum OK'

# --- unpack and install ----------------------------------------------
Expand-Archive -LiteralPath (Join-Path $tmp $archive) -DestinationPath $tmp -Force
$exe = Join-Path $tmp "$name\repository-intelligence.exe"
if (-not (Test-Path -LiteralPath $exe)) { throw "The archive does not contain $name\repository-intelligence.exe" }

New-Item -ItemType Directory -Force -Path $installDir | Out-Null
$installed = Join-Path $installDir 'repository-intelligence.exe'
try {
Copy-Item -LiteralPath $exe -Destination $installed -Force
} catch {
throw "Could not write $installed. If repository-intelligence is running (for example as an agent hook), close it and try again; or set `$env:RI_INSTALL_DIR to another directory."
}

Write-Host "Installed: $installed"
& $installed --version
if ($LASTEXITCODE -ne 0) { throw "The installed binary did not run. Build from source instead: $fromSource" }

# --- PATH hint (never edited automatically) --------------------------
$dirs = @($env:PATH -split ';' | Where-Object { $_ } | ForEach-Object { $_.TrimEnd('\') })
if ($dirs -notcontains $installDir.TrimEnd('\')) {
Write-Host ''
Write-Host "$installDir is not on your PATH. To add it for your user account, run:"
Write-Host " [Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path','User') + ';$installDir', 'User')"
Write-Host 'then open a new terminal. (This installer does not change your PATH.)'
}
Write-Host ''
Write-Host "Next: run 'repository-intelligence --help' to get started."
} finally {
Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue
}
}

Install-Tool
Loading
Loading