winx_code_agent/utils/
mode_prompts.rs1use std::fmt::Write as _;
11
12use crate::types::{AllowedCommands, AllowedGlobs, CodeWriterConfig, Modes};
13
14const WCGW_PROMPT: &str = "# Operating mode: wcgw (full access)
16
17- Use the provided shell, file-read and file-write tools to accomplish the objective.
18- Do not provide code snippets unless asked — edit the code directly with the winx tools.
19- Do not install new tools/packages before checking that the tool (or an alternative) doesn't already exist.
20- Do not use echo/cat to write files; always use FileWriteOrEdit to create/update files.
21- Do not send Ctrl-c / interrupts without asking — programs often keep running with no visible output.
22- Provide as many file paths as you need to ReadFiles in a single call.
23- Run `pwd` if you hit a file/dir not found error, to make sure you're not lost.";
24
25const ARCHITECT_PROMPT: &str = "# Operating mode: architect (read-only)
27
28- You may NOT edit, create, or overwrite any file. FileWriteOrEdit is disabled in this mode.
29- You may NOT run commands that change disk, packages, system config or environment — read-only commands only.
30- The shell runs with `-r` (restricted): you cannot change directory.
31- Only run commands that help you explore the repo, understand the system, or read what's relevant.
32- Do not send Ctrl-c / interrupts without asking the user.
33- When an implementation is requested, share minimal snippets (use `...` for elided lines), not whole files.
34
35# How to respond
36- Read as many relevant files as possible first; be comprehensive.
37- Start from the folder structure (ignore .git, node_modules, target, .venv, etc.).
38- Provide as many file paths as you need to ReadFiles in a single call.";
39
40const RUN_COMMAND_COMMON: &str =
42 "- Do not send Ctrl-c / interrupts without asking — programs often keep running silently.
43- Do not use echo/cat to write files; always use FileWriteOrEdit.
44- Do not provide code snippets unless asked — edit the code directly with the winx tools.";
45
46pub fn mode_prompt(mode: Modes, config: Option<&CodeWriterConfig>) -> String {
49 match mode {
50 Modes::Wcgw => WCGW_PROMPT.to_string(),
51 Modes::Architect => ARCHITECT_PROMPT.to_string(),
52 Modes::CodeWriter => code_writer_prompt(config),
53 }
54}
55
56fn code_writer_prompt(config: Option<&CodeWriterConfig>) -> String {
57 let mut out = String::from("# Operating mode: code_writer\n\n");
58
59 match config.map(|c| &c.allowed_globs) {
61 Some(AllowedGlobs::List(globs)) if globs.is_empty() => {
62 out.push_str("- You are NOT allowed to edit or create any file.\n");
63 }
64 Some(AllowedGlobs::List(globs)) => {
65 let _ = writeln!(
66 out,
67 "- You may edit/create files matching ONLY these globs: {}",
68 globs.join(", ")
69 );
70 }
71 _ => out.push_str("- You may edit/create files within the provided repository only.\n"),
72 }
73
74 match config.map(|c| &c.allowed_commands) {
75 Some(AllowedCommands::List(cmds)) if cmds.is_empty() => {
76 out.push_str("- You are NOT allowed to run any commands.\n");
77 }
78 Some(AllowedCommands::List(cmds)) => {
79 let _ = writeln!(out, "- You may run ONLY the following commands: {}", cmds.join(", "));
80 out.push_str(RUN_COMMAND_COMMON);
81 }
82 _ => {
83 out.push_str(
84 "- You may run commands for project setup, code writing, testing, running and debugging only.\n\
85 - Do not add/remove packages or change system configuration or environment.\n",
86 );
87 out.push_str(RUN_COMMAND_COMMON);
88 }
89 }
90
91 out
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn architect_is_read_only_and_names_disabled_tool() {
100 let p = mode_prompt(Modes::Architect, None);
101 assert!(p.contains("read-only"));
102 assert!(p.contains("FileWriteOrEdit is disabled"));
103 }
104
105 #[test]
106 fn code_writer_lists_allowed_globs_and_commands() {
107 let config = CodeWriterConfig {
108 allowed_globs: AllowedGlobs::List(vec!["src/**/*.rs".to_string()]),
109 allowed_commands: AllowedCommands::List(vec!["cargo test".to_string()]),
110 };
111 let p = mode_prompt(Modes::CodeWriter, Some(&config));
112 assert!(p.contains("src/**/*.rs"));
113 assert!(p.contains("cargo test"));
114 assert!(p.contains("ONLY"));
115 }
116
117 #[test]
118 fn code_writer_empty_lists_forbid() {
119 let config = CodeWriterConfig {
120 allowed_globs: AllowedGlobs::List(vec![]),
121 allowed_commands: AllowedCommands::List(vec![]),
122 };
123 let p = mode_prompt(Modes::CodeWriter, Some(&config));
124 assert!(p.contains("NOT allowed to edit"));
125 assert!(p.contains("NOT allowed to run any commands"));
126 }
127
128 #[test]
129 fn wcgw_is_full_access() {
130 assert!(mode_prompt(Modes::Wcgw, None).contains("full access"));
131 }
132}