1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum GuideLanguage {
9 Chinese,
10 English,
11}
12
13impl GuideLanguage {
14 pub fn detect(source: &str) -> Self {
18 if source.chars().any(is_cjk) {
19 Self::Chinese
20 } else {
21 Self::English
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
30pub struct GuideBuildContext {
31 pub language: GuideLanguage,
33 pub include_best_practices: bool,
35 pub max_examples_per_tool: usize,
37 pub activated_discoverable_tools: std::collections::BTreeSet<String>,
41}
42
43impl Default for GuideBuildContext {
44 fn default() -> Self {
45 Self {
46 language: GuideLanguage::English,
47 include_best_practices: true,
48 max_examples_per_tool: 2,
49 activated_discoverable_tools: std::collections::BTreeSet::new(),
50 }
51 }
52}
53
54impl GuideBuildContext {
55 pub fn from_system_prompt(prompt: &str) -> Self {
61 Self {
62 language: GuideLanguage::detect(prompt),
63 ..Self::default()
64 }
65 }
66
67 pub fn best_practices(&self) -> &'static [&'static str] {
69 match self.language {
70 GuideLanguage::Chinese => &[
71 "Assist with defensive security tasks only; refuse offensive security requests, credential harvesting, and malware-oriented code changes.",
72 "For multi-step or non-trivial work, use Task to maintain a shared task list and keep statuses updated continuously.",
73 "Keep exactly one Task item in in_progress whenever possible; mark items completed immediately after finishing.",
74 "Read existing files before editing them. Use Edit for targeted replacements and Write only for full-file writes.",
75 "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.",
76 "For code search, narrow scope first: Glob -> Grep(files_with_matches) -> Read -> Grep(content/multiline).",
77 "Avoid broad Grep queries; keep head_limit small and add path/glob/type before multiline or content-heavy searches.",
78 "Use exec_command only for real terminal operations (build/test/git/npm/docker/etc.), not for file browsing or user-facing communication.",
79 "When multiple exec_command calls are independent, run them in parallel. For dependent commands, chain with &&.",
80 "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.",
81 "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.",
82 "**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.",
83 "For commit requests: inspect git status, git diff, and recent git log first; commit only when explicitly requested; avoid interactive git flags; use HEREDOC for multi-line commit messages.",
84 "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.",
85 "When referencing code locations in responses, use file_path:line_number format.",
86 "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.",
87 ],
88 GuideLanguage::English => &[
89 "Assist with defensive security tasks only; refuse offensive security requests, credential harvesting, and malware-oriented code changes.",
90 "For multi-step or non-trivial work, use Task to maintain a shared task list and keep statuses updated continuously.",
91 "Keep exactly one Task item in in_progress whenever possible; mark items completed immediately after finishing.",
92 "Read existing files before editing them. Use Edit for targeted replacements and Write only for full-file writes.",
93 "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.",
94 "For code search, narrow scope first: Glob -> Grep(files_with_matches) -> Read -> Grep(content/multiline).",
95 "Avoid broad Grep queries; keep head_limit small and add path/glob/type before multiline or content-heavy searches.",
96 "Use exec_command only for real terminal operations (build/test/git/npm/docker/etc.), not for file browsing or user-facing communication.",
97 "When multiple exec_command calls are independent, run them in parallel. For dependent commands, chain with &&.",
98 "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.",
99 "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.",
100 "**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.",
101 "For commit requests: inspect git status, git diff, and recent git log first; commit only when explicitly requested; avoid interactive git flags; use HEREDOC for multi-line commit messages.",
102 "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.",
103 "When referencing code locations in responses, use file_path:line_number format.",
104 "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.",
105 ],
106 }
107 }
108}
109
110fn is_cjk(ch: char) -> bool {
111 matches!(
112 ch,
113 '\u{3400}'..='\u{4DBF}' | '\u{4E00}'..='\u{9FFF}' | '\u{F900}'..='\u{FAFF}'
114 )
115}
116
117#[cfg(test)]
118mod tests {
119 use super::{GuideBuildContext, GuideLanguage};
120
121 #[test]
122 fn detect_language_prefers_chinese_when_cjk_present() {
123 assert_eq!(
124 GuideLanguage::detect("Please help me modify this file"),
125 GuideLanguage::English
126 );
127 }
128
129 #[test]
130 fn best_practices_prefer_delegated_plan_without_legacy_mode_switch() {
131 for language in [GuideLanguage::Chinese, GuideLanguage::English] {
132 let context = GuideBuildContext {
133 language,
134 ..GuideBuildContext::default()
135 };
136 let guidance = context.best_practices().join("\n");
137 assert!(guidance.contains("delegate planning to its read-only child"));
138 assert!(guidance.contains("already-active legacy plan-mode session"));
139 }
140 }
141
142 #[test]
143 fn best_practices_teach_model_owned_exec_polling() {
144 for language in [GuideLanguage::Chinese, GuideLanguage::English] {
145 let context = GuideBuildContext {
146 language,
147 ..GuideBuildContext::default()
148 };
149 let guidance = context.best_practices().join("\n");
150 assert!(guidance.contains("exec_command"));
151 assert!(guidance.contains("write_stdin with empty chars"));
152 assert!(guidance.contains("does not send an automatic user notification"));
153 assert!(guidance.contains("follow its advisory"));
154 assert!(guidance.contains("does not prove completion"));
155 assert!(!guidance.contains("Bash"));
156 }
157 }
158
159 #[test]
160 fn detect_language_defaults_to_english_without_cjk() {
161 assert_eq!(
162 GuideLanguage::detect("Please inspect the codebase"),
163 GuideLanguage::English
164 );
165 }
166
167 #[test]
168 fn from_system_prompt_carries_detected_language() {
169 let context = GuideBuildContext::from_system_prompt("You are a coding assistant.");
170 assert_eq!(context.language, GuideLanguage::English);
171 }
172}