1const GIT_TOOL_GUIDANCE: &str = "For Git work, prefer available dedicated Git tools over reconstructing repository state by reading files one by one. Start with status and staged/unstaged diff; for branch comparison or conflicts, inspect the merge base, unique commits, and unmerged index before reading the necessary diff hunks and surrounding source to resolve intent. Batch independent read-only queries. Preserve dirty user work and unique commits; verify the resolved diff and relevant checks. Use exec_command only when a dedicated tool cannot provide the needed operation and your role permits shell execution. A Supervisor without shell access delegates Git execution to an authorized worker and reviews its evidence; missing tools never expand permissions.";
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum GuideLanguage {
11 Chinese,
12 English,
13}
14
15impl GuideLanguage {
16 pub fn detect(source: &str) -> Self {
20 if source.chars().any(is_cjk) {
21 Self::Chinese
22 } else {
23 Self::English
24 }
25 }
26}
27
28#[derive(Debug, Clone)]
32pub struct GuideBuildContext {
33 pub language: GuideLanguage,
35 pub include_best_practices: bool,
37 pub max_examples_per_tool: usize,
39 pub activated_discoverable_tools: std::collections::BTreeSet<String>,
43}
44
45impl Default for GuideBuildContext {
46 fn default() -> Self {
47 Self {
48 language: GuideLanguage::English,
49 include_best_practices: true,
50 max_examples_per_tool: 2,
51 activated_discoverable_tools: std::collections::BTreeSet::new(),
52 }
53 }
54}
55
56impl GuideBuildContext {
57 pub fn from_system_prompt(prompt: &str) -> Self {
63 Self {
64 language: GuideLanguage::detect(prompt),
65 ..Self::default()
66 }
67 }
68
69 pub fn best_practices(&self) -> &'static [&'static str] {
71 match self.language {
72 GuideLanguage::Chinese => &[
73 "Assist with defensive security tasks only; refuse offensive security requests, credential harvesting, and malware-oriented code changes.",
74 "For multi-step or non-trivial work, use Task to maintain a shared task list and keep statuses updated continuously.",
75 "Keep exactly one Task item in in_progress whenever possible; mark items completed immediately after finishing.",
76 "Read existing files before editing them. Use Edit for targeted replacements and Write only for full-file writes.",
77 "Use Glob for file discovery and Grep for content search. Do not use exec_command for find/grep/cat/head/tail/sed/awk/echo style file operations when dedicated tools exist.",
78 "For code search, narrow scope first: Glob -> Grep(files_with_matches) -> Read -> Grep(content/multiline).",
79 "Avoid broad Grep queries; keep head_limit small and add path/glob/type before multiline or content-heavy searches.",
80 "Use exec_command only for real terminal operations (build/test/git/npm/docker/etc.), not for file browsing or user-facing communication.",
81 "When multiple exec_command calls are independent, run them in parallel. For dependent commands, chain with &&.",
82 "When exec_command returns a running session_id, use write_stdin with empty chars to pull incremental output and final exit status. Choose yield_time_ms to bound each wait; completion does not send an automatic user notification. Unread completed results expire five minutes after completion, including during a diagnostic pause; handles do not survive Host restart. Poll required commands to a terminal result before finishing the task.",
83 "When polling reports sustained silence and repeated empty readouts, follow its advisory: do useful work or check one command-owned progress signal before polling again. Quiet output or an existing report does not prove completion; keep the handle, continue waiting when appropriate, and report a concrete blocker if progress cannot be established. A Human-controlled Root can pause polling after the reminder is repeatedly ignored, retaining the command handle for continuation. Do not stop/restart a command only because it is quiet.",
84 "**Parallel tool calls are strongly preferred.** The following read-only tools can safely run in parallel and SHOULD be called together in the same response whenever possible: FileExists, Glob, GetCurrentDir, GetFileInfo, Grep, Read, ViewImage, WebFetch, session_history_current, session_history. For example, if you need to read 3 files, emit all 3 Read calls in one response instead of one at a time.",
85 GIT_TOOL_GUIDANCE,
86 "For commit requests: inspect git status, git diff, and recent git log first; commit within current or standing user authorization; avoid interactive git flags; use HEREDOC for multi-line commit messages.",
87 "For pull request requests: review all commits/diff since base branch, use gh to create PR with summary and test plan, and return the PR URL.",
88 "When referencing code locations in responses, use file_path:line_number format.",
89 "Use Plan to delegate planning to its read-only child and keep the root session in normal orchestration mode. Use ExitPlanMode only to leave an already-active legacy plan-mode session.",
90 ],
91 GuideLanguage::English => &[
92 "Assist with defensive security tasks only; refuse offensive security requests, credential harvesting, and malware-oriented code changes.",
93 "For multi-step or non-trivial work, use Task to maintain a shared task list and keep statuses updated continuously.",
94 "Keep exactly one Task item in in_progress whenever possible; mark items completed immediately after finishing.",
95 "Read existing files before editing them. Use Edit for targeted replacements and Write only for full-file writes.",
96 "Use Glob for file discovery and Grep for content search. Do not use exec_command for find/grep/cat/head/tail/sed/awk/echo style file operations when dedicated tools exist.",
97 "For code search, narrow scope first: Glob -> Grep(files_with_matches) -> Read -> Grep(content/multiline).",
98 "Avoid broad Grep queries; keep head_limit small and add path/glob/type before multiline or content-heavy searches.",
99 "Use exec_command only for real terminal operations (build/test/git/npm/docker/etc.), not for file browsing or user-facing communication.",
100 "When multiple exec_command calls are independent, run them in parallel. For dependent commands, chain with &&.",
101 "When exec_command returns a running session_id, use write_stdin with empty chars to pull incremental output and final exit status. Choose yield_time_ms to bound each wait; completion does not send an automatic user notification. Unread completed results expire five minutes after completion, including during a diagnostic pause; handles do not survive Host restart. Poll required commands to a terminal result before finishing the task.",
102 "When polling reports sustained silence and repeated empty readouts, follow its advisory: do useful work or check one command-owned progress signal before polling again. Quiet output or an existing report does not prove completion; keep the handle, continue waiting when appropriate, and report a concrete blocker if progress cannot be established. A Human-controlled Root can pause polling after the reminder is repeatedly ignored, retaining the command handle for continuation. Do not stop/restart a command only because it is quiet.",
103 "**Parallel tool calls are strongly preferred.** The following read-only tools can safely run in parallel and SHOULD be called together in the same response whenever possible: FileExists, Glob, GetCurrentDir, GetFileInfo, Grep, Read, ViewImage, WebFetch, session_history_current, session_history. For example, if you need to read 3 files, emit all 3 Read calls in one response instead of one at a time.",
104 GIT_TOOL_GUIDANCE,
105 "For commit requests: inspect git status, git diff, and recent git log first; commit within current or standing user authorization; avoid interactive git flags; use HEREDOC for multi-line commit messages.",
106 "For pull request requests: review all commits/diff since base branch, use gh to create PR with summary and test plan, and return the PR URL.",
107 "When referencing code locations in responses, use file_path:line_number format.",
108 "Use Plan to delegate planning to its read-only child and keep the root session in normal orchestration mode. Use ExitPlanMode only to leave an already-active legacy plan-mode session.",
109 ],
110 }
111 }
112}
113
114fn is_cjk(ch: char) -> bool {
115 matches!(
116 ch,
117 '\u{3400}'..='\u{4DBF}' | '\u{4E00}'..='\u{9FFF}' | '\u{F900}'..='\u{FAFF}'
118 )
119}
120
121#[cfg(test)]
122mod tests {
123 use super::{GuideBuildContext, GuideLanguage};
124
125 #[test]
126 fn detect_language_prefers_chinese_when_cjk_present() {
127 assert_eq!(
128 GuideLanguage::detect("Please help me modify this file"),
129 GuideLanguage::English
130 );
131 }
132
133 #[test]
134 fn best_practices_prefer_delegated_plan_without_legacy_mode_switch() {
135 for language in [GuideLanguage::Chinese, GuideLanguage::English] {
136 let context = GuideBuildContext {
137 language,
138 ..GuideBuildContext::default()
139 };
140 let guidance = context.best_practices().join("\n");
141 assert!(guidance.contains("delegate planning to its read-only child"));
142 assert!(guidance.contains("already-active legacy plan-mode session"));
143 }
144 }
145
146 #[test]
147 fn best_practices_teach_model_owned_exec_polling() {
148 for language in [GuideLanguage::Chinese, GuideLanguage::English] {
149 let context = GuideBuildContext {
150 language,
151 ..GuideBuildContext::default()
152 };
153 let guidance = context.best_practices().join("\n");
154 assert!(guidance.contains("exec_command"));
155 assert!(guidance.contains("write_stdin with empty chars"));
156 assert!(guidance.contains("does not send an automatic user notification"));
157 assert!(guidance.contains("follow its advisory"));
158 assert!(guidance.contains("does not prove completion"));
159 assert!(!guidance.contains("Bash"));
160 }
161 }
162
163 #[test]
164 fn best_practices_prefer_git_tools_and_preserve_role_and_work_boundaries() {
165 for language in [GuideLanguage::Chinese, GuideLanguage::English] {
166 let guidance = GuideBuildContext {
167 language,
168 ..GuideBuildContext::default()
169 }
170 .best_practices()
171 .join("\n");
172 for required in [
173 "prefer available dedicated Git tools",
174 "status and staged/unstaged diff",
175 "merge base, unique commits, and unmerged index",
176 "necessary diff hunks and surrounding source",
177 "Preserve dirty user work and unique commits",
178 "Supervisor without shell access delegates Git execution to an authorized worker",
179 "missing tools never expand permissions",
180 "current or standing user authorization",
181 ] {
182 assert!(guidance.contains(required), "missing {required}");
183 }
184 }
185 }
186
187 #[test]
188 fn detect_language_defaults_to_english_without_cjk() {
189 assert_eq!(
190 GuideLanguage::detect("Please inspect the codebase"),
191 GuideLanguage::English
192 );
193 }
194
195 #[test]
196 fn from_system_prompt_carries_detected_language() {
197 let context = GuideBuildContext::from_system_prompt("You are a coding assistant.");
198 assert_eq!(context.language, GuideLanguage::English);
199 }
200}