mod init;
mod pool_worker;
use std::sync::Arc;
use algocline_app::{AppConfig, AppService, EngineApi};
use algocline_core::execution::ExecutionService;
use algocline_engine::Executor;
use algocline_mcp::AlcService;
use clap::{Parser, Subcommand};
use rmcp::{transport::stdio, ServiceExt};
#[derive(Parser)]
#[command(
name = "alc",
version,
about = "algocline — LLM amplification engine (MCP server)"
)]
#[command(
long_about = "By default `alc` runs as an MCP stdio server, intended to be launched \
by an MCP client such as Claude Code. Use subcommands for one-shot maintenance tasks. \
For ad-hoc shell access to MCP tools, run `alc` from an agent-block harness that calls the \
MCP server directly."
)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
Init {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
rest: Vec<String>,
},
Update {
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
rest: Vec<String>,
},
#[command(hide = true)]
PoolWorker {
#[arg(long)]
sid: String,
#[arg(long)]
sock: std::path::PathBuf,
},
}
fn setup_tracing(log_dir: Option<&std::path::Path>) -> anyhow::Result<()> {
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
let stderr_layer = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_ansi(false);
let registry = tracing_subscriber::registry()
.with(EnvFilter::from_default_env())
.with(stderr_layer);
if let Some(dir) = log_dir {
let file_appender = tracing_appender::rolling::daily(dir, "tracing.log");
let file_layer = tracing_subscriber::fmt::layer()
.with_writer(file_appender)
.with_ansi(false);
registry.with(file_layer).try_init()?;
} else {
registry.try_init()?;
}
Ok(())
}
fn resolve_lib_paths() -> Vec<algocline_app::SearchPath> {
use algocline_app::SearchPath;
let mut paths = Vec::new();
if let Ok(env_paths) = std::env::var("ALC_PACKAGES_PATH") {
for p in env_paths.split(':') {
let path = std::path::PathBuf::from(p);
if path.is_dir() {
paths.push(SearchPath::env(path));
}
}
}
if let Some(home) = dirs::home_dir() {
let packages = home.join(".algocline").join("packages");
if packages.is_dir() {
paths.push(SearchPath::default_global(packages));
}
}
paths
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Some(Commands::Init { rest }) => return init::run(&rest, false).await,
Some(Commands::Update { rest }) => return init::run(&rest, true).await,
Some(Commands::PoolWorker { sid, sock }) => {
let config = AppConfig::from_env();
setup_tracing(config.log_dir.as_deref())?;
return pool_worker::run(sid, sock).await;
}
None => {} }
let config = AppConfig::from_env();
setup_tracing(config.log_dir.as_deref())?;
algocline_engine::card::init_event_bus();
match init::distribute_types() {
Ok(init::DistributedTypes { alc, alc_shapes }) => {
tracing::debug!(
"types installed: {} + {}",
alc.display(),
alc_shapes.display()
)
}
Err(e) => tracing::warn!("failed to install type stubs: {e}"),
}
tracing::info!("algocline server starting");
let search_paths = resolve_lib_paths();
let lib_paths: Vec<_> = search_paths.iter().map(|sp| sp.path.clone()).collect();
let executor = Arc::new(Executor::new(lib_paths).await?);
let app_dir = config.app_dir(); let app: Arc<AppService> = Arc::new(AppService::new(executor, config, search_paths));
let server = AlcService::new(
app.clone() as Arc<dyn EngineApi>,
app.clone() as Arc<dyn ExecutionService>,
app_dir,
);
let service = server.serve(stdio()).await?;
service.waiting().await?;
Ok(())
}