Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
733c7a6
feat(i18n): Add comprehensive multi-language support for 12 languages
RIVALHIDE Dec 29, 2025
0f2f0cf
security(i18n): fix SonarQube vulnerability in fallback_handler.py
RIVALHIDE Dec 29, 2025
551864f
refactor(docs): consolidate i18n documentation into single comprehens…
RIVALHIDE Dec 29, 2025
254f5f2
fix(i18n): Fix all linting, formatting, and type errors
Jan 2, 2026
21e8d7a
fix(i18n): Complete Russian translations and pluralization fixes
Jan 2, 2026
ed602b2
Update cortex/i18n/language_manager.py
RIVALHIDE Jan 2, 2026
3bc19fd
Update cortex/i18n/translator.py
RIVALHIDE Jan 2, 2026
1535961
Update cortex/i18n/fallback_handler.py
RIVALHIDE Jan 2, 2026
472238b
fix(i18n): Fix Hindi example in pluralization.py to use pure Devanagari
Jan 2, 2026
b51b7db
ci(security): Fix CodeQL Analysis workflow configuration
Jan 2, 2026
6ad1bd2
Fixed all the potential issues
Jan 5, 2026
6494535
I fixed a typo in the placholder name in ch ja and de file
Jan 5, 2026
4738daf
Fix the lint errors
Jan 5, 2026
d226c60
fix: resolve black and ruff linting errors
Jan 5, 2026
f5ddd6b
added french and Portuguese
RIVALHIDE Jan 5, 2026
f4bf8a9
changes requested
RIVALHIDE Jan 5, 2026
b8bae47
chore(ci): remove duplicate CodeQL workflow
RIVALHIDE Jan 5, 2026
a6f533a
feat(i18n): add --set-language flag with human-readable names and fix…
RIVALHIDE Jan 6, 2026
e4ba731
Merge branch 'main' into issue-93
RIVALHIDE Jan 8, 2026
2eaf566
Fix the Python test Errors
RIVALHIDE Jan 8, 2026
5de53ff
Add package name validation to prevent command injection
RIVALHIDE Jan 9, 2026
2540bc3
Merge upstream/main: resolve conflicts in cli.py and add codeql.yml
RIVALHIDE Jan 10, 2026
a80d031
Changed the email Configuration to rigt email
RIVALHIDE Jan 10, 2026
8ee14e5
Update tests/test_env_manager.py
RIVALHIDE Jan 10, 2026
b6cf7b1
Update tests/test_i18n.py
RIVALHIDE Jan 10, 2026
af13bc3
Update cortex/translations/README.md
RIVALHIDE Jan 10, 2026
6e91a3e
fix: Add missing pt and fr language codes to CLI help text
RIVALHIDE Jan 10, 2026
fa8f843
fix(i18n): Complete Italian translation file
RIVALHIDE Jan 10, 2026
677cd0c
Merge branch 'main' into issue-93
Anshgrover23 Jan 11, 2026
2a79f8c
Fix the remaining Issues
RIVALHIDE Jan 12, 2026
a99deea
Merge branch 'main' into issue-93
RIVALHIDE Jan 12, 2026
637100d
Merge branch 'main' into issue-93
RIVALHIDE Jan 12, 2026
692f5ae
Merge branch 'main' into issue-93
Anshgrover23 Jan 12, 2026
388db56
Merge branch 'main' into issue-93
Anshgrover23 Jan 12, 2026
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
6 changes: 6 additions & 0 deletions .github/codeql-config.yml
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RIVALHIDE Why this file changed ?

Copy link
Contributor Author

@RIVALHIDE RIVALHIDE Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Anshgrover23 The codeql-config.yml file was inadvertently modified during AI-based testing. I apologize for the oversight; the original configuration has now been restored and pushed.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# CodeQL Configuration for Cortex Linux
# Uses default setup (no advanced configuration)
name: "CodeQL"
queries:
- uses: security-extended
- uses: security-and-quality
45 changes: 45 additions & 0 deletions .github/workflows/codeql-analysis.yml
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RIVALHIDE Again, why this one also changed ?

Copy link
Contributor Author

@RIVALHIDE RIVALHIDE Jan 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Anshgrover23 The workflows/codeql-analysis.yml file was inadvertently modified during AI-based testing. I apologize for the oversight; the original configuration has now been restored and pushed. Additionally, support has been added for cortex --set-language "English" alongside cortex config es, ensuring both the existing and the more human-readable syntax work as intended.
It was also identified that two CodeQL workflows were performing overlapping scans. To avoid redundant executions, the duplicate codeql.yml workflow has been removed, retaining the more comprehensive codeql-analysis.yml.

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: CodeQL Analysis

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 0'

