Refactor CLI, add refresh command, fix port detection, add device tracking

- Remove build/upload/monitor subcommands (projects are self-contained)
- Remove ctrlc dependency (only used by removed monitor watch mode)
- Update next-steps messaging to reference project scripts directly

- Add 'anvil refresh [DIR] [--force]' to update project scripts
  to latest templates without touching user code

- Fix Windows port detection: replace fragile findstr/batch TOML
  parsing with proper comment-skipping logic; add _detect_port.ps1
  helper for reliable JSON-based port detection via PowerShell

- Add .anvil.local for machine-specific config (gitignored)
  - 'anvil devices --set [PORT] [-d DIR]' saves port + VID:PID
  - 'anvil devices --get [-d DIR]' shows saved port status
  - VID:PID tracks USB devices across COM port reassignment
  - Port resolution: -p flag > VID:PID > saved port > auto-detect
  - Uppercase normalization for Windows COM port names

- Update all .bat/.sh templates to read from .anvil.local
- Remove port entries from .anvil.toml (no machine-specific config in git)
- Add .anvil.local to .gitignore template
- Expand 'anvil devices' output with VID:PID, serial number, and
  usage instructions
This commit is contained in:
Eric Ratliff
2026-02-16 08:29:33 -06:00
parent 3298844399
commit 8fe1ef0e27
25 changed files with 2551 additions and 731 deletions

View File

@@ -43,73 +43,31 @@ enum Commands {
Setup,
/// List connected boards and serial ports
Devices,
Devices {
/// Save a port to .anvil.local for this project
#[arg(long, conflicts_with = "get")]
set: bool,
/// Compile a sketch (and optionally upload)
Build {
/// Path to sketch directory
sketch: String,
/// Show the saved port for this project
#[arg(long, conflicts_with = "set")]
get: bool,
/// Compile only -- do not upload
#[arg(long)]
verify: bool,
/// Port name (e.g. COM3, /dev/ttyUSB0). Auto-detects if omitted with --set.
port_or_dir: Option<String>,
/// Open serial monitor after upload
#[arg(long)]
monitor: bool,
/// Delete cached build artifacts first
#[arg(long)]
clean: bool,
/// Show full compiler output
#[arg(long)]
verbose: bool,
/// Serial port (auto-detected if omitted)
#[arg(short, long)]
port: Option<String>,
/// Serial monitor baud rate
#[arg(short, long)]
baud: Option<u32>,
/// Override Fully Qualified Board Name
#[arg(long)]
fqbn: Option<String>,
/// Path to project directory (defaults to current directory)
#[arg(long, short = 'd', value_name = "DIR")]
dir: Option<String>,
},
/// Upload cached build artifacts (no recompile)
Upload {
/// Path to sketch directory
sketch: String,
/// Update project scripts to the latest version
Refresh {
/// Path to project directory (defaults to current directory)
dir: Option<String>,
/// Serial port (auto-detected if omitted)
#[arg(short, long)]
port: Option<String>,
/// Show full avrdude output
/// Overwrite scripts even if they have been modified
#[arg(long)]
verbose: bool,
/// Override Fully Qualified Board Name
#[arg(long)]
fqbn: Option<String>,
},
/// Open serial monitor
Monitor {
/// Serial port (auto-detected if omitted)
#[arg(short, long)]
port: Option<String>,
/// Baud rate (default: from project config or 115200)
#[arg(short, long)]
baud: Option<u32>,
/// Persistent mode: reconnect after upload/reset/replug
#[arg(long)]
watch: bool,
force: bool,
},
}
@@ -133,7 +91,7 @@ fn main() -> Result<()> {
} else {
anyhow::bail!(
"Project name required.\n\
Usage: anvil new <name>\n\
Usage: anvil new <n>\n\
List templates: anvil new --list-templates"
);
}
@@ -144,31 +102,24 @@ fn main() -> Result<()> {
Commands::Setup => {
commands::setup::run_setup()
}
Commands::Devices => {
commands::devices::scan_devices()
Commands::Devices { set, get, port_or_dir, dir } => {
if set {
commands::devices::set_port(
port_or_dir.as_deref(),
dir.as_deref(),
)
} else if get {
commands::devices::get_port(
dir.as_deref().or(port_or_dir.as_deref()),
)
} else {
commands::devices::scan_devices()
}
}
Commands::Build {
sketch, verify, monitor, clean, verbose,
port, baud, fqbn,
} => {
commands::build::run_build(
&sketch, verify, monitor, clean, verbose,
port.as_deref(), baud, fqbn.as_deref(),
)
}
Commands::Upload { sketch, port, verbose, fqbn } => {
commands::build::run_upload_only(
&sketch,
port.as_deref(),
verbose,
fqbn.as_deref(),
)
}
Commands::Monitor { port, baud, watch } => {
commands::monitor::run_monitor(
port.as_deref(),
baud,
watch,
Commands::Refresh { dir, force } => {
commands::refresh::run_refresh(
dir.as_deref(),
force,
)
}
}
@@ -193,4 +144,4 @@ fn print_banner() {
.bright_cyan()
);
println!();
}
}