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.
Files changed (100) hide show
  1. package/.env.example +42 -0
  2. package/.github/workflows/ci.yml +30 -0
  3. package/.github/workflows/rust.yml +22 -0
  4. package/.grok/.env.example +85 -0
  5. package/.grok/COMPLETE_FIX_SUMMARY.md +466 -0
  6. package/.grok/ENV_CONFIG_GUIDE.md +173 -0
  7. package/.grok/QUICK_REFERENCE.md +180 -0
  8. package/.grok/README.md +104 -0
  9. package/.grok/TESTING_GUIDE.md +393 -0
  10. package/CHANGELOG.md +465 -0
  11. package/CODE_REVIEW_SUMMARY.md +414 -0
  12. package/COMPLETE_FIX_SUMMARY.md +415 -0
  13. package/CONFIGURATION.md +489 -0
  14. package/CONTEXT_FILES_GUIDE.md +419 -0
  15. package/CONTRIBUTING.md +55 -0
  16. package/CURSOR_POSITION_FIX.md +206 -0
  17. package/Cargo.toml +88 -0
  18. package/ERROR_HANDLING_REPORT.md +361 -0
  19. package/FINAL_FIX_SUMMARY.md +462 -0
  20. package/FIXES.md +37 -0
  21. package/FIXES_SUMMARY.md +87 -0
  22. package/GROK_API_MIGRATION_SUMMARY.md +111 -0
  23. package/LICENSE +22 -0
  24. package/MIGRATION_TO_GROK_API.md +223 -0
  25. package/README.md +504 -0
  26. package/REVIEW_COMPLETE.md +416 -0
  27. package/REVIEW_QUICK_REFERENCE.md +173 -0
  28. package/SECURITY.md +463 -0
  29. package/SECURITY_AUDIT.md +661 -0
  30. package/SETUP.md +287 -0
  31. package/TESTING_TOOLS.md +88 -0
  32. package/TESTING_TOOL_EXECUTION.md +239 -0
  33. package/TOOL_EXECUTION_FIX.md +491 -0
  34. package/VERIFICATION_CHECKLIST.md +419 -0
  35. package/docs/API.md +74 -0
  36. package/docs/CHAT_LOGGING.md +39 -0
  37. package/docs/CURSOR_FIX_DEMO.md +306 -0
  38. package/docs/ERROR_HANDLING_GUIDE.md +547 -0
  39. package/docs/FILE_OPERATIONS.md +449 -0
  40. package/docs/INTERACTIVE.md +401 -0
  41. package/docs/PROJECT_CREATION_GUIDE.md +570 -0
  42. package/docs/QUICKSTART.md +378 -0
  43. package/docs/QUICK_REFERENCE.md +691 -0
  44. package/docs/RELEASE_NOTES_0.1.2.md +240 -0
  45. package/docs/TOOLS.md +459 -0
  46. package/docs/TOOLS_QUICK_REFERENCE.md +210 -0
  47. package/docs/ZED_INTEGRATION.md +371 -0
  48. package/docs/extensions.md +464 -0
  49. package/docs/settings.md +293 -0
  50. package/examples/extensions/logging-hook/README.md +91 -0
  51. package/examples/extensions/logging-hook/extension.json +22 -0
  52. package/package.json +30 -0
  53. package/scripts/test_acp.py +252 -0
  54. package/scripts/test_acp.sh +143 -0
  55. package/scripts/test_acp_simple.sh +72 -0
  56. package/src/acp/mod.rs +741 -0
  57. package/src/acp/protocol.rs +323 -0
  58. package/src/acp/security.rs +298 -0
  59. package/src/acp/tools.rs +697 -0
  60. package/src/bin/banner_demo.rs +216 -0
  61. package/src/bin/docgen.rs +18 -0
  62. package/src/bin/installer.rs +217 -0
  63. package/src/cli/app.rs +310 -0
  64. package/src/cli/commands/acp.rs +721 -0
  65. package/src/cli/commands/chat.rs +485 -0
  66. package/src/cli/commands/code.rs +513 -0
  67. package/src/cli/commands/config.rs +394 -0
  68. package/src/cli/commands/health.rs +442 -0
  69. package/src/cli/commands/history.rs +421 -0
  70. package/src/cli/commands/mod.rs +14 -0
  71. package/src/cli/commands/settings.rs +1384 -0
  72. package/src/cli/mod.rs +166 -0
  73. package/src/config/mod.rs +2212 -0
  74. package/src/display/ascii_art.rs +139 -0
  75. package/src/display/banner.rs +289 -0
  76. package/src/display/components/input.rs +323 -0
  77. package/src/display/components/mod.rs +2 -0
  78. package/src/display/components/settings_list.rs +306 -0
  79. package/src/display/interactive.rs +1255 -0
  80. package/src/display/mod.rs +62 -0
  81. package/src/display/terminal.rs +42 -0
  82. package/src/display/tips.rs +316 -0
  83. package/src/grok_client_ext.rs +177 -0
  84. package/src/hooks/loader.rs +407 -0
  85. package/src/hooks/mod.rs +158 -0
  86. package/src/lib.rs +174 -0
  87. package/src/main.rs +65 -0
  88. package/src/mcp/client.rs +195 -0
  89. package/src/mcp/config.rs +20 -0
  90. package/src/mcp/mod.rs +6 -0
  91. package/src/mcp/protocol.rs +67 -0
  92. package/src/utils/auth.rs +41 -0
  93. package/src/utils/chat_logger.rs +568 -0
  94. package/src/utils/context.rs +390 -0
  95. package/src/utils/mod.rs +16 -0
  96. package/src/utils/network.rs +320 -0
  97. package/src/utils/rate_limiter.rs +166 -0
  98. package/src/utils/session.rs +73 -0
  99. package/src/utils/shell_permissions.rs +389 -0
  100. package/src/utils/telemetry.rs +41 -0