permissions:
security-events: write
contents: read

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'python' ]

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
config-file: .github/codeql-config.yml

- name: Autobuild
uses: github/codeql-action/autobuild@v3

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{ matrix.language }}"
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ cortex-detect-hardware
- All tests must pass
- Documentation required for new features

## AI/IDE Agents Used

Used Cursor Copilot with Claude Opus 4.5 model for generating test cases and documentation. Core implementation was done manually.

## Contact

- Discord: https://discord.gg/uCqHvxjU83
Expand Down
151 changes: 140 additions & 11 deletions cortex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cortex.api_key_detector import auto_detect_api_key, setup_api_key
from cortex.ask import AskHandler
from cortex.branding import VERSION, console, cx_header, cx_print, show_banner
from cortex.config_manager import ConfigManager
from cortex.coordinator import InstallationCoordinator, InstallationStep, StepStatus
from cortex.demo import run_demo
from cortex.dependency_importer import (
Expand All @@ -19,6 +20,7 @@
format_package_list,
)
from cortex.env_manager import EnvironmentManager, get_env_manager
from cortex.i18n import LanguageManager
from cortex.installation_history import InstallationHistory, InstallationStatus, InstallationType
from cortex.llm.interpreter import CommandInterpreter
from cortex.network_config import NetworkConfig
Expand All @@ -37,11 +39,34 @@


class CortexCLI:
def __init__(self, verbose: bool = False):
def __init__(self, verbose: bool = False, language: str | None = None):
self.spinner_chars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
self.spinner_idx = 0
self.verbose = verbose

# Initialize language manager with ConfigManager for saved preferences
config_mgr = ConfigManager()
self.lang_manager = LanguageManager(prefs_manager=config_mgr)
detected_language = language or self.lang_manager.detect_language()
from cortex.i18n import get_translator

self.translator = get_translator(detected_language)

def t(self, key: str, **kwargs) -> str:
"""Get a translated string by key.

Shortcut method that delegates to self.translator.get() for retrieving
localized strings with optional variable interpolation.

Args:
key: Translation key in dot notation (e.g., 'install.success').
**kwargs: Variables for string interpolation (e.g., package='nginx').

Returns:
str: The translated string, or a fallback placeholder if key is missing.
"""
return self.translator.get(key, **kwargs)

# Define a method to handle Docker-specific permission repairs
def docker_permissions(self, args: argparse.Namespace) -> int:
"""Handle the diagnosis and repair of Docker file permissions.
Expand Down Expand Up @@ -514,11 +539,19 @@ def _sandbox_test(self, sandbox, args: argparse.Namespace) -> int:

def _sandbox_promote(self, sandbox, args: argparse.Namespace) -> int:
"""Promote a tested package to main system."""
from cortex.validators import validate_package_name

name = args.name
package = args.package
dry_run = getattr(args, "dry_run", False)
skip_confirm = getattr(args, "yes", False)

# Validate package name before passing to system commands
is_valid, error = validate_package_name(package)
if not is_valid:
self._print_error(f"Invalid package name: {error}")
return 1

if dry_run:
result = sandbox.promote(name, package, dry_run=True)
cx_print(f"Would run: sudo apt-get install -y {package}", "info")
Expand Down Expand Up @@ -672,22 +705,20 @@ def install(
start_time = datetime.now()

try:
self._print_status("🧠", "Understanding request...")
self._print_status("🧠", self.t("status.understanding"))

interpreter = CommandInterpreter(api_key=api_key, provider=provider)

self._print_status("📦", "Planning installation...")
self._print_status("📦", self.t("status.planning"))

for _ in range(10):
self._animate_spinner("Analyzing system requirements...")
self._animate_spinner(self.t("status.analyzing"))
self._clear_line()

commands = interpreter.parse(f"install {software}")

if not commands:
self._print_error(
"No commands generated. Please try again with a different request."
)
self._print_error(self.t("errors.no_commands"))
return 1

# Extract packages from commands for tracking
Expand All @@ -699,13 +730,13 @@ def install(
InstallationType.INSTALL, packages, commands, start_time
)

self._print_status("⚙️", f"Installing {software}...")
print("\nGenerated commands:")
self._print_status("⚙️", self.t("install.installing", package=software))
print(f"\n{self.t('install.generated_commands')}:")
for i, cmd in enumerate(commands, 1):
print(f" {i}. {cmd}")

if dry_run:
print("\n(Dry run mode - commands not executed)")
print(f"\n({self.t('install.dry_run_note')})")
if install_id:
history.update_installation(install_id, InstallationStatus.SUCCESS)
return 0
Expand Down Expand Up @@ -1388,6 +1419,83 @@ def _env_load(self, env_mgr: EnvironmentManager, args: argparse.Namespace) -> in

return 0

def config(self, args: argparse.Namespace) -> int:
"""Handle CLI configuration commands.

