Fix build errors and update dependencies
- Add ProjectOperationsService.cs to project file compilation - Update System.Text.Json from 8.0.0 to 8.0.5 to fix security vulnerabilities - Fix .NET Framework 4.8 compatibility (remove entireProcessTree parameter from Process.Kill) - Remove ExcludeAssets restriction from Microsoft.VisualStudio.SDK package - Add project operation endpoints (NuGet, build, clean, restore, directory creation) - Update AGENTS.md with MSBuild build instructions and warnings - Replace roslyn-api skill with roslyn-bridge skill - Update settings with additional auto-approvals Build now completes successfully with MSBuild (0 errors, 34 warnings). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,15 @@
|
|||||||
"Bash(move:*)",
|
"Bash(move:*)",
|
||||||
"Bash(tree:*)",
|
"Bash(tree:*)",
|
||||||
"Bash(dir:*)",
|
"Bash(dir:*)",
|
||||||
"Bash(git config:*)"
|
"Bash(git config:*)",
|
||||||
|
"Bash(del \"C:\\Users\\AJ\\Desktop\\RoslynBridge\\RoslynBridge\\Services\\ShellService.cs\")",
|
||||||
|
"Skill(roslyn-bridge)",
|
||||||
|
"Bash(dotnet restore:*)",
|
||||||
|
"Bash(msbuild:*)",
|
||||||
|
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" /t:Restore,Build /p:Configuration=Debug)",
|
||||||
|
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" RoslynBridge.sln /t:Restore /t:Build /p:Configuration=Debug)",
|
||||||
|
"Bash(del \".claude\\init.md\")",
|
||||||
|
"Bash(git add:*)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
---
|
|
||||||
name: roslyn-api
|
|
||||||
description: Use this for C# code analysis, querying .NET projects, finding symbols, getting diagnostics, or any Roslyn/semantic analysis tasks using the bridge server
|
|
||||||
---
|
|
||||||
|
|
||||||
# Roslyn API Testing Guide
|
|
||||||
|
|
||||||
Use this guide when testing or accessing the Claude Roslyn Bridge HTTP endpoints.
|
|
||||||
|
|
||||||
## Server Info
|
|
||||||
- **Base URL**: `http://localhost:59123/query`
|
|
||||||
- **Method**: POST
|
|
||||||
- **Content-Type**: application/json
|
|
||||||
|
|
||||||
## Correct Command Syntax
|
|
||||||
|
|
||||||
### Using curl (Recommended on Windows)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test if server is running
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
|
||||||
|
|
||||||
# Get document info
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdocument\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
|
||||||
|
|
||||||
# Get symbol at position
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsymbol\",\"filePath\":\"C:\\\\Users\\\\AJ\\\\Desktop\\\\PepLib\\\\PepLib\\\\Program.cs\",\"line\":10,\"column\":5}"
|
|
||||||
```
|
|
||||||
|
|
||||||
**IMPORTANT curl syntax rules:**
|
|
||||||
- Use `-X POST` (NOT `-Method POST`)
|
|
||||||
- Use `-H` for headers (NOT `-Headers`)
|
|
||||||
- Use `-d` for data (NOT `-Body`)
|
|
||||||
- Escape backslashes in file paths: `\\\\` becomes `\\` in JSON
|
|
||||||
|
|
||||||
### Using PowerShell (Alternative)
|
|
||||||
|
|
||||||
Run these commands **directly in PowerShell**, NOT via `powershell -Command`:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Test server
|
|
||||||
$body = @{queryType='getprojects'} | ConvertTo-Json
|
|
||||||
Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -ContentType 'application/json'
|
|
||||||
|
|
||||||
# Get document
|
|
||||||
$body = @{
|
|
||||||
queryType='getdocument'
|
|
||||||
filePath='C:\path\to\file.cs'
|
|
||||||
} | ConvertTo-Json
|
|
||||||
Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -ContentType 'application/json'
|
|
||||||
```
|
|
||||||
|
|
||||||
**IMPORTANT PowerShell rules:**
|
|
||||||
- Run directly in PowerShell console, NOT via `bash -c` or `powershell -Command`
|
|
||||||
- Use single quotes around URI and content type
|
|
||||||
- File paths don't need escaping in PowerShell hash tables
|
|
||||||
|
|
||||||
## Quick Reference: All Endpoints
|
|
||||||
|
|
||||||
### Query Endpoints
|
|
||||||
|
|
||||||
| Endpoint | Required Fields | Optional Fields |
|
|
||||||
|----------|----------------|-----------------|
|
|
||||||
| `getprojects` | - | - |
|
|
||||||
| `getdocument` | `filePath` | - |
|
|
||||||
| `getsymbol` | `filePath`, `line`, `column` | - |
|
|
||||||
| `getdiagnostics` | - | `filePath` |
|
|
||||||
| `findreferences` | `filePath`, `line`, `column` | - |
|
|
||||||
| `findsymbol` | `symbolName` | `parameters.kind` |
|
|
||||||
| `gettypemembers` | `symbolName` | `parameters.includeInherited` |
|
|
||||||
| `gettypehierarchy` | `symbolName` | `parameters.direction` |
|
|
||||||
| `findimplementations` | `symbolName` OR `filePath`+`line`+`column` | - |
|
|
||||||
| `getnamespacetypes` | `symbolName` | - |
|
|
||||||
| `getcallhierarchy` | `filePath`, `line`, `column` | `parameters.direction` |
|
|
||||||
| `getsolutionoverview` | - | - |
|
|
||||||
| `getsymbolcontext` | `filePath`, `line`, `column` | - |
|
|
||||||
| `searchcode` | `symbolName` (regex) | `parameters.scope` |
|
|
||||||
|
|
||||||
### Editing Endpoints
|
|
||||||
|
|
||||||
| Endpoint | Required Fields | Optional Fields |
|
|
||||||
|----------|----------------|-----------------|
|
|
||||||
| `formatdocument` | `filePath` | - |
|
|
||||||
| `organizeusings` | `filePath` | - |
|
|
||||||
| `renamesymbol` | `filePath`, `line`, `column`, `parameters.newName` | - |
|
|
||||||
| `addmissingusing` | `filePath`, `line`, `column` | - |
|
|
||||||
| `applycodefix` | `filePath`, `line`, `column` | - |
|
|
||||||
|
|
||||||
## Common Test Examples
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Get all projects in solution
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
|
||||||
|
|
||||||
# Get solution overview
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsolutionoverview\"}"
|
|
||||||
|
|
||||||
# Get diagnostics for entire solution
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdiagnostics\"}"
|
|
||||||
|
|
||||||
# Find all classes containing "Helper"
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"searchcode\",\"symbolName\":\".*Helper\",\"parameters\":{\"scope\":\"classes\"}}"
|
|
||||||
|
|
||||||
# Format a document
|
|
||||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"formatdocument\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Response Format
|
|
||||||
|
|
||||||
Success:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": true,
|
|
||||||
"message": "Optional message",
|
|
||||||
"data": { /* Response data */ }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Error:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"success": false,
|
|
||||||
"error": "Error message",
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Line numbers are **1-based**
|
|
||||||
- Column numbers are **0-based**
|
|
||||||
- File paths in JSON need escaped backslashes: `C:\\path\\to\\file.cs`
|
|
||||||
- All workspace modifications use VS threading model (`JoinableTaskFactory.SwitchToMainThreadAsync()`)
|
|
||||||
- The Visual Studio extension must be running for endpoints to work
|
|
||||||
196
.claude/skills/roslyn-bridge/SKILL.md
Normal file
196
.claude/skills/roslyn-bridge/SKILL.md
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
---
|
||||||
|
name: roslyn-bridge
|
||||||
|
description: Use this for C# code analysis, querying .NET projects, finding symbols, getting diagnostics, or any Roslyn/semantic analysis tasks using the bridge server
|
||||||
|
---
|
||||||
|
|
||||||
|
# Roslyn Bridge Testing Guide
|
||||||
|
|
||||||
|
Use this guide when testing or accessing the Claude Roslyn Bridge HTTP endpoints.
|
||||||
|
|
||||||
|
## Server Info
|
||||||
|
- **Base URL**: `http://localhost:59123`
|
||||||
|
- **Endpoints**:
|
||||||
|
- `/query` - Main query endpoint for all Roslyn operations
|
||||||
|
- `/health` - Health check endpoint to verify server is running
|
||||||
|
- **Method**: POST
|
||||||
|
- **Content-Type**: application/json
|
||||||
|
|
||||||
|
## ⚠️ CRITICAL: Command Syntax Rules
|
||||||
|
|
||||||
|
**ALWAYS use curl with the Bash tool. NEVER pipe curl output to PowerShell.**
|
||||||
|
|
||||||
|
### ✅ CORRECT: Using curl with Bash tool
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test if server is running
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
||||||
|
|
||||||
|
# Get diagnostics
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdiagnostics\"}"
|
||||||
|
|
||||||
|
# Get document info
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdocument\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||||
|
|
||||||
|
# Get symbol at position
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsymbol\",\"filePath\":\"C:\\\\Users\\\\AJ\\\\Desktop\\\\PepLib\\\\PepLib\\\\Program.cs\",\"line\":10,\"column\":5}"
|
||||||
|
```
|
||||||
|
|
||||||
|
**IMPORTANT curl syntax rules:**
|
||||||
|
- Use `-X POST` (NOT `-Method POST`)
|
||||||
|
- Use `-H` for headers (NOT `-Headers`)
|
||||||
|
- Use `-d` for data (NOT `-Body`)
|
||||||
|
- Escape backslashes in file paths: `\\\\` becomes `\\` in JSON
|
||||||
|
- **DO NOT pipe to PowerShell** - the JSON output is already formatted
|
||||||
|
|
||||||
|
### ❌ WRONG: DO NOT DO THIS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# NEVER pipe curl to PowerShell - this will fail on Windows
|
||||||
|
curl ... | powershell -Command "$json = $input | ..." # ❌ WRONG
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using PowerShell (Alternative)
|
||||||
|
|
||||||
|
Run these commands **directly in PowerShell**, NOT via `powershell -Command`:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Test server
|
||||||
|
$body = @{queryType='getprojects'} | ConvertTo-Json
|
||||||
|
Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -ContentType 'application/json'
|
||||||
|
|
||||||
|
# Get document
|
||||||
|
$body = @{
|
||||||
|
queryType='getdocument'
|
||||||
|
filePath='C:\path\to\file.cs'
|
||||||
|
} | ConvertTo-Json
|
||||||
|
Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -ContentType 'application/json'
|
||||||
|
```
|
||||||
|
|
||||||
|
**IMPORTANT PowerShell rules:**
|
||||||
|
- Run directly in PowerShell console, NOT via `bash -c` or `powershell -Command`
|
||||||
|
- Use single quotes around URI and content type
|
||||||
|
- File paths don't need escaping in PowerShell hash tables
|
||||||
|
|
||||||
|
## Quick Reference: All Endpoints
|
||||||
|
|
||||||
|
### Query Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Required Fields | Optional Fields | Description |
|
||||||
|
|----------|----------------|-----------------|-------------|
|
||||||
|
| `getprojects` | - | - | Get all projects in the solution |
|
||||||
|
| `getdocument` | `filePath` | - | Get document information for a specific file |
|
||||||
|
| `getsymbol` | `filePath`, `line`, `column` | - | Get symbol information at a specific position |
|
||||||
|
| `getsemanticmodel` | `filePath` | - | Verify semantic model availability (not serializable) |
|
||||||
|
| `getsyntaxtree` | `filePath` | - | Get the syntax tree (source code) for a file |
|
||||||
|
| `getdiagnostics` | - | `filePath` | Get compilation errors and warnings |
|
||||||
|
| `findreferences` | `filePath`, `line`, `column` | - | Find all references to a symbol |
|
||||||
|
| `findsymbol` | `symbolName` | `parameters.kind` | Find symbols by name (supports filtering by kind) |
|
||||||
|
| `gettypemembers` | `symbolName` | `parameters.includeInherited` | Get all members of a type |
|
||||||
|
| `gettypehierarchy` | `symbolName` | `parameters.direction` | Get base types or derived types |
|
||||||
|
| `findimplementations` | `symbolName` OR `filePath`+`line`+`column` | - | Find implementations of an interface/abstract member |
|
||||||
|
| `getnamespacetypes` | `symbolName` | - | Get all types in a namespace |
|
||||||
|
| `getcallhierarchy` | `filePath`, `line`, `column` | `parameters.direction` | Get callers or callees of a method |
|
||||||
|
| `getsolutionoverview` | - | - | Get high-level solution statistics |
|
||||||
|
| `getsymbolcontext` | `filePath`, `line`, `column` | - | Get contextual information about a symbol's location |
|
||||||
|
| `searchcode` | `symbolName` (regex) | `parameters.scope` | Search for code patterns using regex |
|
||||||
|
|
||||||
|
### Editing Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Required Fields | Optional Fields | Description |
|
||||||
|
|----------|----------------|-----------------|-------------|
|
||||||
|
| `formatdocument` | `filePath` | - | Format a document according to coding style |
|
||||||
|
| `organizeusings` | `filePath` | - | Sort and remove unused using statements |
|
||||||
|
| `renamesymbol` | `filePath`, `line`, `column`, `parameters.newName` | - | Rename a symbol across the solution |
|
||||||
|
| `addmissingusing` | `filePath`, `line`, `column` | - | Add missing using statement for a symbol |
|
||||||
|
| `applycodefix` | `filePath`, `line`, `column` | - | Apply available code fixes at a position |
|
||||||
|
|
||||||
|
### Project Operation Endpoints
|
||||||
|
|
||||||
|
| Endpoint | Required Fields | Optional Fields | Description |
|
||||||
|
|----------|----------------|-----------------|-------------|
|
||||||
|
| `addnugetpackage` | `projectName`, `packageName` | `version` | Add a NuGet package to a project |
|
||||||
|
| `removenugetpackage` | `projectName`, `packageName` | - | Remove a NuGet package from a project |
|
||||||
|
| `buildproject` | `projectName` | `configuration` | Build a project or solution |
|
||||||
|
| `cleanproject` | `projectName` | - | Clean build output |
|
||||||
|
| `restorepackages` | `projectName` | - | Restore NuGet packages |
|
||||||
|
| `createdirectory` | `directoryPath` | - | Create a new directory |
|
||||||
|
|
||||||
|
## Common Test Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Health check - verify server is running
|
||||||
|
curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"
|
||||||
|
|
||||||
|
# Get all projects in solution
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
||||||
|
|
||||||
|
# Get solution overview
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsolutionoverview\"}"
|
||||||
|
|
||||||
|
# Get diagnostics for entire solution
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdiagnostics\"}"
|
||||||
|
|
||||||
|
# Get semantic model for a file
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsemanticmodel\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||||
|
|
||||||
|
# Get syntax tree for a file
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsyntaxtree\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||||
|
|
||||||
|
# Find all classes containing "Helper"
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"searchcode\",\"symbolName\":\".*Helper\",\"parameters\":{\"scope\":\"classes\"}}"
|
||||||
|
|
||||||
|
# Format a document
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"formatdocument\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||||
|
|
||||||
|
# Add NuGet package
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"addnugetpackage\",\"projectName\":\"RoslynBridge\",\"packageName\":\"Newtonsoft.Json\"}"
|
||||||
|
|
||||||
|
# Add NuGet package with version
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"addnugetpackage\",\"projectName\":\"RoslynBridge\",\"packageName\":\"Newtonsoft.Json\",\"version\":\"13.0.3\"}"
|
||||||
|
|
||||||
|
# Remove NuGet package
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"removenugetpackage\",\"projectName\":\"RoslynBridge\",\"packageName\":\"Newtonsoft.Json\"}"
|
||||||
|
|
||||||
|
# Build project
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"buildproject\",\"projectName\":\"RoslynBridge\"}"
|
||||||
|
|
||||||
|
# Build with configuration
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"buildproject\",\"projectName\":\"RoslynBridge\",\"configuration\":\"Release\"}"
|
||||||
|
|
||||||
|
# Clean project
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"cleanproject\",\"projectName\":\"RoslynBridge\"}"
|
||||||
|
|
||||||
|
# Restore packages
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"restorepackages\",\"projectName\":\"RoslynBridge\"}"
|
||||||
|
|
||||||
|
# Create directory
|
||||||
|
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"createdirectory\",\"directoryPath\":\"C:\\\\path\\\\to\\\\new\\\\directory\"}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response Format
|
||||||
|
|
||||||
|
Success:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"message": "Optional message",
|
||||||
|
"data": { /* Response data */ }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Error:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"error": "Error message",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Line numbers are **1-based**
|
||||||
|
- Column numbers are **0-based**
|
||||||
|
- File paths in JSON need escaped backslashes: `C:\\path\\to\\file.cs`
|
||||||
|
- All workspace modifications use VS threading model (`JoinableTaskFactory.SwitchToMainThreadAsync()`)
|
||||||
|
- The Visual Studio extension must be running for endpoints to work
|
||||||
@@ -6,7 +6,11 @@
|
|||||||
- Claude skills: `.claude/skills/roslyn-api/SKILL.md` (HTTP query reference for local discovery/testing).
|
- Claude skills: `.claude/skills/roslyn-api/SKILL.md` (HTTP query reference for local discovery/testing).
|
||||||
|
|
||||||
## Build, Test, and Development Commands
|
## Build, Test, and Development Commands
|
||||||
- Build (CLI): `msbuild RoslynBridge.sln /p:Configuration=Debug`
|
- **IMPORTANT**: This VSIX project MUST be built with MSBuild, NOT `dotnet build`. Using `dotnet build` will fail with missing namespace errors.
|
||||||
|
- Build (CLI): `msbuild RoslynBridge.sln /p:Configuration=Debug` or with full path:
|
||||||
|
```powershell
|
||||||
|
& 'C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe' RoslynBridge.sln /t:Restore /t:Build /p:Configuration=Debug
|
||||||
|
```
|
||||||
- Build (VS): Open `RoslynBridge.sln`, set `RoslynBridge` as startup, press F5 to launch the Experimental Instance.
|
- Build (VS): Open `RoslynBridge.sln`, set `RoslynBridge` as startup, press F5 to launch the Experimental Instance.
|
||||||
- Health check (server running in VS):
|
- Health check (server running in VS):
|
||||||
- PowerShell: `$b=@{queryType='getprojects'}|ConvertTo-Json; Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $b -ContentType 'application/json'`
|
- PowerShell: `$b=@{queryType='getprojects'}|ConvertTo-Json; Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $b -ContentType 'application/json'`
|
||||||
|
|||||||
@@ -39,5 +39,13 @@ namespace RoslynBridge.Constants
|
|||||||
public const string RenameSymbol = "renamesymbol";
|
public const string RenameSymbol = "renamesymbol";
|
||||||
public const string OrganizeUsings = "organizeusings";
|
public const string OrganizeUsings = "organizeusings";
|
||||||
public const string AddMissingUsing = "addmissingusing";
|
public const string AddMissingUsing = "addmissingusing";
|
||||||
|
|
||||||
|
// Project operation endpoints
|
||||||
|
public const string AddNuGetPackage = "addnugetpackage";
|
||||||
|
public const string RemoveNuGetPackage = "removenugetpackage";
|
||||||
|
public const string BuildProject = "buildproject";
|
||||||
|
public const string CleanProject = "cleanproject";
|
||||||
|
public const string RestorePackages = "restorepackages";
|
||||||
|
public const string CreateDirectory = "createdirectory";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,13 @@ namespace RoslynBridge.Models
|
|||||||
public int? Line { get; set; }
|
public int? Line { get; set; }
|
||||||
public int? Column { get; set; }
|
public int? Column { get; set; }
|
||||||
public Dictionary<string, string>? Parameters { get; set; }
|
public Dictionary<string, string>? Parameters { get; set; }
|
||||||
|
|
||||||
|
// Project operation parameters
|
||||||
|
public string? ProjectName { get; set; }
|
||||||
|
public string? PackageName { get; set; }
|
||||||
|
public string? Version { get; set; }
|
||||||
|
public string? Configuration { get; set; }
|
||||||
|
public string? DirectoryPath { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QueryResponse
|
public class QueryResponse
|
||||||
|
|||||||
@@ -78,6 +78,7 @@
|
|||||||
<Compile Include="Services\DocumentQueryService.cs" />
|
<Compile Include="Services\DocumentQueryService.cs" />
|
||||||
<Compile Include="Services\IRoslynQueryService.cs" />
|
<Compile Include="Services\IRoslynQueryService.cs" />
|
||||||
<Compile Include="Services\IWorkspaceProvider.cs" />
|
<Compile Include="Services\IWorkspaceProvider.cs" />
|
||||||
|
<Compile Include="Services\ProjectOperationsService.cs" />
|
||||||
<Compile Include="Services\RefactoringService.cs" />
|
<Compile Include="Services\RefactoringService.cs" />
|
||||||
<Compile Include="Services\RoslynQueryService.cs" />
|
<Compile Include="Services\RoslynQueryService.cs" />
|
||||||
<Compile Include="Services\SymbolQueryService.cs" />
|
<Compile Include="Services\SymbolQueryService.cs" />
|
||||||
@@ -98,9 +99,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.8.37221" ExcludeAssets="runtime">
|
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.8.37221" />
|
||||||
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.8.2369">
|
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.8.2369">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
@@ -119,7 +118,7 @@
|
|||||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.8.14" />
|
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.8.14" />
|
||||||
|
|
||||||
<!-- HTTP server -->
|
<!-- HTTP server -->
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.0" />
|
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
|||||||
300
RoslynBridge/Services/ProjectOperationsService.cs
Normal file
300
RoslynBridge/Services/ProjectOperationsService.cs
Normal file
@@ -0,0 +1,300 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using RoslynBridge.Models;
|
||||||
|
|
||||||
|
namespace RoslynBridge.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service for safe project operations (NuGet, build, etc.)
|
||||||
|
/// Only allows specific, whitelisted operations
|
||||||
|
/// </summary>
|
||||||
|
public class ProjectOperationsService
|
||||||
|
{
|
||||||
|
private const int DefaultTimeout = 120000; // 2 minutes
|
||||||
|
private const int MaxTimeout = 600000; // 10 minutes
|
||||||
|
private readonly IWorkspaceProvider _workspaceProvider;
|
||||||
|
|
||||||
|
public ProjectOperationsService(IWorkspaceProvider workspaceProvider)
|
||||||
|
{
|
||||||
|
_workspaceProvider = workspaceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add a NuGet package to a project
|
||||||
|
/// </summary>
|
||||||
|
public async Task<QueryResponse> AddNuGetPackageAsync(string projectName, string packageName, string? version = null)
|
||||||
|
{
|
||||||
|
var projectPath = GetProjectPath(projectName);
|
||||||
|
if (projectPath == null)
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Project not found: {projectName}. Use 'getprojects' to see available projects."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(packageName))
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = "Package name is required"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = string.IsNullOrWhiteSpace(version)
|
||||||
|
? $"add \"{projectPath}\" package {packageName}"
|
||||||
|
: $"add \"{projectPath}\" package {packageName} --version {version}";
|
||||||
|
|
||||||
|
return await ExecuteDotNetCommandAsync(command, $"Add {packageName} to {projectName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a NuGet package from a project
|
||||||
|
/// </summary>
|
||||||
|
public async Task<QueryResponse> RemoveNuGetPackageAsync(string projectName, string packageName)
|
||||||
|
{
|
||||||
|
var projectPath = GetProjectPath(projectName);
|
||||||
|
if (projectPath == null)
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Project not found: {projectName}. Use 'getprojects' to see available projects."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(packageName))
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = "Package name is required"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = $"remove \"{projectPath}\" package {packageName}";
|
||||||
|
return await ExecuteDotNetCommandAsync(command, $"Remove {packageName} from {projectName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build a project or solution
|
||||||
|
/// </summary>
|
||||||
|
public async Task<QueryResponse> BuildAsync(string projectName, string? configuration = null)
|
||||||
|
{
|
||||||
|
var projectPath = GetProjectPath(projectName);
|
||||||
|
if (projectPath == null)
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Project not found: {projectName}. Use 'getprojects' to see available projects."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = string.IsNullOrWhiteSpace(configuration)
|
||||||
|
? $"build \"{projectPath}\""
|
||||||
|
: $"build \"{projectPath}\" --configuration {configuration}";
|
||||||
|
|
||||||
|
return await ExecuteDotNetCommandAsync(command, $"Build {projectName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clean build output
|
||||||
|
/// </summary>
|
||||||
|
public async Task<QueryResponse> CleanAsync(string projectName)
|
||||||
|
{
|
||||||
|
var projectPath = GetProjectPath(projectName);
|
||||||
|
if (projectPath == null)
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Project not found: {projectName}. Use 'getprojects' to see available projects."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = $"clean \"{projectPath}\"";
|
||||||
|
return await ExecuteDotNetCommandAsync(command, $"Clean {projectName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restore NuGet packages
|
||||||
|
/// </summary>
|
||||||
|
public async Task<QueryResponse> RestoreAsync(string projectName)
|
||||||
|
{
|
||||||
|
var projectPath = GetProjectPath(projectName);
|
||||||
|
if (projectPath == null)
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Project not found: {projectName}. Use 'getprojects' to see available projects."
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var command = $"restore \"{projectPath}\"";
|
||||||
|
return await ExecuteDotNetCommandAsync(command, $"Restore {projectName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new directory
|
||||||
|
/// </summary>
|
||||||
|
public Task<QueryResponse> CreateDirectoryAsync(string directoryPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(directoryPath))
|
||||||
|
{
|
||||||
|
return Task.FromResult(new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = "Directory path is required"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool existed = Directory.Exists(directoryPath);
|
||||||
|
if (!existed)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(new QueryResponse
|
||||||
|
{
|
||||||
|
Success = true,
|
||||||
|
Message = existed ? $"Directory already exists: {directoryPath}" : $"Successfully created directory: {directoryPath}",
|
||||||
|
Data = new
|
||||||
|
{
|
||||||
|
DirectoryPath = directoryPath,
|
||||||
|
AlreadyExisted = existed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Error creating directory: {ex.Message}"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string? GetProjectPath(string projectName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(projectName))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var workspace = _workspaceProvider.Workspace;
|
||||||
|
if (workspace?.CurrentSolution == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find project by name
|
||||||
|
var project = workspace.CurrentSolution.Projects
|
||||||
|
.FirstOrDefault(p => p.Name.Equals(projectName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
return project?.FilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<QueryResponse> ExecuteDotNetCommandAsync(string arguments, string operationName)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var processStartInfo = new ProcessStartInfo
|
||||||
|
{
|
||||||
|
FileName = "dotnet",
|
||||||
|
Arguments = arguments,
|
||||||
|
UseShellExecute = false,
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
RedirectStandardError = true,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
StandardOutputEncoding = Encoding.UTF8,
|
||||||
|
StandardErrorEncoding = Encoding.UTF8
|
||||||
|
};
|
||||||
|
|
||||||
|
using var process = new Process { StartInfo = processStartInfo };
|
||||||
|
|
||||||
|
var outputBuilder = new StringBuilder();
|
||||||
|
var errorBuilder = new StringBuilder();
|
||||||
|
|
||||||
|
process.OutputDataReceived += (sender, args) =>
|
||||||
|
{
|
||||||
|
if (args.Data != null)
|
||||||
|
{
|
||||||
|
outputBuilder.AppendLine(args.Data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
process.ErrorDataReceived += (sender, args) =>
|
||||||
|
{
|
||||||
|
if (args.Data != null)
|
||||||
|
{
|
||||||
|
errorBuilder.AppendLine(args.Data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var startTime = DateTime.Now;
|
||||||
|
process.Start();
|
||||||
|
process.BeginOutputReadLine();
|
||||||
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
|
bool completed = await Task.Run(() => process.WaitForExit(MaxTimeout));
|
||||||
|
|
||||||
|
if (!completed)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"{operationName} timed out after {MaxTimeout}ms"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var endTime = DateTime.Now;
|
||||||
|
var duration = (endTime - startTime).TotalMilliseconds;
|
||||||
|
|
||||||
|
var stdout = outputBuilder.ToString();
|
||||||
|
var stderr = errorBuilder.ToString();
|
||||||
|
var exitCode = process.ExitCode;
|
||||||
|
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = exitCode == 0,
|
||||||
|
Message = exitCode == 0 ? $"{operationName} completed successfully" : $"{operationName} failed with exit code {exitCode}",
|
||||||
|
Data = new
|
||||||
|
{
|
||||||
|
Operation = operationName,
|
||||||
|
Command = $"dotnet {arguments}",
|
||||||
|
ExitCode = exitCode,
|
||||||
|
Output = stdout,
|
||||||
|
Error = stderr,
|
||||||
|
Duration = Math.Round(duration, 2)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new QueryResponse
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = $"Error executing {operationName}: {ex.Message}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ namespace RoslynBridge.Services
|
|||||||
private readonly DocumentQueryService _documentService;
|
private readonly DocumentQueryService _documentService;
|
||||||
private readonly DiagnosticsService _diagnosticsService;
|
private readonly DiagnosticsService _diagnosticsService;
|
||||||
private readonly RefactoringService _refactoringService;
|
private readonly RefactoringService _refactoringService;
|
||||||
|
private readonly ProjectOperationsService _projectOperationsService;
|
||||||
|
|
||||||
public RoslynQueryService(AsyncPackage package)
|
public RoslynQueryService(AsyncPackage package)
|
||||||
{
|
{
|
||||||
@@ -28,6 +29,7 @@ namespace RoslynBridge.Services
|
|||||||
_documentService = new DocumentQueryService(package, _workspaceProvider);
|
_documentService = new DocumentQueryService(package, _workspaceProvider);
|
||||||
_diagnosticsService = new DiagnosticsService(package, _workspaceProvider);
|
_diagnosticsService = new DiagnosticsService(package, _workspaceProvider);
|
||||||
_refactoringService = new RefactoringService(package, _workspaceProvider);
|
_refactoringService = new RefactoringService(package, _workspaceProvider);
|
||||||
|
_projectOperationsService = new ProjectOperationsService(_workspaceProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitializeAsync()
|
public async Task InitializeAsync()
|
||||||
@@ -39,6 +41,25 @@ namespace RoslynBridge.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Handle project operations (don't require workspace)
|
||||||
|
var queryType = request.QueryType?.ToLowerInvariant();
|
||||||
|
switch (queryType)
|
||||||
|
{
|
||||||
|
case QueryTypes.AddNuGetPackage:
|
||||||
|
return await _projectOperationsService.AddNuGetPackageAsync(request.ProjectName ?? string.Empty, request.PackageName ?? string.Empty, request.Version);
|
||||||
|
case QueryTypes.RemoveNuGetPackage:
|
||||||
|
return await _projectOperationsService.RemoveNuGetPackageAsync(request.ProjectName ?? string.Empty, request.PackageName ?? string.Empty);
|
||||||
|
case QueryTypes.BuildProject:
|
||||||
|
return await _projectOperationsService.BuildAsync(request.ProjectName ?? string.Empty, request.Configuration);
|
||||||
|
case QueryTypes.CleanProject:
|
||||||
|
return await _projectOperationsService.CleanAsync(request.ProjectName ?? string.Empty);
|
||||||
|
case QueryTypes.RestorePackages:
|
||||||
|
return await _projectOperationsService.RestoreAsync(request.ProjectName ?? string.Empty);
|
||||||
|
case QueryTypes.CreateDirectory:
|
||||||
|
return await _projectOperationsService.CreateDirectoryAsync(request.DirectoryPath ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Roslyn queries, ensure workspace is initialized
|
||||||
if (_workspaceProvider.Workspace == null)
|
if (_workspaceProvider.Workspace == null)
|
||||||
{
|
{
|
||||||
await InitializeAsync();
|
await InitializeAsync();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
|
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
|
||||||
<Metadata>
|
<Metadata>
|
||||||
<Identity Id="RoslynBridge.b2c3d4e5-f6a7-4b5c-9d8e-0f1a2b3c4d5e" Version="1.0.0" Language="en-US" Publisher="Your Name" />
|
<Identity Id="RoslynBridge.b2c3d4e5-f6a7-4b5c-9d8e-0f1a2b3c4d5e" Version="1.0.0" Language="en-US" Publisher="AJ Isaacs" />
|
||||||
<DisplayName>Claude Roslyn Bridge</DisplayName>
|
<DisplayName>Roslyn Bridge</DisplayName>
|
||||||
<Description xml:space="preserve">Visual Studio extension that provides access to Roslyn APIs through an HTTP bridge for Claude integration.</Description>
|
<Description xml:space="preserve">Visual Studio extension that provides access to Roslyn APIs through an HTTP bridge for Claude integration.</Description>
|
||||||
<MoreInfo>https://github.com/yourusername/ClaudeRoslynBridge</MoreInfo>
|
<MoreInfo>https://github.com/yourusername/ClaudeRoslynBridge</MoreInfo>
|
||||||
<License>README.txt</License>
|
<License>README.txt</License>
|
||||||
|
|||||||
Reference in New Issue
Block a user