anveesa 0.4.3 → 0.4.4

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/Cargo.lock CHANGED
@@ -60,7 +60,7 @@ dependencies = [
60
60
 
61
61
  [[package]]
62
62
  name = "anveesa"
63
- version = "0.4.3"
63
+ version = "0.4.4"
64
64
  dependencies = [
65
65
  "anyhow",
66
66
  "base64",
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "anveesa"
3
- version = "0.4.3"
3
+ version = "0.4.4"
4
4
  edition = "2024"
5
5
  default-run = "anveesa"
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anveesa",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "A terminal CLI that wraps AI providers (OpenAI-compatible APIs and local CLIs) into a single unified command",
5
5
  "main": "bin/anveesa.js",
6
6
  "bin": {
package/src/tools.rs CHANGED
@@ -40,6 +40,12 @@ These actions can require the user to approve them, so explain what you intend t
40
40
  " For any multi-step task, start by calling set_plan with a list of the steps you will take. \
41
41
  After each step completes, call complete_task with the zero-based index of that step. \
42
42
  Do not describe your plan in prose — use set_plan instead.",
43
+ );
44
+ text.push_str(
45
+ " CRITICAL — avoid redundant tool calls: All previous tool results are in your context. \
46
+ Do NOT re-read or re-list files and directories you have already inspected in this conversation. \
47
+ Before calling read_file or list_dir, check your conversation history first. \
48
+ Only call tools for information you do not yet have.",
43
49
  );
44
50
  text.push_str(
45
51
  " If a tool call fails or a command times out, do NOT retry it automatically. \
package/src/tui.rs CHANGED
@@ -86,7 +86,9 @@ pub struct App {
86
86
  pending_prompt: String,
87
87
  streaming_started_at: Option<Instant>,
88
88
  tool_started_at: Option<Instant>,
89
- unread_count: usize, // messages added while scrolled away
89
+ unread_count: usize,
90
+ // files/dirs already read this session — injected into workspace context each turn
91
+ seen_paths: std::collections::BTreeSet<String>,
90
92
 
91
93
  // input
92
94
  input: String,
@@ -173,6 +175,7 @@ impl App {
173
175
  streaming_started_at: None,
174
176
  tool_started_at: None,
175
177
  unread_count: 0,
178
+ seen_paths: std::collections::BTreeSet::new(),
176
179
 
177
180
  input: String::new(),
178
181
  input_cursor: 0,
@@ -538,6 +541,7 @@ fn handle_slash_command(app: &mut App, text: &str) -> bool {
538
541
  app.accumulated_response.clear();
539
542
  app.usage = Usage::default();
540
543
  app.pending_image = None;
544
+ app.seen_paths.clear();
541
545
  app.input.clear();
542
546
  app.input_cursor = 0;
543
547
  if let Some(path) = &app.session_path {
@@ -690,7 +694,11 @@ async fn submit_prompt(app: &mut App, text: String) -> Result<()> {
690
694
  let config = app.config.clone();
691
695
  let options = app.options.clone();
692
696
  let history = app.history.clone();
693
- let workspace_context = app.workspace_context.clone();
697
+ // Augment workspace context with already-seen paths so the model doesn't re-scan them
698
+ let workspace_context = augmented_workspace_context(
699
+ app.workspace_context.as_deref(),
700
+ &app.seen_paths,
701
+ );
694
702
  let policy = app.policy;
695
703
  let mcp_arc = app.mcp.clone();
696
704
  let tui_tx = app.stream_tx.clone();
@@ -780,6 +788,8 @@ async fn handle_stream_event(app: &mut App, ev: TuiEvent) {
780
788
  }
781
789
  TuiEvent::ToolDone { summary, ok } => {
782
790
  let elapsed_ms = app.tool_started_at.take().map(|t| t.elapsed().as_millis());
791
+ // Record the inspected path so we can tell the model what it already knows
792
+ record_seen_path(&mut app.seen_paths, &summary);
783
793
  app.pending_tool = Some(PendingTool { summary });
784
794
  commit_pending_tool_timed(app, ok, elapsed_ms);
785
795
  app.tool_status = "Thinking".to_string();
@@ -819,6 +829,39 @@ async fn handle_stream_event(app: &mut App, ev: TuiEvent) {
819
829
  }
820
830
  }
821
831
 
832
+ /// Extract a path from a tool call summary string and record it as "already seen".
833
+ fn record_seen_path(seen: &mut std::collections::BTreeSet<String>, summary: &str) {
834
+ // Summaries look like "read file src/foo.ts" or "list directory src/bar"
835
+ // or "git status", "web search `...`" — only record file/dir paths
836
+ for prefix in &["read file ", "list directory "] {
837
+ if let Some(path) = summary.strip_prefix(prefix) {
838
+ let path = path.trim().to_string();
839
+ if !path.is_empty() {
840
+ seen.insert(path);
841
+ }
842
+ return;
843
+ }
844
+ }
845
+ }
846
+
847
+ /// Build an augmented workspace context that includes already-seen paths.
848
+ fn augmented_workspace_context(
849
+ base: Option<&str>,
850
+ seen: &std::collections::BTreeSet<String>,
851
+ ) -> Option<String> {
852
+ if seen.is_empty() {
853
+ return base.map(str::to_string);
854
+ }
855
+ let seen_note = format!(
856
+ "\nAlready inspected this session (do NOT re-read these):\n{}",
857
+ seen.iter().map(|p| format!(" - {p}")).collect::<Vec<_>>().join("\n")
858
+ );
859
+ Some(match base {
860
+ Some(b) => format!("{b}{seen_note}"),
861
+ None => seen_note,
862
+ })
863
+ }
864
+
822
865
  /// Flush streaming_buf to messages and accumulated_response.
823
866
  fn flush_streaming_buf(app: &mut App) {
824
867
  if !app.streaming_buf.is_empty() {