Skip to content
Merged
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
10 changes: 5 additions & 5 deletions crates/fastskill-cli/src/commands/skillopt/export.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `fastskill skillopt export` subcommand
//! `fastskill optimize export` subcommand

use crate::error::{CliError, CliResult};
use cli_framework::command::{FromArgValueMap, IntoCommandSpec};
Expand All @@ -8,7 +8,7 @@ use cli_framework::spec::value::ArgValue;
use std::collections::HashMap;
use std::path::PathBuf;

/// Arguments for `fastskill skillopt export`
/// Arguments for `fastskill optimize export`
#[derive(Debug)]
pub struct ExportArgs {
/// Path to the run directory
Expand All @@ -22,7 +22,7 @@ impl IntoCommandSpec for ExportArgs {
fn command_spec() -> CommandSpec {
CommandSpec {
summary: "Export the best skill document from a completed run",
syntax: Some("skillopt export <run-dir> --out <path>"),
syntax: Some("optimize export <run-dir> --out <path>"),
args: vec![
ArgSpec {
name: "run-dir",
Expand Down Expand Up @@ -66,15 +66,15 @@ impl FromArgValueMap for ExportArgs {
pub async fn execute_export(args: ExportArgs) -> CliResult<()> {
if !args.run_dir.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_RUN_DIR_MISSING: run directory not found: {}",
"OPTIMIZE_RUN_DIR_MISSING: run directory not found: {}",
args.run_dir.display()
)));
}

let best_skill_path = args.run_dir.join("best_skill.md");
if !best_skill_path.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_EXPORT_BEST_MISSING: best_skill.md not found in run directory: {}",
"OPTIMIZE_EXPORT_BEST_MISSING: best_skill.md not found in run directory: {}",
args.run_dir.display()
)));
}
Expand Down
10 changes: 5 additions & 5 deletions crates/fastskill-cli/src/commands/skillopt/inspect.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `fastskill skillopt inspect` subcommand
//! `fastskill optimize inspect` subcommand

use crate::error::{CliError, CliResult};
use cli_framework::command::{FromArgValueMap, IntoCommandSpec};
Expand All @@ -8,7 +8,7 @@ use cli_framework::spec::value::ArgValue;
use std::collections::HashMap;
use std::path::PathBuf;

/// Arguments for `fastskill skillopt inspect`
/// Arguments for `fastskill optimize inspect`
#[derive(Debug)]
pub struct InspectArgs {
/// Path to the run directory
Expand All @@ -34,7 +34,7 @@ impl IntoCommandSpec for InspectArgs {
fn command_spec() -> CommandSpec {
CommandSpec {
summary: "Inspect per-step artifacts from a training run",
syntax: Some("skillopt inspect <run-dir> --step <n> [--show <mode>]"),
syntax: Some("optimize inspect <run-dir> --step <n> [--show <mode>]"),
args: vec![
ArgSpec {
name: "run-dir",
Expand Down Expand Up @@ -100,15 +100,15 @@ impl FromArgValueMap for InspectArgs {
pub async fn execute_inspect(args: InspectArgs) -> CliResult<()> {
if !args.run_dir.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_RUN_DIR_MISSING: run directory not found: {}",
"OPTIMIZE_RUN_DIR_MISSING: run directory not found: {}",
args.run_dir.display()
)));
}

let step_dir = args.run_dir.join(format!("step-{}", args.step));
if !step_dir.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_STEP_NOT_FOUND: no artifacts for step {} in: {}",
"OPTIMIZE_STEP_NOT_FOUND: no artifacts for step {} in: {}",
args.step,
args.run_dir.display()
)));
Expand Down
2 changes: 1 addition & 1 deletion crates/fastskill-cli/src/commands/skillopt/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! SkillOpt command group — iterative skill-document optimization via text-gradient.
//! Optimize command group — iterative skill-document optimization via text-gradient.
//!
//! All subcommands are registered individually via `AppBuilder::register::<T>()` in main.rs.
//! The per-subcommand Args structs implement `IntoCommandSpec + FromArgValueMap` directly.
Expand Down
37 changes: 21 additions & 16 deletions crates/fastskill-cli/src/commands/skillopt/resume.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `fastskill skillopt resume` subcommand
//! `fastskill optimize resume` subcommand

use super::config::{build_run_config, load_suite_with_splits, validate_config, SkillOptToml};
use crate::error::{CliError, CliResult};
Expand All @@ -9,7 +9,7 @@ use cli_framework::spec::value::ArgValue;
use std::collections::HashMap;
use std::path::PathBuf;

/// Arguments for `fastskill skillopt resume`
/// Arguments for `fastskill optimize resume`
#[derive(Debug)]
pub struct ResumeArgs {
/// Path to the run directory to resume
Expand All @@ -20,7 +20,7 @@ impl IntoCommandSpec for ResumeArgs {
fn command_spec() -> CommandSpec {
CommandSpec {
summary: "Resume an interrupted optimization run",
syntax: Some("skillopt resume <run-dir>"),
syntax: Some("optimize resume <run-dir>"),
args: vec![ArgSpec {
name: "run-dir",
kind: ArgKind::Positional,
Expand Down Expand Up @@ -50,29 +50,34 @@ pub async fn execute_resume(args: ResumeArgs) -> CliResult<()> {
// 1. Verify run dir exists
if !args.run_dir.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_RUN_DIR_MISSING: run directory not found: {}",
"OPTIMIZE_RUN_DIR_MISSING: run directory not found: {}",
args.run_dir.display()
)));
}

// 2. Read stored skillopt.toml from the run directory
let stored_config_path = args.run_dir.join("skillopt.toml");
// 2. Read stored config from the run directory; try optimize.toml first, then skillopt.toml
let stored_config_path = {
let new_path = args.run_dir.join("optimize.toml");
if new_path.exists() {
new_path
} else {
args.run_dir.join("skillopt.toml") // backward compat for pre-rename run dirs
}
};
if !stored_config_path.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_RUN_DIR_CORRUPT: missing skillopt.toml in run directory: {}",
"OPTIMIZE_RUN_DIR_CORRUPT: missing optimize.toml in run directory: {}",
args.run_dir.display()
)));
}

