feat: enhance agent personality, tool guidance, and memory hygiene

- Expand communication style presets (professional, expressive, custom)
- Enrich SOUL.md with human-like tone and emoji-awareness guidance
- Add crash recovery and sub-task scoping guidance to AGENTS.md scaffold
- Add 'Use when / Don't use when' guidance to TOOLS.md and runtime prompts
- Implement memory hygiene system with configurable archiving and retention
- Add MemoryConfig options: hygiene_enabled, archive_after_days, purge_after_days, conversation_retention_days
- Archive old daily memory and session files to archive subdirectories
- Purge old archives and prune stale SQLite conversation rows
- Add comprehensive tests for new features
This commit is contained in:
argenis de la rosa 2026-02-14 11:28:39 -05:00
parent f4f180ac41
commit ec2d5cc93d
29 changed files with 3600 additions and 116 deletions

View file

@ -8,7 +8,7 @@
dead_code
)]
use anyhow::Result;
use anyhow::{bail, Result};
use clap::{Parser, Subcommand};
use tracing::{info, Level};
use tracing_subscriber::FmtSubscriber;
@ -17,15 +17,20 @@ mod agent;
mod channels;
mod config;
mod cron;
mod daemon;
mod doctor;
mod gateway;
mod health;
mod heartbeat;
mod integrations;
mod memory;
mod migration;
mod observability;
mod onboard;
mod providers;
mod runtime;
mod security;
mod service;
mod skills;
mod tools;
mod tunnel;
@ -43,6 +48,20 @@ struct Cli {
command: Commands,
}
#[derive(Subcommand, Debug)]
enum ServiceCommands {
/// Install daemon service unit for auto-start and restart
Install,
/// Start daemon service
Start,
/// Stop daemon service
Stop,
/// Check daemon service status
Status,
/// Uninstall daemon service unit
Uninstall,
}
#[derive(Subcommand, Debug)]
enum Commands {
/// Initialize your workspace and configuration
@ -51,6 +70,10 @@ enum Commands {
#[arg(long)]
interactive: bool,
/// Reconfigure channels only (fast repair flow)
#[arg(long)]
channels_only: bool,
/// API key (used in quick mode, ignored with --interactive)
#[arg(long)]
api_key: Option<String>,
@ -71,7 +94,7 @@ enum Commands {
provider: Option<String>,
/// Model to use
#[arg(short, long)]
#[arg(long)]
model: Option<String>,
/// Temperature (0.0 - 2.0)
@ -86,10 +109,30 @@ enum Commands {
port: u16,
/// Host to bind to
#[arg(short, long, default_value = "127.0.0.1")]
#[arg(long, default_value = "127.0.0.1")]
host: String,
},
/// Start long-running autonomous runtime (gateway + channels + heartbeat + scheduler)
Daemon {
/// Port to listen on (use 0 for random available port)
#[arg(short, long, default_value = "8080")]
port: u16,
/// Host to bind to
#[arg(long, default_value = "127.0.0.1")]
host: String,
},
/// Manage OS service lifecycle (launchd/systemd user service)
Service {
#[command(subcommand)]
service_command: ServiceCommands,
},
/// Run diagnostics for daemon/scheduler/channel freshness
Doctor,
/// Show system status (full details)
Status,
@ -116,6 +159,26 @@ enum Commands {
#[command(subcommand)]
skill_command: SkillCommands,
},
/// Migrate data from other agent runtimes
Migrate {
#[command(subcommand)]
migrate_command: MigrateCommands,
},
}
#[derive(Subcommand, Debug)]
enum MigrateCommands {
/// Import memory from an OpenClaw workspace into this ZeroClaw workspace
Openclaw {
/// Optional path to OpenClaw workspace (defaults to ~/.openclaw/workspace)
#[arg(long)]
source: Option<std::path::PathBuf>,
/// Validate and preview migration without writing any data
#[arg(long)]
dry_run: bool,
},
}
#[derive(Subcommand, Debug)]
@ -198,11 +261,21 @@ async fn main() -> Result<()> {
// Onboard runs quick setup by default, or the interactive wizard with --interactive
if let Commands::Onboard {
interactive,
channels_only,
api_key,
provider,
} = &cli.command
{
let config = if *interactive {
if *interactive && *channels_only {
bail!("Use either --interactive or --channels-only, not both");
}
if *channels_only && (api_key.is_some() || provider.is_some()) {
bail!("--channels-only does not accept --api-key or --provider");
}
let config = if *channels_only {
onboard::run_channels_repair_wizard()?
} else if *interactive {
onboard::run_wizard()?
} else {
onboard::run_quick_setup(api_key.as_deref(), provider.as_deref())?
@ -236,6 +309,15 @@ async fn main() -> Result<()> {
gateway::run_gateway(&host, port, config).await
}
Commands::Daemon { port, host } => {
if port == 0 {
info!("🧠 Starting ZeroClaw Daemon on {host} (random port)");
} else {
info!("🧠 Starting ZeroClaw Daemon on {host}:{port}");
}
daemon::run(config, host, port).await
}
Commands::Status => {
println!("🦀 ZeroClaw Status");
println!();
@ -307,6 +389,10 @@ async fn main() -> Result<()> {
Commands::Cron { cron_command } => cron::handle_command(cron_command, config),
Commands::Service { service_command } => service::handle_command(service_command, &config),
Commands::Doctor => doctor::run(&config),
Commands::Channel { channel_command } => match channel_command {
ChannelCommands::Start => channels::start_channels(config).await,
ChannelCommands::Doctor => channels::doctor_channels(config).await,
@ -320,5 +406,20 @@ async fn main() -> Result<()> {
Commands::Skills { skill_command } => {
skills::handle_command(skill_command, &config.workspace_dir)
}
Commands::Migrate { migrate_command } => {
migration::handle_command(migrate_command, &config).await
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn cli_definition_has_no_flag_conflicts() {
Cli::command().debug_assert();
}
}