├── .assets
├── architecture_stateMachine.png
├── readme_example_1_create_ents.gif
├── readme_example_2_move_to_layer.gif
└── readme_example_3_move_to_layer_fix.gif
├── .gitattributes
├── .github
└── workflows
│ ├── ci-build.yml
│ ├── installer-build.yml
│ └── release-build-as-zip.yml
├── .gitignore
├── LICENSE
├── README.md
├── Scripture.Installer.Bundle
├── Bundle.wxs
└── Scripture.Installer.Bundle.wixproj
├── Scripture.Installer
├── Product.wxs
└── Scripture.Installer.wixproj
├── Scripture.sln
├── Scripture
├── Properties
│ └── launchSettings.json
├── ScriptExecutor.cs
├── Scripture.csproj
├── ScriptureExtension.cs
└── appsettings.json
├── ScriptureCore.Tests
├── RuntimeCompilerTests.cs
└── ScriptureCore.Tests.csproj
├── ScriptureCore
├── ConfigurationService.cs
├── ICompiler.cs
├── ILLMServices.cs
├── IScriptExecutor.cs
├── OpenAIService.cs
├── RuntimeCompiler.cs
├── ScriptureCore.csproj
├── ServiceLocator.cs
└── ServiceRegistration.cs
├── ScriptureHarnessApp
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── ScriptureHarnessApp.csproj
└── appsettings.json
└── ScriptureUI
├── Converters
└── InverseBooleanConverter.cs
├── ScriptureControl.xaml
├── ScriptureControl.xaml.cs
└── ScriptureUI.csproj
/.assets/architecture_stateMachine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimitrovakulenko/Scripture/c9592891dd5333cac60fe0f9ed52bec3db3af911/.assets/architecture_stateMachine.png
--------------------------------------------------------------------------------
/.assets/readme_example_1_create_ents.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimitrovakulenko/Scripture/c9592891dd5333cac60fe0f9ed52bec3db3af911/.assets/readme_example_1_create_ents.gif
--------------------------------------------------------------------------------
/.assets/readme_example_2_move_to_layer.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimitrovakulenko/Scripture/c9592891dd5333cac60fe0f9ed52bec3db3af911/.assets/readme_example_2_move_to_layer.gif
--------------------------------------------------------------------------------
/.assets/readme_example_3_move_to_layer_fix.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dimitrovakulenko/Scripture/c9592891dd5333cac60fe0f9ed52bec3db3af911/.assets/readme_example_3_move_to_layer_fix.gif
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/workflows/ci-build.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a .NET project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
3 |
4 | name: CI Build
5 |
6 | on:
7 | push:
8 | branches: [ "master" ]
9 | pull_request:
10 | branches: [ "master" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: windows-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Setup .NET
21 | uses: actions/setup-dotnet@v4
22 | with:
23 | dotnet-version: 8.0.x
24 |
25 | - name: Restore dependencies
26 | run: dotnet restore
27 |
28 | - name: Build
29 | run: dotnet build --no-restore
30 |
31 | - name: Test
32 | run: dotnet test --no-build --verbosity normal
33 |
--------------------------------------------------------------------------------
/.github/workflows/installer-build.yml:
--------------------------------------------------------------------------------
1 | name: Build installers
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: write
8 |
9 | jobs:
10 | create-zip:
11 | runs-on: windows-latest
12 |
13 | steps:
14 | - uses: actions/checkout@v4
15 |
16 | - name: Setup .NET
17 | uses: actions/setup-dotnet@v4
18 | with:
19 | dotnet-version: 8.0.x
20 |
21 | - name: Setup MSBuild
22 | uses: microsoft/setup-msbuild@v1
23 |
24 | - name: Install WiX Toolset 3.14
25 | run: choco install wixtoolset --version 3.14.0
26 | continue-on-error: true
27 |
28 | - name: Restore dependencies
29 | run: dotnet restore
30 |
31 | - name: Build Release
32 | run: dotnet build --no-restore --configuration Release
33 |
34 | - name: Build MSI Installer
35 | run: msbuild Scripture.Installer/Scripture.Installer.wixproj /p:Configuration=Release
36 |
37 | - name: Build Bundle Installer
38 | run: msbuild Scripture.Installer.Bundle/Scripture.Installer.Bundle.wixproj /p:Configuration=Release
39 |
40 | - name: Set timestamp
41 | id: set_timestamp
42 | run: |
43 | $timestamp = Get-Date -Format "yy-MM-dd-HH-mm"
44 | echo "timestamp=$timestamp" | Out-File -FilePath $env:GITHUB_ENV -Append
45 | shell: pwsh
46 |
47 | - name: Create GitHub Release
48 | id: create_release
49 | uses: actions/create-release@v1
50 | env:
51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 | with:
53 | tag_name: "v${{ env.timestamp }}"
54 | release_name: "Release ${{ env.timestamp }}"
55 | draft: false
56 | prerelease: false
57 |
58 | - name: Upload MSI Artifact to Release
59 | uses: actions/upload-release-asset@v1
60 | env:
61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
62 | with:
63 | upload_url: ${{ steps.create_release.outputs.upload_url }}
64 | asset_path: "Scripture.Installer/bin/x64/Release/ScriptureAutocadPlugin.msi"
65 | asset_name: "ScriptureAutocadPlugin-${{ env.timestamp }}.msi"
66 | label: "MSI Installer"
67 | asset_content_type: "application/octet-stream"
68 |
69 | - name: Upload EXE Artifact to Release
70 | uses: actions/upload-release-asset@v1
71 | env:
72 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
73 | with:
74 | upload_url: ${{ steps.create_release.outputs.upload_url }}
75 | asset_path: "Scripture.Installer.Bundle/bin/x64/Release/ScriptureInstaller.exe"
76 | asset_name: "ScriptureBundleInstaller-${{ env.timestamp }}.exe"
77 | label: "Bundle Installer"
78 | asset_content_type: "application/octet-stream"
79 |
80 |
--------------------------------------------------------------------------------
/.github/workflows/release-build-as-zip.yml:
--------------------------------------------------------------------------------
1 | name: Create Release Zip
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | jobs:
7 | create-zip:
8 | runs-on: windows-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v4
12 |
13 | - name: Setup .NET
14 | uses: actions/setup-dotnet@v4
15 | with:
16 | dotnet-version: 8.0.x
17 |
18 | - name: Restore dependencies
19 | run: dotnet restore
20 |
21 | - name: Build Release
22 | run: dotnet build --no-restore --configuration Release
23 |
24 | - name: Set timestamp
25 | id: set_timestamp
26 | run: |
27 | $timestamp = Get-Date -Format "yy-MM-dd-HH-mm"
28 | echo "timestamp=$timestamp" | Out-File -FilePath $env:GITHUB_ENV -Append
29 | shell: pwsh
30 |
31 | - name: Upload Zip Archive
32 | uses: actions/upload-artifact@v3
33 | with:
34 | name: ScripturePlugin-${{ env.timestamp }}
35 | path: "./Scripture/bin/x64/Release/net8.0-windows/*"
36 |
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
364 | /ScriptureHarnessApp/appsettings.json
365 | /Scripture/appsettings.json
366 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Dmytro Vakulenko
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SCRIPTURE - LLM-Based Automation Plugin for AutoCAD
2 |
3 | SCRIPTURE is a powerful automation tool for AutoCAD that allows users to automate their workflows by simply describing the actions to be automated with text.
4 |
5 | This tool is currently a prototype and is designed for research and experimentation.
6 |
7 | ## Example
8 |
9 | 
10 |
11 | 
12 |
13 | ## Prerequisites
14 |
15 | - **AutoCAD** (or similar): Make sure AutoCAD or similar application supporting running .net8 objectARX plugins is installed on your machine
16 | - **Access to an LLM**: Requires an API key and endpoint for either:
17 | - **Microsoft Azure OpenAI API**
18 | - **OpenAI API** (not tested yet)
19 |
20 | ## How to Try It
21 |
22 | ### Install or Compile
23 |
24 | You have two options to get started with SCRIPTURE:
25 |
26 | 1. **Compile Locally**
27 | Clone the repository and build the solution locally using dotnet tool.
28 |
29 | 2. **Install via MSI or Bundle**
30 |
31 | - **MSI Installer**: Use the MSI installer to install the plugin. You need to ensure .NET 8 runtime is already installed.
32 | - **Bundle Installer**: Use the bundle (`bundle.exe`) to install both the .NET 8 runtime (if not installed) and the plugin.
33 |
34 | Please use next link: https://github.com/dimitrovakulenko/Scripture/releases to download an installer.
35 |
36 | ### Configure `appSettings.json`
37 |
38 | You can find 'appSettings.json' file in `C:\Program Files\Scripture Plugin\` (%PROGRAMFILES%) folder in case of using installer and in the bin folder of scripture project in case of local build.
39 | To use SCRIPTURE, you need to configure the `appSettings.json` file.
40 |
41 | - **Azure OpenAI API Configuration**:
42 |
43 | ```json
44 | {
45 | "InitialScriptModel": {
46 | "ApiKey": "YOUR_AZURE_OPENAI_API_KEY",
47 | "Endpoint": "https://YOUR_AZURE_ENDPOINT.openai.azure.com/",
48 | "ModelName": "YOUR_MODEL_DEPLOYMENT_NAME"
49 | }
50 | }
51 | ```
52 |
53 | - Set ApiKey to your Azure OpenAI API key.
54 | - Set Endpoint to your Azure OpenAI resource endpoint URL.
55 | - Set ModelName to your Azure deployment name.
56 |
57 | - **OpenAI API Configuration**:
58 |
59 | ```json
60 | {
61 | "InitialScriptModel": {
62 | "ApiKey": "YOUR_OPENAI_API_KEY",
63 | "Endpoint": "",
64 | "ModelName": "CHOSEN_MODEL_NAME"
65 | }
66 | }
67 | ```
68 |
69 | - Set ApiKey to your OpenAI API key.
70 | - Set ModelName to the desired model (e.g., gpt-4o).
71 | - Keep Endpoint empty.
72 |
73 | ### Running the Plugin in AutoCAD
74 |
75 | After installation or compilation, you can run the plugin in AutoCAD:
76 |
77 | - In AutoCAD, use the command `NETLOAD`
78 | - Select `scripture.dll` file from the plugin folder (`C:\Program Files\Scripture Plugin\` in case of full install, binaries folder in case of local build)
79 |
80 | ## Using the Plugin
81 |
82 | Once the plugin is loaded in AutoCAD, you can use it as follows:
83 |
84 | **Step 1: Enter a Prompt and Generate Script**
85 |
86 | Start by entering a prompt that describes the action you want to automate, and click the "Generate Script" button. If the LLM (Language Model) successfully generates the script, you will be automatically navigated to Step 3.
87 |
88 | **Step 3: Execute or Save as Plugin**
89 |
90 | At this stage, you can either execute the script immediately or save it as a separate DLL/plugin for future use. If you choose to save the script, it will be compiled into a custom command dll that can be used in AutoCAD anytime by loading the DLL with the `NETLOAD` command and executing corresponding command.
91 |
92 | **Step 2: Fix Script Errors (Optional)**
93 |
94 | If script generation fails or produces errors, you will be taken to Step 2.
95 |
96 | Here, you can attempt to fix the script automatically by clicking "Try Fix" or manually edit the script using the AvalonEdit control.
97 |
98 | Once you've made changes, click "Recompile Script" to verify if the errors are resolved.
99 |
100 | ## Software Architecture
101 |
102 | ### Software Architecture: Concepts
103 |
104 | - Initial script generation: ask LLM to generate script by description, try compile script, if there are errors ask LLM to fix the errors maximum N times
105 | - Automatic fixing / fine-tuning: provide additional content to LLM when it tries to fix compilation errors, an additional content can be all existing properties and methods of a specific class to avoid hallucinations/unexisting methods creation
106 |
107 | 
108 |
109 | ### Software Architecture: Technical
110 |
111 | **The solution**
112 |
113 | Consists of next projects:
114 | - ScriptureCore: contains all core services required by the plugin: script generation service (communication to LLM), script commpilation service (Roslyn API).
115 | - ScriptureCore.Tests: contains tests for the core services
116 | - ScriptureUI: a simple WPF control to host the UI; a proper viewmodel tbd;
117 | - ScriptureHarnessApp: enabled testing fo the UI control without running the complete plugin from Autocad
118 | - Scripture: Autocad .Net plugin, depends on ObjectARX.Net API
119 |
120 | **Installer**
121 |
122 | Installer implemented using Wix 3.1x.
123 | Scripture.Installer project results in plugin msi installer.
124 | Scripture.Bundle.Installer project results in exe file that install plugin and .Net runtime.
125 |
126 | **CI & Release Builds**
127 |
128 | Implemented as github actions
129 |
130 | ## License
131 |
132 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
133 |
--------------------------------------------------------------------------------
/Scripture.Installer.Bundle/Bundle.wxs:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Scripture.Installer.Bundle/Scripture.Installer.Bundle.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x64
6 | 3.10
7 | 0b0c1701-e398-4762-96e4-70286ec7df6f
8 | 2.0
9 | ScriptureInstaller
10 | Bundle
11 |
12 |
13 | bin\$(Configuration)\
14 | obj\$(Configuration)\
15 | Debug
16 |
17 |
18 | bin\$(Configuration)\
19 | obj\$(Configuration)\
20 |
21 |
22 | Debug
23 | bin\$(Platform)\$(Configuration)\
24 | obj\$(Platform)\$(Configuration)\
25 |
26 |
27 | bin\$(Platform)\$(Configuration)\
28 | obj\$(Platform)\$(Configuration)\
29 |
30 |
31 |
32 |
33 |
34 |
35 | $(WixExtDir)\WixBalExtension.dll
36 | WixBalExtension
37 |
38 |
39 |
40 |
41 | WixUtilExtension
42 | WixUtilExtension
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | powershell -Command "If (-Not (Test-Path %27dotnet-runtime-8.0.8-win-x64.exe%27)) { Invoke-WebRequest -Uri %27https://download.visualstudio.microsoft.com/download/pr/cc913baa-9bce-482e-bdfc-56c4b6fafd10/e3f24f2ab2fc02b395c1b67f5193b8d1/dotnet-runtime-8.0.8-win-x64.exe%27 -OutFile %27dotnet-runtime-8.0.8-win-x64.exe%27 }"
52 |
53 |
61 |
--------------------------------------------------------------------------------
/Scripture.Installer/Product.wxs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/Scripture.Installer/Scripture.Installer.wixproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | x64
6 | 3.10
7 | fd30421a-aaea-4457-99c2-ca8766cffe7f
8 | 2.0
9 | ScriptureAutocadPlugin
10 | Package
11 |
12 |
13 | bin\$(Configuration)\
14 | obj\$(Configuration)\
15 | Debug
16 |
17 |
18 | bin\$(Configuration)\
19 | obj\$(Configuration)\
20 |
21 |
22 | Debug
23 | bin\$(Platform)\$(Configuration)\
24 | obj\$(Platform)\$(Configuration)\
25 |
26 |
27 | bin\$(Platform)\$(Configuration)\
28 | obj\$(Platform)\$(Configuration)\
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
46 |
--------------------------------------------------------------------------------
/Scripture.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.11.35312.102
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scripture", "Scripture\Scripture.csproj", "{5B476D04-2EF5-4495-B38A-80E6103DA77F}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptureUI", "ScriptureUI\ScriptureUI.csproj", "{7DABDA32-5907-4828-BFF2-070811407807}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptureHarnessApp", "ScriptureHarnessApp\ScriptureHarnessApp.csproj", "{A448526D-7CB7-4B2A-8232-EBE6DC1397B2}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptureCore", "ScriptureCore\ScriptureCore.csproj", "{889C6936-11C4-41E3-A8B0-7E15C932F34C}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptureCore.Tests", "ScriptureCore.Tests\ScriptureCore.Tests.csproj", "{B4FF6167-BD4C-47BC-BF99-848C19172467}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Any CPU = Debug|Any CPU
19 | Debug|x64 = Debug|x64
20 | Release|Any CPU = Release|Any CPU
21 | Release|x64 = Release|x64
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Debug|Any CPU.ActiveCfg = Debug|x64
25 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Debug|Any CPU.Build.0 = Debug|x64
26 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Debug|x64.ActiveCfg = Debug|x64
27 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Debug|x64.Build.0 = Debug|x64
28 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Release|Any CPU.ActiveCfg = Release|x64
29 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Release|Any CPU.Build.0 = Release|x64
30 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Release|x64.ActiveCfg = Release|x64
31 | {5B476D04-2EF5-4495-B38A-80E6103DA77F}.Release|x64.Build.0 = Release|x64
32 | {7DABDA32-5907-4828-BFF2-070811407807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {7DABDA32-5907-4828-BFF2-070811407807}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {7DABDA32-5907-4828-BFF2-070811407807}.Debug|x64.ActiveCfg = Debug|Any CPU
35 | {7DABDA32-5907-4828-BFF2-070811407807}.Debug|x64.Build.0 = Debug|Any CPU
36 | {7DABDA32-5907-4828-BFF2-070811407807}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {7DABDA32-5907-4828-BFF2-070811407807}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {7DABDA32-5907-4828-BFF2-070811407807}.Release|x64.ActiveCfg = Release|Any CPU
39 | {7DABDA32-5907-4828-BFF2-070811407807}.Release|x64.Build.0 = Release|Any CPU
40 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Debug|x64.ActiveCfg = Debug|Any CPU
43 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Debug|x64.Build.0 = Debug|Any CPU
44 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Release|x64.ActiveCfg = Release|Any CPU
47 | {A448526D-7CB7-4B2A-8232-EBE6DC1397B2}.Release|x64.Build.0 = Release|Any CPU
48 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Debug|x64.ActiveCfg = Debug|Any CPU
51 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Debug|x64.Build.0 = Debug|Any CPU
52 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Release|Any CPU.ActiveCfg = Release|Any CPU
53 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Release|Any CPU.Build.0 = Release|Any CPU
54 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Release|x64.ActiveCfg = Release|Any CPU
55 | {889C6936-11C4-41E3-A8B0-7E15C932F34C}.Release|x64.Build.0 = Release|Any CPU
56 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
57 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Debug|Any CPU.Build.0 = Debug|Any CPU
58 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Debug|x64.ActiveCfg = Debug|Any CPU
59 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Debug|x64.Build.0 = Debug|Any CPU
60 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Release|Any CPU.ActiveCfg = Release|Any CPU
61 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Release|Any CPU.Build.0 = Release|Any CPU
62 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Release|x64.ActiveCfg = Release|Any CPU
63 | {B4FF6167-BD4C-47BC-BF99-848C19172467}.Release|x64.Build.0 = Release|Any CPU
64 | EndGlobalSection
65 | GlobalSection(SolutionProperties) = preSolution
66 | HideSolutionNode = FALSE
67 | EndGlobalSection
68 | GlobalSection(ExtensibilityGlobals) = postSolution
69 | SolutionGuid = {75014393-555F-429E-B9FC-3EA1206A2DFE}
70 | EndGlobalSection
71 | EndGlobal
72 |
--------------------------------------------------------------------------------
/Scripture/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Scripture": {
4 | "commandName": "Project"
5 | },
6 | "Acad": {
7 | "commandName": "Executable",
8 | "executablePath": "D:\\ACAD\\venn\\AutoCAD 2025\\acad.exe"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/Scripture/ScriptExecutor.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.AutoCAD.ApplicationServices;
2 | using ScriptureCore;
3 |
4 | namespace Scripture
5 | {
6 | internal class ScriptExecutor : IScriptExecutor
7 | {
8 | public void Execute(string dllPath, string commandName)
9 | {
10 | var activeDocument = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
11 |
12 | // Set FILEDIA to 0 to suppress file dialog
13 | activeDocument.SendStringToExecute("FILEDIA 0\n", false, false, true);
14 |
15 | // Load the DLL using NETLOAD
16 | activeDocument.SendStringToExecute($"NETLOAD \"{dllPath}\"\n", false, false, true);
17 |
18 | // Restore FILEDIA to 1
19 | activeDocument.SendStringToExecute("FILEDIA 1\n", false, false, true);
20 |
21 | // Execute the command
22 | activeDocument.SendStringToExecute($"{commandName}\n", false, false, true);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Scripture/Scripture.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows
4 | Scripture
5 | enable
6 | enable
7 | x64
8 | false
9 | true
10 | true
11 | true
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | PreserveNewest
26 |
27 |
28 |
29 |
30 | $(SolutionDir)ScriptureUI\bin\$(Configuration)\net8.0-windows\
31 | $(UserProfile)\.nuget\packages\
32 | $(TargetDir)
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/Scripture/ScriptureExtension.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.AutoCAD.Runtime;
2 | using Autodesk.AutoCAD.Windows;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using ScriptureCore;
5 | using ScriptureUI;
6 | using System.Runtime.Versioning;
7 | using System.Windows.Forms.Integration;
8 |
9 | namespace Scripture
10 | {
11 | [SupportedOSPlatform("windows")]
12 | public class ScriptureExtension : IExtensionApplication
13 | {
14 | private PaletteSet? _paletteSet;
15 |
16 | public void Initialize()
17 | {
18 | var serviceCollection = new ServiceCollection();
19 | ServiceRegistration.RegisterServices(serviceCollection, new ScriptExecutor());
20 |
21 | // Build the service provider and set it in ServiceLocator
22 | var serviceProvider = serviceCollection.BuildServiceProvider();
23 | ServiceLocator.SetServiceProvider(serviceProvider);
24 |
25 | // build the palette
26 | _paletteSet = new PaletteSet("Scripture Panel");
27 | _paletteSet.Visible = true;
28 | _paletteSet.Size = new System.Drawing.Size(600, 600);
29 | _paletteSet.DockEnabled = DockSides.Left;
30 | _paletteSet.Dock = DockSides.Left;
31 |
32 | // Create an instance of the WPF UserControl
33 | var wpfControl = new ScriptureControl();
34 |
35 | // Create an ElementHost to host the WPF control
36 | ElementHost elementHost = new ElementHost
37 | {
38 | Child = wpfControl, // Assign the WPF UserControl
39 | Dock = DockStyle.Fill // Make the WPF control fill the parent panel
40 | };
41 |
42 | // Add the ElementHost to a WinForms Panel
43 | Panel panel = new Panel
44 | {
45 | Dock = DockStyle.Fill
46 | };
47 | panel.Controls.Add(elementHost);
48 |
49 | // Add the panel to the PaletteSet
50 | _paletteSet.Add("Scripture UI", panel);
51 | }
52 |
53 | public void Terminate()
54 | {
55 |
56 | }
57 |
58 | [CommandMethod("ShowScripturePanel")]
59 | public void ShowScripturePanel()
60 | {
61 | if (_paletteSet != null)
62 | {
63 | _paletteSet.Visible = true; // Make the PaletteSet visible
64 | }
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Scripture/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "InitialScriptModel": {
3 | "ApiKey": "YOURAPIKEY",
4 | "Endpoint": "https://scripture.openai.azure.com",
5 | "DeploymentName": "gpt-4o"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ScriptureCore.Tests/RuntimeCompilerTests.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.AutoCAD.ApplicationServices;
2 | using Autodesk.AutoCAD.DatabaseServices;
3 | using Autodesk.AutoCAD.EditorInput;
4 | using Autodesk.AutoCAD.Geometry;
5 | using Microsoft.Extensions.DependencyInjection;
6 |
7 | namespace ScriptureCore.Tests
8 | {
9 | public class RuntimeCompilerTests
10 | {
11 | public RuntimeCompilerTests()
12 | {
13 | // hacky load of autocad dlls
14 | try
15 | {
16 | Document doc = Application.DocumentManager.MdiActiveDocument;
17 | Editor ed = doc.Editor;
18 | ObjectId objectId = new ObjectId();
19 | Point3d point3D = new Point3d();
20 | if (objectId.IsErased) point3D = point3D * 2;
21 | }
22 | catch { }
23 |
24 | // register services
25 | var serviceCollection = new ServiceCollection();
26 | ServiceRegistration.RegisterServices(serviceCollection, null);
27 |
28 | var serviceProvider = serviceCollection.BuildServiceProvider();
29 | ServiceLocator.SetServiceProvider(serviceProvider);
30 | }
31 |
32 | [Fact]
33 | public void TestAutocadDllsFound()
34 | {
35 | var code = @"
36 | using Autodesk.AutoCAD.ApplicationServices;
37 | using Autodesk.AutoCAD.DatabaseServices;
38 | using Autodesk.AutoCAD.EditorInput;
39 | using Autodesk.AutoCAD.Geometry;
40 |
41 | namespace AutoCADPlugin
42 | {
43 | public class PolygonSelector
44 | {
45 | public void SelectSmallPolygons()
46 | {
47 | }
48 | }
49 | }";
50 |
51 | var compiler = ServiceLocator.GetService();
52 |
53 | var (success, errors) = compiler.TestCompile(code);
54 |
55 | Assert.True(success, $"Compilation failed with errors: {string.Join(Environment.NewLine, errors)}");
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/ScriptureCore.Tests/ScriptureCore.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 | true
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/ScriptureCore/ConfigurationService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using System.Reflection;
3 |
4 | namespace ScriptureCore
5 | {
6 | public class ConfigurationService
7 | {
8 | private readonly IConfiguration _configuration;
9 |
10 | public ConfigurationService()
11 | {
12 | var pluginLocation = Path.GetDirectoryName(
13 | Assembly.GetExecutingAssembly().Location);
14 | if (string.IsNullOrEmpty(pluginLocation))
15 | throw new Exception("Executing dll doesn't exist?");
16 |
17 | _configuration = new ConfigurationBuilder()
18 | .SetBasePath(pluginLocation)
19 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
20 | .Build();
21 | }
22 |
23 | public IConfiguration GetConfiguration() => _configuration;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ScriptureCore/ICompiler.cs:
--------------------------------------------------------------------------------
1 | namespace ScriptureCore
2 | {
3 | public interface ICompiler
4 | {
5 | (bool Success, List Errors) TestCompile(string code);
6 |
7 | (bool Success, List Errors) CompileTo(string code, string filepath);
8 |
9 | string GetFullyQualifiedTypeName(string typeName, string code);
10 |
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ScriptureCore/ILLMServices.cs:
--------------------------------------------------------------------------------
1 | namespace ScriptureCore
2 | {
3 | public interface ILLMServices
4 | {
5 | Task GenerateInitialScriptAsync(string prompt);
6 |
7 | ///
8 | ///
9 | ///
10 | ///
11 | ///
12 | /// additional data about available properties/methods is provided in some cases
13 | ///
14 | Task TryFixScriptAsync(string script, List errorMessages, bool provideAdditionalMetadata);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ScriptureCore/IScriptExecutor.cs:
--------------------------------------------------------------------------------
1 | namespace ScriptureCore
2 | {
3 | public interface IScriptExecutor
4 | {
5 | public void Execute(string dllPath, string commandName);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ScriptureCore/OpenAIService.cs:
--------------------------------------------------------------------------------
1 | using Azure;
2 | using Azure.AI.OpenAI;
3 | using Microsoft.Extensions.Configuration;
4 | using OpenAI;
5 | using OpenAI.Chat;
6 | using System.ClientModel;
7 | using System.Text;
8 | using System.Text.RegularExpressions;
9 |
10 | namespace ScriptureCore
11 | {
12 | internal class OpenAIService : ILLMServices
13 | {
14 | private readonly ChatClient _initialScriptClient;
15 |
16 | public OpenAIService(IConfiguration configuration)
17 | {
18 | string? apiKey = configuration["InitialScriptModel:ApiKey"];
19 | string? endpoint = configuration["InitialScriptModel:Endpoint"];
20 | string? modelName = configuration["InitialScriptModel:ModelName"];
21 |
22 | if (string.IsNullOrEmpty(apiKey))
23 | {
24 | throw new ArgumentNullException(nameof(apiKey), "API key must not be null or empty.");
25 | }
26 |
27 | // Check if the endpoint is specified to determine if it's Azure or OpenAI
28 | if (string.IsNullOrEmpty(endpoint))
29 | {
30 | // OpenAI directly
31 | var openAiClient = new OpenAIClient(apiKey);
32 | _initialScriptClient = openAiClient.GetChatClient(modelName);
33 | }
34 | else
35 | {
36 | // Azure OpenAI
37 | if (string.IsNullOrEmpty(modelName))
38 | {
39 | throw new ArgumentNullException(nameof(modelName), "Deployment name must not be null or empty for Azure.");
40 | }
41 |
42 | var azureClient = new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(apiKey));
43 | _initialScriptClient = azureClient.GetChatClient(modelName);
44 | }
45 | }
46 |
47 | public async Task GenerateInitialScriptAsync(string prompt)
48 | {
49 | ChatCompletionOptions options = new()
50 | {
51 | MaxOutputTokenCount = 1024,
52 | Temperature = 0.3f,
53 | };
54 |
55 | var messages = new List()
56 | {
57 | new SystemChatMessage(GenerateInitialScriptSystemMessage()),
58 | new UserChatMessage(prompt),
59 | };
60 |
61 | var completeAnswer = new StringBuilder();
62 | ClientResult completionsResponse;
63 |
64 | do
65 | {
66 | completionsResponse = await _initialScriptClient.CompleteChatAsync(messages, options);
67 |
68 | // Add the generated response to the complete answer
69 | completeAnswer.Append(
70 | RemoveCodeFence(
71 | string.Join(
72 | "",
73 | completionsResponse.Value.Content.Select(c => c.Text))));
74 |
75 | // Add the new response to the message list to continue the conversation contextually
76 | messages.Add(new AssistantChatMessage(string.Join("", completionsResponse.Value.Content.Select(c => c.Text))));
77 | }
78 | while (completionsResponse.Value.FinishReason == ChatFinishReason.Length);
79 |
80 | return completeAnswer.ToString();
81 | }
82 |
83 | public async Task TryFixScriptAsync(string script, List errorMessages, bool provideAdditionalMetadata)
84 | {
85 | var messages = new List
86 | {
87 | new SystemChatMessage(@"You are an expert C# developer specializing in AutoCAD ObjectARX.NET API.
88 | Your task is to fix the provided script based on the error messages below.
89 | Please ensure:
90 | 1. The corrected version is complete, with all necessary 'using' statements, namespaces, and class definitions.
91 | 2. Return only the corrected C# code, without any extra explanations or comments."),
92 |
93 | new UserChatMessage($"Here is the script that needs fixing:\n```csharp\n{script}\n```"),
94 | new UserChatMessage($"Here are the error messages:\n{string.Join("\n", errorMessages)}")
95 | };
96 |
97 | if (provideAdditionalMetadata)
98 | {
99 | messages.AddRange(CollectExtraMessagesForErrors(script, errorMessages));
100 | }
101 |
102 | ChatCompletionOptions options = new()
103 | {
104 | MaxOutputTokenCount = 1024,
105 | Temperature = 0.3f,
106 | };
107 |
108 | var completeAnswer = new StringBuilder();
109 | ClientResult completionsResponse;
110 |
111 | do
112 | {
113 | completionsResponse = await _initialScriptClient.CompleteChatAsync(messages, options);
114 |
115 | // Add the generated response to the complete answer
116 | completeAnswer.Append(
117 | RemoveCodeFence(
118 | string.Join(
119 | "",
120 | completionsResponse.Value.Content.Select(c => c.Text))));
121 |
122 | // Add the new response to the message list to continue the conversation contextually
123 | messages.Add(new AssistantChatMessage(string.Join("", completionsResponse.Value.Content.Select(c => c.Text))));
124 | }
125 | while (completionsResponse.Value.FinishReason == ChatFinishReason.Length);
126 |
127 | return completeAnswer.ToString();
128 | }
129 |
130 | private List CollectExtraMessagesForErrors(string script, List errorMessages)
131 | {
132 | var result = new List();
133 |
134 | var typeNames = new List();
135 | foreach (var errorMessage in errorMessages)
136 | {
137 | if (Regex.IsMatch(errorMessage, @"'([^']+)' does not contain a definition for '([^']+)'"))
138 | {
139 | var typeName = Regex.Match(errorMessage, @"'([^']+)' does not contain a definition for").Groups[1].Value;
140 | typeNames.Add(typeName);
141 | }
142 | else if (Regex.IsMatch(errorMessage, @"Non-invocable member '([^']+)' cannot be used like a method"))
143 | {
144 | var typeName = Regex.Match(errorMessage, @"Non-invocable member '([^\.]+)\.").Groups[1].Value;
145 | typeNames.Add(typeName);
146 | }
147 | }
148 |
149 | if (typeNames.Any())
150 | {
151 | var compiler = ServiceLocator.GetService();
152 | foreach (var typeName in typeNames)
153 | {
154 | var reflectionInfo = GetReflectionInfo(
155 | compiler.GetFullyQualifiedTypeName(
156 | typeName, script));
157 | if (!string.IsNullOrEmpty(reflectionInfo))
158 | {
159 | result.Add(new UserChatMessage($"Here is the information about the type '{typeName}':\n{reflectionInfo}"));
160 | }
161 | }
162 | }
163 |
164 | return result;
165 | }
166 |
167 | private string GetReflectionInfo(string typeName)
168 | {
169 | try
170 | {
171 | var type = Type.GetType(typeName);
172 | if (type == null)
173 | {
174 | return string.Empty;
175 | }
176 |
177 | var properties = type.GetProperties();
178 | var methods = type.GetMethods();
179 |
180 | var propertyInfo = string.Join(", ", properties.Select(p => $"{p.PropertyType.Name} {p.Name}"));
181 | var methodInfo = string.Join(", ", methods.Select(m => $"{m.ReturnType.Name} {m.Name}()"));
182 |
183 | return $"Properties: {propertyInfo}\nMethods: {methodInfo}";
184 | }
185 | catch
186 | {
187 | return string.Empty;
188 | }
189 | }
190 |
191 |
192 | private static string RemoveCodeFence(string code)
193 | {
194 | if (code.StartsWith("```csharp", StringComparison.OrdinalIgnoreCase))
195 | {
196 | code = code.Substring(9).TrimStart(); // Remove ```csharp and any extra whitespace/new lines
197 | }
198 |
199 | if (code.EndsWith("```", StringComparison.OrdinalIgnoreCase))
200 | {
201 | code = code.Substring(0, code.Length - 3).TrimEnd(); // Remove ``` and any extra whitespace/new lines
202 | }
203 |
204 | return code;
205 | }
206 |
207 | private string GenerateInitialScriptSystemMessage()
208 | {
209 | return @"You are an expert C# developer specializing in AutoCAD ObjectARX.NET API.
210 | Your task is to generate a C# function to solve the user's problem based on the given description.
211 | The function must use ObjectARX.NET classes and methods to accomplish the user's requirements.
212 | Please ensure:
213 | 1. The function is complete, with all necessary 'using' statements, namespaces, and class definitions.
214 | 2. Decompose the problem into multiple sub-functions if needed.
215 | 3. Use only .NET classes related to ObjectARX.
216 | 4. Return only the C# code, without any extra explanations or comments.
217 |
218 | The user's request is: ";
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/ScriptureCore/RuntimeCompiler.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.CSharp.Syntax;
4 |
5 | namespace ScriptureCore
6 | {
7 | internal class RuntimeCompiler: ICompiler
8 | {
9 | public (bool Success, List Errors) TestCompile(string code)
10 | {
11 | var syntaxTree = CSharpSyntaxTree.ParseText(code);
12 |
13 | var references = GetReferences();
14 |
15 | var compilation = CSharpCompilation.Create("TestCompilation")
16 | .AddReferences(references)
17 | .AddSyntaxTrees(syntaxTree)
18 | .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
19 |
20 | var result = compilation.Emit(Stream.Null);
21 |
22 | var errors = result.Diagnostics
23 | .Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
24 | .Select(diagnostic => diagnostic.GetMessage())
25 | .ToList();
26 |
27 | return (result.Success, errors);
28 | }
29 |
30 | private List GetReferences()
31 | {
32 | var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
33 | return loadedAssemblies
34 | .Where(assembly => !assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location))
35 | .Select(assembly => MetadataReference.CreateFromFile(assembly.Location))
36 | .ToList();
37 | }
38 |
39 | public (bool Success, List Errors) CompileTo(string code, string filepath)
40 | {
41 | var syntaxTree = CSharpSyntaxTree.ParseText(code);
42 |
43 | var references = GetReferences();
44 |
45 | var compilation = CSharpCompilation.Create(Path.GetFileNameWithoutExtension(filepath))
46 | .AddReferences(references)
47 | .AddSyntaxTrees(syntaxTree)
48 | .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
49 |
50 | using (var fs = new FileStream(filepath, FileMode.Create))
51 | {
52 | var result = compilation.Emit(fs);
53 |
54 | var errors = result.Diagnostics
55 | .Where(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
56 | .Select(diagnostic => diagnostic.GetMessage())
57 | .ToList();
58 |
59 | return (result.Success, errors);
60 | }
61 | }
62 |
63 | public string GetFullyQualifiedTypeName(string typeName, string code)
64 | {
65 | var syntaxTree = CSharpSyntaxTree.ParseText(code);
66 | var root = syntaxTree.GetRoot();
67 |
68 | var compilation = CSharpCompilation.Create("TempCompilation")
69 | .AddSyntaxTrees(syntaxTree)
70 | .AddReferences(
71 | GetReferences());
72 | var semanticModel = compilation.GetSemanticModel(syntaxTree);
73 |
74 | var typeNodes = syntaxTree.GetRoot().DescendantNodes().OfType();
75 | foreach (var typeNode in typeNodes)
76 | {
77 | var symbolInfo = semanticModel.GetSymbolInfo(typeNode);
78 | if (symbolInfo.Symbol != null && symbolInfo.Symbol.Name == typeName)
79 | {
80 | var typeSymbol = symbolInfo.Symbol as ITypeSymbol;
81 | if (typeSymbol != null)
82 | {
83 | return typeSymbol.ToDisplayString();
84 | }
85 | }
86 | }
87 | return typeName;
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/ScriptureCore/ScriptureCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ScriptureCore/ServiceLocator.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System;
3 |
4 | namespace ScriptureCore
5 | {
6 | public static class ServiceLocator
7 | {
8 | public static IServiceProvider? ServiceProvider { get; private set; }
9 |
10 | public static void SetServiceProvider(IServiceProvider serviceProvider)
11 | {
12 | ServiceProvider = serviceProvider;
13 | }
14 |
15 | public static T GetService() where T : class
16 | {
17 | if (ServiceProvider == null)
18 | {
19 | throw new InvalidOperationException("ServiceProvider is not set. Make sure to call SetServiceProvider during application initialization.");
20 | }
21 |
22 | return ServiceProvider.GetRequiredService();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ScriptureCore/ServiceRegistration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using System.Reflection;
3 |
4 | namespace ScriptureCore
5 | {
6 | public static class ServiceRegistration
7 | {
8 | public static void RegisterServices(IServiceCollection services, IScriptExecutor? scriptExecutor)
9 | {
10 | // Register ConfigurationService as a singleton
11 | services.AddSingleton();
12 |
13 | // Register OpenAIService as a singleton
14 | services.AddSingleton(provider =>
15 | {
16 | var configService = provider.GetRequiredService();
17 | return new OpenAIService(configService.GetConfiguration());
18 | });
19 |
20 | services.AddSingleton();
21 |
22 | if(scriptExecutor != null)
23 | services.AddSingleton(provide =>
24 | {
25 | return scriptExecutor;
26 | });
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ScriptureHarnessApp/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/ScriptureHarnessApp/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Autodesk.AutoCAD.ApplicationServices;
2 | using Autodesk.AutoCAD.DatabaseServices;
3 | using Autodesk.AutoCAD.EditorInput;
4 | using Autodesk.AutoCAD.Geometry;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using ScriptureCore;
7 |
8 | namespace ScriptureHarnessApp
9 | {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : System.Windows.Application
14 | {
15 | public App()
16 | {
17 | string[] args = Environment.GetCommandLineArgs();
18 | bool loadAutocadDlls = !args.Contains("--noAutoCADDlls"); // Default to true unless explicitly disabled
19 |
20 | // Load AutoCAD DLLs if required
21 | if (loadAutocadDlls)
22 | {
23 | LoadAutoCADDlls();
24 | }
25 |
26 | var serviceCollection = new ServiceCollection();
27 | ServiceRegistration.RegisterServices(serviceCollection, null);
28 |
29 | // Build the service provider and set it in ServiceLocator
30 | var serviceProvider = serviceCollection.BuildServiceProvider();
31 | ServiceLocator.SetServiceProvider(serviceProvider);
32 | }
33 |
34 | private void LoadAutoCADDlls()
35 | {
36 | try
37 | {
38 | Document doc = Application.DocumentManager.MdiActiveDocument;
39 | Editor ed = doc.Editor;
40 | ObjectId objectId = new ObjectId();
41 | Point3d point3D = new Point3d();
42 | if (objectId.IsErased) point3D = point3D * 2;
43 | }
44 | catch { }
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/ScriptureHarnessApp/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/ScriptureHarnessApp/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ScriptureHarnessApp/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Data;
5 | using System.Windows.Documents;
6 | using System.Windows.Input;
7 | using System.Windows.Media;
8 | using System.Windows.Media.Imaging;
9 | using System.Windows.Navigation;
10 | using System.Windows.Shapes;
11 |
12 | namespace ScriptureHarnessApp
13 | {
14 | ///
15 | /// Interaction logic for MainWindow.xaml
16 | ///
17 | public partial class MainWindow : Window
18 | {
19 | public MainWindow()
20 | {
21 | InitializeComponent();
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/ScriptureHarnessApp/ScriptureHarnessApp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net8.0-windows
6 | enable
7 | enable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | PreserveNewest
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ScriptureHarnessApp/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "InitialScriptModel": {
3 | "ApiKey": "YOURAPIKEY",
4 | "Endpoint": "https://scripture.openai.azure.com",
5 | "DeploymentName": "gpt-4o"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/ScriptureUI/Converters/InverseBooleanConverter.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.Windows.Data;
3 |
4 | namespace ScriptureUI.Converters
5 | {
6 | public class InverseBooleanConverter : IValueConverter
7 | {
8 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
9 | {
10 | if (value is bool booleanValue)
11 | {
12 | return !booleanValue;
13 | }
14 | return false;
15 | }
16 |
17 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
18 | {
19 | throw new NotImplementedException();
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ScriptureUI/ScriptureControl.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
47 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
77 |
78 |
79 |
80 |
81 |
82 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/ScriptureUI/ScriptureControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using ScriptureCore;
2 | using System.ComponentModel;
3 | using System.Configuration;
4 | using System.IO;
5 | using System.Runtime.CompilerServices;
6 | using System.Text.RegularExpressions;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 |
10 | namespace ScriptureUI
11 | {
12 | public partial class ScriptureControl : UserControl, INotifyPropertyChanged
13 | {
14 | public ScriptureControl()
15 | {
16 | InitializeComponent();
17 |
18 | ScriptEditor.Options.IndentationSize = 4;
19 | ScriptEditor.Options.ConvertTabsToSpaces = false;
20 |
21 | _dllPath = ConfigurationManager.AppSettings["DllPath"];
22 | _dllPath = !string.IsNullOrWhiteSpace(_dllPath)
23 | ? _dllPath
24 | : System.IO.Path.Combine(
25 | Environment.GetFolderPath(
26 | Environment.SpecialFolder.MyDocuments), "AutoCADPlugins", "scripturePlugin.dll");
27 |
28 |
29 | DataContext = this;
30 | }
31 |
32 | public string? _dllPath;
33 | public string? DllPath
34 | {
35 | get => _dllPath;
36 | set
37 | {
38 | if (_dllPath != value)
39 | {
40 | _dllPath = value;
41 | OnPropertyChanged();
42 | }
43 | }
44 | }
45 |
46 | private bool _generatingScript = false;
47 | public bool GeneratingScript
48 | {
49 | get => _generatingScript;
50 | set
51 | {
52 | if (_generatingScript != value)
53 | {
54 | _generatingScript = value;
55 | OnPropertyChanged();
56 |
57 | if (!_generatingScript)
58 | {
59 | ProgressStatusText = "";
60 | _scriptCompiledWithoutErrors = false;
61 | }
62 | }
63 | }
64 | }
65 |
66 | public bool _scriptCompiledWithoutErrors = false;
67 | public bool ScriptCompiledWithoutErrors
68 | {
69 | get => _scriptCompiledWithoutErrors;
70 | set
71 | {
72 | if (_scriptCompiledWithoutErrors != value)
73 | {
74 | _scriptCompiledWithoutErrors = value;
75 | OnPropertyChanged();
76 | }
77 | }
78 | }
79 |
80 | public bool _executingScript = false;
81 | public bool ExecutingScript
82 | {
83 | get => _executingScript;
84 | set
85 | {
86 | if (_executingScript != value)
87 | {
88 | _executingScript = value;
89 | OnPropertyChanged();
90 | }
91 | }
92 | }
93 |
94 | private void SaveDllPathToConfig(string dllPath)
95 | {
96 | var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
97 | config.AppSettings.Settings.Remove("DllPath");
98 | config.AppSettings.Settings.Add("DllPath", dllPath);
99 | config.Save(ConfigurationSaveMode.Modified);
100 |
101 | ConfigurationManager.RefreshSection("appSettings");
102 | }
103 |
104 | private static int _fixErrorAttemptsNumber = 5;
105 | private async void OnGenerateScriptClick(object sender, RoutedEventArgs e)
106 | {
107 | string userPrompt = ScriptDescriptionTextBox.Text;
108 |
109 | if (string.IsNullOrWhiteSpace(userPrompt))
110 | {
111 | ScriptDescriptionTextBox.Text = "Please enter script descripion here";
112 | return;
113 | }
114 |
115 | try
116 | {
117 | GeneratingScript = true;
118 |
119 | var llmServices = ServiceLocator.GetService();
120 |
121 | ProgressStatusText = "Generating initial script";
122 | var generatedScript = await llmServices.GenerateInitialScriptAsync(userPrompt);
123 |
124 | int attemptIndex = 0;
125 | while (true)
126 | {
127 | ScriptEditor.Text = generatedScript;
128 |
129 | Recompile();
130 |
131 | if (attemptIndex == _fixErrorAttemptsNumber
132 | || LastCompilationStatus.Success)
133 | {
134 | if (!LastCompilationStatus.Success)
135 | MainTabControl.SelectedIndex = 1;
136 |
137 | break;
138 |
139 | }
140 |
141 | attemptIndex++;
142 | ProgressStatusText = $"Fixing generated script (attempt {attemptIndex} out of {_fixErrorAttemptsNumber})";
143 |
144 | generatedScript = await llmServices.TryFixScriptAsync(generatedScript, LastCompilationStatus.Errors, false);
145 | }
146 | }
147 | catch (Exception ex)
148 | {
149 | MessageBox.Show(ex.ToString(), "Error");
150 | }
151 | finally
152 | {
153 | GeneratingScript = false;
154 | }
155 | }
156 |
157 | private (bool Success, List Errors) LastCompilationStatus { get; set; }
158 |
159 | private void Recompile()
160 | {
161 | var compiler = ServiceLocator.GetService();
162 |
163 | LastCompilationStatus = compiler.TestCompile(ScriptEditor.Text);
164 | ScriptStatusTextBox.Text = LastCompilationStatus.Success
165 | ? "Successfully compiled"
166 | : $"Compilation failed:{Environment.NewLine} " +
167 | $"{string.Join(Environment.NewLine, LastCompilationStatus.Errors)}";
168 |
169 | if (LastCompilationStatus.Success)
170 | {
171 | MainTabControl.SelectedIndex = 2;
172 |
173 | var commandName = ExtractCommandName(ScriptEditor.Text);
174 | if (commandName != null)
175 | {
176 | CommandNameTextBox.Text = commandName;
177 | }
178 | }
179 |
180 | ScriptCompiledWithoutErrors = LastCompilationStatus.Success;
181 | }
182 |
183 | private void OnRecompileScriptClick(object sender, RoutedEventArgs e)
184 | {
185 | Recompile();
186 | }
187 |
188 | private async void OnTryFixClick(object sender, RoutedEventArgs e)
189 | {
190 | try
191 | {
192 | GeneratingScript = true;
193 |
194 | var llmServices = ServiceLocator.GetService();
195 |
196 | string script = ScriptEditor.Text;
197 |
198 | if (LastCompilationStatus.Errors.Count == 0)
199 | return;
200 |
201 | ProgressStatusText = "Fixing generated script";
202 | var fixedScript = await llmServices.TryFixScriptAsync(script, LastCompilationStatus.Errors, true);
203 |
204 | if (!string.IsNullOrEmpty(fixedScript))
205 | {
206 | ScriptEditor.Text = fixedScript;
207 |
208 | Recompile();
209 | }
210 | else
211 | throw new Exception("No reply");
212 | }
213 | catch (Exception ex)
214 | {
215 | MessageBox.Show(ex.ToString(), "Error");
216 | }
217 | finally
218 | {
219 | GeneratingScript = false;
220 | }
221 | }
222 |
223 | private void OnExecutionModeChanged(object sender, RoutedEventArgs e)
224 | {
225 | if (PluginOptionsPanel is null)
226 | return;
227 |
228 | if (((RadioButton)sender).IsChecked ?? false)
229 | {
230 | var radioButton = sender as RadioButton;
231 |
232 | if (radioButton?.Content.ToString() == "Create Plugin")
233 | {
234 | PluginOptionsPanel.Visibility = Visibility.Visible;
235 | ExecuteScriptButton.Content = "Create Plugin";
236 | }
237 | else
238 | {
239 | PluginOptionsPanel.Visibility = Visibility.Collapsed;
240 | ExecuteScriptButton.Content = "Execute Script";
241 | }
242 | }
243 | }
244 |
245 | private void OnExecuteScriptClick(object sender, RoutedEventArgs e)
246 | {
247 | try
248 | {
249 | string script = ScriptEditor.Text;
250 | var commandName = ExtractCommandName(script);
251 | if (commandName is null)
252 | throw new Exception("Command name not found");
253 |
254 | var compiler = ServiceLocator.GetService();
255 |
256 | if (CreatePluginRadioButton.IsChecked ?? false)
257 | {
258 | if (string.IsNullOrWhiteSpace(DllPath))
259 | {
260 | var res = MessageBox.Show("Enter dll name please", "Error");
261 | return;
262 | }
263 |
264 | if (File.Exists(DllPath))
265 | {
266 | var res = MessageBox.Show("The dll already exists, overwrite?", "Warning", MessageBoxButton.YesNo);
267 | if (res == MessageBoxResult.No)
268 | return;
269 | }
270 |
271 | SaveDllPathToConfig(DllPath);
272 |
273 | var customCommandName = CommandNameTextBox.Text;
274 | if (!string.IsNullOrWhiteSpace(customCommandName))
275 | {
276 | script = ReplaceCommandName(script, commandName, customCommandName);
277 | commandName = customCommandName;
278 | }
279 |
280 | var (success, errors) = compiler.CompileTo(script, DllPath);
281 |
282 | MessageBox.Show(success ? "Succeeded" : $"Failed:\n {string.Join(',', errors)}", "Result");
283 | }
284 | else
285 | {
286 | var tempFileName = Path.Combine(Path.GetTempPath(), $"GeneratedScript_{Guid.NewGuid()}.dll");
287 |
288 | var (success, errors) = compiler.CompileTo(script, tempFileName);
289 |
290 | if (!success)
291 | {
292 | MessageBox.Show($"Failed to compile:#\n{string.Join(',', errors)}", "Result");
293 | return;
294 | }
295 |
296 | var scriptExecutor = ServiceLocator.GetService();
297 | scriptExecutor.Execute(tempFileName, commandName);
298 | }
299 | }
300 | catch (Exception ex)
301 | {
302 | MessageBox.Show(ex.ToString(), "Error");
303 | }
304 | }
305 |
306 | private string _progressStatusText = "";
307 | public string ProgressStatusText
308 | {
309 | get => _progressStatusText;
310 | set
311 | {
312 | if (_progressStatusText != value)
313 | {
314 | _progressStatusText = value;
315 | OnPropertyChanged();
316 | }
317 | }
318 | }
319 |
320 | private string? ExtractCommandName(string script)
321 | {
322 | var match = Regex.Match(script, @"\[CommandMethod\(""([^""]+)""\)\]");
323 | return match.Success ? match.Groups[1].Value : null;
324 | }
325 |
326 | private string ReplaceCommandName(string script, string oldCommandName, string newCommandName)
327 | {
328 | return script.Replace($"[CommandMethod(\"{oldCommandName}\")]", $"[CommandMethod(\"{newCommandName}\")]");
329 | }
330 |
331 | public event PropertyChangedEventHandler? PropertyChanged;
332 |
333 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null!)
334 | {
335 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
336 | }
337 | }
338 | }
339 |
--------------------------------------------------------------------------------
/ScriptureUI/ScriptureUI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | enable
6 | true
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------