aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorcopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>2025-08-24 16:51:12 +0000
committercopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>2025-08-24 16:51:12 +0000
commit5dabc731cfb0f90afd1fc69b2603070149da43be (patch)
tree846e9d54ec0120d500b0516d5280593936b4b12e
parent07b88dccb464dca3bad51e7ad270625872f565ae (diff)
downloadsoon-5dabc731cfb0f90afd1fc69b2603070149da43be.tar.gz
soon-5dabc731cfb0f90afd1fc69b2603070149da43be.zip
Comprehensive upgrade: v0.2.0 with improved features and code quality
Co-authored-by: HsiangNianian <44714368+HsiangNianian@users.noreply.github.com>
-rw-r--r--Cargo.lock6
-rw-r--r--Cargo.toml6
-rw-r--r--README.md62
-rw-r--r--src/main.rs103
4 files changed, 111 insertions, 66 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 83fa450..9ffdee7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -127,9 +127,9 @@ dependencies = [
[[package]]
name = "counter"
-version = "0.6.0"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f009fcafa949dc1fc46a762dae84d0c2687d3b550906b633c4979d58d2c6ae52"
+checksum = "337a5b3c62043f6a4f5331bdb467a7f7a2099dce8522bcded6d4ebda1214fa4c"
dependencies = [
"num-traits",
]
@@ -246,7 +246,7 @@ dependencies = [
[[package]]
name = "soon"
-version = "0.1.6"
+version = "0.2.0"
dependencies = [
"clap",
"colored",
diff --git a/Cargo.toml b/Cargo.toml
index 7d87936..09d4ab0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "soon"
-version = "0.1.6"
+version = "0.2.0"
edition = "2021"
description = "Predict your next shell command based on history β€” like shell autocomplete, but MORE stupid"
license = "MIT"
@@ -13,7 +13,7 @@ name = "soon"
path = "src/main.rs"
[dependencies]
-clap = { version = "4.0", features = ["derive"] }
+clap = { version = "4.5", features = ["derive"] }
colored = "3.0.0"
-counter = "0.6.0"
+counter = "0.7.0"
dirs = "6.0.0"
diff --git a/README.md b/README.md
index d4217ef..2177ad6 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,9 @@
<img align="right" src="https://repology.org/badge/vertical-allrepos/soon.svg?columns=2" />
- 🐚 Shell-aware (supports Bash, Zsh, Fish)
-- πŸ“Š Shows your most used commands
+- πŸ“Š Shows your most used commands and analyzes patterns
+- 🧠 Smart learning from command history
+- πŸ”„ Easy update management and version checking
- 🌍 i18n support (EN/δΈ­ζ–‡) (WIP)
- πŸ’‘ Designed for clarity β€” **not** an autocomplete tool, but a prediction assistant.
@@ -39,7 +41,7 @@ pip install soon-bin
## Usage
```shell
-»»»» soon help 0|00:00:54
+»»»» soon help
Predict your next shell command based on history
Usage: soon [OPTIONS] [COMMAND]
@@ -47,18 +49,18 @@ Usage: soon [OPTIONS] [COMMAND]
Commands:
now Show the most likely next command
stats Show most used commands
- learn Train prediction (WIP)
+ learn Train prediction and analyze command patterns
which Display detected current shell
version Show version information
- update Update self [WIP]
+ update Check for updates and show installation options
show-cache Show cached main commands
show-internal-cache Show internal cache commands
cache Cache a command to soon cache (for testing)
help Print this message or the help of the given subcommand(s)
Options:
- --shell <SHELL>
- --ngram <NGRAM> [default: 3]
+ --shell <SHELL> Override shell type (bash, zsh, fish, etc.)
+ --ngram <NGRAM> Set n-gram size for prediction accuracy [default: 3]
--debug Enable debug output
-h, --help Print help
-V, --version Print version
@@ -66,28 +68,28 @@ Options:
### Main Commands
-| Command | Description |
-|-----------------------|--------------------------------------------------|
-| `now` | Show the most likely next command |
-| `stats` | Show most used commands |
-| `learn` | Train prediction (WIP) |
-| `which` | Display detected current shell |
-| `version` | Show version information |
-| `update` | Update self (WIP) |
-| `show-cache` | Show cached main commands |
-| `show-internal-cache` | Show internal cache commands |
-| `cache <NUM>` | Set cache size to `<NUM>` and refresh cache |
-| `help` | Print this message or the help of subcommands |
+| Command | Description |
+|-----------------------|------------------------------------------------------|
+| `now` | Show the most likely next command |
+| `stats` | Show most used commands |
+| `learn` | Train prediction and analyze command patterns |
+| `which` | Display detected current shell |
+| `version` | Show version information |
+| `update` | Check for updates and show installation options |
+| `show-cache` | Show cached main commands |
+| `show-internal-cache` | Show internal cache commands |
+| `cache <NUM>` | Set cache size to `<NUM>` and refresh cache |
+| `help` | Print this message or the help of subcommands |
### Options
-| Option | Description |
-|------------------|---------------------------------------------|
-| `--shell <SHELL>`| Specify shell type (bash, zsh, fish, etc.) |
-| `--ngram <NGRAM>`| Set n-gram length for prediction (default: 3)|
-| `--debug` | Enable debug output |
-| `-h, --help` | Print help |
-| `-V, --version` | Print version |
+| Option | Description |
+|------------------|-----------------------------------------------------|
+| `--shell <SHELL>`| Override shell type (bash, zsh, fish, etc.) |
+| `--ngram <NGRAM>`| Set n-gram size for prediction accuracy (default: 3)|
+| `--debug` | Enable debug output |
+| `-h, --help` | Print help |
+| `-V, --version` | Print version |
---
@@ -98,6 +100,16 @@ Options:
soon now
```
+#### Analyze your command patterns and learn insights
+```shell
+soon learn
+```
+
+#### Check for updates
+```shell
+soon update
+```
+
#### Show your most used commands
```shell
soon stats
diff --git a/src/main.rs b/src/main.rs
index 736d41e..245810f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,9 +16,9 @@ use std::path::PathBuf;
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
- #[arg(long)]
+ #[arg(long, help = "Override shell type (bash, zsh, fish, etc.)")]
shell: Option<String>,
- #[arg(long, default_value_t = 3)]
+ #[arg(long, default_value_t = 3, help = "Set n-gram size for prediction accuracy")]
ngram: usize,
#[arg(long, help = "Enable debug output")]
debug: bool,
@@ -30,13 +30,13 @@ enum Commands {
Now,
/// Show most used commands
Stats,
- /// Train prediction (WIP)
+ /// Train prediction and analyze command patterns
Learn,
/// Display detected current shell
Which,
/// Show version information
Version,
- /// Update self [WIP]
+ /// Check for updates and show installation options
Update,
/// Show cached main commands
ShowCache,
@@ -67,6 +67,7 @@ fn history_path(shell: &str) -> Option<PathBuf> {
#[derive(Debug)]
struct HistoryItem {
cmd: String,
+ #[allow(dead_code)] // Reserved for future features like directory-aware predictions
path: Option<String>,
}
@@ -78,6 +79,7 @@ fn load_history(shell: &str) -> Vec<HistoryItem> {
if !path.exists() {
eprintln!("⚠️ History file not found: {}", path.display());
+ eprintln!("πŸ’‘ Tip: Use your shell first to build up command history, or specify a different shell with --shell");
return vec![];
}
@@ -249,26 +251,6 @@ fn soon_show_internal_cache() {
println!("\n{}: {}", "Cache path".dimmed(), path.display());
}
-fn cache_main_cmd(cmd: &str) {
- let cmd = main_cmd(cmd);
- if cmd.is_empty() {
- return;
- }
-
- let path = get_cache_path();
- let mut file = match OpenOptions::new().append(true).create(true).open(&path) {
- Ok(f) => f,
- Err(e) => {
- eprintln!("⚠️ Failed to open cache file: {}", e);
- return;
- }
- };
-
- if let Err(e) = writeln!(file, "{}", cmd) {
- eprintln!("⚠️ Failed to write to cache: {}", e);
- }
-}
-
fn is_ignored_command(cmd: &str) -> bool {
let ignored = ["soon", "cd", "ls", "pwd", "exit", "clear"];
ignored.contains(&cmd)
@@ -434,7 +416,7 @@ fn soon_now(shell: &str, ngram: usize, debug: bool) {
}
}
-fn soon_cache(shell: &str, ngram: usize, cmd: &str) {
+fn soon_cache(shell: &str, ngram: usize, _cmd: &str) {
overwrite_soon_cache_from_history(shell, ngram);
println!("Cached main commands refreshed from history.");
println!("(Tip: soon cache now always reflects the latest {ngram} main commands from your history.)");
@@ -481,11 +463,48 @@ fn soon_stats(shell: &str) {
);
}
-fn soon_learn(_shell: &str) {
- println!(
- "{}",
- "🧠 [soon learn] feature under development...".yellow()
- );
+fn soon_learn(shell: &str) {
+ println!("{}", "🧠 Analyzing command patterns...".cyan().bold());
+
+ let history = load_history(shell);
+ if history.is_empty() {
+ eprintln!("{}", "⚠️ No history found to learn from".red());
+ return;
+ }
+
+ // Analyze command patterns
+ let total_commands = history.len();
+ let unique_commands: std::collections::HashSet<String> = history
+ .iter()
+ .map(|h| main_cmd(&h.cmd).to_string())
+ .collect();
+
+ println!("πŸ“Š Learning insights:");
+ println!(" β€’ Total commands in history: {}", total_commands);
+ println!(" β€’ Unique command types: {}", unique_commands.len());
+
+ if total_commands > 0 {
+ let repetition_rate = ((total_commands - unique_commands.len()) as f64 / total_commands as f64) * 100.0;
+ println!(" β€’ Command repetition rate: {:.1}%", repetition_rate);
+ }
+
+ // Find most common command patterns
+ let mut cmd_counts = std::collections::HashMap::new();
+ for item in &history {
+ let cmd = main_cmd(&item.cmd);
+ *cmd_counts.entry(cmd.to_string()).or_insert(0) += 1;
+ }
+
+ let mut sorted_cmds: Vec<_> = cmd_counts.iter().collect();
+ sorted_cmds.sort_by(|a, b| b.1.cmp(a.1));
+
+ println!("\nπŸ’‘ Top command patterns:");
+ for (i, (cmd, count)) in sorted_cmds.iter().take(5).enumerate() {
+ let percentage = (**count as f64 / total_commands as f64) * 100.0;
+ println!(" {}: {} ({:.1}% of usage)", i + 1, cmd, percentage);
+ }
+
+ println!("\n✨ Learning complete! Use 'soon now' for predictions.");
}
fn soon_which(shell: &str) {
@@ -505,10 +524,21 @@ fn soon_version() {
}
fn soon_update() {
- println!(
- "{}",
- "πŸ”„ [soon update] feature under development...".yellow()
- );
+ println!("{}", "πŸ”„ Checking for updates...".cyan().bold());
+
+ let current_version = env!("CARGO_PKG_VERSION");
+ println!("Current version: {}", current_version.green());
+
+ println!("\nπŸ“¦ Update options:");
+ println!(" β€’ Cargo: {}", "cargo install soon".dimmed());
+ println!(" β€’ Python: {}", "pip install --upgrade soon-bin".dimmed());
+ println!(" β€’ Arch Linux: {}", "paru -Syu soon".dimmed());
+ println!(" β€’ From source: {}", "git pull && cargo install --path .".dimmed());
+
+ println!("\nπŸ”— More info: {}", "https://github.com/HsiangNianian/soon".blue().underline());
+
+ // In a future version, this could check GitHub releases API for newer versions
+ println!("\nπŸ’‘ Future enhancement: automatic version checking will be added.");
}
fn main() {
@@ -516,7 +546,10 @@ fn main() {
let shell = cli.shell.clone().unwrap_or_else(detect_shell);
if shell == "unknown" && !matches!(cli.command, Some(Commands::Which)) {
- eprintln!("{}", "⚠️ Unknown shell. Please specify with --shell.".red());
+ eprintln!("{}", "⚠️ Unknown shell detected.".red());
+ eprintln!("πŸ’‘ Please specify your shell with: --shell <SHELL>");
+ eprintln!(" Supported shells: bash, zsh, fish");
+ eprintln!(" Example: soon now --shell zsh");
std::process::exit(1);
}