Skip to content
Open
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
1 change: 0 additions & 1 deletion cortex-app-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ impl Default for RateLimitConfig {
by_user: false,
trust_proxy: false,
exempt_paths: vec!["/health".to_string()],
trust_proxy: false,
}
}
}
Expand Down
45 changes: 38 additions & 7 deletions cortex-cli/src/agent_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,18 @@ pub struct ListArgs {
pub json: bool,

/// Show only primary agents.
#[arg(long)]
#[arg(long, conflicts_with = "mode")]
pub primary: bool,

/// Show only subagents.
#[arg(long)]
#[arg(long, conflicts_with = "mode")]
pub subagents: bool,

/// Filter by agent mode (primary, subagent, all).
/// Alternative to --primary and --subagents flags.
#[arg(long, value_name = "MODE")]
pub mode: Option<String>,

/// Show all agents including hidden ones.
#[arg(long)]
pub all: bool,
Expand Down Expand Up @@ -943,6 +948,17 @@ async fn run_list(args: ListArgs) -> Result<()> {

let agents = load_all_agents()?;

// Determine mode filter from either --mode flag or --primary/--subagents flags
let mode_filter: Option<&str> = if let Some(ref mode) = args.mode {
Some(mode.as_str())
} else if args.primary {
Some("primary")
} else if args.subagents {
Some("subagent")
} else {
None
};

// Filter agents
let mut filtered: Vec<_> = agents
.iter()
Expand All @@ -952,11 +968,26 @@ async fn run_list(args: ListArgs) -> Result<()> {
return false;
}
// Filter by mode
if args.primary && !matches!(a.mode, AgentMode::Primary | AgentMode::All) {
return false;
}
if args.subagents && !matches!(a.mode, AgentMode::Subagent | AgentMode::All) {
return false;
if let Some(mode) = mode_filter {
match mode.to_lowercase().as_str() {
"primary" => {
if !matches!(a.mode, AgentMode::Primary | AgentMode::All) {
return false;
}
}
"subagent" | "subagents" => {
if !matches!(a.mode, AgentMode::Subagent | AgentMode::All) {
return false;
}
}
"all" => {
// Show all modes, no filtering
}
_ => {
// Unknown mode, show warning but don't filter
// (handled after the loop)
}
}
}
// Filter by pattern
if let Some(ref pattern) = args.filter {
Expand Down
200 changes: 199 additions & 1 deletion cortex-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ pub enum ColorMode {
///
/// If no subcommand is specified, starts the interactive TUI.
#[derive(Parser)]
#[command(name = "cortex")]
#[command(name = "cortex", bin_name = "cortex")]
#[command(author, version, long_version = get_long_version())]
#[command(about = "Cortex - AI Coding Agent", long_about = None)]
#[command(
Expand Down Expand Up @@ -735,6 +735,21 @@ enum ConfigSubcommand {

/// Unset (remove) a configuration value
Unset(ConfigUnsetArgs),

/// Generate a well-commented config.toml template with all options
Init(ConfigInitArgs),
}

/// Arguments for config init.
#[derive(Args)]
struct ConfigInitArgs {
/// Output file path (defaults to ~/.config/cortex/config.toml)
#[arg(short, long)]
output: Option<std::path::PathBuf>,

/// Force overwrite if file already exists
#[arg(short, long)]
force: bool,
}

/// Arguments for config get.
Expand Down Expand Up @@ -785,6 +800,7 @@ enum SandboxCommand {

/// Features command.
#[derive(Args)]
#[command(name = "features")]
struct FeaturesCommand {
#[command(subcommand)]
sub: FeaturesSubcommand,
Expand Down Expand Up @@ -2159,6 +2175,9 @@ async fn show_config(config_cli: ConfigCommand) -> Result<()> {
ConfigSubcommand::Unset(args) => {
return config_unset(&config_path, &args.key);
}
ConfigSubcommand::Init(args) => {
return config_init(&config_path, args);
}
}
}

Expand Down Expand Up @@ -2403,6 +2422,185 @@ fn config_unset(config_path: &std::path::Path, key: &str) -> Result<()> {
Ok(())
}

/// Generate a well-commented config.toml template with all options.
fn config_init(default_path: &std::path::Path, args: ConfigInitArgs) -> Result<()> {
let output_path = args
.output
.as_ref()
.map(|p| p.as_path())
.unwrap_or(default_path);

// Check if file already exists
if output_path.exists() && !args.force {
bail!(
"Config file already exists at: {}\n\
Use --force to overwrite, or specify a different output path with --output",
output_path.display()
);
}

// Create parent directory if needed
if let Some(parent) = output_path.parent() {
std::fs::create_dir_all(parent)?;
}

// Generate the well-commented template
let template = generate_config_template();

// Write atomically to prevent corruption
cortex_common::atomic_write(output_path, template.as_bytes())
.map_err(|e| anyhow::anyhow!("Failed to write config: {}", e))?;

print_success(&format!(
"Config template generated at: {}",
output_path.display()
));
println!("\nEdit this file to customize Cortex behavior.");
println!("For more information, see: https://docs.cortex.foundation/config");

Ok(())
}

/// Generate a well-commented config.toml template with all available options.
fn generate_config_template() -> String {
r#"# ═══════════════════════════════════════════════════════════════════════════════
# CORTEX CLI CONFIGURATION
# ═══════════════════════════════════════════════════════════════════════════════
#
# This file configures the Cortex CLI behavior.
# All settings are optional - Cortex uses sensible defaults.
#
# Documentation: https://docs.cortex.foundation/config
# ═══════════════════════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────────────────────
# MODEL CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[model]
# Default model to use (uncomment to override)
# default = "claude-sonnet-4-20250514"

# Default provider (anthropic, openai, google, etc.)
# provider = "anthropic"

# Maximum context window size in tokens
# Higher values allow longer conversations but cost more
# model_context_window = 128000

# Token limit at which to trigger auto-compaction
# When conversation reaches this limit, older messages will be summarized
# model_auto_compact_token_limit = 100000

# ───────────────────────────────────────────────────────────────────────────────
# SANDBOX CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[sandbox]
# Sandbox mode controls file system access restrictions
# Options:
# - "danger-full-access" : No restrictions (default, use with caution)
# - "read-only" : Read-only filesystem access
# - "workspace-write" : Read access + write to workspace only
# mode = "danger-full-access"

# ───────────────────────────────────────────────────────────────────────────────
# APPROVAL CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[approval]
# Approval mode controls when to ask for confirmation before executing actions
# Options:
# - "always" : Always ask for approval
# - "on-request" : Ask for approval when the model requests it
# - "never" : Never ask for approval (autonomous mode)
# mode = "on-request"

# ───────────────────────────────────────────────────────────────────────────────
# TUI CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[tui]
# Theme for the TUI interface
# Options: "ocean" (default), "midnight", "forest", "monochrome"
# theme = "ocean"

# Maximum line width for text wrapping (0 = auto-detect from terminal)
# line_width = 0

# Show line numbers in code blocks
# show_line_numbers = true

# Enable syntax highlighting in code blocks
# syntax_highlighting = true

# ───────────────────────────────────────────────────────────────────────────────
# OUTPUT CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[output]
# Default output format for list commands
# Options: "text" (default), "json", "csv"
# format = "text"

# Enable built-in pager for long output (true/false)
# pager = false

# Pager command (if pager = true and you want a custom pager)
# pager_command = "less -R"

# ───────────────────────────────────────────────────────────────────────────────
# NETWORK CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[network]
# HTTP proxy URL (e.g., "http://proxy.example.com:8080")
# proxy = ""

# Connection timeout in seconds
# timeout = 30

# ───────────────────────────────────────────────────────────────────────────────
# LOGGING CONFIGURATION
# ───────────────────────────────────────────────────────────────────────────────

[logging]
# Log level: error, warn, info, debug, trace
# level = "info"

# Log file path (empty = no file logging)
# file = ""

# ───────────────────────────────────────────════════════════════════════════════
# EXPERIMENTAL FEATURES
# ═══════════════════════════════════════════════════════════════════════════════
# These features are under active development and may change or be removed.

[experimental]
# Enable web search tool
# web_search = false

# Enable ghost snapshots for faster iteration
# ghost_snapshots = false

# ───────────────────────────────────────────────────────────────────────────────
# CUSTOM COMMANDS
# ───────────────────────────────────────────────────────────────────────────────
# Define custom slash commands that can be used in the TUI

# [[commands]]
# name = "test"
# description = "Run project tests"
# command = "npm test"

# [[commands]]
# name = "lint"
# description = "Run linter"
# command = "npm run lint"
"#
.to_string()
}

async fn list_features() -> Result<()> {
println!("{:<30} {:<12} {:<8}", "Feature", "Stage", "Enabled");
println!("{}", "-".repeat(52));
Expand Down
Loading