skydeckai-code 0.1.39__py3-none-any.whl → 0.1.40__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skydeckai-code
3
- Version: 0.1.39
3
+ Version: 0.1.40
4
4
  Summary: This MCP server provides a comprehensive set of tools for AI-driven Development workflows including file operations, code analysis, multi-language execution, web content fetching with HTML-to-markdown conversion, multi-engine web search, code content searching, and system information retrieval.
5
5
  Project-URL: Homepage, https://github.com/skydeckai/skydeckai-code
6
6
  Project-URL: Repository, https://github.com/skydeckai/skydeckai-code
@@ -43,13 +43,13 @@ An MCP server that provides a comprehensive set of tools for AI-driven developme
43
43
 
44
44
  This mcp server was formerly known as `mcp-server-aidd`. It was renamed to `skydeckai-code` to credit the team at [SkyDeck.ai](https://skydeck.ai) with creating this application along with [East Agile](https://eastagile.com). But more importantly we realized that the term AI Driven Development (AIDD) was just not catching on. People did not understand at a glance what it was about. And nor did LLMs. "Code" was far more intuitive. And linguistically intuitive is important in the world of agentic AI.
45
45
 
46
- <a href="https://glama.ai/mcp/servers/mpixtij6se"><img width="380" height="200" src="https://glama.ai/mcp/servers/mpixtij6se/badge" alt="AiDD Server MCP server" /></a>
46
+ [![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/fe7a40fd-30c1-4767-84f9-d33bf997497e)
47
47
 
48
48
  ## Installation
49
49
 
50
50
  ```bash
51
- # Using pip
52
- pip install skydeckai-code
51
+ # Using uvx
52
+ uvx skydeckai-code
53
53
  ```
54
54
 
55
55
  ## Claude Desktop Setup
@@ -69,9 +69,9 @@ Add to your `claude_desktop_config.json`:
69
69
 
70
70
  ## SkyDeck AI Helper App
71
71
 
72
- If you're using SkyDeck AI Helper app, you can search for "SkyDeckAI Code" and install it.
72
+ If you're using MseeP AI Helper app, you can search for "SkyDeckAI Code" and install it.
73
73
 
74
- ![SkyDeck AI Helper App](/screenshots/skydeck_ai_helper.png)
74
+ ![MseeP AI Helper App](/screenshots/mseep_ai_helper.png)
75
75
 
76
76
  ## Key Features
77
77
 
@@ -131,34 +131,6 @@ If you're using SkyDeck AI Helper app, you can search for "SkyDeckAI Code" and i
131
131
  | delete_file | path: string | Success confirmation |
132
132
  | get_file_info | path: string | File metadata (size, timestamps, permissions) |
133
133
 
134
- **CLI Usage:**
135
-
136
- ```bash
137
- # Read entire file
138
- skydeckai-code-cli --tool read_file --args '{"files": [{"path": "src/main.py"}]}'
139
-
140
- # Read 10 lines starting from line 20
141
- skydeckai-code-cli --tool read_file --args '{"files": [{"path": "src/main.py", "offset": 20, "limit": 10}]}'
142
-
143
- # Read from line 50 to the end of the file
144
- skydeckai-code-cli --tool read_file --args '{"files": [{"path": "src/main.py", "offset": 50}]}'
145
-
146
- # Read multiple files with different line ranges
147
- skydeckai-code-cli --tool read_file --args '{"files": [
148
- {"path": "src/main.py", "offset": 1, "limit": 10},
149
- {"path": "README.md"}
150
- ]}'
151
-
152
- # Write file
153
- skydeckai-code-cli --tool write_file --args '{"path": "output.txt", "content": "Hello World"}'
154
-
155
- # Copy file or directory
156
- skydeckai-code-cli --tool copy_file --args '{"source": "config.json", "destination": "config.backup.json"}'
157
-
158
- # Get file info
159
- skydeckai-code-cli --tool get_file_info --args '{"path": "src/main.py"}'
160
- ```
161
-
162
134
  ### Complex File Operations
163
135
 
164
136
  #### edit_file
@@ -208,16 +180,6 @@ Generates complete directory structure:
208
180
 
209
181
  Returns: JSON tree structure of directory contents.
210
182
 
211
- **CLI Usage:**
212
-
213
- ```bash
214
- # List directory
215
- skydeckai-code-cli --tool list_directory --args '{"path": "."}'
216
-
217
- # Search for Python files
218
- skydeckai-code-cli --tool search_files --args '{"pattern": ".py", "path": "src"}'
219
- ```
220
-
221
183
  ### Code Analysis
222
184
 
223
185
  #### codebase_mapper
@@ -252,19 +214,6 @@ Supported Languages:
252
214
  - C# (.cs)
253
215
  - Kotlin (.kt, .kts)
254
216
 
255
- **CLI Usage:**
256
-
257
- ```bash
258
- # Map the entire codebase structure
259
- skydeckai-code-cli --tool codebase_mapper --args '{"path": "."}'
260
-
261
- # Map only the source directory
262
- skydeckai-code-cli --tool codebase_mapper --args '{"path": "src"}'
263
-
264
- # Map a specific component or module
265
- skydeckai-code-cli --tool codebase_mapper --args '{"path": "src/components"}'
266
- ```
267
-
268
217
  #### search_code
269
218
 
270
219
  Fast content search tool using regular expressions:
@@ -295,29 +244,6 @@ Matching lines grouped by file with line numbers, sorted by file modification ti
295
244
 
296
245
  This tool uses ripgrep when available for optimal performance, with a Python fallback implementation. It's ideal for finding specific code patterns like function declarations, imports, variable usages, or error handling.
297
246
 
298
- **CLI Usage:**
299
-
300
- ```bash
301
- # Find function and class declarations in JavaScript files
302
- skydeckai-code-cli --tool search_code --args '{
303
- "patterns": ["function\\s+\\w+", "class\\s+\\w+"],
304
- "include": "*.js"
305
- }'
306
-
307
- # Find all console.log statements with errors or warnings
308
- skydeckai-code-cli --tool search_code --args '{
309
- "patterns": ["console\\.log.*[eE]rror", "console\\.log.*[wW]arning"],
310
- "path": "src"
311
- }'
312
-
313
- # Find import and export statements in TypeScript files
314
- skydeckai-code-cli --tool search_code --args '{
315
- "patterns": ["import.*from", "export.*"],
316
- "include": "*.{ts,tsx}",
317
- "exclude": "node_modules/**"
318
- }'
319
- ```
320
-
321
247
  ### System Information
322
248
 
323
249
  | Tool | Parameters | Returns |
@@ -348,13 +274,6 @@ Returns:
348
274
 
349
275
  Provides essential system information in a clean, readable format.
350
276
 
351
- **CLI Usage:**
352
-
353
- ```bash
354
- # Get system information
355
- skydeckai-code-cli --tool get_system_info
356
- ```
357
-
358
277
  ### Screen Context and Image Tools
359
278
 
360
279
  #### get_active_apps
@@ -518,28 +437,6 @@ Response content as text with HTTP status code and size information. For binary
518
437
 
519
438
  This tool can be used to access web APIs, fetch documentation, or download content from the web while respecting size limits (10MB max) and security constraints.
520
439
 
521
- **CLI Usage:**
522
-
523
- ```bash
524
- # Fetch JSON from an API
525
- skydeckai-code-cli --tool web_fetch --args '{
526
- "url": "https://api.github.com/users/octocat",
527
- "headers": {"Accept": "application/json"}
528
- }'
529
-
530
- # Download content to a file
531
- skydeckai-code-cli --tool web_fetch --args '{
532
- "url": "https://github.com/github/github-mcp-server/blob/main/README.md",
533
- "save_to_file": "downloads/readme.md"
534
- }'
535
-
536
- # Fetch a webpage and convert to markdown for better readability
537
- skydeckai-code-cli --tool web_fetch --args '{
538
- "url": "https://example.com",
539
- "convert_html_to_markdown": true
540
- }'
541
- ```
542
-
543
440
  #### web_search
544
441
 
545
442
  Performs a robust web search using multiple search engines and returns concise, relevant results.
@@ -566,27 +463,6 @@ A list of search results formatted in markdown, including titles, URLs, and snip
566
463
 
567
464
  This tool uses a multi-engine approach that tries different search engines with various parsing strategies to ensure reliable results. You can specify a preferred engine, but some engines may block automated access, in which case the tool will fall back to alternative engines when "auto" is selected.
568
465
 
569
- **CLI Usage:**
570
-
571
- ```bash
572
- # Search with default settings (auto engine selection)
573
- skydeckai-code-cli --tool web_search --args '{
574
- "query": "latest python release features"
575
- }'
576
-
577
- # Try DuckDuckGo if you want alternative results
578
- skydeckai-code-cli --tool web_search --args '{
579
- "query": "machine learning frameworks comparison",
580
- "search_engine": "duckduckgo"
581
- }'
582
-
583
- # Use Bing for reliable results
584
- skydeckai-code-cli --tool web_search --args '{
585
- "query": "best programming practices 2023",
586
- "search_engine": "bing"
587
- }'
588
- ```
589
-
590
466
  ### Utility Tools
591
467
 
592
468
  #### batch_tools
@@ -640,47 +516,6 @@ This tool provides efficient execution of multiple operations in a single reques
640
516
  1. Use paths relative to the current working directory (e.g., "project/src" rather than just "src"), or
641
517
  2. Include an explicit tool invocation to change directories using `update_allowed_directory`
642
518
 
643
- **CLI Usage:**
644
-
645
- ```bash
646
- # Setup a new project with multiple steps in sequential order (using proper paths)
647
- skydeckai-code-cli --tool batch_tools --args '{
648
- "description": "Setup new project",
649
- "sequential": true,
650
- "invocations": [
651
- {"tool": "create_directory", "arguments": {"path": "project"}},
652
- {"tool": "create_directory", "arguments": {"path": "project/src"}},
653
- {"tool": "write_file", "arguments": {"path": "project/README.md", "content": "# Project\n\nA new project."}}
654
- ]
655
- }'
656
-
657
- # Create nested structure using relative paths (without changing directory)
658
- skydeckai-code-cli --tool batch_tools --args '{
659
- "description": "Create project structure",
660
- "sequential": true,
661
- "invocations": [
662
- {"tool": "create_directory", "arguments": {"path": "project/src"}},
663
- {"tool": "create_directory", "arguments": {"path": "project/docs"}},
664
- {"tool": "write_file", "arguments": {"path": "project/README.md", "content": "# Project"}}
665
- ]
666
- }'
667
-
668
- # Gather system information and take a screenshot (tasks can run in parallel)
669
- skydeckai-code-cli --tool batch_tools --args '{
670
- "description": "System diagnostics",
671
- "sequential": false,
672
- "invocations": [
673
- {"tool": "get_system_info", "arguments": {}},
674
- {"tool": "capture_screenshot", "arguments": {
675
- "output_path": "diagnostics/screen.png",
676
- "capture_mode": {
677
- "type": "full"
678
- }
679
- }}
680
- ]
681
- }'
682
- ```
683
-
684
519
  #### think
685
520
 
686
521
  A tool for complex reasoning and brainstorming without making changes to the repository.
@@ -701,20 +536,6 @@ Your thoughts formatted as markdown, with a note indicating this was a thinking
701
536
 
702
537
  This tool is useful for thinking through complex problems, brainstorming solutions, or laying out implementation plans without making any actual changes. It's a great way to document your reasoning process, evaluate different approaches, or plan out a multi-step strategy before taking action.
703
538
 
704
- **CLI Usage:**
705
-
706
- ```bash
707
- # Analyze a bug and plan a fix
708
- skydeckai-code-cli --tool think --args '{
709
- "thought": "# Bug Analysis\n\n## Observed Behavior\nThe login endpoint returns a 500 error when email contains Unicode characters.\n\n## Root Cause\nThe database adapter is not properly encoding Unicode strings before constructing the SQL query.\n\n## Potential Fixes\n1. Update the database adapter to use parameterized queries\n2. Add input validation to reject Unicode in emails\n3. Encode email input manually before database operations\n\nFix #1 is the best approach as it solves the core issue and improves security."
710
- }'
711
-
712
- # Evaluate design alternatives
713
- skydeckai-code-cli --tool think --args '{
714
- "thought": "# API Design Options\n\n## REST vs GraphQL\nFor this use case, GraphQL would provide more flexible data fetching but adds complexity. REST is simpler and sufficient for our current needs.\n\n## Authentication Methods\nJWT-based authentication offers stateless operation and better scalability compared to session-based auth.\n\nRecommendation: Use REST with JWT authentication for the initial implementation."
715
- }'
716
- ```
717
-
718
539
  ### Code Execution
719
540
 
720
541
  #### execute_code
@@ -745,34 +566,6 @@ Executes code in various programming languages with safety measures and restrict
745
566
  | code | string | Yes | Code to execute |
746
567
  | timeout | integer | No | Maximum execution time (default: 5s) |
747
568
 
748
- **CLI Usage:**
749
-
750
- ```bash
751
- # Python example
752
- skydeckai-code-cli --tool execute_code --args '{
753
- "language": "python",
754
- "code": "print(sum(range(10)))"
755
- }'
756
-
757
- # JavaScript example
758
- skydeckai-code-cli --tool execute_code --args '{
759
- "language": "javascript",
760
- "code": "console.log(Array.from({length: 5}, (_, i) => i*2))"
761
- }'
762
-
763
- # Ruby example
764
- skydeckai-code-cli --tool execute_code --args '{
765
- "language": "ruby",
766
- "code": "puts (1..5).reduce(:+)"
767
- }'
768
-
769
- # Go example
770
- skydeckai-code-cli --tool execute_code --args '{
771
- "language": "go",
772
- "code": "fmt.Println(\"Hello, Go!\")"
773
- }'
774
- ```
775
-
776
569
  **Requirements:**
777
570
 
778
571
  - Respective language runtimes must be installed
@@ -805,25 +598,6 @@ Executes shell scripts (bash/sh) with safety measures and restrictions.
805
598
  | script | string | Yes | Shell script to execute |
806
599
  | timeout | integer | No | Maximum execution time (default: 300s, max: 600s) |
807
600
 
808
- **CLI Usage:**
809
-
810
- ```bash
811
- # List directory contents with details
812
- skydeckai-code-cli --tool execute_shell_script --args '{
813
- "script": "ls -la"
814
- }'
815
-
816
- # Find all Python files recursively
817
- skydeckai-code-cli --tool execute_shell_script --args '{
818
- "script": "find . -name \"*.py\" -type f"
819
- }'
820
-
821
- # Complex script with multiple commands
822
- skydeckai-code-cli --tool execute_shell_script --args '{
823
- "script": "echo \"System Info:\" && uname -a && echo \"\nDisk Usage:\" && df -h"
824
- }'
825
- ```
826
-
827
601
  **Features:**
828
602
 
829
603
  - Uses /bin/sh for maximum compatibility across systems
@@ -850,20 +624,6 @@ Configuration file: `~/.skydeckai_code/config.json`
850
624
  }
851
625
  ```
852
626
 
853
- ## CLI Usage
854
-
855
- Basic command structure:
856
-
857
- ```bash
858
- skydeckai-code-cli --tool <tool_name> --args '<json_arguments>'
859
-
860
- # List available tools
861
- skydeckai-code-cli --list-tools
862
-
863
- # Enable debug output
864
- skydeckai-code-cli --debug --tool <tool_name> --args '<json_arguments>'
865
- ```
866
-
867
627
  ## Debugging
868
628
 
869
629
  Use MCP Inspector for debugging:
@@ -8,7 +8,7 @@ src/aidd/tools/code_analysis.py,sha256=fDpm2o_If5PsngXzHN2-ezSkPVT0ZxivLuzmHrOAm
8
8
  src/aidd/tools/code_execution.py,sha256=7HKstQ-LTjGEUn87LhowOJbd4Pq_zG0xkO-K0JJ-EFs,15513
9
9
  src/aidd/tools/code_tools.py,sha256=rJx_CMq0mB7aBJ6YcNB_6geFnjHU4OaGcXyuu909xhM,16010
10
10
  src/aidd/tools/directory_tools.py,sha256=GMG4-9iO5RfTkbhlWaW40GPKa1qujMPTN32pwxjUU4E,18052
11
- src/aidd/tools/file_tools.py,sha256=GYzP6WxGbV1V42FlWNSuGIyiCtRp09kwF6lOZrFtq_U,43112
11
+ src/aidd/tools/file_tools.py,sha256=8Z38Tva8Qe0Fd2d6vU-DW-weHyExfUrIiRsxqrBlw6A,46720
12
12
  src/aidd/tools/get_active_apps_tool.py,sha256=BjLF7iXSDgyAmm_gfFgAul2Gn3iX-CNVYHM7Sh4jTAI,19427
13
13
  src/aidd/tools/get_available_windows_tool.py,sha256=OVIYhItTn9u_DftOr3vPCT-R0DOFvMEEJXA6tD6gqWQ,15952
14
14
  src/aidd/tools/image_tools.py,sha256=wT3EcJAfZWcM0IsXdDfbTNjgFhKZM9nu2wHN6Mk_TTQ,5970
@@ -18,8 +18,8 @@ src/aidd/tools/screenshot_tool.py,sha256=NMO5B4UG8qfMEOMRd2YoOjtwz_oQ2y1UAGU22jV
18
18
  src/aidd/tools/state.py,sha256=RWSw0Jfsui8FqC0xsI7Ik07tAg35hRwLHa5xGBVbiI4,1493
19
19
  src/aidd/tools/system_tools.py,sha256=XgdIgKeqePZx5pj59zH7Jhs2Abn55XUf0tvKbKMVtPo,7400
20
20
  src/aidd/tools/web_tools.py,sha256=gdsj2DEVYb_oYChItK5I1ugt2w25U7IAa5kEw9q6MVg,35534
21
- skydeckai_code-0.1.39.dist-info/METADATA,sha256=AwWLNrB3UMqXpsqiWYw6-ZSN8h6LRiZr_2PNt0yqfd4,31952
22
- skydeckai_code-0.1.39.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- skydeckai_code-0.1.39.dist-info/entry_points.txt,sha256=ZkU1spOhLEnz5MpUn4dDihVcE0DMUC6ejzbsF-eNth4,88
24
- skydeckai_code-0.1.39.dist-info/licenses/LICENSE,sha256=uHse04vmI6ZjW7TblegFl30X-sDyyF0-QvH8ItPca3c,10865
25
- skydeckai_code-0.1.39.dist-info/RECORD,,
21
+ skydeckai_code-0.1.40.dist-info/METADATA,sha256=NF7EYPF0yQaPYYMrDsSzlBF7zAgn1UhjCAs-zBwuDO8,24594
22
+ skydeckai_code-0.1.40.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
+ skydeckai_code-0.1.40.dist-info/entry_points.txt,sha256=ZkU1spOhLEnz5MpUn4dDihVcE0DMUC6ejzbsF-eNth4,88
24
+ skydeckai_code-0.1.40.dist-info/licenses/LICENSE,sha256=uHse04vmI6ZjW7TblegFl30X-sDyyF0-QvH8ItPca3c,10865
25
+ skydeckai_code-0.1.40.dist-info/RECORD,,
@@ -232,11 +232,15 @@ def edit_file_tool():
232
232
  "description": "Make line-based edits to a text file. "
233
233
  "WHEN TO USE: When you need to make selective changes to specific parts of a file while preserving the rest of the content. "
234
234
  "Useful for modifying configuration values, updating text while maintaining file structure, or making targeted code changes. "
235
+ "IMPORTANT: For multiple edits to the same file, use a single tool call with multiple edits in the 'edits' array rather than multiple tool calls. "
236
+ "This is more efficient and ensures all edits are applied atomically. "
235
237
  "WHEN NOT TO USE: When you want to completely replace a file's contents (use write_file instead), when you need to create a new file (use write_file instead), "
236
238
  "or when you want to apply highly complex edits with context. "
237
239
  "RETURNS: A git-style diff showing the changes made, along with information about any failed matches. "
238
240
  "The response includes sections for failed matches (if any) and the unified diff output. "
239
- "Always use dryRun first to preview changes before applying them. Only works within the allowed directory.",
241
+ "Only works within the allowed directory. "
242
+ "EXAMPLES: For a single edit: {\"path\": \"config.js\", \"edits\": [{\"oldText\": \"port: 3000\", \"newText\": \"port: 8080\"}]}. "
243
+ "For multiple edits: {\"path\": \"app.py\", \"edits\": [{\"oldText\": \"debug=False\", \"newText\": \"debug=True\"}, {\"oldText\": \"version='1.0'\", \"newText\": \"version='2.0'\"}]}",
240
244
  "inputSchema": {
241
245
  "type": "object",
242
246
  "properties": {
@@ -260,20 +264,26 @@ def edit_file_tool():
260
264
  },
261
265
  "required": ["oldText", "newText"]
262
266
  },
263
- "description": "List of edit operations to perform on the file. Each edit specifies text to find (oldText) and text to replace it with (newText). The edits are applied in sequence, and each one can modify the result of previous edits."
264
- },
265
- "dryRun": {
266
- "type": "boolean",
267
- "description": "Preview changes without applying them to the file. Set to true to see what changes would be made without actually modifying the file. Highly recommended before making actual changes.",
268
- "default": False
267
+ "description": "MUST be an array of edit objects, NOT a string. Each edit object must contain 'oldText' and 'newText' properties. "
268
+ "For multiple edits, use: [{\"oldText\": \"text1\", \"newText\": \"replacement1\"}, {\"oldText\": \"text2\", \"newText\": \"replacement2\"}]. "
269
+ "For single edit, still use array: [{\"oldText\": \"text\", \"newText\": \"replacement\"}]. "
270
+ "The edits are applied in sequence, and each one can modify the result of previous edits. "
271
+ "AVOID multiple tool calls for the same file - instead, group all edits into a single call."
269
272
  },
270
273
  "options": {
271
274
  "type": "object",
272
275
  "properties": {
273
276
  "partialMatch": {
274
277
  "type": "boolean",
275
- "description": "Enable fuzzy matching for finding text. When true, the tool will try to find the best match even if it's not an exact match, using a confidence threshold of 80%.",
278
+ "description": "Enable fuzzy matching for finding text. When true, the tool will try to find the best match even if it's not an exact match, using the confidenceThreshold (default 80%).",
276
279
  "default": True
280
+ },
281
+ "confidenceThreshold": {
282
+ "type": "number",
283
+ "description": "Minimum confidence threshold for fuzzy matching (0.0 to 1.0). Higher values require more exact matches. Default is 0.8 (80% confidence).",
284
+ "minimum": 0.0,
285
+ "maximum": 1.0,
286
+ "default": 0.8
277
287
  }
278
288
  }
279
289
  }
@@ -784,11 +794,17 @@ def find_best_match(content: str, pattern: str, partial_match: bool = True) -> t
784
794
 
785
795
  return best_start, best_end, best_score
786
796
 
787
- async def apply_file_edits(file_path: str, edits: List[dict], dry_run: bool = False, options: dict = None) -> str:
788
- """Apply edits to a file with optional formatting and return diff."""
797
+ async def apply_file_edits(file_path: str, edits: List[dict], options: dict = None) -> tuple[str, bool, int, int]:
798
+ """Apply edits to a file with optional formatting and return diff.
799
+
800
+ Returns:
801
+ tuple: (result_text, has_changes, successful_edits, failed_edits)
802
+ """
789
803
  # Set default options
790
804
  options = options or {}
791
805
  partial_match = options.get('partialMatch', True)
806
+ # Use 0.8 confidence threshold to prevent false positives while allowing reasonable fuzzy matches
807
+ confidence_threshold = options.get('confidenceThreshold', 0.8)
792
808
 
793
809
  # Read file content
794
810
  with open(file_path, 'r', encoding='utf-8') as f:
@@ -797,9 +813,10 @@ async def apply_file_edits(file_path: str, edits: List[dict], dry_run: bool = Fa
797
813
  # Track modifications
798
814
  modified_content = content
799
815
  failed_matches = []
816
+ successful_edits = []
800
817
 
801
818
  # Apply each edit
802
- for edit in edits:
819
+ for edit_idx, edit in enumerate(edits):
803
820
  old_text = edit['oldText']
804
821
  new_text = edit['newText']
805
822
 
@@ -810,7 +827,7 @@ async def apply_file_edits(file_path: str, edits: List[dict], dry_run: bool = Fa
810
827
  # Find best match
811
828
  start, end, confidence = find_best_match(working_content, search_text, partial_match)
812
829
 
813
- if confidence >= 0.8:
830
+ if confidence >= confidence_threshold:
814
831
  # Fix indentation while preserving relative structure
815
832
  if start >= 0:
816
833
  # Get the indentation of the first line of the matched text
@@ -851,31 +868,77 @@ async def apply_file_edits(file_path: str, edits: List[dict], dry_run: bool = Fa
851
868
 
852
869
  # Apply the edit
853
870
  modified_content = modified_content[:start] + replacement + modified_content[end:]
871
+ successful_edits.append({
872
+ 'index': edit_idx,
873
+ 'oldText': old_text,
874
+ 'newText': new_text,
875
+ 'confidence': confidence
876
+ })
854
877
  else:
855
878
  failed_matches.append({
879
+ 'index': edit_idx,
856
880
  'oldText': old_text,
881
+ 'newText': new_text,
857
882
  'confidence': confidence,
858
883
  'bestMatch': working_content[start:end] if start >= 0 and end > start else None
859
884
  })
860
885
 
861
886
  # Create diff
862
887
  diff = create_unified_diff(content, modified_content, os.path.basename(file_path))
888
+ has_changes = modified_content != content
863
889
 
864
- # Write changes if not dry run
865
- if not dry_run and not failed_matches:
890
+ # CRITICAL FIX: Write changes even if some edits failed (partial success)
891
+ # This prevents the infinite retry loop
892
+ if has_changes:
866
893
  with open(file_path, 'w', encoding='utf-8') as f:
867
894
  f.write(modified_content)
868
895
 
869
- # Return results
870
- failed_matches_text = '=== Failed Matches ===\n' + json.dumps(failed_matches, indent=2) + '\n\n' if failed_matches else ''
871
- diff_text = f'=== Diff ===\n{diff}'
872
- return failed_matches_text + diff_text
896
+ # Build comprehensive result message
897
+ result_parts = []
898
+
899
+ # Summary
900
+ total_edits = len(edits)
901
+ successful_count = len(successful_edits)
902
+ failed_count = len(failed_matches)
903
+
904
+ result_parts.append(f'=== Edit Summary ===')
905
+ result_parts.append(f'Total edits: {total_edits}')
906
+ result_parts.append(f'Successful: {successful_count}')
907
+ result_parts.append(f'Failed: {failed_count}')
908
+ result_parts.append(f'File modified: {has_changes}')
909
+ result_parts.append('')
910
+
911
+ # Failed matches details
912
+ if failed_matches:
913
+ result_parts.append('=== Failed Matches ===')
914
+ for failed in failed_matches:
915
+ result_parts.append(f"Edit #{failed['index'] + 1}: Confidence {failed['confidence']:.2f}")
916
+ result_parts.append(f" Searched for: {repr(failed['oldText'][:100])}...")
917
+ if failed['bestMatch']:
918
+ result_parts.append(f" Best match: {repr(failed['bestMatch'][:100])}...")
919
+ result_parts.append('')
920
+
921
+ # Successful edits
922
+ if successful_edits:
923
+ result_parts.append('=== Successful Edits ===')
924
+ for success in successful_edits:
925
+ result_parts.append(f"Edit #{success['index'] + 1}: Confidence {success['confidence']:.2f}")
926
+ result_parts.append('')
927
+
928
+ # Diff
929
+ if diff.strip():
930
+ result_parts.append('=== Diff ===')
931
+ result_parts.append(diff)
932
+ else:
933
+ result_parts.append('=== No Changes ===')
934
+ result_parts.append('No modifications were made to the file.')
935
+
936
+ return '\n'.join(result_parts), has_changes, successful_count, failed_count
873
937
 
874
938
  async def handle_edit_file(arguments: dict):
875
939
  """Handle editing a file with pattern matching and formatting."""
876
940
  path = arguments.get("path")
877
941
  edits = arguments.get("edits")
878
- dry_run = arguments.get("dryRun", False)
879
942
  options = arguments.get("options", {})
880
943
 
881
944
  if not path:
@@ -900,8 +963,14 @@ async def handle_edit_file(arguments: dict):
900
963
  raise ValueError(f"Access denied: Path ({full_path}) must be within allowed directory ({state.allowed_directory})")
901
964
 
902
965
  try:
903
- result = await apply_file_edits(full_path, edits, dry_run, options)
904
- return [TextContent(type="text", text=result)]
966
+ result_text, has_changes, successful_count, failed_count = await apply_file_edits(full_path, edits, options)
967
+
968
+ # CRITICAL FIX: Raise an exception only if ALL edits failed AND no changes were made
969
+ # This prevents silent failures that cause infinite retry loops
970
+ if failed_count > 0 and successful_count == 0:
971
+ raise ValueError(f"All {failed_count} edits failed to match. No changes were made to the file. Check the 'oldText' patterns and ensure they match the file content exactly.")
972
+
973
+ return [TextContent(type="text", text=result_text)]
905
974
  except Exception as e:
906
975
  raise ValueError(f"Error editing file: {str(e)}")
907
976