@@ -0,0 +1,421 @@
1
+ //! Chat history viewer command
2
+ //!
3
+ //! This module provides commands to view, search, and manage chat session logs.
4
+
5
+ use anyhow::{Context, Result};
6
+ use colored::*;
7
+ use std::path::PathBuf;
8
+
9
+ use crate::cli::{print_error, print_info, print_success};
10
+ use crate::utils::chat_logger::{ChatLogger, ChatLoggerConfig, ChatSession};
11
+
12
+ /// Handle history-related commands
13
+ pub async fn handle_history_action(action: crate::HistoryAction) -> Result<()> {
14
+ match action {
15
+ crate::HistoryAction::List => list_sessions().await,
16
+ crate::HistoryAction::View { session_id } => view_session(&session_id).await,
17
+ crate::HistoryAction::Search { query } => search_sessions(&query).await,
18
+ crate::HistoryAction::Clear { confirm } => clear_history(confirm).await,
19
+ }
20
+ }
21
+
22
+ /// List all available chat sessions
23
+ async fn list_sessions() -> Result<()> {
24
+ let config = get_logger_config();
25
+ let logger =
26
+ ChatLogger::new(config).context("Failed to initialize chat logger for listing sessions")?;
27
+
28
+ let sessions = logger
29
+ .list_sessions()
30
+ .context("Failed to list chat sessions")?;
31
+
32
+ if sessions.is_empty() {
33
+ print_info("No chat sessions found.");
34
+ return Ok(());
35
+ }
36
+
37
+ println!("\n{}", "=".repeat(80).bright_cyan());
38
+ println!(
39
+ "{}",
40
+ format!(" CHAT SESSIONS ({} total)", sessions.len())
41
+ .bright_cyan()
42
+ .bold()
43
+ );
44
+ println!("{}\n", "=".repeat(80).bright_cyan());
45
+
46
+ for (i, session_id) in sessions.iter().enumerate() {
47
+ // Try to load session metadata
48
+ match logger.load_session(session_id) {
49
+ Ok(session) => {
50
+ let start_time = session.start_time.format("%Y-%m-%d %H:%M:%S UTC");
51
+ let msg_count = session.messages.len();
52
+ let status = if session.end_time.is_some() {
53
+ "Completed".green()
54
+ } else {
55
+ "Active".yellow()
56
+ };
57
+
58
+ println!(
59
+ "{}. {} {}",
60
+ format!("{:3}", i + 1).bright_black(),
61
+ session_id.bright_white().bold(),
62
+ status
63
+ );
64
+ println!(
65
+ " {} {} | {} messages",
66
+ "Started:".bright_black(),
67
+ start_time.to_string().bright_white(),
68
+ msg_count.to_string().bright_cyan()
69
+ );
70
+
71
+ if let Some(end_time) = session.end_time {
72
+ let duration = end_time
73
+ .signed_duration_since(session.start_time)
74
+ .num_seconds();
75
+ println!(
76
+ " {} {}",
77
+ "Duration:".bright_black(),
78
+ format!("{} seconds", duration).bright_white()
79
+ );
80
+ }
81
+
82
+ // Show first user message preview if available
83
+ if let Some(first_msg) = session.messages.iter().find(|m| m.role == "user") {
84
+ let preview = first_msg
85
+ .content
86
+ .lines()
87
+ .next()
88
+ .unwrap_or("")
89
+ .chars()
90
+ .take(60)
91
+ .collect::<String>();
92
+ if !preview.is_empty() {
93
+ println!(
94
+ " {} {}{}",
95
+ "Preview:".bright_black(),
96
+ preview.bright_white(),
97
+ if first_msg.content.len() > 60 {
98
+ "..."
99
+ } else {
100
+ ""
101
+ }
102
+ );
103
+ }
104
+ }
105
+ println!();
106
+ }
107
+ Err(e) => {
108
+ println!(
109
+ "{}. {} {}",
110
+ format!("{:3}", i + 1).bright_black(),
111
+ session_id.bright_white().bold(),
112
+ "(error loading)".red()
113
+ );
114
+ println!(" Error: {}", e.to_string().red());
115
+ println!();
116
+ }
117
+ }
118
+ }
119
+
120
+ println!("{}", "=".repeat(80).bright_cyan());
121
+ println!(
122
+ "\n{} {}",
123
+ "Tip:".bright_cyan().bold(),
124
+ "View a session with: grok history view <session-id>".bright_white()
125
+ );
126
+
127
+ Ok(())
128
+ }
129
+
130
+ /// View a specific chat session
131
+ async fn view_session(session_id: &str) -> Result<()> {
132
+ let config = get_logger_config();
133
+ let logger =
134
+ ChatLogger::new(config).context("Failed to initialize chat logger for viewing session")?;
135
+
136
+ let session = logger
137
+ .load_session(session_id)
138
+ .with_context(|| format!("Failed to load session: {}", session_id))?;
139
+
140
+ display_session(&session)?;
141
+
142
+ Ok(())
143
+ }
144
+
145
+ /// Display a chat session in formatted output
146
+ fn display_session(session: &ChatSession) -> Result<()> {
147
+ println!("\n{}", "=".repeat(80).bright_cyan());
148
+ println!(
149
+ "{}",
150
+ format!(" CHAT SESSION: {}", session.session_id)
151
+ .bright_cyan()
152
+ .bold()
153
+ );
154
+ println!("{}\n", "=".repeat(80).bright_cyan());
155
+
156
+ // Display metadata
157
+ println!(
158
+ "{} {}",
159
+ "Start Time:".bright_white().bold(),
160
+ session.start_time.format("%Y-%m-%d %H:%M:%S UTC")
161
+ );
162
+ if let Some(end_time) = session.end_time {
163
+ println!(
164
+ "{} {}",
165
+ "End Time: ".bright_white().bold(),
166
+ end_time.format("%Y-%m-%d %H:%M:%S UTC")
167
+ );
168
+ let duration = end_time
169
+ .signed_duration_since(session.start_time)
170
+ .num_seconds();
171
+ println!(
172
+ "{} {} seconds",
173
+ "Duration: ".bright_white().bold(),
174
+ duration
175
+ );
176
+ } else {
177
+ println!(
178
+ "{} {}",
179
+ "Status: ".bright_white().bold(),
180
+ "Active".yellow()
181
+ );
182
+ }
183
+ println!(
184
+ "{} {}\n",
185
+ "Messages: ".bright_white().bold(),
186
+ session.messages.len()
187
+ );
188
+
189
+ println!("{}", "-".repeat(80).bright_black());
190
+ println!();
191
+
192
+ // Display messages
193
+ for (i, msg) in session.messages.iter().enumerate() {
194
+ let role_display = match msg.role.as_str() {
195
+ "user" => "USER".bright_green().bold(),
196
+ "assistant" => "ASSISTANT".bright_blue().bold(),
197
+ "system" => "SYSTEM".bright_yellow().bold(),
198
+ _ => msg.role.bright_white().bold(),
199
+ };
200
+
201
+ let time = msg.timestamp.format("%H:%M:%S");
202
+
203
+ println!(
204
+ "{} {} {}",
205
+ format!("[{}]", i + 1).bright_black(),
206
+ role_display,
207
+ format!("({})", time).bright_black()
208
+ );
209
+ println!("{}", "-".repeat(80).bright_black());
210
+
211
+ // Print message content with proper wrapping
212
+ for line in msg.content.lines() {
213
+ println!("{}", line);
214
+ }
215
+
216
+ // Display metadata if present
217
+ if let Some(metadata) = &msg.metadata {
218
+ println!();
219
+ println!("{}", "Metadata:".bright_black());
220
+ match serde_json::to_string_pretty(metadata) {
221
+ Ok(json) => {
222
+ for line in json.lines() {
223
+ println!(" {}", line.bright_black());
224
+ }
225
+ }
226
+ Err(_) => println!(" {}", "(error displaying metadata)".red()),
227
+ }
228
+ }
229
+
230
+ println!();
231
+ }
232
+
233
+ println!("{}", "=".repeat(80).bright_cyan());
234
+ println!(
235
+ "{} {} messages in session {}\n",
236
+ "Total:".bright_cyan().bold(),
237
+ session.messages.len(),
238
+ session.session_id.bright_white()
239
+ );
240
+
241
+ Ok(())
242
+ }
243
+
244
+ /// Search through chat sessions for a query string
245
+ async fn search_sessions(query: &str) -> Result<()> {
246
+ let config = get_logger_config();
247
+ let logger = ChatLogger::new(config)
248
+ .context("Failed to initialize chat logger for searching sessions")?;
249
+
250
+ let sessions = logger
251
+ .list_sessions()
252
+ .context("Failed to list chat sessions")?;
253
+
254
+ if sessions.is_empty() {
255
+ print_info("No chat sessions found.");
256
+ return Ok(());
257
+ }
258
+
259
+ let query_lower = query.to_lowercase();
260
+ let mut matches = Vec::new();
261
+
262
+ for session_id in sessions {
263
+ if let Ok(session) = logger.load_session(&session_id) {
264
+ // Search in messages
265
+ for (msg_idx, msg) in session.messages.iter().enumerate() {
266
+ if msg.content.to_lowercase().contains(&query_lower) {
267
+ matches.push((session.clone(), msg_idx, msg.clone()));
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ if matches.is_empty() {
274
+ print_info(&format!("No matches found for query: '{}'", query));
275
+ return Ok(());
276
+ }
277
+
278
+ println!("\n{}", "=".repeat(80).bright_cyan());
279
+ println!(
280
+ "{}",
281
+ format!(
282
+ " SEARCH RESULTS: {} matches for '{}'",
283
+ matches.len(),
284
+ query
285
+ )
286
+ .bright_cyan()
287
+ .bold()
288
+ );
289
+ println!("{}\n", "=".repeat(80).bright_cyan());
290
+
291
+ for (i, (session, msg_idx, msg)) in matches.iter().enumerate() {
292
+ println!(
293
+ "{}. {} {}",
294
+ format!("{:3}", i + 1).bright_black(),
295
+ "Session:".bright_white().bold(),
296
+ session.session_id.bright_cyan()
297
+ );
298
+ println!(
299
+ " {} Message {} by {}",
300
+ "Match:".bright_black(),
301
+ msg_idx + 1,
302
+ msg.role.bright_white()
303
+ );
304
+
305
+ // Show context around the match
306
+ let content_lines: Vec<&str> = msg.content.lines().collect();
307
+ let matching_lines: Vec<(usize, &str)> = content_lines
308
+ .iter()
309
+ .enumerate()
310
+ .filter(|(_, line)| line.to_lowercase().contains(&query_lower))
311
+ .map(|(idx, line)| (idx, *line))
312
+ .collect();
313
+
314
+ for (line_idx, line) in matching_lines.iter().take(3) {
315
+ // Show up to 3 matching lines
316
+ // Highlight the query in the line
317
+ let highlighted = highlight_query(line, query);
318
+ println!(
319
+ " {}: {}",
320
+ format!("L{}", line_idx + 1).bright_black(),
321
+ highlighted
322
+ );
323
+ }
324
+
325
+ println!();
326
+ }
327
+
328
+ println!("{}", "=".repeat(80).bright_cyan());
329
+ println!(
330
+ "\n{} {}",
331
+ "Tip:".bright_cyan().bold(),
332
+ "View full session with: grok history view <session-id>".bright_white()
333
+ );
334
+
335
+ Ok(())
336
+ }
337
+
338
+ /// Highlight query string in text
339
+ fn highlight_query(text: &str, query: &str) -> String {
340
+ let query_lower = query.to_lowercase();
341
+ let text_lower = text.to_lowercase();
342
+
343
+ if let Some(pos) = text_lower.find(&query_lower) {
344
+ let before = &text[..pos];
345
+ let matched = &text[pos..pos + query.len()];
346
+ let after = &text[pos + query.len()..];
347
+
348
+ format!("{}{}{}", before, matched.bright_yellow().bold(), after)
349
+ } else {
350
+ text.to_string()
351
+ }
352
+ }
353
+
354
+ /// Clear chat history
355
+ async fn clear_history(confirm: bool) -> Result<()> {
356
+ if !confirm {
357
+ print_error("This will delete all chat session logs!");
358
+ println!(
359
+ "{} {}",
360
+ "To confirm, run:".bright_white(),
361
+ "grok history clear --confirm".bright_cyan()
362
+ );
363
+ return Ok(());
364
+ }
365
+
366
+ let config = get_logger_config();
367
+
368
+ if !config.log_dir.exists() {
369
+ print_info("No chat history to clear.");
370
+ return Ok(());
371
+ }
372
+
373
+ // Delete all log files
374
+ let entries = std::fs::read_dir(&config.log_dir).context("Failed to read log directory")?;
375
+
376
+ let mut deleted_count = 0;
377
+ for entry in entries.flatten() {
378
+ if entry.path().is_file() {
379
+ if let Err(e) = std::fs::remove_file(entry.path()) {
380
+ print_error(&format!(
381
+ "Failed to delete {}: {}",
382
+ entry.path().display(),
383
+ e
384
+ ));
385
+ } else {
386
+ deleted_count += 1;
387
+ }
388
+ }
389
+ }
390
+
391
+ print_success(&format!(
392
+ "Cleared chat history: {} files deleted",
393
+ deleted_count
394
+ ));
395
+
396
+ Ok(())
397
+ }
398
+
399
+ /// Get chat logger configuration from environment
400
+ fn get_logger_config() -> ChatLoggerConfig {
401
+ let log_dir = std::env::var("GROK_CHAT_LOG_DIR")
402
+ .ok()
403
+ .map(PathBuf::from)
404
+ .unwrap_or_else(|| {
405
+ dirs::home_dir()
406
+ .unwrap_or_else(|| PathBuf::from("."))
407
+ .join(".grok")
408
+ .join("logs")
409
+ .join("chat_sessions")
410
+ });
411
+
412
+ ChatLoggerConfig {
413
+ enabled: true,
414
+ log_dir,
415
+ json_format: true,
416
+ text_format: true,
417
+ max_file_size_mb: 10,
418
+ rotation_count: 5,
419
+ include_system: true,
420
+ }
421
+ }
@@ -0,0 +1,14 @@
1
+ //! Command handlers for grok-cli
2
+ //!
3
+ //! This module contains all the command handler implementations for the various
4
+ //! CLI commands supported by grok-cli.
5
+
6
+ pub mod acp;
7
+ pub mod chat;
8
+ pub mod code;
9
+ pub mod config;
10
+ pub mod health;
11
+ pub mod history;
12
+ pub mod settings;
13
+
14
+ // Re-export all command handlers