Skip to content

Commit 8008824

Browse files
echobtBounty Botfactorydroid
authored
fix: batch fixes for issues #2074, 2075, 2077, 2078, 2080, 2082, 2083, 2086, 2088, 2089 [skip ci] (#399)
Fixes: - #2074: Add alias command for custom shortcuts - #2075: Add --system flag to exec command (run already has it) - #2077: Add --max-tokens flag to exec command (run already has it) - #2078: Plugin system already exists (no changes needed) - #2080: Add cache management command - #2082: Add graceful shutdown message on SIGTERM - #2083: Add logs command to view debug logs - #2086: Add shell/interactive/repl command - #2088: Add workspace/project management command - #2089: Add file permissions display (rw-r--r-- format) in debug file Also fixes pre-existing build issues: - Add trust_proxy field to RateLimitConfig - Add cookies feature to reqwest - Fix borrow checker issue in scrape_cmd Co-authored-by: Bounty Bot <bounty-bot@factory.ai> Co-authored-by: Droid Agent <droid@factory.ai>
1 parent 73becc2 commit 8008824

10 files changed

Lines changed: 1683 additions & 1 deletion

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ simd-json = "0.14"
130130
toml = "0.8"
131131

132132
# HTTP client
133-
reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls"], default-features = false }
133+
reqwest = { version = "0.12", features = ["json", "stream", "rustls-tls", "cookies"], default-features = false }
134134

135135
# Error handling
136136
thiserror = "2.0"

