feat(cli): add cron subcommand for managing scheduled routines (#1017)

* feat(cli): add cron subcommand for managing scheduled routines
  Rebase onto staging branch and address collaborator review:
  - Fix .unwrap_or(None) → proper error propagation in set_enabled()
  - Add --yes/-y flag for non-interactive deletion with confirmation prompt
  - Add --json flag for machine-readable output in list and history
  - Preserve error context chain with {e:#} in run_cron_cli()

  Note: GATEWAY_USER_ID is trusted from the environment; future work may
  add authentication for multi-tenant deployments.

* fix(cli): reject invalid cron timezones

* refactor(cli): rename cron subcommand to routines

The system manages all routine types (cron, webhook, event, manual),
not just cron schedules. Rename the CLI subcommand to reflect this:
- `ironclaw cron` -> `ironclaw routines` (with `cron` as hidden alias)
- List shows all routines by default, add --trigger filter
- Remove cron-trigger-only validation
- Simplify require_routine helper (no trigger type check)

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: reidliu41 <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Zaki Manian
2026-03-12 12:47:23 -07:00
committed by GitHub
co-authored by reidliu41 Claude Opus 4.6
parent 5dfa666691
commit bcda73c2e0
8 changed files with 766 additions and 83 deletions
+29
View File
@@ -7,6 +7,7 @@
//! - Managing WASM tools (`tool install`, `tool list`, `tool remove`)
//! - Managing MCP servers (`mcp add`, `mcp auth`, `mcp list`, `mcp test`)
//! - Querying workspace memory (`memory search`, `memory read`, `memory write`)
//! - Managing routines (`routines list`, `routines create`, `routines edit`, ...)
//! - Managing OS service (`service install`, `service start`, `service stop`)
//! - Listing configured channels (`channels list`)
//! - Active health diagnostics (`doctor`)
@@ -15,6 +16,7 @@
mod channels;
mod completion;
mod config;
mod routines;
mod doctor;
#[cfg(feature = "import")]
pub mod import;
@@ -31,6 +33,7 @@ mod tool;
pub use channels::{ChannelsCommand, run_channels_command};
pub use completion::Completion;
pub use config::{ConfigCommand, run_config_command};
pub use routines::{RoutinesCommand, run_routines_command};
pub use doctor::run_doctor_command;
#[cfg(feature = "import")]
pub use import::{ImportCommand, run_import_command};
@@ -147,6 +150,15 @@ pub enum Command {
)]
Channels(ChannelsCommand),
/// Manage routines (scheduled, event-driven, webhook, manual)
#[command(
subcommand,
alias = "cron",
about = "Manage routines",
long_about = "List, create, edit, enable/disable, delete, and view history of routines.\nExamples:\n ironclaw routines list\n ironclaw routines create --name daily-digest --schedule '0 0 9 * * *' --prompt 'Summarize today'"
)]
Routines(RoutinesCommand),
/// Manage MCP servers (hosted tool providers)
#[command(
subcommand,
@@ -281,6 +293,23 @@ pub async fn init_secrets_store()
Ok(crate::db::create_secrets_store(&config.database, crypto).await?)
}
/// Run the Routines CLI subcommand.
pub async fn run_routines_cli(
routines_cmd: &RoutinesCommand,
config_path: Option<&std::path::Path>,
) -> anyhow::Result<()> {
let config = crate::config::Config::from_env_with_toml(config_path)
.await
.map_err(|e| anyhow::anyhow!("{e:#}"))?;
let db: Arc<dyn crate::db::Database> = crate::db::connect_from_config(&config.database)
.await
.map_err(|e| anyhow::anyhow!("{e:#}"))?;
let user_id = std::env::var("GATEWAY_USER_ID").unwrap_or_else(|_| "default".to_string());
run_routines_command(routines_cmd.clone(), db, &user_id).await
}
/// Run the Memory CLI subcommand.
pub async fn run_memory_command(mem_cmd: &MemoryCommand) -> anyhow::Result<()> {
let config = crate::config::Config::from_env()