返回 CodeWhale
legacy.rs
根目录 / crates / tui / src / commands / groups / plugins / legacy.rs
1 //! Legacy executable plugin-tool inventory (`[tools].plugin_dir`).
2 //!
3 //! These are scripts, not declarative bundles: they are discovered by
4 //! scanning a directory, they carry their own approval requirement, and
5 //! they never share bundle trust state. `/plugin tools` reports them
6 //! read-only — nothing here installs, trusts, or executes anything.
7 //!
8 //! FEAT-020: this module consumes the portable `PluginLegacyScan` from the
9 //! plugin facet; no concrete `App` or `PluginMetadata` crosses the boundary.
10
11 use codewhale_command_contract::facets::{CommandPluginContext, CommandPresentationContext};
12 use std::fmt::Write as _;
13
14 use crate::commands::CommandResult;
15
16 pub(super) fn legacy_tools(
17 presentation: &mut dyn CommandPresentationContext,
18 plugin: &dyn CommandPluginContext,
19 name: Option<&str>,
20 ) -> CommandResult {
21 let scan = match plugin.legacy_scan() {
22 Ok(Some(scan)) => scan,
23 Ok(None) | Err(_) => {
24 return super::action_error(
25 presentation,
26 "Could not resolve the legacy executable plugin-tool directory",
27 );
28 }
29 };
30 match name {
31 Some(name) => show_legacy_tool_detail(presentation, name, &scan),
32 None => list_legacy_tools(presentation, &scan),
33 }
34 }
35
36 fn list_legacy_tools(
37 presentation: &mut dyn CommandPresentationContext,
38 scan: &codewhale_command_contract::facets::PluginLegacyScan,
39 ) -> CommandResult {
40 if scan.tools.is_empty() {
41 return CommandResult::message(
42 presentation
43 .translate(
44 "cmd_plugin_none_found",
45 &[("dir", &scan.dir.display().to_string())],
46 )
47 .unwrap_or_default(),
48 );
49 }
50 let mut output = presentation
51 .translate(
52 "cmd_plugin_legacy_list_header",
53 &[
54 ("count", &scan.tools.len().to_string()),
55 ("dir", &scan.dir.display().to_string()),
56 ],
57 )
58 .unwrap_or_default();
59 output.push('\n');
60 for tool in &scan.tools {
61 let _ = writeln!(
62 output,
63 "• {} — {}\n {}",
64 tool.name,
65 tool.description,
66 tool.path.display()
67 );
68 }
69 super::append_diagnostics(presentation, &mut output, &scan.diagnostics);
70 CommandResult::message(output)
71 }
72
73 fn show_legacy_tool_detail(
74 presentation: &mut dyn CommandPresentationContext,
75 name: &str,
76 scan: &codewhale_command_contract::facets::PluginLegacyScan,
77 ) -> CommandResult {
78 let Some(tool) = scan.tools.iter().find(|tool| tool.name == name) else {
79 return CommandResult::error(
80 presentation
81 .translate("cmd_plugin_not_found", &[("name", name)])
82 .unwrap_or_default(),
83 );
84 };
85 let schema = tool
86 .input_schema
87 .clone()
88 .unwrap_or_else(|| "{}".to_string());
89 let mut output = format!("{}\n{:=<40}\n", tool.name, "");
90 let _ = writeln!(
91 output,
92 "{}",
93 presentation
94 .translate(
95 "cmd_plugin_detail_description",
96 &[("description", &tool.description)],
97 )
98 .unwrap_or_default()
99 );
100 let _ = writeln!(
101 output,
102 "{}",
103 presentation
104 .translate("cmd_plugin_detail_schema", &[("schema", &schema)])
105 .unwrap_or_default()
106 );
107 let _ = writeln!(
108 output,
109 "{}",
110 presentation
111 .translate(
112 "cmd_plugin_detail_approval",
113 &[("approval", &tool.approval)]
114 )
115 .unwrap_or_default()
116 );
117 let _ = writeln!(
118 output,
119 "{}",
120 presentation
121 .translate(
122 "cmd_plugin_detail_path",
123 &[("path", &tool.path.display().to_string())]
124 )
125 .unwrap_or_default()
126 );
127 let diagnostics: Vec<_> = scan
128 .diagnostics
129 .iter()
130 .filter(|diagnostic| diagnostic.path.as_deref() == Some(tool.path.as_path()))
131 .cloned()
132 .collect();
133 super::append_diagnostics(presentation, &mut output, &diagnostics);
134 CommandResult::message(output)
135 }
136
136 lines RUST