Routes configuration subcommands to their respective handlers.
Currently supports language configuration via the 'language' subcommand.

Args:
args: Parsed CLI arguments containing config_action attribute
and any subcommand-specific options.

Returns:
int: Exit code (0 on success, 1 on error or unknown subcommand).
"""
config_action = getattr(args, "config_action", None)

if not config_action:
self._print_error("Please specify a subcommand (language)")
return 1

if config_action == "language":
return self._config_language(args)
else:
self._print_error(f"Unknown config subcommand: {config_action}")
return 1

def _config_language(self, args: argparse.Namespace) -> int:
"""Handle language configuration."""
config_mgr = ConfigManager()
lang_mgr = LanguageManager()
Comment on lines +1447 to +1450
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Inconsistent LanguageManager instantiation.

In __init__ (line 49), LanguageManager is instantiated with prefs_manager=config_mgr, but here a new instance is created without the preferences manager. This may cause inconsistent behavior if LanguageManager relies on prefs_manager for certain operations.

🔧 Suggested fix
     def _config_language(self, args: argparse.Namespace) -> int:
         """Handle language configuration."""
         config_mgr = ConfigManager()
-        lang_mgr = LanguageManager()
+        lang_mgr = LanguageManager(prefs_manager=config_mgr)
         new_language = getattr(args, "language_code", None)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _config_language(self, args: argparse.Namespace) -> int:
"""Handle language configuration."""
config_mgr = ConfigManager()
lang_mgr = LanguageManager()
def _config_language(self, args: argparse.Namespace) -> int:
"""Handle language configuration."""
config_mgr = ConfigManager()
lang_mgr = LanguageManager(prefs_manager=config_mgr)
🤖 Prompt for AI Agents
In @cortex/cli.py around lines 1447 - 1450, The LanguageManager is being
instantiated without the prefs_manager in _config_language, causing inconsistent
behavior vs the __init__ usage; update the instantiation in _config_language so
it receives the same ConfigManager instance (config_mgr) as prefs_manager (i.e.,
construct LanguageManager with prefs_manager=config_mgr) so LanguageManager uses
the shared preferences manager consistently across methods like _config_language
and the class __init__.

new_language = getattr(args, "language_code", None)

# Load current preferences with error handling
try:
prefs = config_mgr.load_preferences()
except (OSError, ValueError) as e:
self._print_error(f"Failed to read configuration: {e}")
return 1

current_lang = prefs.get("language", "en")

if new_language:
# Resolve language (accepts codes like 'es' or names like 'Spanish', 'Español')
resolved = lang_mgr.resolve_language(new_language)
if not resolved:
self._print_error(f"Language '{new_language}' is not supported")
cx_print("Supported languages:", "info")
for code, name in lang_mgr.get_available_languages().items():
console.print(f" [green]{code}[/green] - {name}")
return 1

# Save preference with error handling
prefs["language"] = resolved
try:
config_mgr.save_preferences(prefs)
except (OSError, ValueError) as e:
self._print_error(f"Failed to save configuration: {e}")
return 1

lang_name = lang_mgr.get_language_name(resolved)
cx_print(f"✓ Language set to {lang_name} ({resolved})", "success")
cx_print("This will be used for all future Cortex commands.", "info")
return 0
else:
# Show current language and available options
lang_name = lang_mgr.get_language_name(current_lang)
cx_header("Language Configuration")
console.print(f" Current language: [green]{lang_name}[/green] ({current_lang})")
console.print()
console.print("[bold]Available languages:[/bold]")
for code, name in sorted(lang_mgr.get_available_languages().items()):
marker = " [cyan]◄[/cyan]" if code == current_lang else ""
console.print(f" [green]{code}[/green] - {name}{marker}")
console.print()
cx_print("Set language: cortex config language <code>", "info")
cx_print("Example: cortex config language es", "info")
return 0

# --- Shell Environment Analyzer Commands ---
def _env_audit(self, args: argparse.Namespace) -> int:
"""Audit shell environment variables and show their sources."""
Expand Down Expand Up @@ -2036,6 +2144,7 @@ def show_rich_help():
table.add_row("rollback <id>", "Undo installation")
table.add_row("notify", "Manage desktop notifications")
table.add_row("env", "Manage environment variables")
table.add_row("config", "Configure Cortex settings")
table.add_row("cache stats", "Show LLM cache statistics")
table.add_row("stack <name>", "Install the stack")
table.add_row("docker permissions", "Fix Docker bind-mount permissions")
Expand Down Expand Up @@ -2102,6 +2211,12 @@ def main():
# Global flags
parser.add_argument("--version", "-V", action="version", version=f"cortex {VERSION}")
parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed output")
parser.add_argument(
"--language",
"-L",
metavar="CODE",
help="Set display language for this command (e.g., en, es, de, ja)",
)

Comment on lines +2214 to 2220
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: --language/-L is parsed but never applied (flag is ignored).
CortexCLI supports language, but main() doesn’t pass args.language into the constructor (Line 2606).

Proposed diff
-    cli = CortexCLI(verbose=args.verbose)
+    cli = CortexCLI(
+        verbose=args.verbose,
+        language=getattr(args, "language", None),
+    )

Also applies to: 2605-2607

🤖 Prompt for AI Agents
In @cortex/cli.py around lines 2204 - 2210, The new CLI arg parser defines
"--language/-L" but the value is never applied; update main() so that when
instantiating CortexCLI you pass args.language into its constructor (e.g.,
CortexCLI(..., language=args.language)) and ensure CortexCLI's __init__
signature accepts and stores that parameter if not already; also propagate
args.language to any call sites that create CortexCLI within main() so the
parser value is actually used.

✅ Addressed in commit 2a79f8c

subparsers = parser.add_subparsers(dest="command", help="Available commands")

Expand Down Expand Up @@ -2220,6 +2335,18 @@ def main():
cache_subs = cache_parser.add_subparsers(dest="cache_action", help="Cache actions")
cache_subs.add_parser("stats", help="Show cache statistics")

# --- Configuration Commands ---
config_parser = subparsers.add_parser("config", help="Manage Cortex configuration")
config_subs = config_parser.add_subparsers(dest="config_action", help="Configuration actions")

# config language [code]
config_lang_parser = config_subs.add_parser("language", help="Get or set display language")
config_lang_parser.add_argument(
"language_code",
nargs="?",
help="Language code to set (e.g., en, es, fr, de, pt, ja, zh, ko, ar, hi, ru, it)",
)

# --- Sandbox Commands (Docker-based package testing) ---
sandbox_parser = subparsers.add_parser(
"sandbox", help="Test packages in isolated Docker sandbox"
Expand Down Expand Up @@ -2486,7 +2613,7 @@ def main():
return 0

# Initialize the CLI handler
cli = CortexCLI(verbose=args.verbose)
cli = CortexCLI(verbose=args.verbose, language=args.language)

try:
# Route the command to the appropriate method inside the cli object
Expand Down Expand Up @@ -2531,6 +2658,8 @@ def main():
return 1
elif args.command == "env":
return cli.env(args)
elif args.command == "config":
return cli.config(args)
else:
parser.print_help()
return 1
Expand Down
25 changes: 25 additions & 0 deletions cortex/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,31 @@ def _save_preferences(self, preferences: dict[str, Any]) -> None:
except Exception as e:
raise RuntimeError(f"Failed to save preferences: {e}")

def load_preferences(self) -> dict[str, Any]:
"""
Load user preferences from ~/.cortex/preferences.yaml.
Public API for accessing user preferences.
Returns:
Dictionary of preferences (empty dict if none exist)
"""
return self._load_preferences()

def save_preferences(self, preferences: dict[str, Any]) -> None:
"""
Save user preferences to ~/.cortex/preferences.yaml.
Public API for persisting user preferences.
Args:
preferences: Dictionary of preferences to save
Raises:
RuntimeError: If preferences cannot be saved
"""
self._save_preferences(preferences)

def export_configuration(
self,
output_path: str,
Expand Down
25 changes: 25 additions & 0 deletions cortex/i18n/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
I18N Module Initialization

Provides convenient access to i18n components for the rest of Cortex.

Author: Cortex Linux Team
License: Apache 2.0
"""

from cortex.i18n.fallback_handler import FallbackHandler, get_fallback_handler
from cortex.i18n.language_manager import LanguageManager
from cortex.i18n.pluralization import PluralRules
from cortex.i18n.translator import Translator, get_translator, translate

__all__ = [
"Translator",
"LanguageManager",
"PluralRules",
"FallbackHandler",
"get_translator",
"get_fallback_handler",
"translate",
]

__version__ = "0.1.0"
Loading
Loading