diff --git a/Cargo.lock b/Cargo.lock index 86cf85b7..03786f32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -955,7 +955,7 @@ dependencies = [ [[package]] name = "cortex-app-server" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "async-channel", @@ -995,7 +995,7 @@ dependencies = [ [[package]] name = "cortex-apply-patch" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "serde", @@ -1021,7 +1021,7 @@ dependencies = [ [[package]] name = "cortex-cli" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "base64", @@ -1071,7 +1071,7 @@ dependencies = [ [[package]] name = "cortex-collab" -version = "0.1.9" +version = "0.1.10" dependencies = [ "serde", "thiserror 2.0.20", @@ -1081,7 +1081,7 @@ dependencies = [ [[package]] name = "cortex-commands" -version = "0.1.9" +version = "0.1.10" dependencies = [ "chrono", "dirs 6.0.0", @@ -1096,7 +1096,7 @@ dependencies = [ [[package]] name = "cortex-common" -version = "0.1.9" +version = "0.1.10" dependencies = [ "clap", "cortex-protocol", @@ -1130,7 +1130,7 @@ dependencies = [ [[package]] name = "cortex-core" -version = "0.1.9" +version = "0.1.10" dependencies = [ "ahash", "anyhow", @@ -1153,7 +1153,7 @@ dependencies = [ [[package]] name = "cortex-engine" -version = "0.1.9" +version = "0.1.10" dependencies = [ "aes-gcm", "anyhow", @@ -1225,7 +1225,7 @@ dependencies = [ [[package]] name = "cortex-exec" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-engine", "cortex-protocol", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "cortex-execpolicy" -version = "0.1.9" +version = "0.1.10" dependencies = [ "serde", "serde_json", @@ -1256,7 +1256,7 @@ dependencies = [ [[package]] name = "cortex-feedback" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "chrono", @@ -1269,7 +1269,7 @@ dependencies = [ [[package]] name = "cortex-file-search" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "ignore", @@ -1311,7 +1311,7 @@ dependencies = [ [[package]] name = "cortex-keyring-store" -version = "0.1.9" +version = "0.1.10" dependencies = [ "keyring", "thiserror 2.0.20", @@ -1335,7 +1335,7 @@ dependencies = [ [[package]] name = "cortex-lmstudio" -version = "0.1.9" +version = "0.1.10" dependencies = [ "reqwest", "serde", @@ -1349,7 +1349,7 @@ dependencies = [ [[package]] name = "cortex-login" -version = "0.1.9" +version = "0.1.10" dependencies = [ "aes-gcm", "anyhow", @@ -1397,7 +1397,7 @@ dependencies = [ [[package]] name = "cortex-mcp-client" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "async-trait", @@ -1413,7 +1413,7 @@ dependencies = [ [[package]] name = "cortex-mcp-server" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "async-trait", @@ -1426,7 +1426,7 @@ dependencies = [ [[package]] name = "cortex-mcp-types" -version = "0.1.9" +version = "0.1.10" dependencies = [ "schemars", "serde", @@ -1443,7 +1443,7 @@ dependencies = [ [[package]] name = "cortex-network-proxy" -version = "0.1.9" +version = "0.1.10" dependencies = [ "serde", "serde_json", @@ -1453,7 +1453,7 @@ dependencies = [ [[package]] name = "cortex-otel" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-common", "opentelemetry", @@ -1467,7 +1467,7 @@ dependencies = [ [[package]] name = "cortex-plugins" -version = "0.1.9" +version = "0.1.10" dependencies = [ "async-trait", "chrono", @@ -1493,14 +1493,14 @@ dependencies = [ [[package]] name = "cortex-process-hardening" -version = "0.1.9" +version = "0.1.10" dependencies = [ "libc", ] [[package]] name = "cortex-prompt-harness" -version = "0.1.9" +version = "0.1.10" dependencies = [ "chrono", "indexmap", @@ -1511,7 +1511,7 @@ dependencies = [ [[package]] name = "cortex-protocol" -version = "0.1.9" +version = "0.1.10" dependencies = [ "base64", "chrono", @@ -1556,7 +1556,7 @@ dependencies = [ [[package]] name = "cortex-sandbox" -version = "0.1.9" +version = "0.1.10" dependencies = [ "dirs 6.0.0", "landlock", @@ -1583,7 +1583,7 @@ dependencies = [ [[package]] name = "cortex-shell-snapshot" -version = "0.1.9" +version = "0.1.10" dependencies = [ "chrono", "dirs 6.0.0", @@ -1597,7 +1597,7 @@ dependencies = [ [[package]] name = "cortex-skills" -version = "0.1.9" +version = "0.1.10" dependencies = [ "chrono", "dashmap", @@ -1616,7 +1616,7 @@ dependencies = [ [[package]] name = "cortex-slack" -version = "0.1.9" +version = "0.1.10" dependencies = [ "async-trait", "axum", @@ -1666,7 +1666,7 @@ dependencies = [ [[package]] name = "cortex-tui" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "arboard", @@ -1711,7 +1711,7 @@ dependencies = [ [[package]] name = "cortex-tui-buffer" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-tui-core", "smallvec", @@ -1719,7 +1719,7 @@ dependencies = [ [[package]] name = "cortex-tui-capture" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "chrono", @@ -1739,7 +1739,7 @@ dependencies = [ [[package]] name = "cortex-tui-components" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-core", "crossterm", @@ -1749,7 +1749,7 @@ dependencies = [ [[package]] name = "cortex-tui-core" -version = "0.1.9" +version = "0.1.10" dependencies = [ "bitflags 2.13.1", "smallvec", @@ -1758,7 +1758,7 @@ dependencies = [ [[package]] name = "cortex-tui-framework" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-tui-buffer", "cortex-tui-core", @@ -1772,7 +1772,7 @@ dependencies = [ [[package]] name = "cortex-tui-input" -version = "0.1.9" +version = "0.1.10" dependencies = [ "bitflags 2.13.1", "crossterm", @@ -1781,7 +1781,7 @@ dependencies = [ [[package]] name = "cortex-tui-layout" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-tui-buffer", "slotmap", @@ -1790,7 +1790,7 @@ dependencies = [ [[package]] name = "cortex-tui-syntax" -version = "0.1.9" +version = "0.1.10" dependencies = [ "ahash", "cortex-tui-text", @@ -1803,7 +1803,7 @@ dependencies = [ [[package]] name = "cortex-tui-terminal" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-tui-buffer", "cortex-tui-core", @@ -1813,7 +1813,7 @@ dependencies = [ [[package]] name = "cortex-tui-text" -version = "0.1.9" +version = "0.1.10" dependencies = [ "smallvec", "unicode-segmentation", @@ -1822,7 +1822,7 @@ dependencies = [ [[package]] name = "cortex-tui-widgets" -version = "0.1.9" +version = "0.1.10" dependencies = [ "cortex-tui-buffer", "cortex-tui-core", @@ -1833,7 +1833,7 @@ dependencies = [ [[package]] name = "cortex-update" -version = "0.1.9" +version = "0.1.10" dependencies = [ "chrono", "cortex-common", @@ -1860,11 +1860,11 @@ dependencies = [ [[package]] name = "cortex-utils-cache" -version = "0.1.9" +version = "0.1.10" [[package]] name = "cortex-utils-file-safety" -version = "0.1.9" +version = "0.1.10" dependencies = [ "tempfile", "thiserror 2.0.20", @@ -1872,11 +1872,11 @@ dependencies = [ [[package]] name = "cortex-utils-git" -version = "0.1.9" +version = "0.1.10" [[package]] name = "cortex-utils-image" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "base64", @@ -1884,7 +1884,7 @@ dependencies = [ [[package]] name = "cortex-utils-pty" -version = "0.1.9" +version = "0.1.10" dependencies = [ "anyhow", "portable-pty", @@ -1892,7 +1892,7 @@ dependencies = [ [[package]] name = "cortex-utils-string" -version = "0.1.9" +version = "0.1.10" dependencies = [ "unicode-width", ] diff --git a/Cargo.toml b/Cargo.toml index 63b99dd3..c9af51a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,7 +104,7 @@ members = [ [workspace.package] # CLI version - kept in sync with VERSION_CLI and src/cortex-cli/VERSION # Run `scripts/check-cli-version.sh` to verify consistency -version = "0.1.9" +version = "0.1.10" edition = "2024" rust-version = "1.98" authors = ["Cortex Team "] diff --git a/VERSION_CLI b/VERSION_CLI index 1a030947..9767cc98 100644 --- a/VERSION_CLI +++ b/VERSION_CLI @@ -1 +1 @@ -0.1.9 +0.1.10 diff --git a/docs/reference/app-server.openapi.json b/docs/reference/app-server.openapi.json index 4ef31a60..f2f670b6 100644 --- a/docs/reference/app-server.openapi.json +++ b/docs/reference/app-server.openapi.json @@ -270,7 +270,7 @@ "info": { "description": "Supported local session/health contract. Message POST stores a message; it does not generate a model response. Other development endpoints are not part of this stable contract.", "title": "Cortex local session API", - "version": "0.1.9" + "version": "0.1.10" }, "openapi": "3.1.0", "paths": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 0d48215b..2ba5a4a1 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@cortexlm/sdk", - "version": "0.1.9", + "version": "0.1.10", "description": "Typed, local Cortex CLI subprocess client", "license": "Apache-2.0", "private": true, diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh index 5b6e1094..e0c93a5b 100755 --- a/scripts/bump-version.sh +++ b/scripts/bump-version.sh @@ -248,6 +248,32 @@ else echo -e " ${GREEN}✓ Updated Cargo.toml${NC}" fi +# Keep the private SDK package version on VERSION_CLI (packages/sdk tests require it). +SDK_PKG="$REPO_ROOT/packages/sdk/package.json" +echo -e "${BLUE}Updating packages/sdk/package.json...${NC}" +if [ "$DRY_RUN" = true ]; then + echo " Would update version to \"$NEW_VERSION\" in $SDK_PKG" +elif [ -f "$SDK_PKG" ]; then + SDK_PKG_TMP=$(mktemp "${SDK_PKG}.tmp.XXXXXX") + awk -v new_ver="$NEW_VERSION" ' + BEGIN { done = 0 } + !done && /"version"[[:space:]]*:/ { + sub(/"[0-9][^"]*"/, "\"" new_ver "\"") + done = 1 + } + { print } + ' "$SDK_PKG" > "$SDK_PKG_TMP" + if ! grep -q "\"version\": \"$NEW_VERSION\"" "$SDK_PKG_TMP"; then + rm -f "$SDK_PKG_TMP" + echo -e "${RED}ERROR: Failed to update version in packages/sdk/package.json${NC}" + exit 1 + fi + mv "$SDK_PKG_TMP" "$SDK_PKG" + echo -e " ${GREEN}✓ Updated packages/sdk/package.json${NC}" +else + echo -e " ${YELLOW}⚠ packages/sdk/package.json not found — skipped${NC}" +fi + echo "" # Verify consistency diff --git a/scripts/check-cli-version.sh b/scripts/check-cli-version.sh index ad053764..08c051e4 100755 --- a/scripts/check-cli-version.sh +++ b/scripts/check-cli-version.sh @@ -94,6 +94,18 @@ elif [ -n "$CLI_FILE_VERSION" ]; then echo -e "${GREEN}✓ VERSION_CLI matches src/cortex-cli/VERSION${NC}" fi +SDK_PKG="$REPO_ROOT/packages/sdk/package.json" +if [ -f "$SDK_PKG" ]; then + SDK_VERSION=$(grep -m1 '"version"' "$SDK_PKG" | sed 's/.*"\([^"]*\)".*/\1/') + echo "packages/sdk/package.json: $SDK_VERSION" + if [ "$VERSION_CLI" != "$SDK_VERSION" ]; then + echo -e "${RED}ERROR: VERSION_CLI ($VERSION_CLI) does not match packages/sdk/package.json ($SDK_VERSION)${NC}" + ERRORS=$((ERRORS + 1)) + else + echo -e "${GREEN}✓ VERSION_CLI matches packages/sdk/package.json${NC}" + fi +fi + # Check against release version if provided (used in CI) if [ -n "$CLI_RELEASE_VERSION" ]; then echo "" @@ -116,6 +128,7 @@ if [ $ERRORS -gt 0 ]; then echo "1. Update VERSION_CLI with the correct version" echo "2. Update [workspace.package] version in Cargo.toml to match" echo "3. Ensure cortex-cli/Cargo.toml uses 'version.workspace = true'" + echo "4. Update packages/sdk/package.json to the same version" exit 1 fi diff --git a/src/cortex-cli/VERSION b/src/cortex-cli/VERSION index 1a030947..9767cc98 100755 --- a/src/cortex-cli/VERSION +++ b/src/cortex-cli/VERSION @@ -1 +1 @@ -0.1.9 +0.1.10 diff --git a/src/cortex-tui/src/lib.rs b/src/cortex-tui/src/lib.rs index 39d979f0..073b00e3 100644 --- a/src/cortex-tui/src/lib.rs +++ b/src/cortex-tui/src/lib.rs @@ -105,6 +105,7 @@ pub mod lock_proof; pub mod lock_v2; pub mod readme_hero; pub mod readme_hero_boards; +pub mod splash_chrome; // Backtracking system for conversation history navigation pub mod backtrack; diff --git a/src/cortex-tui/src/lock_boards.rs b/src/cortex-tui/src/lock_boards.rs index 1135fdbe..04567e09 100644 --- a/src/cortex-tui/src/lock_boards.rs +++ b/src/cortex-tui/src/lock_boards.rs @@ -17,6 +17,7 @@ use ratatui::style::{Color, Modifier, Style}; use ratatui::text::{Line, Span}; use ratatui::widgets::{Paragraph, Widget}; +use crate::splash_chrome::splash_legend; use crate::ui::text_utils::{ first_fitting_line, fit_line, trim_dangling_separator, wrap_keep_indent, wrap_or_drop, }; @@ -36,9 +37,6 @@ const GHOST_RUNNING: &str = crate::views::minimal_session::PLACEHOLDER_RUNNING; /// Right-hand footer hint, and its narrow form — the live footer's copy. const FOOTER_HINT: &str = crate::widgets::key_hints::FOOTER_HINT_IDLE; const FOOTER_HINT_SHORT: &str = "shift+tab modes"; -/// Keystroke hints under the splash, and the form that fits 40 columns. -const LAUNCH_HINTS: &str = crate::views::minimal_session::EMPTY_SESSION_HINTS; -const LAUNCH_HINTS_NARROW: &str = "/ commands · @ files · ! shell"; /// Rows the composer takes: hairline, prompt, hairline. const COMPOSER_ROWS: u16 = crate::views::minimal_session::COMPOSER_ROWS; @@ -788,7 +786,9 @@ fn bar(filled: u16, total: u16) -> String { /// Launch header: `Welcome to Cortex, the coding agent CLI` then /// `v{version} · / commands · …`. No fake `> cortex` or painted cwd. fn paint_launch_header(area: Rect, buf: &mut Buffer, full: bool) -> u16 { - let w = inner_width(area); + // Full terminal width so the 40-col lock can hold the mid legend; a + // longer version is ellipsized instead of dropping `@ files · ! shell`. + let w = area.width.max(1) as usize; let mut y = area.y; let dim = Style::default().fg(TEXT_DIM); let bold = Style::default().fg(TEXT).add_modifier(Modifier::BOLD); @@ -797,16 +797,7 @@ fn paint_launch_header(area: Rect, buf: &mut Buffer, full: bool) -> u16 { buf.set_string(area.x + 17, y, ", the coding agent CLI", dim); y += 1; if full { - let version = env!("CARGO_PKG_VERSION"); - let full_h = format!("v{version} · {LAUNCH_HINTS}"); - let mid_h = format!("v{version} · {LAUNCH_HINTS_NARROW}"); - let hints = if full_h.chars().count() <= w { - full_h - } else if mid_h.chars().count() <= w { - mid_h - } else { - format!("v{version} · / commands") - }; + let hints = splash_legend(env!("CARGO_PKG_VERSION"), w); buf.set_string( area.x, y, diff --git a/src/cortex-tui/src/splash_chrome.rs b/src/cortex-tui/src/splash_chrome.rs new file mode 100644 index 00000000..1e6c9eac --- /dev/null +++ b/src/cortex-tui/src/splash_chrome.rs @@ -0,0 +1,102 @@ +//! Splash legend + version-width helpers. +//! +//! Split out of [`crate::lock_boards`] so a longer package version can +//! ellipsis without growing that file past the source-policy line-count +//! gate. At 40 columns the mid keystroke legend is kept; `& cloud` is +//! added when it fits. The version shortens — the legend does not. + +/// Full empty-session hints, including the cloud slot when width allows. +const LAUNCH_HINTS: &str = crate::views::minimal_session::EMPTY_SESSION_HINTS; +/// Form that fits a 40-column lock (`& cloud` dropped). +const LAUNCH_HINTS_NARROW: &str = "/ commands · @ files · ! shell"; + +/// Join `v{version}` to a hint legend, ellipsizing the version — never the +/// legend — when the pair is wider than `width`. +pub(crate) fn splash_legend(version: &str, width: usize) -> String { + let ver = format!("v{version}"); + for legend in [LAUNCH_HINTS, LAUNCH_HINTS_NARROW] { + let line = format!("{ver} · {legend}"); + if line.chars().count() <= width { + return line; + } + } + // Keep `/ commands · @ files · ! shell` (and `& cloud` when it fits). + // A longer package version is shortened; the keystroke legend is not. + for legend in [LAUNCH_HINTS, LAUNCH_HINTS_NARROW] { + let suffix = format!(" · {legend}"); + let suffix_len = suffix.chars().count(); + if suffix_len >= width { + continue; + } + let shown = ellipsis_prefix(&ver, width - suffix_len); + if shown.is_empty() { + continue; + } + return format!("{shown}{suffix}"); + } + LAUNCH_HINTS_NARROW.to_string() +} + +fn ellipsis_prefix(text: &str, budget: usize) -> String { + if text.chars().count() <= budget { + return text.to_string(); + } + if budget == 0 { + return String::new(); + } + if budget == 1 { + return "…".to_string(); + } + format!("{}…", text.chars().take(budget - 1).collect::()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn splash_legend_keeps_mid_hints_when_version_grows() { + for version in [env!("CARGO_PKG_VERSION"), "0.1.10", "10.20.30-rc.1"] { + let legend = splash_legend(version, 40); + assert!( + legend.contains("/ commands · @ files · ! shell"), + "40-col splash must keep mid hints for v{version}: {legend}" + ); + assert!( + !legend.contains("& cloud"), + "40-col splash shortens before & cloud: {legend}" + ); + } + } + + #[test] + fn splash_legend_adds_cloud_when_width_allows() { + let legend = splash_legend("0.1.10", 120); + assert!( + legend.contains("/ commands · @ files · ! shell · & cloud"), + "wide splash keeps the cloud slot: {legend}" + ); + assert!( + legend.starts_with("v0.1.10 · "), + "wide splash keeps the full version: {legend}" + ); + } + + #[test] + fn splash_legend_ellipsizes_version_not_legend() { + let exact = splash_legend("0.1.10", 40); + assert_eq!(exact, "v0.1.10 · / commands · @ files · ! shell"); + assert_eq!(exact.chars().count(), 40); + + let overflow = splash_legend("10.20.30-rc.1", 40); + assert!( + overflow.contains("/ commands · @ files · ! shell"), + "overflow must keep the mid legend: {overflow}" + ); + assert!( + overflow.contains('…'), + "a longer version ellipsizes: {overflow}" + ); + assert!(overflow.chars().count() <= 40, "{overflow}"); + } +}