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
17 changes: 16 additions & 1 deletion .github/actions/cora-review-simple/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,22 @@ runs:
shell: bash
run: |
if [ "${{ inputs.cora-version }}" = "latest" ]; then
VERSION=$(curl -sL https://api.github.com/repos/ajianaz/cora-cli/releases/latest | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])")
# Try up to 3 times with delay
for i in 1 2 3; do
VERSION=$(curl -sfL --connect-timeout 10 \
https://api.github.com/repos/ajianaz/cora-cli/releases/latest \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('tag_name', ''))" 2>/dev/null)
if [ -n "$VERSION" ]; then
break
fi
echo "Attempt $i failed, retrying..."
sleep 5
done
# Fallback to a known good version if all retries fail
if [ -z "$VERSION" ]; then
echo "::warning::Failed to resolve latest cora-cli version from GitHub API, using fallback"
VERSION="v0.1.6"
fi
else
VERSION="${{ inputs.cora-version }}"
fi
Expand Down
17 changes: 16 additions & 1 deletion .github/actions/cora-review/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,22 @@ runs:
shell: bash
run: |
if [ "${{ inputs.cora-version }}" = "latest" ]; then
VERSION=$(curl -sL https://api.github.com/repos/ajianaz/cora-cli/releases/latest | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'])")
# Try up to 3 times with delay
for i in 1 2 3; do
VERSION=$(curl -sfL --connect-timeout 10 \
https://api.github.com/repos/ajianaz/cora-cli/releases/latest \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('tag_name', ''))" 2>/dev/null)
if [ -n "$VERSION" ]; then
break
fi
echo "Attempt $i failed, retrying..."
sleep 5
done
# Fallback to a known good version if all retries fail
if [ -z "$VERSION" ]; then
echo "::warning::Failed to resolve latest cora-cli version from GitHub API, using fallback"
VERSION="v0.1.6"
fi
else
VERSION="${{ inputs.cora-version }}"
fi
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.1.7] - 2026-06-01

### Added

- **Diff-hash caching** — review results cached by SHA-256 of diff + model + temperature in `~/.cache/cora/reviews/`. Cache TTL configurable via `llm.cache_ttl` (#100)
- **`--no-cache` flag** — bypass cache for fresh reviews (#100)
- **Configurable LLM parameters** — `llm.temperature` (default: 0), `llm.max_tokens` (default: 4096), `llm.timeout` (default: 120s), `llm.cache_ttl` (default: 1440 min) in `.cora.yaml` (#98 #101)
- **Git ref validation** — rejects refs containing shell metacharacters or path traversal sequences (#73)

### Fixed

- **Temperature default now 0** — eliminates non-deterministic LLM output. Same diff produces identical issues on every run (#98, #97)
- **HTTP timeout actually works** — per-request timeout via reqwest RequestBuilder (not client-level). Configurable timeout respected (#99)
- **Connection pooling** — shared reqwest::Client via LazyLock, reused across all requests (#99)
- **Cache key includes model + temperature** — config changes invalidate cache automatically (#100)
- **Silent config corruption** — malformed `.cora.yaml` now shows clear error with file path and hint (#78)
- **Composite action KeyError on API failure** — version resolution retries 3x with 5s delay, falls back to v0.1.6 with warning. Fixed in both `cora-review` and `cora-review-simple` actions (#102)

## [0.1.6] - 2026-06-01

### Added
Expand Down
74 changes: 73 additions & 1 deletion Cargo.lock

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

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cora-cli"
version = "0.1.6"
version = "0.1.7"
edition = "2024"
description = "CLI-first AI code review — BYOK, diff/scan/branch, pre-commit hooks"
license = "MIT"
Expand Down Expand Up @@ -51,6 +51,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Misc
regex = "1"
futures-util = "0.3"
sha2 = "0.10"

[dev-dependencies]
assert_cmd = "2"
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ Download the latest release from [GitHub Releases](https://github.com/ajianaz/co

```bash
# Determine your platform tag from the releases page, e.g.:
# cora-aarch64-unknown-linux-gnu-v0.1.6.tar.gz
# cora-x86_64-unknown-linux-gnu-v0.1.6.tar.gz
# cora-aarch64-apple-darwin-v0.1.6.tar.gz
# cora-x86_64-pc-windows-msvc-v0.1.6.zip
# cora-aarch64-unknown-linux-gnu-v0.1.7.tar.gz
# cora-x86_64-unknown-linux-gnu-v0.1.7.tar.gz
# cora-aarch64-apple-darwin-v0.1.7.tar.gz
# cora-x86_64-pc-windows-msvc-v0.1.7.zip

# Example: Linux aarch64
VERSION=$(curl -s https://api.github.com/repos/ajianaz/cora-cli/releases/latest | grep tag_name | cut -d'"' -f4)
Expand Down
13 changes: 10 additions & 3 deletions src/commands/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ pub struct ReviewOptions {
pub quiet: bool,
/// Minimum severity level for filtering issues.
pub severity: Option<String>,
/// Disable review caching.
pub no_cache: bool,
}

/// Result of a review command execution.
Expand Down Expand Up @@ -88,9 +90,14 @@ pub async fn execute_review(
);

// 3. Call the LLM engine
let response =
crate::engine::review::review_diff_with_stream(config, llm_config, &diff, opts.stream)
.await?;
let response = crate::engine::review::review_diff_with_cache(
config,
llm_config,
&diff,
opts.stream,
!opts.no_cache,
)
.await?;

// 4. Filter by severity if specified
let min_severity = if let Some(ref sev) = opts.severity {
Expand Down
22 changes: 20 additions & 2 deletions src/config/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ pub fn find_cora_file(start: &Path) -> Result<Option<(PathBuf, CoraFile)>> {
debug!(path = %candidate.display(), "found .cora.yaml");
let content = std::fs::read_to_string(&candidate)
.with_context(|| format!("failed to read {}", candidate.display()))?;
let cora = CoraFile::from_str(&content)?;
let cora = CoraFile::from_str(&content).map_err(|e| {
let msg = e.to_string();
anyhow::anyhow!(
"{}\n → file: {}\n → hint: check for syntax errors (indentation, colons, trailing spaces)",
msg,
candidate.display()
)
})?;
return Ok(Some((candidate, cora)));
}

Expand Down Expand Up @@ -237,7 +244,15 @@ pub fn load_config(
let path = Path::new(path);
let content = std::fs::read_to_string(path)
.with_context(|| format!("failed to read config at {}", path.display()))?;
let cora = CoraFile::from_str(&content)?;
let cora = CoraFile::from_str(&content).map_err(|e| {
let msg = e.to_string();
// Enhance error message with path info and hint about malformed YAML
anyhow::anyhow!(
"{}\n → file: {}\n → hint: check for syntax errors (indentation, colons, trailing spaces)",
msg,
path.display()
)
})?;
cora.merge_into(&mut config);
debug!(path = %path.display(), "loaded explicit config");
} else if let Some((path, cora)) = find_cora_file(&std::env::current_dir()?)? {
Expand Down Expand Up @@ -340,6 +355,9 @@ pub fn build_llm_config(config: &Config, cli_api_key: Option<&str>) -> Result<LL
base_url,
model,
provider,
temperature: config.temperature,
max_tokens: config.max_tokens,
timeout: config.timeout,
})
}

Expand Down
Loading
Loading