cortex-cli/src/alias_cmd.rs

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
//! Alias command for Cortex CLI.
2+
//!
3+
//! Provides user-defined command alias functionality:
4+
//! - Set custom command aliases
5+
//! - List all aliases
6+
//! - Remove aliases
7+
//! - Show alias details
8+
9+
use anyhow::{Result, bail};
10+
use clap::Parser;
11+
use serde::{Deserialize, Serialize};
12+
use std::collections::HashMap;
13+
use std::path::PathBuf;
14+
15+
/// Alias CLI command.
16+
#[derive(Debug, Parser)]
17+
pub struct AliasCli {
18+
#[command(subcommand)]
19+
pub subcommand: AliasSubcommand,
20+
}
21+
22+
/// Alias subcommands.
23+
#[derive(Debug, clap::Subcommand)]
24+
pub enum AliasSubcommand {
25+
/// Set a command alias
26+
Set(AliasSetArgs),
27+
28+
/// List all aliases
29+
#[command(visible_alias = "ls")]
30+
List(AliasListArgs),
31+
32+
/// Remove an alias
33+
#[command(visible_aliases = ["rm", "delete"])]
34+
Remove(AliasRemoveArgs),
35+
36+
/// Show alias details
37+
#[command(visible_alias = "info")]
38+
Show(AliasShowArgs),
39+
}
40+
41+
/// Arguments for alias set command.
42+
#[derive(Debug, Parser)]
43+
pub struct AliasSetArgs {
44+
/// Alias name (short name for the command)
45+
pub name: String,
46+
47+
/// Command to alias (e.g., "exec --output-schema")
48+
pub command: String,
49+
50+
/// Description of the alias
51+
#[arg(long, short = 'd')]
52+
pub description: Option<String>,
53+
54+
/// Force overwrite if alias already exists
55+
#[arg(long, short = 'f')]
56+
pub force: bool,
57+
}
58+
59+
/// Arguments for alias list command.
60+
#[derive(Debug, Parser)]
61+
pub struct AliasListArgs {
62+
/// Output as JSON
63+
#[arg(long)]
64+
pub json: bool,
65+
}
66+
67+
/// Arguments for alias remove command.
68+
#[derive(Debug, Parser)]
69+
pub struct AliasRemoveArgs {
70+
/// Alias name to remove
71+
pub name: String,
72+
73+
/// Skip confirmation prompt
74+
#[arg(long, short = 'y')]
75+
pub yes: bool,
76+
}
77+
78+
/// Arguments for alias show command.
79+
#[derive(Debug, Parser)]
80+
pub struct AliasShowArgs {
81+
/// Alias name to show
82+
pub name: String,
83+
84+
/// Output as JSON
85+
#[arg(long)]
86+
pub json: bool,
87+
}
88+
89+
/// Alias definition.
90+
#[derive(Debug, Clone, Serialize, Deserialize)]
91+
pub struct AliasDefinition {
92+
pub name: String,
93+
pub command: String,
94+
#[serde(skip_serializing_if = "Option::is_none")]
95+
pub description: Option<String>,
96+
}
97+
98+
/// Alias configuration.
99+
#[derive(Debug, Default, Serialize, Deserialize)]
100+
pub struct AliasConfig {
101+
#[serde(default)]
102+
pub aliases: HashMap<String, AliasDefinition>,
103+
}
104+
105+
/// Get the aliases config file path.
106+
fn get_aliases_config_path() -> PathBuf {
107+
dirs::home_dir()
108+
.map(|h| h.join(".cortex").join("aliases.toml"))
109+
.unwrap_or_else(|| PathBuf::from(".cortex/aliases.toml"))
110+
}
111+
112+
/// Load aliases from config file.
113+
fn load_aliases() -> Result<AliasConfig> {
114+
let config_path = get_aliases_config_path();
115+
if !config_path.exists() {
116+
return Ok(AliasConfig::default());
117+
}
118+
119+
let content = std::fs::read_to_string(&config_path)?;
120+
let config: AliasConfig = toml::from_str(&content)?;
121+
Ok(config)
122+
}
123+
124+
/// Save aliases to config file.
125+
fn save_aliases(config: &AliasConfig) -> Result<()> {
126+
let config_path = get_aliases_config_path();
127+
128+
// Create parent directory if needed
129+
if let Some(parent) = config_path.parent() {
130+
if !parent.exists() {
131+
std::fs::create_dir_all(parent)?;
132+
}
133+
}
134+
135+
let content = toml::to_string_pretty(config)?;
136+
std::fs::write(&config_path, content)?;
137+
Ok(())
138+
}
139+
140+
impl AliasCli {
141+
/// Run the alias command.
142+
pub async fn run(self) -> Result<()> {
143+
match self.subcommand {
144+
AliasSubcommand::Set(args) => run_set(args).await,
145+
AliasSubcommand::List(args) => run_list(args).await,
146+
AliasSubcommand::Remove(args) => run_remove(args).await,
147+
AliasSubcommand::Show(args) => run_show(args).await,
148+
}
149+
}
150+
}
151+
152+
async fn run_set(args: AliasSetArgs) -> Result<()> {
153+
let mut config = load_aliases()?;
154+
155+
// Check if alias already exists
156+
if config.aliases.contains_key(&args.name) && !args.force {
157+
bail!(
158+
"Alias '{}' already exists. Use --force to overwrite.",
159+
args.name
160+
);
161+
}
162+
163+
// Create the alias definition
164+
let alias = AliasDefinition {
165+
name: args.name.clone(),
166+
command: args.command.clone(),
167+
description: args.description.clone(),
168+
};
169+
170+
config.aliases.insert(args.name.clone(), alias);
171+
save_aliases(&config)?;
172+
173+
println!("Alias '{}' set to: {}", args.name, args.command);
174+
if let Some(desc) = &args.description {
175+
println!(" Description: {}", desc);
176+
}
177+
178+
Ok(())
179+
}
180+
181+
async fn run_list(args: AliasListArgs) -> Result<()> {
182+
let config = load_aliases()?;
183+
184+
if args.json {
185+
let aliases: Vec<&AliasDefinition> = config.aliases.values().collect();
186+
println!("{}", serde_json::to_string_pretty(&aliases)?);
187+
return Ok(());
188+
}
189+
190+
if config.aliases.is_empty() {
191+
println!("No aliases defined.");
192+
println!("\nUse 'cortex alias set <name> <command>' to create an alias.");
193+
println!("Example: cortex alias set q \"exec --output-schema\"");
194+
return Ok(());
195+
}
196+
197+
println!("Defined Aliases:");
198+
println!("{}", "-".repeat(60));
199+
200+
let mut aliases: Vec<_> = config.aliases.values().collect();
201+
aliases.sort_by(|a, b| a.name.cmp(&b.name));
202+
203+
for alias in aliases {
204+
println!(" {} = {}", alias.name, alias.command);
205+
if let Some(desc) = &alias.description {
206+
println!(" {}", desc);
207+
}
208+
}
209+
210+
println!("\nTotal: {} alias(es)", config.aliases.len());
211+
Ok(())
212+
}
213+
214+
async fn run_remove(args: AliasRemoveArgs) -> Result<()> {
215+
let mut config = load_aliases()?;
216+
217+
if !config.aliases.contains_key(&args.name) {
218+
bail!("Alias '{}' does not exist.", args.name);
219+
}
220+
221+
if !args.yes {
222+
println!(
223+
"Are you sure you want to remove alias '{}'? (y/N)",
224+
args.name
225+
);
226+
let mut input = String::new();
227+
std::io::stdin().read_line(&mut input)?;
228+
if !input.trim().eq_ignore_ascii_case("y") {
229+
println!("Aborted.");
230+
return Ok(());
231+
}
232+
}
233+
234+
config.aliases.remove(&args.name);
235+
save_aliases(&config)?;
236+
237+
println!("Alias '{}' removed.", args.name);
238+
Ok(())
239+
}
240+
241+
async fn run_show(args: AliasShowArgs) -> Result<()> {
242+
let config = load_aliases()?;
243+
244+
let alias = config
245+
.aliases
246+
.get(&args.name)
247+
.ok_or_else(|| anyhow::anyhow!("Alias '{}' does not exist.", args.name))?;
248+
249+
if args.json {
250+
println!("{}", serde_json::to_string_pretty(alias)?);
251+
return Ok(());
252+
}
253+
254+
println!("Alias: {}", alias.name);
255+
println!("{}", "-".repeat(40));
256+
println!(" Command: {}", alias.command);
257+
if let Some(desc) = &alias.description {
258+
println!(" Description: {}", desc);
259+
}
260+
261+
Ok(())
262+
}

0 commit comments

Comments
 (0)