claude: pass --settings as a file path (avoid argv length limit)

This commit is contained in:
müde 2026-05-15 18:12:07 +02:00
parent cf8f1e64b1
commit d8807b8e8c
3 changed files with 26 additions and 5 deletions

View file

@ -127,6 +127,7 @@ async fn serve(
tracing::info!(socket = %socket.display(), "hive-ag3nt serve"); tracing::info!(socket = %socket.display(), "hive-ag3nt serve");
let _ = state; // reserved for future state transitions (turn-loop -> needs-login) let _ = state; // reserved for future state transitions (turn-loop -> needs-login)
let mcp_config = turn::write_mcp_config(socket).await?; let mcp_config = turn::write_mcp_config(socket).await?;
let settings = turn::write_settings(socket).await?;
let label = std::env::var("HIVE_LABEL").unwrap_or_else(|_| "hive-ag3nt".into()); let label = std::env::var("HIVE_LABEL").unwrap_or_else(|_| "hive-ag3nt".into());
let system_prompt = turn::write_system_prompt(socket, &label, mcp::Flavor::Agent).await?; let system_prompt = turn::write_system_prompt(socket, &label, mcp::Flavor::Agent).await?;
loop { loop {
@ -143,6 +144,7 @@ async fn serve(
&prompt, &prompt,
&mcp_config, &mcp_config,
&system_prompt, &system_prompt,
&settings,
&bus, &bus,
mcp::Flavor::Agent, mcp::Flavor::Agent,
) )

View file

@ -126,6 +126,7 @@ async fn one_shot(socket: &Path, req: ManagerRequest) -> Result<()> {
async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> { async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> {
tracing::info!(socket = %socket.display(), "hive-m1nd serve"); tracing::info!(socket = %socket.display(), "hive-m1nd serve");
let mcp_config = turn::write_mcp_config(socket).await?; let mcp_config = turn::write_mcp_config(socket).await?;
let settings = turn::write_settings(socket).await?;
let label = std::env::var("HIVE_LABEL").unwrap_or_else(|_| "hm1nd".into()); let label = std::env::var("HIVE_LABEL").unwrap_or_else(|_| "hm1nd".into());
let system_prompt = turn::write_system_prompt(socket, &label, mcp::Flavor::Manager).await?; let system_prompt = turn::write_system_prompt(socket, &label, mcp::Flavor::Manager).await?;
loop { loop {
@ -159,6 +160,7 @@ async fn serve(socket: &Path, interval: Duration, bus: Bus) -> Result<()> {
&prompt, &prompt,
&mcp_config, &mcp_config,
&system_prompt, &system_prompt,
&settings,
&bus, &bus,
mcp::Flavor::Manager, mcp::Flavor::Manager,
) )

View file

@ -51,6 +51,18 @@ pub async fn write_mcp_config(socket: &Path) -> Result<PathBuf> {
Ok(path) Ok(path)
} }
/// Drop the static `--settings` JSON next to the MCP config so we can
/// pass a path (`--settings <file>`) instead of an ever-growing inline
/// blob — the CLI argv has a finite length budget.
pub async fn write_settings(socket: &Path) -> Result<PathBuf> {
let parent = socket.parent().unwrap_or_else(|| Path::new("/run/hive"));
tokio::fs::create_dir_all(parent).await.ok();
let path = parent.join("claude-settings.json");
tokio::fs::write(&path, CLAUDE_SETTINGS).await?;
tracing::info!(path = %path.display(), "wrote claude settings");
Ok(path)
}
/// Write the agent's / manager's static system prompt to a file next to /// Write the agent's / manager's static system prompt to a file next to
/// the MCP config and return the path. Passed to claude via /// the MCP config and return the path. Passed to claude via
/// `--system-prompt-file`, replacing claude's default system prompt with /// `--system-prompt-file`, replacing claude's default system prompt with
@ -91,16 +103,17 @@ pub async fn drive_turn(
prompt: &str, prompt: &str,
mcp_config: &Path, mcp_config: &Path,
system_prompt: &Path, system_prompt: &Path,
settings: &Path,
bus: &Bus, bus: &Bus,
flavor: mcp::Flavor, flavor: mcp::Flavor,
) -> TurnOutcome { ) -> TurnOutcome {
match run_turn(prompt, mcp_config, system_prompt, bus, flavor).await { match run_turn(prompt, mcp_config, system_prompt, settings, bus, flavor).await {
TurnOutcome::PromptTooLong => { TurnOutcome::PromptTooLong => {
if let Err(e) = compact_session(bus).await { if let Err(e) = compact_session(settings, bus).await {
tracing::warn!(error = %format!("{e:#}"), "compact failed"); tracing::warn!(error = %format!("{e:#}"), "compact failed");
return TurnOutcome::Failed(e); return TurnOutcome::Failed(e);
} }
run_turn(prompt, mcp_config, system_prompt, bus, flavor).await run_turn(prompt, mcp_config, system_prompt, settings, bus, flavor).await
} }
other => other, other => other,
} }
@ -157,6 +170,7 @@ pub async fn run_turn(
prompt: &str, prompt: &str,
mcp_config: &Path, mcp_config: &Path,
system_prompt: &Path, system_prompt: &Path,
settings: &Path,
bus: &Bus, bus: &Bus,
flavor: mcp::Flavor, flavor: mcp::Flavor,
) -> TurnOutcome { ) -> TurnOutcome {
@ -164,6 +178,7 @@ pub async fn run_turn(
prompt, prompt,
mcp_config, mcp_config,
Some(system_prompt), Some(system_prompt),
settings,
bus, bus,
flavor, flavor,
ClaudeMode::Turn, ClaudeMode::Turn,
@ -179,7 +194,7 @@ pub async fn run_turn(
/// Run claude's built-in `/compact` slash command on the persistent /// Run claude's built-in `/compact` slash command on the persistent
/// session so the next turn can fit. No MCP tools needed; we just feed /// session so the next turn can fit. No MCP tools needed; we just feed
/// `/compact` over stdin and let claude rewrite its own history. /// `/compact` over stdin and let claude rewrite its own history.
pub async fn compact_session(bus: &Bus) -> Result<()> { pub async fn compact_session(settings: &Path, bus: &Bus) -> Result<()> {
bus.emit(LiveEvent::Note( bus.emit(LiveEvent::Note(
"context overflow — running /compact on the persistent session".into(), "context overflow — running /compact on the persistent session".into(),
)); ));
@ -187,6 +202,7 @@ pub async fn compact_session(bus: &Bus) -> Result<()> {
"/compact", "/compact",
Path::new("/dev/null"), Path::new("/dev/null"),
None, None,
settings,
bus, bus,
mcp::Flavor::Agent, // tool surface unused for /compact mcp::Flavor::Agent, // tool surface unused for /compact
ClaudeMode::Compact, ClaudeMode::Compact,
@ -206,6 +222,7 @@ async fn run_claude(
prompt: &str, prompt: &str,
mcp_config: &Path, mcp_config: &Path,
system_prompt: Option<&Path>, system_prompt: Option<&Path>,
settings: &Path,
bus: &Bus, bus: &Bus,
flavor: mcp::Flavor, flavor: mcp::Flavor,
mode: ClaudeMode, mode: ClaudeMode,
@ -219,7 +236,7 @@ async fn run_claude(
.arg("haiku") .arg("haiku")
.arg("--continue") .arg("--continue")
.arg("--settings") .arg("--settings")
.arg(CLAUDE_SETTINGS); .arg(settings);
if let Some(p) = system_prompt { if let Some(p) = system_prompt {
cmd.arg("--system-prompt-file").arg(p); cmd.arg("--system-prompt-file").arg(p);
} }