Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,640 changes: 1,320 additions & 1,320 deletions crates/cli/src/app.rs

Large diffs are not rendered by default.

42 changes: 21 additions & 21 deletions crates/cli/src/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,16 @@ pub enum KeyAction {
/// default layout. Bound to `C-x z` (emacs) / `z` (vim), matching
/// tmux's `prefix z` (zoom-pane).
ToggleZoom,
/// Open the selected session's in-TUI canvas surface. Bound to
/// Open the selected session's in-TUI program surface. Bound to
/// `C-x Space` in both profiles because bare modifier double-taps are not
/// delivered reliably by terminal emulators.
OpenCanvas,
/// Save the selected session's in-TUI canvas surface. Bound to
OpenProgram,
/// Save the selected session's in-TUI program surface. Bound to
/// `C-x C-s`, matching the editor-style save chord.
SaveCanvas,
/// Undo the selected canvas edit. Bound to `C-x u` for consistency with
SaveProgram,
/// Undo the selected program edit. Bound to `C-x u` for consistency with
/// emacs-style history commands.
UndoCanvas,
UndoProgram,
OpenDiff,
Interrupt,
OpenCommandPalette,
Expand Down Expand Up @@ -230,9 +230,9 @@ fn emacs() -> Keymap {
(Chord(vec![ctrl('x'), ctrl('f')]), OpenNewSession),
(Chord(vec![ctrl('x'), ch('b')]), OpenSwitchSession),
(Chord(vec![ctrl('x'), ch('k')]), OpenDeleteConfirm),
(Chord(vec![ctrl('x'), ch(' ')]), OpenCanvas),
(Chord(vec![ctrl('x'), ctrl('s')]), SaveCanvas),
(Chord(vec![ctrl('x'), ch('u')]), UndoCanvas),
(Chord(vec![ctrl('x'), ch(' ')]), OpenProgram),
(Chord(vec![ctrl('x'), ctrl('s')]), SaveProgram),
(Chord(vec![ctrl('x'), ch('u')]), UndoProgram),
(Chord(vec![ctrl('x'), ch('d')]), OpenDiff),
(Chord(vec![ctrl('x'), ch('i')]), OpenSendInput),
// `C-x r` opens the rename minibuffer (with current title pre-filled).
Expand Down Expand Up @@ -293,9 +293,9 @@ fn vim() -> Keymap {
(Chord(vec![ch('n')]), OpenNewSession),
(Chord(vec![ctrl('x'), ch('b')]), OpenSwitchSession),
(Chord(vec![shift('K')]), OpenDeleteConfirm),
(Chord(vec![ctrl('x'), ch(' ')]), OpenCanvas),
(Chord(vec![ctrl('x'), ctrl('s')]), SaveCanvas),
(Chord(vec![ctrl('x'), ch('u')]), UndoCanvas),
(Chord(vec![ctrl('x'), ch(' ')]), OpenProgram),
(Chord(vec![ctrl('x'), ctrl('s')]), SaveProgram),
(Chord(vec![ctrl('x'), ch('u')]), UndoProgram),
(Chord(vec![ch('d')]), OpenDiff),
(Chord(vec![ctrl('c')]), Interrupt),
// `r` opens the rename minibuffer; refresh moved to M-x refresh.
Expand Down Expand Up @@ -478,15 +478,15 @@ mod tests {
}

#[test]
fn c_x_space_opens_canvas_without_shadowing_c_x_ctrl_c_quit() {
fn c_x_space_opens_program_without_shadowing_c_x_ctrl_c_quit() {
for profile in [Profile::Emacs, Profile::Vim] {
let km = default_for(profile);
assert!(
matches!(
resolve(&km, vec![ctrl('x'), ch(' ')]),
KeymapResult::Action(KeyAction::OpenCanvas)
KeymapResult::Action(KeyAction::OpenProgram)
),
"C-x Space should open canvas in {profile:?}"
"C-x Space should open program in {profile:?}"
);
assert!(
matches!(
Expand All @@ -499,29 +499,29 @@ mod tests {
}

#[test]
fn c_x_ctrl_s_saves_canvas() {
fn c_x_ctrl_s_saves_program() {
for profile in [Profile::Emacs, Profile::Vim] {
let km = default_for(profile);
assert!(
matches!(
resolve(&km, vec![ctrl('x'), ctrl('s')]),
KeymapResult::Action(KeyAction::SaveCanvas)
KeymapResult::Action(KeyAction::SaveProgram)
),
"C-x C-s should save canvas in {profile:?}"
"C-x C-s should save program in {profile:?}"
);
}
}

#[test]
fn c_x_u_undo_canvas() {
fn c_x_u_undo_program() {
for profile in [Profile::Emacs, Profile::Vim] {
let km = default_for(profile);
assert!(
matches!(
resolve(&km, vec![ctrl('x'), ch('u')]),
KeymapResult::Action(KeyAction::UndoCanvas)
KeymapResult::Action(KeyAction::UndoProgram)
),
"C-x u should trigger UndoCanvas in {profile:?}"
"C-x u should trigger UndoProgram in {profile:?}"
);
}
}
Expand Down
78 changes: 39 additions & 39 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ enum Command {
},
/// Send input to a session.
Send { session_id: String, text: String },
/// Manage a session's orchestration canvas.
Canvas {
/// Manage a session's orchestration program.
Program {
#[command(subcommand)]
command: CanvasCommand,
command: ProgramCommand,
},
/// Internal: `PreToolUse` hook body for the AskUserQuestion chat-gate.
/// Reads the hook payload on stdin; if a chat viewer is active for
Expand Down Expand Up @@ -210,10 +210,10 @@ enum DaemonCommand {
}

#[derive(Debug, Subcommand)]
enum CanvasCommand {
/// Print the canvas Markdown and metadata.
enum ProgramCommand {
/// Print the program Markdown and metadata.
Get { session_id: String },
/// Replace the canvas from a file, stdin, or template.
/// Replace the program from a file, stdin, or template.
Set {
session_id: String,
/// Read Markdown from this file.
Expand All @@ -229,17 +229,17 @@ enum CanvasCommand {
#[arg(long)]
base_version: Option<u64>,
},
/// Edit the canvas in $EDITOR and save it back.
/// Edit the program in $EDITOR and save it back.
Edit { session_id: String },
/// Ask the owning session to execute the full canvas or selected Markdown.
/// Ask the owning session to execute the full program or selected Markdown.
Execute {
session_id: String,
#[arg(long)]
selection: Option<String>,
#[arg(long)]
base_version: Option<u64>,
},
/// List available canvas templates.
/// List available program templates.
Templates,
}

Expand Down Expand Up @@ -450,9 +450,9 @@ async fn main() -> Result<()> {
c.send_input(&session_id, text).await?;
Ok(())
}
Command::Canvas { command } => {
Command::Program { command } => {
let c = connect(&socket).await?;
run_canvas_command(&c, command).await
run_program_command(&c, command).await
}
Command::AskGate => {
// Drain stdin (the PreToolUse payload) so the hook's pipe closes
Expand Down Expand Up @@ -603,28 +603,28 @@ fn command_allows_upgrade_prompt(command: &Command) -> bool {
Command::Daemon { .. }
| Command::Upgrade { .. }
| Command::Acp { .. }
| Command::Canvas { .. }
| Command::Program { .. }
| Command::AskGate
| Command::Mcp
| Command::Adapter { .. }
)
}

async fn run_canvas_command(client: &Client, command: CanvasCommand) -> Result<()> {
async fn run_program_command(client: &Client, command: ProgramCommand) -> Result<()> {
match command {
CanvasCommand::Get { session_id } => {
let result = client.canvas_get(&session_id).await?;
ProgramCommand::Get { session_id } => {
let result = client.program_get(&session_id).await?;
eprintln!(
"session={} version={} updated_at_ms={} template={}",
result.canvas.session_id,
result.canvas.version,
result.canvas.updated_at_ms,
result.canvas.template_id.as_deref().unwrap_or("(none)")
result.program.session_id,
result.program.version,
result.program.updated_at_ms,
result.program.template_id.as_deref().unwrap_or("(none)")
);
print!("{}", result.canvas.markdown);
print!("{}", result.program.markdown);
Ok(())
}
CanvasCommand::Set {
ProgramCommand::Set {
session_id,
file,
stdin,
Expand All @@ -646,29 +646,29 @@ async fn run_canvas_command(client: &Client, command: CanvasCommand) -> Result<(
(markdown, None)
} else {
let template_id = template.expect("template source counted above");
let templates = client.canvas_templates().await?.templates;
let templates = client.program_templates().await?.templates;
let Some(found) = templates.into_iter().find(|t| t.id == template_id) else {
anyhow::bail!("unknown canvas template: {template_id}");
anyhow::bail!("unknown program template: {template_id}");
};
(found.markdown, Some(found.id))
};
let result = client
.canvas_update(agentd_protocol::CanvasUpdateParams {
.program_update(agentd_protocol::ProgramUpdateParams {
session_id,
markdown,
base_version,
actor: agentd_protocol::CanvasUpdateActor::Human,
actor: agentd_protocol::ProgramUpdateActor::Human,
template_id,
note: None,
})
.await?;
println!("updated canvas version {}", result.canvas.version);
println!("updated program version {}", result.program.version);
Ok(())
}
CanvasCommand::Edit { session_id } => {
let current = client.canvas_get(&session_id).await?.canvas;
ProgramCommand::Edit { session_id } => {
let current = client.program_get(&session_id).await?.program;
let dir = tempfile::tempdir()?;
let path = dir.path().join("canvas.md");
let path = dir.path().join("program.md");
std::fs::write(&path, &current.markdown)?;
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
let status = std::process::Command::new(editor).arg(&path).status()?;
Expand All @@ -677,42 +677,42 @@ async fn run_canvas_command(client: &Client, command: CanvasCommand) -> Result<(
}
let markdown = std::fs::read_to_string(&path)?;
if markdown == current.markdown {
println!("canvas unchanged at version {}", current.version);
println!("program unchanged at version {}", current.version);
return Ok(());
}
let result = client
.canvas_update(agentd_protocol::CanvasUpdateParams {
.program_update(agentd_protocol::ProgramUpdateParams {
session_id,
markdown,
base_version: Some(current.version),
actor: agentd_protocol::CanvasUpdateActor::Human,
actor: agentd_protocol::ProgramUpdateActor::Human,
template_id: current.template_id,
note: None,
})
.await?;
println!("updated canvas version {}", result.canvas.version);
println!("updated program version {}", result.program.version);
Ok(())
}
CanvasCommand::Execute {
ProgramCommand::Execute {
session_id,
selection,
base_version,
} => {
let result = client
.canvas_execute(agentd_protocol::CanvasExecuteParams {
.program_execute(agentd_protocol::ProgramExecuteParams {
session_id,
selection,
base_version,
})
.await?;
println!(
"execution prompt sent from canvas version {}",
result.canvas.version
"execution prompt sent from program version {}",
result.program.version
);
Ok(())
}
CanvasCommand::Templates => {
for template in client.canvas_templates().await?.templates {
ProgramCommand::Templates => {
for template in client.program_templates().await?.templates {
let source = if template.built_in {
"built-in"
} else {
Expand Down
14 changes: 7 additions & 7 deletions crates/cli/src/tui_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub struct TuiState {
#[serde(default)]
pub active_window_id: Option<u64>,
#[serde(default)]
pub open_canvas_session_ids: Vec<String>,
pub open_program_session_ids: Vec<String>,
#[serde(default)]
pub widgets: HashMap<String, WidgetState>,
}
Expand All @@ -67,7 +67,7 @@ impl Default for TuiState {
hide_pane_side_borders: default_hide_pane_side_borders(),
main_windows: None,
active_window_id: None,
open_canvas_session_ids: Vec::new(),
open_program_session_ids: Vec::new(),
widgets: HashMap::new(),
}
}
Expand Down Expand Up @@ -101,7 +101,7 @@ mod tests {
use super::*;

#[test]
fn legacy_state_defaults_open_canvas_ids() {
fn legacy_state_defaults_open_program_ids() {
let state: TuiState = serde_json::from_str(
r#"{
"last_selected_session_id": "s1",
Expand All @@ -110,19 +110,19 @@ mod tests {
)
.expect("legacy state should deserialize");

assert!(state.open_canvas_session_ids.is_empty());
assert!(state.open_program_session_ids.is_empty());
}

#[test]
fn state_round_trips_open_canvas_ids() {
fn state_round_trips_open_program_ids() {
let state = TuiState {
open_canvas_session_ids: vec!["s1".into(), "s2".into()],
open_program_session_ids: vec!["s1".into(), "s2".into()],
..TuiState::default()
};

let json = serde_json::to_string(&state).expect("serialize");
let restored: TuiState = serde_json::from_str(&json).expect("deserialize");

assert_eq!(restored.open_canvas_session_ids, vec!["s1", "s2"]);
assert_eq!(restored.open_program_session_ids, vec!["s1", "s2"]);
}
}
Loading
Loading