lattice_ai/opencode/
commands.rs1use lattice_agent::parse_no_args;
10use lattice_grammar::command::LatencyClass;
11use lattice_grammar::effect::Effect;
12use lattice_grammar::registry::{CommandRegistry, ExCommandSpec, SurfaceForm};
13use std::sync::Arc;
14
15use crate::opencode::modes::OpencodeMode;
16
17pub fn register_opencode_ex_commands(registry: &mut CommandRegistry) {
19 registry.register_ex_command(
20 "opencode-term",
21 "Launch the opencode agent's native TUI in a terminal buffer, with \
22 opencode-mode activated. NOTE: opencode's TUI (opentui) needs modern- \
23 terminal image/capability features lattice's emulator doesn't yet \
24 provide, so it may not render here -- use `:opencode` (the ACP \
25 conversation) instead. Kept for agents whose TUIs degrade gracefully.",
26 ExCommandSpec {
27 latency_class: LatencyClass::Reflex,
28 accepts_bang: false,
29 accepts_range: false,
30 parse_args: Arc::new(parse_no_args),
31 apply: Arc::new(|_ctx| {
32 Ok(Effect::SpawnTerminal {
33 cwd: None,
36 cmd_line: Some("opencode".to_string()),
37 env: vec![],
38 activate_minor: Some(OpencodeMode::mode_id().as_str().to_string()),
39 })
40 }),
41 args_schema: vec![],
42 surface_form: SurfaceForm::Keyword,
43 },
44 );
45}
46
47#[cfg(test)]
48mod tests {
49 #![allow(clippy::unwrap_used, clippy::panic)]
50 use super::*;
51 use lattice_grammar::args::Args;
52 use lattice_grammar::registry::ExCommandContext;
53 use lattice_grammar::{CancellationToken, Count, Register};
54
55 fn ctx() -> ExCommandContext {
56 ExCommandContext {
57 bang: false,
58 args: Args::None,
59 range: None,
60 register: Register::default(),
61 count: Count::default(),
62 buffer_id: lattice_core::BufferId::default(),
63 cursor: Default::default(),
70 buffer: Default::default(),
71 path: None,
72 syntax: None,
73 cancel: CancellationToken::new(),
74 }
75 }
76
77 #[test]
80 fn opencode_term_spawns_terminal_and_activates_mode() {
81 let mut registry = CommandRegistry::new();
82 register_opencode_ex_commands(&mut registry);
83 let id = registry
84 .id_by_name("opencode-term")
85 .expect("`:opencode-term` registered");
86 let spec = registry.ex_command_spec(id).expect("spec present");
87 match (spec.apply)(&ctx()).expect("apply ok") {
88 Effect::SpawnTerminal {
89 cmd_line,
90 activate_minor,
91 ..
92 } => {
93 assert_eq!(cmd_line.as_deref(), Some("opencode"));
94 assert_eq!(activate_minor.as_deref(), Some("opencode-mode"));
95 }
96 other => panic!("expected SpawnTerminal, got {other:?}"),
97 }
98 }
99}