Add arc init command to initialize a new arc project

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-10 13:00:36 -04:00
parent 44731e0926
commit 8cec8d6546
3 changed files with 78 additions and 1 deletions

View file

@ -37,7 +37,7 @@ The OpenAPI spec at `docs/api-reference/arc-api.yaml` is the source of truth for
Arc is an AI-powered workflow orchestration platform. Workflows are defined as DOT graphs, where each node is a stage (agent, prompt, command, conditional, human, parallel, etc.) executed by the workflow engine.
### Rust crates (`lib/crates/`)
- **arc-cli** — CLI entry point. Commands: `run`, `exec`, `serve`, `validate`, `parse`, `cp`, `model`, `doctor`, `install`, `ps`, `system prune`, `llm`
- **arc-cli** — CLI entry point. Commands: `run`, `exec`, `serve`, `validate`, `parse`, `cp`, `model`, `doctor`, `init`, `install`, `ps`, `system prune`, `llm`
- **arc-workflows** — Core workflow engine. Parses DOT graphs, runs stages, manages checkpoints/resume, hooks, retros, and human-in-the-loop interactions
- **arc-agent** — AI coding agent with tool use (Bash, Read, Write, Edit, Glob, Grep, WebFetch). `Sandbox` trait abstracts execution environments
- **arc-api** — Axum HTTP server. Routes for runs, sessions, models, completions, usage. SSE event streaming. Demo mode via header

View file

@ -0,0 +1,70 @@
use anyhow::{bail, Context, Result};
use std::path::PathBuf;
pub async fn run_init() -> Result<()> {
let output = std::process::Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
.context("failed to run git")?;
if !output.status.success() {
bail!("not a git repository — run `git init` first");
}
let repo_root = PathBuf::from(
String::from_utf8(output.stdout)
.context("git output was not valid UTF-8")?
.trim(),
);
let arc_toml = repo_root.join("arc.toml");
if arc_toml.exists() {
bail!(
"already initialized — arc.toml exists at {}",
arc_toml.display()
);
}
// Create arc.toml
std::fs::write(&arc_toml, "version = 1\n\n[arc]\nroot = \"arc/\"\n")
.with_context(|| format!("failed to write {}", arc_toml.display()))?;
eprintln!("Created {}", arc_toml.display());
// Create hello workflow directory
let workflow_dir = repo_root.join("arc/workflows/hello");
std::fs::create_dir_all(&workflow_dir)
.with_context(|| format!("failed to create {}", workflow_dir.display()))?;
// Create workflow.dot
let dot_path = workflow_dir.join("workflow.dot");
std::fs::write(
&dot_path,
r#"digraph Hello {
graph [goal="Say hello and demonstrate a basic arc workflow"]
rankdir=LR
start [shape=Mdiamond, label="Start"]
exit [shape=Msquare, label="Exit"]
greet [label="Greet", prompt="Say hello! Introduce yourself and explain that this is a test of the arc workflow engine."]
start -> greet -> exit
}
"#,
)
.with_context(|| format!("failed to write {}", dot_path.display()))?;
eprintln!("Created {}", dot_path.display());
// Create workflow.toml
let toml_path = workflow_dir.join("workflow.toml");
std::fs::write(
&toml_path,
"version = 1\ngraph = \"workflow.dot\"\n\n[sandbox]\nprovider = \"local\"\n",
)
.with_context(|| format!("failed to write {}", toml_path.display()))?;
eprintln!("Created {}", toml_path.display());
eprintln!("\nProject initialized! Run a workflow with:\n arc run arc/workflows/hello/workflow.toml --no-retro");
Ok(())
}

View file

@ -1,5 +1,6 @@
mod cli_config;
mod doctor;
mod init;
mod install;
mod logging;
@ -85,6 +86,8 @@ enum Command {
#[arg(short, long)]
live: bool,
},
/// Initialize a new arc project
Init,
/// Set up the Arc environment (LLMs, certs, GitHub)
Install,
/// List workflow runs
@ -191,6 +194,7 @@ async fn main_inner() -> Result<()> {
#[cfg(feature = "server")]
Command::Serve(_) => "serve",
Command::Doctor { .. } => "doctor",
Command::Init => "init",
Command::Install => "install",
Command::Ps(_) => "ps",
Command::Pr { .. } => "pr",
@ -396,6 +400,9 @@ async fn main_inner() -> Result<()> {
let exit_code = doctor::run_doctor(verbose, live).await;
std::process::exit(exit_code);
}
Command::Init => {
init::run_init().await?;
}
Command::Install => {
install::run_install().await?;
}