let config_str = std::fs::read_to_string(&stored_config_path).map_err(|e| {
CliError::Config(format!(
"SKILLOPT_RUN_DIR_CORRUPT: cannot read skillopt.toml: {e}"
))
CliError::Config(format!("OPTIMIZE_RUN_DIR_CORRUPT: cannot read config: {e}"))
})?;

let cfg: SkillOptToml = toml::from_str(&config_str).map_err(|e| {
CliError::Config(format!(
"SKILLOPT_RUN_DIR_CORRUPT: invalid skillopt.toml: {e}"
"OPTIMIZE_RUN_DIR_CORRUPT: invalid config toml: {e}"
))
})?;

Expand All @@ -87,7 +92,7 @@ pub async fn execute_resume(args: ResumeArgs) -> CliResult<()> {
let checks = if let Some(ref checks_path) = cfg.checks {
let checks_path = base_dir.join(checks_path);
fastskill_evals::load_checks(&checks_path)
.map_err(|e| CliError::Config(format!("SKILLOPT_CHECKS_PARSE_ERROR: {e}")))?
.map_err(|e| CliError::Config(format!("OPTIMIZE_CHECKS_PARSE_ERROR: {e}")))?
} else {
vec![]
};
Expand All @@ -97,7 +102,7 @@ pub async fn execute_resume(args: ResumeArgs) -> CliResult<()> {
Some(a) => a,
None => {
eprintln!(
"SKILLOPT_OPTIMIZER_DEFAULT_WARN: optimizer_agent not set, defaulting to target_agent '{}'",
"OPTIMIZE_OPTIMIZER_DEFAULT_WARN: optimizer_agent not set, defaulting to target_agent '{}'",
cfg.target_agent
);
cfg.target_agent.clone()
Expand All @@ -107,11 +112,11 @@ pub async fn execute_resume(args: ResumeArgs) -> CliResult<()> {
// 6. Read skill document
let skill_path = base_dir.join(&cfg.skill);
let initial_skill_md = std::fs::read_to_string(&skill_path)
.map_err(|e| CliError::Config(format!("SKILLOPT_SKILL_NOT_FOUND: {e}")))?;
.map_err(|e| CliError::Config(format!("OPTIMIZE_SKILL_NOT_FOUND: {e}")))?;

// 7. Build RunConfig
let run_config = build_run_config(&cfg, &optimizer_agent)
.map_err(|e| CliError::Config(format!("SKILLOPT_TRAINING_FAILED: invalid config: {e}")))?;
.map_err(|e| CliError::Config(format!("OPTIMIZE_TRAINING_FAILED: invalid config: {e}")))?;

// 8. Resume training
let outcome = aikit_skillopt::resume_skill(
Expand All @@ -123,7 +128,7 @@ pub async fn execute_resume(args: ResumeArgs) -> CliResult<()> {
run_config,
)
.await
.map_err(|e| CliError::Config(format!("SKILLOPT_TRAINING_FAILED: {e}")))?;
.map_err(|e| CliError::Config(format!("OPTIMIZE_TRAINING_FAILED: {e}")))?;

println!("{}", outcome.best_artifact_path.display());
Ok(())
Expand Down
34 changes: 17 additions & 17 deletions crates/fastskill-cli/src/commands/skillopt/run.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `fastskill skillopt run` subcommand
//! `fastskill optimize run` subcommand

use super::config::{build_run_config, load_suite_with_splits, validate_config, SkillOptToml};
use crate::error::{CliError, CliResult};
Expand All @@ -9,10 +9,10 @@ use cli_framework::spec::value::ArgValue;
use std::collections::HashMap;
use std::path::PathBuf;

/// Arguments for `fastskill skillopt run`
/// Arguments for `fastskill optimize run`
#[derive(Debug)]
pub struct RunArgs {
/// Path to skillopt.toml config file
/// Path to optimize config file
pub config: PathBuf,

/// Override the out_dir from the config file
Expand All @@ -26,15 +26,15 @@ impl IntoCommandSpec for RunArgs {
fn command_spec() -> CommandSpec {
CommandSpec {
summary: "Run skill optimization from a config file",
syntax: Some("skillopt run --config <path> [--out-dir <dir>] [--resume <run-dir>]"),
syntax: Some("optimize run --config <path> [--out-dir <dir>] [--resume <run-dir>]"),
args: vec![
ArgSpec {
name: "config",
kind: ArgKind::Option,
long: Some("config"),
value_type: ArgValueType::String,
cardinality: Cardinality::Required,
help: "Path to skillopt.toml config file",
help: "Path to optimize config file",
..Default::default()
},
ArgSpec {
Expand Down Expand Up @@ -95,17 +95,17 @@ pub async fn execute_run(args: RunArgs) -> CliResult<()> {
// 1. Read config file
if !args.config.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_CONFIG_MISSING: config file not found: {}",
"OPTIMIZE_CONFIG_MISSING: config file not found: {}",
args.config.display()
)));
}

let config_str = std::fs::read_to_string(&args.config).map_err(|e| {
CliError::Config(format!("SKILLOPT_CONFIG_MISSING: cannot read config: {e}"))
CliError::Config(format!("OPTIMIZE_CONFIG_MISSING: cannot read config: {e}"))
})?;

let mut cfg: SkillOptToml = toml::from_str(&config_str)
.map_err(|e| CliError::Config(format!("SKILLOPT_INVALID_TOML: {e}")))?;
.map_err(|e| CliError::Config(format!("OPTIMIZE_INVALID_TOML: {e}")))?;

if let Some(out_dir) = args.out_dir {
cfg.out_dir = out_dir.to_string_lossy().to_string();
Expand All @@ -126,15 +126,15 @@ pub async fn execute_run(args: RunArgs) -> CliResult<()> {

if selection_count == 0 {
return Err(CliError::Config(
"SKILLOPT_NO_SELECTION_CASES: suite has zero cases tagged 'selection'".to_string(),
"OPTIMIZE_NO_SELECTION_CASES: suite has zero cases tagged 'selection'".to_string(),
));
}

// 4. Load checks
let checks = if let Some(ref checks_path) = cfg.checks {
let checks_path = config_dir.join(checks_path);
fastskill_evals::load_checks(&checks_path)
.map_err(|e| CliError::Config(format!("SKILLOPT_CHECKS_PARSE_ERROR: {e}")))?
.map_err(|e| CliError::Config(format!("OPTIMIZE_CHECKS_PARSE_ERROR: {e}")))?
} else {
vec![]
};
Expand All @@ -144,7 +144,7 @@ pub async fn execute_run(args: RunArgs) -> CliResult<()> {
Some(a) => a,
None => {
eprintln!(
"SKILLOPT_OPTIMIZER_DEFAULT_WARN: optimizer_agent not set, defaulting to target_agent '{}'",
"OPTIMIZE_OPTIMIZER_DEFAULT_WARN: optimizer_agent not set, defaulting to target_agent '{}'",
cfg.target_agent
);
cfg.target_agent.clone()
Expand All @@ -154,31 +154,31 @@ pub async fn execute_run(args: RunArgs) -> CliResult<()> {
// 6. Read skill document
let skill_path = config_dir.join(&cfg.skill);
let initial_skill_md = std::fs::read_to_string(&skill_path).map_err(|e| {
CliError::Config(format!("SKILLOPT_SKILL_NOT_FOUND: cannot read skill: {e}"))
CliError::Config(format!("OPTIMIZE_SKILL_NOT_FOUND: cannot read skill: {e}"))
})?;

// 7. Allocate timestamped run directory
let out_base = config_dir.join(&cfg.out_dir);
std::fs::create_dir_all(&out_base).map_err(|e| {
CliError::Config(format!(
"SKILLOPT_OUT_DIR_UNWRITABLE: cannot create out_dir: {e}"
"OPTIMIZE_OUT_DIR_UNWRITABLE: cannot create out_dir: {e}"
))
})?;

let timestamp = chrono::Utc::now().format("%Y-%m-%dT%H-%M-%SZ").to_string();
let run_dir = out_base.join(&timestamp);
std::fs::create_dir_all(&run_dir).map_err(|e| {
CliError::Config(format!(
"SKILLOPT_OUT_DIR_UNWRITABLE: cannot create run dir: {e}"
"OPTIMIZE_OUT_DIR_UNWRITABLE: cannot create run dir: {e}"
))
})?;

// 8. Copy config for provenance (before calling train_skill)
std::fs::write(run_dir.join("skillopt.toml"), &config_str).map_err(CliError::Io)?;
std::fs::write(run_dir.join("optimize.toml"), &config_str).map_err(CliError::Io)?;

// 9. Build RunConfig via serde_json (avoids direct GateMetric/SlowUpdateMode imports)
let run_config = build_run_config(&cfg, &optimizer_agent)
.map_err(|e| CliError::Config(format!("SKILLOPT_TRAINING_FAILED: invalid config: {e}")))?;
.map_err(|e| CliError::Config(format!("OPTIMIZE_TRAINING_FAILED: invalid config: {e}")))?;

// 10. Build inputs and invoke training loop
let inputs = aikit_skillopt::SkillOptInputs {
Expand All @@ -192,7 +192,7 @@ pub async fn execute_run(args: RunArgs) -> CliResult<()> {

let outcome = aikit_skillopt::train_skill(inputs)
.await
.map_err(|e| CliError::Config(format!("SKILLOPT_TRAINING_FAILED: {e}")))?;
.map_err(|e| CliError::Config(format!("OPTIMIZE_TRAINING_FAILED: {e}")))?;

println!("{}", outcome.best_artifact_path.display());
Ok(())
Expand Down
12 changes: 6 additions & 6 deletions crates/fastskill-cli/src/commands/skillopt/status.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `fastskill skillopt status` subcommand
//! `fastskill optimize status` subcommand

use crate::error::{CliError, CliResult};
use cli_framework::command::{FromArgValueMap, IntoCommandSpec};
Expand All @@ -9,7 +9,7 @@ use serde::Deserialize;
use std::collections::HashMap;
use std::path::{Path, PathBuf};

/// Arguments for `fastskill skillopt status`
/// Arguments for `fastskill optimize status`
#[derive(Debug)]
pub struct StatusArgs {
/// Path to the run directory
Expand All @@ -23,7 +23,7 @@ impl IntoCommandSpec for StatusArgs {
fn command_spec() -> CommandSpec {
CommandSpec {
summary: "Show the status of a training run",
syntax: Some("skillopt status <run-dir> [--watch]"),
syntax: Some("optimize status <run-dir> [--watch]"),
args: vec![
ArgSpec {
name: "run-dir",
Expand Down Expand Up @@ -81,7 +81,7 @@ struct StepRecordView {
pub async fn execute_status(args: StatusArgs) -> CliResult<()> {
if !args.run_dir.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_RUN_DIR_MISSING: run directory not found: {}",
"OPTIMIZE_RUN_DIR_MISSING: run directory not found: {}",
args.run_dir.display()
)));
}
Expand All @@ -103,15 +103,15 @@ fn render_status(run_dir: &Path) -> CliResult<()> {

if !state_path.exists() || !history_path.exists() {
return Err(CliError::Config(format!(
"SKILLOPT_RUN_DIR_CORRUPT: missing runtime_state.json or history.json in: {}",
"OPTIMIZE_RUN_DIR_CORRUPT: missing runtime_state.json or history.json in: {}",
run_dir.display()
)));
}

let state_bytes = std::fs::read(&state_path).map_err(CliError::Io)?;
let state: RuntimeStateView = serde_json::from_slice(&state_bytes).map_err(|e| {
CliError::Config(format!(
"SKILLOPT_RUN_DIR_CORRUPT: malformed runtime_state.json: {e}"
"OPTIMIZE_RUN_DIR_CORRUPT: malformed runtime_state.json: {e}"
))
})?;

Expand Down
Loading
Loading