Skip to main content

lattice_ai/opencode/
modes.rs

1//! `opencode-mode` — the minor mode layered on the opencode terminal buffer.
2//!
3//! v1 is a Manual-activation **marker** minor over `terminal-mode`, activated
4//! by `:opencode` on the terminal running the agent. It carries no per-buffer
5//! resources: opencode's own TUI owns the conversation, the prompt (readline,
6//! `/` commands, model switching), history, and edit review, so lattice adds
7//! nothing on the hot path. The mode exists as the buffer's *identity* and as
8//! the **seam** for future lattice-native integration (IDE-native edit review,
9//! a headerline status row) — mirroring `claude-code-mode`'s I1 marker shell.
10
11use lattice_mode::registry::ModeRegistry;
12use lattice_mode::{ActivationPolicy, LifecycleFuture, Mode, ModeContext, ModeId, ModeKind};
13
14/// The opencode terminal minor mode.
15pub struct OpencodeMode;
16
17impl OpencodeMode {
18    /// The mode id (`opencode-mode`).
19    pub fn mode_id() -> ModeId {
20        ModeId::new("opencode-mode")
21    }
22}
23
24impl Mode for OpencodeMode {
25    type Guard = ();
26
27    fn id(&self) -> ModeId {
28        Self::mode_id()
29    }
30
31    fn kind(&self) -> ModeKind {
32        ModeKind::Minor
33    }
34
35    /// Manual — activated explicitly by `:opencode` on the agent terminal.
36    fn activation_policy(&self) -> ActivationPolicy {
37        ActivationPolicy::Manual
38    }
39
40    fn on_activate(&self, _ctx: ModeContext) -> LifecycleFuture<'_, ()> {
41        Box::pin(async { Ok(()) })
42    }
43}
44
45/// Register `opencode-mode` against `registry`. Called from
46/// [`super::install`].
47pub fn register_opencode_modes(registry: &mut ModeRegistry) {
48    registry
49        .register(OpencodeMode)
50        .expect("opencode-mode register");
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn id_kind_and_policy() {
59        assert_eq!(OpencodeMode::mode_id().as_str(), "opencode-mode");
60        assert_eq!(OpencodeMode.kind(), ModeKind::Minor);
61        assert_eq!(OpencodeMode.activation_policy(), ActivationPolicy::Manual);
62    }
63
64    #[test]
65    fn registers_without_conflict() {
66        let mut registry = ModeRegistry::new();
67        register_opencode_modes(&mut registry);
68        assert!(registry.is_registered(OpencodeMode::mode_id()));
69    }
70}