返回 DeepSeek-TUI-2026
jobs.rs
根目录 / crates / tui / src / commands / jobs.rs
1 //! Shell job-center commands.
2
3 use crate::tui::app::{App, AppAction, ShellJobAction};
4
5 use super::CommandResult;
6
7 pub fn jobs(_app: &mut App, args: Option<&str>) -> CommandResult {
8 let raw = args.unwrap_or("").trim();
9 if raw.is_empty() || raw.eq_ignore_ascii_case("list") {
10 return CommandResult::action(AppAction::ShellJob(ShellJobAction::List));
11 }
12
13 let mut parts = raw.splitn(3, char::is_whitespace);
14 let action = parts.next().unwrap_or("").to_ascii_lowercase();
15 let id = parts.next().map(str::trim).filter(|s| !s.is_empty());
16 let rest = parts.next().map(str::trim).unwrap_or("");
17
18 match action.as_str() {
19 "list" => CommandResult::action(AppAction::ShellJob(ShellJobAction::List)),
20 "show" | "inspect" => match id {
21 Some(id) => CommandResult::action(AppAction::ShellJob(ShellJobAction::Show {
22 id: id.to_string(),
23 })),
24 None => CommandResult::error("Usage: /jobs show <id>"),
25 },
26 "poll" | "wait" => match id {
27 Some(id) => CommandResult::action(AppAction::ShellJob(ShellJobAction::Poll {
28 id: id.to_string(),
29 wait: action == "wait",
30 })),
31 None => CommandResult::error("Usage: /jobs poll <id>"),
32 },
33 "stdin" | "send" => match id {
34 Some(id) if !rest.is_empty() => {
35 CommandResult::action(AppAction::ShellJob(ShellJobAction::SendStdin {
36 id: id.to_string(),
37 input: rest.to_string(),
38 close: false,
39 }))
40 }
41 _ => CommandResult::error("Usage: /jobs stdin <id> <input>"),
42 },
43 "close-stdin" | "eof" => match id {
44 Some(id) => CommandResult::action(AppAction::ShellJob(ShellJobAction::SendStdin {
45 id: id.to_string(),
46 input: String::new(),
47 close: true,
48 })),
49 None => CommandResult::error("Usage: /jobs close-stdin <id>"),
50 },
51 "cancel" | "kill" | "stop" => match id {
52 Some(id) => CommandResult::action(AppAction::ShellJob(ShellJobAction::Cancel {
53 id: id.to_string(),
54 })),
55 None => CommandResult::error("Usage: /jobs cancel <id>"),
56 },
57 _ => CommandResult::error(
58 "Usage: /jobs [list|show <id>|poll <id>|wait <id>|stdin <id> <input>|close-stdin <id>|cancel <id>]",
59 ),
60 }
61 }
62
63 #[cfg(test)]
64 mod tests {
65 use super::*;
66 use crate::config::Config;
67 use crate::tui::app::TuiOptions;
68 use std::path::PathBuf;
69
70 fn app() -> App {
71 App::new(
72 TuiOptions {
73 model: "deepseek-v4-pro".to_string(),
74 workspace: PathBuf::from("."),
75 config_path: None,
76 config_profile: None,
77 allow_shell: false,
78 use_alt_screen: false,
79 use_mouse_capture: false,
80 use_bracketed_paste: true,
81 max_subagents: 2,
82 skills_dir: PathBuf::from("."),
83 memory_path: PathBuf::from("memory.md"),
84 notes_path: PathBuf::from("notes.txt"),
85 mcp_config_path: PathBuf::from("mcp.json"),
86 use_memory: false,
87 start_in_agent_mode: false,
88 skip_onboarding: true,
89 yolo: false,
90 resume_session_id: None,
91 initial_input: None,
92 },
93 &Config::default(),
94 )
95 }
96
97 #[test]
98 fn parses_job_actions() {
99 let mut app = app();
100 let show = jobs(&mut app, Some("show shell_abcd"));
101 assert!(matches!(
102 show.action,
103 Some(AppAction::ShellJob(ShellJobAction::Show { id })) if id == "shell_abcd"
104 ));
105
106 let send = jobs(&mut app, Some("stdin shell_abcd y"));
107 assert!(matches!(
108 send.action,
109 Some(AppAction::ShellJob(ShellJobAction::SendStdin { id, input, close: false }))
110 if id == "shell_abcd" && input == "y"
111 ));
112 }
113 }
114
114 lines RUST