grok-cli-acp 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +42 -0
- package/.github/workflows/ci.yml +30 -0
- package/.github/workflows/rust.yml +22 -0
- package/.grok/.env.example +85 -0
- package/.grok/COMPLETE_FIX_SUMMARY.md +466 -0
- package/.grok/ENV_CONFIG_GUIDE.md +173 -0
- package/.grok/QUICK_REFERENCE.md +180 -0
- package/.grok/README.md +104 -0
- package/.grok/TESTING_GUIDE.md +393 -0
- package/CHANGELOG.md +465 -0
- package/CODE_REVIEW_SUMMARY.md +414 -0
- package/COMPLETE_FIX_SUMMARY.md +415 -0
- package/CONFIGURATION.md +489 -0
- package/CONTEXT_FILES_GUIDE.md +419 -0
- package/CONTRIBUTING.md +55 -0
- package/CURSOR_POSITION_FIX.md +206 -0
- package/Cargo.toml +88 -0
- package/ERROR_HANDLING_REPORT.md +361 -0
- package/FINAL_FIX_SUMMARY.md +462 -0
- package/FIXES.md +37 -0
- package/FIXES_SUMMARY.md +87 -0
- package/GROK_API_MIGRATION_SUMMARY.md +111 -0
- package/LICENSE +22 -0
- package/MIGRATION_TO_GROK_API.md +223 -0
- package/README.md +504 -0
- package/REVIEW_COMPLETE.md +416 -0
- package/REVIEW_QUICK_REFERENCE.md +173 -0
- package/SECURITY.md +463 -0
- package/SECURITY_AUDIT.md +661 -0
- package/SETUP.md +287 -0
- package/TESTING_TOOLS.md +88 -0
- package/TESTING_TOOL_EXECUTION.md +239 -0
- package/TOOL_EXECUTION_FIX.md +491 -0
- package/VERIFICATION_CHECKLIST.md +419 -0
- package/docs/API.md +74 -0
- package/docs/CHAT_LOGGING.md +39 -0
- package/docs/CURSOR_FIX_DEMO.md +306 -0
- package/docs/ERROR_HANDLING_GUIDE.md +547 -0
- package/docs/FILE_OPERATIONS.md +449 -0
- package/docs/INTERACTIVE.md +401 -0
- package/docs/PROJECT_CREATION_GUIDE.md +570 -0
- package/docs/QUICKSTART.md +378 -0
- package/docs/QUICK_REFERENCE.md +691 -0
- package/docs/RELEASE_NOTES_0.1.2.md +240 -0
- package/docs/TOOLS.md +459 -0
- package/docs/TOOLS_QUICK_REFERENCE.md +210 -0
- package/docs/ZED_INTEGRATION.md +371 -0
- package/docs/extensions.md +464 -0
- package/docs/settings.md +293 -0
- package/examples/extensions/logging-hook/README.md +91 -0
- package/examples/extensions/logging-hook/extension.json +22 -0
- package/package.json +30 -0
- package/scripts/test_acp.py +252 -0
- package/scripts/test_acp.sh +143 -0
- package/scripts/test_acp_simple.sh +72 -0
- package/src/acp/mod.rs +741 -0
- package/src/acp/protocol.rs +323 -0
- package/src/acp/security.rs +298 -0
- package/src/acp/tools.rs +697 -0
- package/src/bin/banner_demo.rs +216 -0
- package/src/bin/docgen.rs +18 -0
- package/src/bin/installer.rs +217 -0
- package/src/cli/app.rs +310 -0
- package/src/cli/commands/acp.rs +721 -0
- package/src/cli/commands/chat.rs +485 -0
- package/src/cli/commands/code.rs +513 -0
- package/src/cli/commands/config.rs +394 -0
- package/src/cli/commands/health.rs +442 -0
- package/src/cli/commands/history.rs +421 -0
- package/src/cli/commands/mod.rs +14 -0
- package/src/cli/commands/settings.rs +1384 -0
- package/src/cli/mod.rs +166 -0
- package/src/config/mod.rs +2212 -0
- package/src/display/ascii_art.rs +139 -0
- package/src/display/banner.rs +289 -0
- package/src/display/components/input.rs +323 -0
- package/src/display/components/mod.rs +2 -0
- package/src/display/components/settings_list.rs +306 -0
- package/src/display/interactive.rs +1255 -0
- package/src/display/mod.rs +62 -0
- package/src/display/terminal.rs +42 -0
- package/src/display/tips.rs +316 -0
- package/src/grok_client_ext.rs +177 -0
- package/src/hooks/loader.rs +407 -0
- package/src/hooks/mod.rs +158 -0
- package/src/lib.rs +174 -0
- package/src/main.rs +65 -0
- package/src/mcp/client.rs +195 -0
- package/src/mcp/config.rs +20 -0
- package/src/mcp/mod.rs +6 -0
- package/src/mcp/protocol.rs +67 -0
- package/src/utils/auth.rs +41 -0
- package/src/utils/chat_logger.rs +568 -0
- package/src/utils/context.rs +390 -0
- package/src/utils/mod.rs +16 -0
- package/src/utils/network.rs +320 -0
- package/src/utils/rate_limiter.rs +166 -0
- package/src/utils/session.rs +73 -0
- package/src/utils/shell_permissions.rs +389 -0
- package/src/utils/telemetry.rs +41 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
//! Project context file loading utilities
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides functionality to detect and load project-specific
|
|
4
|
+
//! context files (like GEMINI.md, .grok/context.md, etc.) that help ground
|
|
5
|
+
//! the AI agent in project conventions and guidelines.
|
|
6
|
+
|
|
7
|
+
use anyhow::{anyhow, Result};
|
|
8
|
+
use std::fs;
|
|
9
|
+
use std::path::{Path, PathBuf};
|
|
10
|
+
|
|
11
|
+
/// Standard context file names to search for, in order of preference
|
|
12
|
+
const CONTEXT_FILE_NAMES: &[&str] = &[
|
|
13
|
+
"GEMINI.md",
|
|
14
|
+
".gemini.md",
|
|
15
|
+
".claude.md",
|
|
16
|
+
".zed/rules",
|
|
17
|
+
".grok/context.md",
|
|
18
|
+
".ai/context.md",
|
|
19
|
+
"CONTEXT.md",
|
|
20
|
+
".gemini/context.md",
|
|
21
|
+
".cursor/rules",
|
|
22
|
+
"AI_RULES.md",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/// Maximum context file size to load (5 MB)
|
|
26
|
+
const MAX_CONTEXT_SIZE: u64 = 5 * 1024 * 1024;
|
|
27
|
+
|
|
28
|
+
/// Load project context from standard context files
|
|
29
|
+
///
|
|
30
|
+
/// Searches for context files in the project root directory in the following order:
|
|
31
|
+
/// 1. GEMINI.md
|
|
32
|
+
/// 2. .gemini.md
|
|
33
|
+
/// 3. .claude.md
|
|
34
|
+
/// 4. .zed/rules
|
|
35
|
+
/// 5. .grok/context.md
|
|
36
|
+
/// 6. .ai/context.md
|
|
37
|
+
/// 7. CONTEXT.md
|
|
38
|
+
/// 8. .gemini/context.md
|
|
39
|
+
/// 9. .cursor/rules
|
|
40
|
+
/// 10. AI_RULES.md
|
|
41
|
+
///
|
|
42
|
+
/// Returns the content of the first file found, or None if no context file exists.
|
|
43
|
+
pub fn load_project_context<P: AsRef<Path>>(project_root: P) -> Result<Option<String>> {
|
|
44
|
+
let project_root = project_root.as_ref();
|
|
45
|
+
|
|
46
|
+
if !project_root.exists() || !project_root.is_dir() {
|
|
47
|
+
return Err(anyhow!(
|
|
48
|
+
"Project root does not exist or is not a directory: {:?}",
|
|
49
|
+
project_root
|
|
50
|
+
));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for file_name in CONTEXT_FILE_NAMES {
|
|
54
|
+
let file_path = project_root.join(file_name);
|
|
55
|
+
|
|
56
|
+
if file_path.exists() && file_path.is_file() {
|
|
57
|
+
// Check file size before reading
|
|
58
|
+
let metadata = fs::metadata(&file_path)?;
|
|
59
|
+
if metadata.len() > MAX_CONTEXT_SIZE {
|
|
60
|
+
eprintln!(
|
|
61
|
+
"Warning: Context file {} is too large ({} bytes), skipping",
|
|
62
|
+
file_path.display(),
|
|
63
|
+
metadata.len()
|
|
64
|
+
);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
match fs::read_to_string(&file_path) {
|
|
69
|
+
Ok(content) => {
|
|
70
|
+
if content.trim().is_empty() {
|
|
71
|
+
// Skip empty files and continue searching
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
return Ok(Some(content));
|
|
75
|
+
}
|
|
76
|
+
Err(e) => {
|
|
77
|
+
eprintln!(
|
|
78
|
+
"Warning: Failed to read context file {}: {}",
|
|
79
|
+
file_path.display(),
|
|
80
|
+
e
|
|
81
|
+
);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// No context file found
|
|
89
|
+
Ok(None)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Load and merge multiple project context files
|
|
93
|
+
///
|
|
94
|
+
/// Unlike load_project_context which returns the first file found,
|
|
95
|
+
/// this function loads and merges all available context files,
|
|
96
|
+
/// allowing projects to use multiple context sources (e.g., both
|
|
97
|
+
/// .zed/rules and .gemini.md).
|
|
98
|
+
///
|
|
99
|
+
/// Returns a merged context string, or None if no files are found.
|
|
100
|
+
pub fn load_and_merge_project_context<P: AsRef<Path>>(project_root: P) -> Result<Option<String>> {
|
|
101
|
+
let project_root = project_root.as_ref();
|
|
102
|
+
|
|
103
|
+
if !project_root.exists() || !project_root.is_dir() {
|
|
104
|
+
return Err(anyhow!(
|
|
105
|
+
"Project root does not exist or is not a directory: {:?}",
|
|
106
|
+
project_root
|
|
107
|
+
));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
let mut merged_content = Vec::new();
|
|
111
|
+
|
|
112
|
+
for file_name in CONTEXT_FILE_NAMES {
|
|
113
|
+
let file_path = project_root.join(file_name);
|
|
114
|
+
|
|
115
|
+
if file_path.exists() && file_path.is_file() {
|
|
116
|
+
// Check file size before reading
|
|
117
|
+
let metadata = fs::metadata(&file_path)?;
|
|
118
|
+
if metadata.len() > MAX_CONTEXT_SIZE {
|
|
119
|
+
eprintln!(
|
|
120
|
+
"Warning: Context file {} is too large ({} bytes), skipping",
|
|
121
|
+
file_path.display(),
|
|
122
|
+
metadata.len()
|
|
123
|
+
);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
match fs::read_to_string(&file_path) {
|
|
128
|
+
Ok(content) => {
|
|
129
|
+
if !content.trim().is_empty() {
|
|
130
|
+
// Add source annotation
|
|
131
|
+
let annotated = format!("## From: {}\n\n{}\n", file_name, content.trim());
|
|
132
|
+
merged_content.push(annotated);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
Err(e) => {
|
|
136
|
+
eprintln!(
|
|
137
|
+
"Warning: Failed to read context file {}: {}",
|
|
138
|
+
file_path.display(),
|
|
139
|
+
e
|
|
140
|
+
);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if merged_content.is_empty() {
|
|
148
|
+
Ok(None)
|
|
149
|
+
} else {
|
|
150
|
+
Ok(Some(merged_content.join("\n---\n\n")))
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/// Get all available context file paths in the project
|
|
155
|
+
///
|
|
156
|
+
/// Returns a vector of paths to all existing context files.
|
|
157
|
+
pub fn get_all_context_file_paths<P: AsRef<Path>>(project_root: P) -> Vec<PathBuf> {
|
|
158
|
+
let project_root = project_root.as_ref();
|
|
159
|
+
|
|
160
|
+
if !project_root.exists() || !project_root.is_dir() {
|
|
161
|
+
return Vec::new();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let mut paths = Vec::new();
|
|
165
|
+
|
|
166
|
+
for file_name in CONTEXT_FILE_NAMES {
|
|
167
|
+
let file_path = project_root.join(file_name);
|
|
168
|
+
if file_path.exists() && file_path.is_file() {
|
|
169
|
+
paths.push(file_path);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
paths
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/// Get the path of the context file if it exists
|
|
177
|
+
///
|
|
178
|
+
/// Returns the path to the first available context file, or None if no file exists.
|
|
179
|
+
pub fn get_context_file_path<P: AsRef<Path>>(project_root: P) -> Option<PathBuf> {
|
|
180
|
+
let project_root = project_root.as_ref();
|
|
181
|
+
|
|
182
|
+
if !project_root.exists() || !project_root.is_dir() {
|
|
183
|
+
return None;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for file_name in CONTEXT_FILE_NAMES {
|
|
187
|
+
let file_path = project_root.join(file_name);
|
|
188
|
+
if file_path.exists() && file_path.is_file() {
|
|
189
|
+
return Some(file_path);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
None
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// Format context content for injection into system prompt
|
|
197
|
+
///
|
|
198
|
+
/// Wraps the context content with appropriate markers to distinguish it
|
|
199
|
+
/// from the base system prompt.
|
|
200
|
+
pub fn format_context_for_prompt(context: &str) -> String {
|
|
201
|
+
format!(
|
|
202
|
+
"\n\n## Project Context\n\nThe following context has been loaded from the project:\n\n{}\n\n---\n",
|
|
203
|
+
context.trim()
|
|
204
|
+
)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Validate that context content is reasonable for injection
|
|
208
|
+
///
|
|
209
|
+
/// Checks for potential issues like excessive length or problematic content.
|
|
210
|
+
pub fn validate_context(context: &str) -> Result<()> {
|
|
211
|
+
let trimmed = context.trim();
|
|
212
|
+
|
|
213
|
+
if trimmed.is_empty() {
|
|
214
|
+
return Err(anyhow!("Context content is empty"));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check token estimate (rough estimate: 1 token ≈ 4 characters)
|
|
218
|
+
let estimated_tokens = trimmed.len() / 4;
|
|
219
|
+
if estimated_tokens > 100_000 {
|
|
220
|
+
return Err(anyhow!(
|
|
221
|
+
"Context is too large (estimated {} tokens). Consider reducing size.",
|
|
222
|
+
estimated_tokens
|
|
223
|
+
));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
Ok(())
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/// Create a default context file template
|
|
230
|
+
pub fn create_default_context_template() -> String {
|
|
231
|
+
r#"# Project Context
|
|
232
|
+
|
|
233
|
+
This file provides context for AI assistants working on this project.
|
|
234
|
+
|
|
235
|
+
## Project Overview
|
|
236
|
+
<!-- Briefly describe what this project does -->
|
|
237
|
+
|
|
238
|
+
## Architecture
|
|
239
|
+
<!-- Describe the high-level architecture and key components -->
|
|
240
|
+
|
|
241
|
+
## Development Guidelines
|
|
242
|
+
<!-- Any coding standards, conventions, or best practices -->
|
|
243
|
+
|
|
244
|
+
## Key Technologies
|
|
245
|
+
<!-- List main frameworks, libraries, and tools used -->
|
|
246
|
+
|
|
247
|
+
## Common Tasks
|
|
248
|
+
<!-- Frequently performed development tasks and how to do them -->
|
|
249
|
+
|
|
250
|
+
## Important Notes
|
|
251
|
+
<!-- Any gotchas, quirks, or important considerations -->
|
|
252
|
+
"#
|
|
253
|
+
.to_string()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#[cfg(test)]
|
|
257
|
+
mod tests {
|
|
258
|
+
use super::*;
|
|
259
|
+
use std::fs;
|
|
260
|
+
use tempfile::tempdir;
|
|
261
|
+
|
|
262
|
+
#[test]
|
|
263
|
+
fn test_load_project_context_gemini_md() {
|
|
264
|
+
let temp_dir = tempdir().unwrap();
|
|
265
|
+
let gemini_file = temp_dir.path().join("GEMINI.md");
|
|
266
|
+
fs::write(&gemini_file, "# Test Project\nThis is a test context.").unwrap();
|
|
267
|
+
|
|
268
|
+
let result = load_project_context(temp_dir.path()).unwrap();
|
|
269
|
+
assert!(result.is_some());
|
|
270
|
+
assert!(result.unwrap().contains("Test Project"));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
#[test]
|
|
274
|
+
fn test_load_project_context_grok_dir() {
|
|
275
|
+
let temp_dir = tempdir().unwrap();
|
|
276
|
+
let grok_dir = temp_dir.path().join(".grok");
|
|
277
|
+
fs::create_dir(&grok_dir).unwrap();
|
|
278
|
+
let context_file = grok_dir.join("context.md");
|
|
279
|
+
fs::write(&context_file, "# Grok Context\nGrok-specific context.").unwrap();
|
|
280
|
+
|
|
281
|
+
let result = load_project_context(temp_dir.path()).unwrap();
|
|
282
|
+
assert!(result.is_some());
|
|
283
|
+
assert!(result.unwrap().contains("Grok Context"));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
#[test]
|
|
287
|
+
fn test_load_project_context_priority() {
|
|
288
|
+
let temp_dir = tempdir().unwrap();
|
|
289
|
+
|
|
290
|
+
// Create multiple context files
|
|
291
|
+
fs::write(temp_dir.path().join("GEMINI.md"), "GEMINI content").unwrap();
|
|
292
|
+
fs::write(temp_dir.path().join("CONTEXT.md"), "CONTEXT content").unwrap();
|
|
293
|
+
|
|
294
|
+
let result = load_project_context(temp_dir.path()).unwrap();
|
|
295
|
+
assert!(result.is_some());
|
|
296
|
+
// Should prefer GEMINI.md
|
|
297
|
+
assert_eq!(result.unwrap(), "GEMINI content");
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#[test]
|
|
301
|
+
fn test_load_project_context_no_file() {
|
|
302
|
+
let temp_dir = tempdir().unwrap();
|
|
303
|
+
let result = load_project_context(temp_dir.path()).unwrap();
|
|
304
|
+
assert!(result.is_none());
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
#[test]
|
|
308
|
+
fn test_load_project_context_empty_file() {
|
|
309
|
+
let temp_dir = tempdir().unwrap();
|
|
310
|
+
let gemini_file = temp_dir.path().join("GEMINI.md");
|
|
311
|
+
fs::write(&gemini_file, " \n\n ").unwrap();
|
|
312
|
+
|
|
313
|
+
let result = load_project_context(temp_dir.path()).unwrap();
|
|
314
|
+
assert!(result.is_none());
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
#[test]
|
|
318
|
+
fn test_get_context_file_path() {
|
|
319
|
+
let temp_dir = tempdir().unwrap();
|
|
320
|
+
let gemini_file = temp_dir.path().join("GEMINI.md");
|
|
321
|
+
fs::write(&gemini_file, "test").unwrap();
|
|
322
|
+
|
|
323
|
+
let result = get_context_file_path(temp_dir.path());
|
|
324
|
+
assert!(result.is_some());
|
|
325
|
+
assert_eq!(result.unwrap(), gemini_file);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
#[test]
|
|
329
|
+
fn test_format_context_for_prompt() {
|
|
330
|
+
let context = "Test context content";
|
|
331
|
+
let formatted = format_context_for_prompt(context);
|
|
332
|
+
assert!(formatted.contains("## Project Context"));
|
|
333
|
+
assert!(formatted.contains("Test context content"));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
#[test]
|
|
337
|
+
fn test_validate_context() {
|
|
338
|
+
assert!(validate_context("Valid content").is_ok());
|
|
339
|
+
assert!(validate_context("").is_err());
|
|
340
|
+
assert!(validate_context(" ").is_err());
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
#[test]
|
|
344
|
+
fn test_create_default_template() {
|
|
345
|
+
let template = create_default_context_template();
|
|
346
|
+
assert!(template.contains("# Project Context"));
|
|
347
|
+
assert!(template.contains("## Project Overview"));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
#[test]
|
|
351
|
+
fn test_load_and_merge_multiple_contexts() {
|
|
352
|
+
let temp_dir = tempdir().unwrap();
|
|
353
|
+
|
|
354
|
+
// Create multiple context files
|
|
355
|
+
fs::write(
|
|
356
|
+
temp_dir.path().join("GEMINI.md"),
|
|
357
|
+
"# Gemini Context\nGemini rules.",
|
|
358
|
+
)
|
|
359
|
+
.unwrap();
|
|
360
|
+
fs::write(
|
|
361
|
+
temp_dir.path().join(".claude.md"),
|
|
362
|
+
"# Claude Context\nClaude rules.",
|
|
363
|
+
)
|
|
364
|
+
.unwrap();
|
|
365
|
+
|
|
366
|
+
let result = load_and_merge_project_context(temp_dir.path()).unwrap();
|
|
367
|
+
assert!(result.is_some());
|
|
368
|
+
|
|
369
|
+
let merged = result.unwrap();
|
|
370
|
+
assert!(merged.contains("GEMINI.md"));
|
|
371
|
+
assert!(merged.contains("Gemini rules"));
|
|
372
|
+
assert!(merged.contains(".claude.md"));
|
|
373
|
+
assert!(merged.contains("Claude rules"));
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
#[test]
|
|
377
|
+
fn test_get_all_context_file_paths() {
|
|
378
|
+
let temp_dir = tempdir().unwrap();
|
|
379
|
+
|
|
380
|
+
// Create multiple context files
|
|
381
|
+
fs::write(temp_dir.path().join("GEMINI.md"), "test1").unwrap();
|
|
382
|
+
|
|
383
|
+
let zed_dir = temp_dir.path().join(".zed");
|
|
384
|
+
fs::create_dir(&zed_dir).unwrap();
|
|
385
|
+
fs::write(zed_dir.join("rules"), "test2").unwrap();
|
|
386
|
+
|
|
387
|
+
let paths = get_all_context_file_paths(temp_dir.path());
|
|
388
|
+
assert_eq!(paths.len(), 2);
|
|
389
|
+
}
|
|
390
|
+
}
|
package/src/utils/mod.rs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//! Utility modules for grok-cli
|
|
2
|
+
//!
|
|
3
|
+
//! This module contains various utility functions and helpers used throughout
|
|
4
|
+
//! the application, including network utilities, file handling, and other
|
|
5
|
+
//! common functionality.
|
|
6
|
+
|
|
7
|
+
pub mod auth;
|
|
8
|
+
pub mod chat_logger;
|
|
9
|
+
pub mod context;
|
|
10
|
+
pub mod network;
|
|
11
|
+
pub mod rate_limiter;
|
|
12
|
+
pub mod session;
|
|
13
|
+
pub mod shell_permissions;
|
|
14
|
+
pub mod telemetry;
|
|
15
|
+
|
|
16
|
+
// Re-export commonly used utilities
|