├── .config └── tsaoptions.json ├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── .pipelines ├── Build-Official.yml ├── Package-Official.yml ├── Release-Official.yml └── templates │ ├── finalize.yml │ ├── linux-build.yml │ ├── linux-package.yml │ ├── mac-build.yml │ ├── mac-package.yml │ ├── module-build.yml │ ├── module-package.yml │ ├── nupkg-package.yml │ ├── release-msix-bundle.yml │ ├── release-publish-github.yml │ ├── release-publish-module.yml │ ├── release-publish-nuget.yml │ ├── sign-files.yml │ ├── update-nuget-config.yml │ ├── wait-for-approval.yml │ ├── windows-build.yml │ └── windows-package.yml ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── build.psm1 ├── docs ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FAQ.md ├── SECURITY.md ├── SUPPORT.md ├── cmdlets │ ├── AIShell.md │ ├── Invoke-AIShell.md │ ├── Resolve-Error.md │ └── Start-AIShell.md ├── development │ ├── AzureOAIDeployment │ │ ├── DeployingAzureOAI.md │ │ └── main.bicep │ └── CreatingAnAgent.md └── media │ ├── AIShell-overall-arch.png │ ├── DemoGIFs │ ├── Invoke-AIShell.gif │ ├── Resolve-error.gif │ ├── azure-agent.gif │ ├── insert-code.gif │ ├── predictiveintellisensedemo.gif │ ├── standalone-startup.gif │ ├── start-aishell.gif │ └── switch-agents.gif │ └── Icons │ ├── AIShellIconPNG.png │ └── AIShellIconSVG.svg ├── global.json ├── shell ├── AIShell.Abstraction │ ├── AIShell.Abstraction.csproj │ ├── AssemblyInfo.cs │ ├── CommandBase.cs │ ├── IHost.cs │ ├── ILLMAgent.cs │ ├── IRenderElement.cs │ ├── IShell.cs │ ├── IStreamRender.cs │ ├── NamedPipe.cs │ └── UserAction.cs ├── AIShell.App │ ├── AIShell.App.csproj │ └── Program.cs ├── AIShell.Integration │ ├── AIShell.Integration.csproj │ ├── AIShell.psd1 │ ├── AIShell.psm1 │ ├── Channel.cs │ ├── Commands │ │ ├── InvokeAishCommand.cs │ │ ├── ResolveErrorCommand.cs │ │ └── StartAishCommand.cs │ ├── ErrorFeedback.cs │ ├── InitAndCleanup.cs │ ├── Predictor.cs │ └── README.md ├── AIShell.Kernel │ ├── AIShell.Kernel.csproj │ ├── AssemblyInfo.cs │ ├── Command │ │ ├── AgentCommand.cs │ │ ├── ClearCommand.cs │ │ ├── CodeCommand.cs │ │ ├── CommandRunner.cs │ │ ├── DislikeCommand.cs │ │ ├── ExitCommand.cs │ │ ├── HelpCommand.cs │ │ ├── LikeCommand.cs │ │ ├── RefreshCommand.cs │ │ ├── RenderCommand.cs │ │ └── RetryCommand.cs │ ├── Exception.cs │ ├── Host.cs │ ├── LLMAgent.cs │ ├── Render │ │ ├── MarkdownRender.cs │ │ ├── PagingRender.cs │ │ └── StreamRender.cs │ ├── Setting.cs │ ├── Shell.cs │ ├── ShellIntegration │ │ ├── Channel.cs │ │ └── ReadKeyProxy.cs │ └── Utility │ │ ├── Clipboard.cs │ │ ├── LoadContext.cs │ │ ├── ReadLineHelper.cs │ │ ├── ShellArgs.cs │ │ └── Utils.cs ├── Markdown.VT │ ├── ColorCode.VT │ │ ├── Parser │ │ │ ├── Bash.cs │ │ │ ├── Json.cs │ │ │ └── PowerShell.cs │ │ └── VTSyntaxHighlighter.cs │ ├── Markdown.VT.csproj │ └── Render │ │ ├── Blocks │ │ ├── CodeBlockRenderer.cs │ │ ├── HeadingBlockRenderer.cs │ │ ├── ListBlockRenderer.cs │ │ ├── ListItemBlockRenderer.cs │ │ ├── ParagraphBlockRenderer.cs │ │ ├── QuoteBlockRenderer.cs │ │ └── ThematicBreakRenderer.cs │ │ ├── Extensions │ │ └── VTTableRenderer.cs │ │ ├── Inlines │ │ ├── AutolinkInlineRenderer.cs │ │ ├── CodeInlineRenderer.cs │ │ ├── DelimiterInlineRenderer.cs │ │ ├── EmphasisInlineRenderer.cs │ │ ├── LeafInlineRenderer.cs │ │ ├── LineBreakInlineRenderer.cs │ │ ├── LinkInlineRenderer.cs │ │ └── LiteralInlineRenderer.cs │ │ ├── VTEscapeSequences.cs │ │ ├── VTObjectRenderer.cs │ │ └── VTRenderer.cs ├── README.md ├── ReadLine │ ├── Accessibility.cs │ ├── BasicEditing.cs │ ├── CharMap.cs │ ├── Clipboard.cs │ ├── Cmdlets.cs │ ├── Completion.cs │ ├── Completion.vi.cs │ ├── ConsoleKeyChordConverter.cs │ ├── ConsoleLib.cs │ ├── DisplayBlockBase.cs │ ├── Disposable.cs │ ├── History.cs │ ├── HistoryQueue.cs │ ├── KeyBindings.cs │ ├── KeyBindings.vi.cs │ ├── Keys.cs │ ├── KillYank.cs │ ├── Movement.cs │ ├── Movement.vi.cs │ ├── Movement.vi.multiline.cs │ ├── Options.cs │ ├── PSReadLine.csproj │ ├── PSReadLineResources.Designer.cs │ ├── PSReadLineResources.resx │ ├── PlatformWindows.cs │ ├── Position.cs │ ├── Prediction.Entry.cs │ ├── Prediction.Views.cs │ ├── Prediction.cs │ ├── PublicAPI.cs │ ├── ReadLine.cs │ ├── ReadLine.vi.cs │ ├── Render.Helper.cs │ ├── Render.cs │ ├── Replace.vi.cs │ ├── ScreenCapture.cs │ ├── StringBuilderExtensions.cs │ ├── UndoRedo.cs │ ├── UndoRedo.vi.cs │ ├── ViRegister.cs │ ├── VisualEditing.vi.cs │ ├── Words.cs │ ├── Words.vi.cs │ └── YankPaste.vi.cs ├── agents │ ├── AIShell.Interpreter.Agent │ │ ├── AIShell.Interpreter.Agent.csproj │ │ ├── Agent.cs │ │ ├── ExecutionService │ │ │ ├── CodeExecutionService.cs │ │ │ ├── Languages │ │ │ │ ├── PowerShell.cs │ │ │ │ ├── Python.cs │ │ │ │ └── SubprocessLanguage.cs │ │ │ └── OutputData.cs │ │ ├── Helpers.cs │ │ ├── Model │ │ │ ├── BaseModel.cs │ │ │ ├── FunctionCallingModel.cs │ │ │ └── TextBasedModel.cs │ │ ├── ModelInfo.cs │ │ ├── README.md │ │ ├── Service.cs │ │ ├── Settings.cs │ │ ├── TaskCompletionChat.cs │ │ ├── Utility │ │ │ ├── DataPacket.cs │ │ │ ├── TaskCompletionChatPrompts.cs │ │ │ └── ToolResponsePacket.cs │ │ └── assets │ │ │ ├── Interpreter-Agent-Architecture.png │ │ │ ├── InterpreterAgentDemoSpedUp.gif │ │ │ └── InterpreterAgentFlowChart.png │ ├── AIShell.Ollama.Agent │ │ ├── AIShell.Ollama.Agent.csproj │ │ ├── Command.cs │ │ ├── OllamaAgent.cs │ │ ├── README.md │ │ └── Settings.cs │ ├── AIShell.OpenAI.Agent │ │ ├── AIShell.OpenAI.Agent.csproj │ │ ├── Agent.cs │ │ ├── Command.cs │ │ ├── GPT.cs │ │ ├── Helpers.cs │ │ ├── ModelInfo.cs │ │ ├── README.md │ │ ├── Service.cs │ │ └── Settings.cs │ ├── AIShell.PhiSilica.Agent │ │ ├── AIShell.PhiSilica.Agent.csproj │ │ └── PhiSilicaAgent.cs │ └── Microsoft.Azure.Agent │ │ ├── AzureAgent.cs │ │ ├── AzureCopilotReceiver.cs │ │ ├── ChatSession.cs │ │ ├── Command.cs │ │ ├── DataRetriever.cs │ │ ├── Microsoft.Azure.Agent.csproj │ │ ├── README.md │ │ ├── Schema.cs │ │ ├── Telemetry.cs │ │ └── Utils.cs ├── nuget.config ├── shell.common.props └── shell.sln └── tools ├── assets ├── AppxManifest.xml ├── Square150x150Logo-Preview.png ├── Square150x150Logo.png ├── Square44x44Logo-Preview.png ├── Square44x44Logo.png ├── Square44x44Logo.targetsize-48-Preview.png ├── Square44x44Logo.targetsize-48.png ├── Square44x44Logo.targetsize-48_altform-unplated-Preview.png ├── Square44x44Logo.targetsize-48_altform-unplated.png ├── StoreLogo-Preview.png └── StoreLogo.png ├── metadata.json ├── packaging ├── packaging.psd1 ├── packaging.psm1 └── packaging.strings.psd1 └── scripts └── install-aishell.ps1 /.config/tsaoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "instanceUrl": "https://msazure.visualstudio.com", 3 | "projectName": "One", 4 | "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core\\AIShell", 5 | "notificationAliases": [ 6 | "dongbow@microsoft.com", 7 | "slee@microsoft.com" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.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: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "main" ] 9 | pull_request: 10 | branches: [ "main" ] 11 | 12 | jobs: 13 | build-linux: 14 | runs-on: ubuntu-latest 15 | env: 16 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Setup .NET 21 | uses: actions/setup-dotnet@v4 22 | with: 23 | dotnet-version: 8.0.409 24 | - name: Build 25 | shell: pwsh 26 | run: | 27 | Import-Module ./build.psm1 -Force 28 | Start-Build -Verbose 29 | 30 | build-windows: 31 | runs-on: windows-latest 32 | env: 33 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Setup .NET 38 | uses: actions/setup-dotnet@v4 39 | with: 40 | dotnet-version: 8.0.409 41 | - name: Build 42 | shell: pwsh 43 | run: | 44 | Import-Module ./build.psm1 -Force 45 | Start-Build -Verbose 46 | 47 | build-macos: 48 | runs-on: macos-latest 49 | env: 50 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 51 | 52 | steps: 53 | - uses: actions/checkout@v4 54 | - name: Setup .NET 55 | uses: actions/setup-dotnet@v4 56 | with: 57 | dotnet-version: 8.0.409 58 | - name: Build 59 | shell: pwsh 60 | run: | 61 | Import-Module ./build.psm1 -Force 62 | Start-Build -Verbose 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | bin/ 3 | obj/ 4 | .vs/ 5 | .vscode/ 6 | shell/**/*.sln 7 | !shell/shell.sln 8 | -------------------------------------------------------------------------------- /.pipelines/Build-Official.yml: -------------------------------------------------------------------------------- 1 | name: AIShell-Build-$(Build.BuildId) 2 | trigger: none 3 | 4 | parameters: 5 | - name: SKIP_SIGNING 6 | displayName: Skip Signing 7 | type: string 8 | default: 'NO' 9 | 10 | resources: 11 | repositories: 12 | - repository: onebranchTemplates 13 | type: git 14 | name: OneBranch.Pipelines/GovernedTemplates 15 | ref: refs/heads/main 16 | 17 | variables: 18 | - name: DOTNET_CLI_TELEMETRY_OPTOUT 19 | value: 1 20 | - name: POWERSHELL_TELEMETRY_OPTOUT 21 | value: 1 22 | - name: DOTNET_NOLOGO 23 | value: 1 24 | - name: branchCounterKey 25 | value: $[format('{0:yyyyMMdd}-{1}', pipeline.startTime,variables['Build.SourceBranch'])] 26 | - name: branchCounter 27 | value: $[counter(variables['branchCounterKey'], 1)] 28 | - name: BUILDSECMON_OPT_IN 29 | value: true 30 | - name: LinuxContainerImage 31 | value: mcr.microsoft.com/onebranch/cbl-mariner/build:2.0 32 | - name: WindowsContainerImage 33 | value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest 34 | - name: CDP_DEFINITION_BUILD_COUNT 35 | value: $[counter('', 0)] 36 | - name: SKIP_SIGNING 37 | value: ${{ parameters.SKIP_SIGNING }} 38 | - group: mscodehub-feed-read-general 39 | - group: mscodehub-feed-read-akv 40 | 41 | extends: 42 | template: v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates 43 | parameters: 44 | customTags: 'ES365AIMigrationTooling' 45 | featureFlags: 46 | LinuxHostVersion: 47 | Network: KS3 48 | WindowsHostVersion: 49 | Version: 2022 50 | Network: KS3 51 | globalSdl: 52 | disableLegacyManifest: true 53 | # disabled Armorty as we dont have any ARM templates to scan. It fails on some sample ARM templates. 54 | armory: 55 | enabled: false 56 | sbom: 57 | enabled: true 58 | buildComponentPath: $(Build.SourcesDirectory)\AIShell\shell 59 | codeql: 60 | compiled: 61 | enabled: true 62 | cg: 63 | enabled: true 64 | ignoreDirectories: 'docs,test,tools' 65 | asyncSdl: 66 | enabled: true 67 | credscan: 68 | enabled: true 69 | scanFolder: $(Build.SourcesDirectory) 70 | binskim: 71 | enabled: false 72 | apiscan: 73 | enabled: false 74 | tsaOptionsFile: .config\tsaoptions.json 75 | 76 | stages: 77 | - stage: macos 78 | displayName: macOS - build and sign 79 | jobs: 80 | - template: /.pipelines/templates/mac-build.yml@self 81 | parameters: 82 | architecture: x64 83 | - template: /.pipelines/templates/mac-build.yml@self 84 | parameters: 85 | architecture: arm64 86 | 87 | - stage: linux 88 | displayName: linux - build and sign 89 | jobs: 90 | - template: /.pipelines/templates/linux-build.yml@self 91 | parameters: 92 | architecture: x64 93 | - template: /.pipelines/templates/linux-build.yml@self 94 | parameters: 95 | architecture: arm64 96 | 97 | - stage: windows 98 | displayName: windows - build and sign 99 | jobs: 100 | - template: /.pipelines/templates/windows-build.yml@self 101 | parameters: 102 | architecture: x86 103 | - template: /.pipelines/templates/windows-build.yml@self 104 | parameters: 105 | architecture: x64 106 | - template: /.pipelines/templates/windows-build.yml@self 107 | parameters: 108 | architecture: arm64 109 | 110 | - stage: module 111 | displayName: module - build and sign 112 | jobs: 113 | - template: /.pipelines/templates/module-build.yml@self 114 | -------------------------------------------------------------------------------- /.pipelines/Package-Official.yml: -------------------------------------------------------------------------------- 1 | trigger: none 2 | 3 | parameters: # parameters are shown up in ADO UI in a build queue time 4 | - name: 'debug' 5 | displayName: 'Enable debug output' 6 | type: boolean 7 | default: false 8 | 9 | variables: 10 | - name: CDP_DEFINITION_BUILD_COUNT 11 | value: $[counter('', 0)] # needed for onebranch.pipeline.version task 12 | - name: system.debug 13 | value: ${{ parameters.debug }} 14 | - name: ENABLE_PRS_DELAYSIGN 15 | value: 1 16 | - name: ob_outputDirectory 17 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 18 | - name: WindowsContainerImage 19 | value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' # Docker image which is used to build the project 20 | - name: LinuxContainerImage 21 | value: mcr.microsoft.com/onebranch/cbl-mariner/build:2.0 22 | - group: mscodehub-feed-read-general 23 | - group: mscodehub-feed-read-akv 24 | - name: branchCounterKey 25 | value: $[format('{0:yyyyMMdd}-{1}', pipeline.startTime,variables['Build.SourceBranch'])] 26 | - name: branchCounter 27 | value: $[counter(variables['branchCounterKey'], 1)] 28 | 29 | resources: 30 | pipelines: 31 | - pipeline: AIShellBuildPipeline 32 | source: 'AIShell-Build-Official' 33 | trigger: 34 | branches: 35 | include: 36 | - release-* 37 | 38 | repositories: 39 | - repository: templates 40 | type: git 41 | name: OneBranch.Pipelines/GovernedTemplates 42 | ref: refs/heads/main 43 | 44 | extends: 45 | template: v2/OneBranch.Official.CrossPlat.yml@templates 46 | parameters: 47 | cloudvault: 48 | enabled: false 49 | featureFlags: 50 | linuxEsrpSigning: true 51 | LinuxHostVersion: 52 | Network: KS3 53 | WindowsHostVersion: 54 | Version: 2022 55 | Network: KS3 56 | globalSdl: 57 | disableLegacyManifest: true 58 | # disabled Armorty as we dont have any ARM templates to scan. It fails on some sample ARM templates. 59 | armory: 60 | enabled: false 61 | sbom: 62 | enabled: true 63 | compiled: 64 | enabled: false 65 | cg: 66 | enabled: true 67 | ignoreDirectories: 'docs,shell,test,tools' 68 | asyncSdl: 69 | enabled: true 70 | credscan: 71 | enabled: true 72 | scanFolder: $(Build.SourcesDirectory) 73 | binskim: 74 | enabled: false 75 | apiscan: 76 | enabled: false 77 | tsaOptionsFile: .config\tsaoptions.json 78 | 79 | stages: 80 | - stage: mac 81 | jobs: 82 | - template: /.pipelines/templates/mac-package.yml@self 83 | parameters: 84 | architecture: x64 85 | - template: /.pipelines/templates/mac-package.yml@self 86 | parameters: 87 | architecture: arm64 88 | 89 | - stage: windows 90 | jobs: 91 | - template: /.pipelines/templates/windows-package.yml@self 92 | parameters: 93 | architecture: x86 94 | - template: /.pipelines/templates/windows-package.yml@self 95 | parameters: 96 | architecture: x64 97 | - template: /.pipelines/templates/windows-package.yml@self 98 | parameters: 99 | architecture: arm64 100 | 101 | - stage: linux 102 | jobs: 103 | - template: /.pipelines/templates/linux-package.yml@self 104 | parameters: 105 | architecture: x64 106 | - template: /.pipelines/templates/linux-package.yml@self 107 | parameters: 108 | architecture: arm64 109 | 110 | - stage: module 111 | jobs: 112 | - template: /.pipelines/templates/module-package.yml@self 113 | 114 | - stage: nupkg 115 | jobs: 116 | - template: /.pipelines/templates/nupkg-package.yml@self 117 | -------------------------------------------------------------------------------- /.pipelines/templates/finalize.yml: -------------------------------------------------------------------------------- 1 | # This was used before migrating to OneBranch to deal with one of the SDL taks from failing with a warning instead of an error. 2 | steps: 3 | - pwsh: | 4 | throw "Jobs with an Issue will not work for release. Please fix the issue and try again." 5 | displayName: Check for SucceededWithIssues 6 | condition: eq(variables['Agent.JobStatus'],'SucceededWithIssues') 7 | -------------------------------------------------------------------------------- /.pipelines/templates/linux-package.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | architecture: x64 3 | 4 | jobs: 5 | - job: package_${{ parameters.architecture }} 6 | displayName: Package linux ${{ parameters.architecture }} 7 | condition: succeeded() 8 | pool: 9 | type: linux 10 | 11 | variables: 12 | - name: runCodesignValidationInjection 13 | value: false 14 | - name: nugetMultiFeedWarnLevel 15 | value: none 16 | - name: NugetSecurityAnalysisWarningLevel 17 | value: none 18 | - name: skipNugetSecurityAnalysis 19 | value: true 20 | - group: DotNetPrivateBuildAccess 21 | - name: ob_sdl_sbom_enabled 22 | value: true 23 | - name: ob_sdl_codeql_compiled_enabled 24 | value: false 25 | - name: ob_outputDirectory 26 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 27 | - name: repoRoot 28 | value: $(Build.SourcesDirectory)/AIShell 29 | - name: ob_sdl_binskim_enabled 30 | value: true 31 | - name: ob_sdl_tsa_configFile 32 | value: $(repoRoot)/.config/tsaoptions.json 33 | - name: Architecture 34 | value: ${{ parameters.architecture }} 35 | 36 | steps: 37 | - checkout: self 38 | clean: true 39 | env: 40 | ob_restore_phase: true 41 | 42 | - pwsh: | 43 | Get-ChildItem -Path env: 44 | displayName: Capture environment 45 | env: 46 | ob_restore_phase: true 47 | 48 | - download: AIShellBuildPipeline 49 | artifact: drop_linux_sign_$(Architecture) 50 | displayName: 'Download signed artifacts' 51 | env: 52 | ob_restore_phase: true 53 | 54 | - pwsh: | 55 | $signedFilesPath = "$(Pipeline.Workspace)/AIShellBuildPipeline/drop_linux_sign_$(Architecture)" 56 | Write-Verbose -Verbose "Downloaded artifacts: " 57 | Get-ChildItem $signedFilesPath -Recurse | Out-String -Width 500 -Stream 58 | 59 | Write-Verbose -Message "Checking 'aish' exists in '$signedFilesPath'" -Verbose 60 | if (-not (Test-Path $signedFilesPath/aish)) { 61 | throw "aish not found in $signedFilesPath" 62 | } 63 | 64 | $version = (Get-Item "$signedFilesPath/AIShell.Abstraction.dll").VersionInfo.ProductVersion 65 | $vstsCommandString = "vso[task.setvariable variable=PackageVersion]$version" 66 | Write-Host ("sending " + $vstsCommandString) 67 | Write-Host "##$vstsCommandString" 68 | displayName: 'List Downloaded Artifacts' 69 | # Diagnostics is not critical it passes every time it runs 70 | continueOnError: true 71 | env: 72 | ob_restore_phase: true 73 | 74 | - pwsh: | 75 | Import-Module $(repoRoot)/tools/packaging -Force 76 | $signedFilesPath = "$(Pipeline.Workspace)/AIShellBuildPipeline/drop_linux_sign_$(Architecture)" 77 | 78 | $outDir = "$(Pipeline.Workspace)/AIShellBuildPipeline/pkgs" 79 | $null = New-Item -ItemType Directory $outDir 80 | 81 | New-TarballPackage -PackageSourcePath $signedFilesPath -Version $(PackageVersion) -Architecture $(Architecture) -CurrentLocation $outDir -Force 82 | 83 | $vstsCommandString = "vso[task.setvariable variable=PackagePath]$outDir" 84 | Write-Host ("sending " + $vstsCommandString) 85 | Write-Host "##$vstsCommandString" 86 | displayName: 'Package $(Architecture)' 87 | env: 88 | ob_restore_phase: true 89 | 90 | - pwsh: | 91 | if (-not (Test-Path $(ob_outputDirectory))) { 92 | $null = New-Item -ItemType Directory -Path $(ob_outputDirectory) -Force 93 | } 94 | 95 | $pkgFilter = "AIShell-*.tar.gz" 96 | Write-Verbose -Verbose "pkgFilter: $pkgFilter" 97 | 98 | $pkgPath = Get-ChildItem -Path $(PackagePath) -Filter $pkgFilter -File | ForEach-Object FullName 99 | Write-Verbose -Verbose "pkgPath: $pkgPath" 100 | Copy-Item -Path $pkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose 101 | displayName: 'Copy artifacts to output directory' 102 | 103 | - pwsh: | 104 | Get-ChildItem -Path $(ob_outputDirectory) -Recurse | Out-String -Width 500 -Stream 105 | displayName: 'List uploaded artifacts' 106 | -------------------------------------------------------------------------------- /.pipelines/templates/mac-build.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | architecture: 'x64' 3 | 4 | jobs: 5 | - job: build_${{ parameters.architecture }} 6 | displayName: Build macOS ${{ parameters.architecture }} 7 | condition: succeeded() 8 | pool: 9 | type: linux 10 | isCustom: true 11 | name: Azure Pipelines 12 | vmImage: 'macOS-latest' 13 | 14 | variables: 15 | - name: HOMEBREW_NO_ANALYTICS 16 | value: 1 17 | - name: runCodesignValidationInjection 18 | value: false 19 | - name: NugetSecurityAnalysisWarningLevel 20 | value: none 21 | - group: DotNetPrivateBuildAccess 22 | - name: ob_outputDirectory 23 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 24 | - name: repoRoot 25 | value: $(Build.SourcesDirectory) 26 | - name: Architecture 27 | value: ${{ parameters.architecture }} 28 | - name: Runtime 29 | value: 'osx-$(Architecture)' 30 | 31 | steps: 32 | - checkout: self 33 | clean: true 34 | env: 35 | ob_restore_phase: true 36 | 37 | - pwsh: | 38 | Get-ChildItem Env: 39 | displayName: Capture environment variables 40 | env: 41 | ob_restore_phase: true 42 | 43 | - template: /.pipelines/templates/update-nuget-config.yml@self 44 | parameters: 45 | repoRoot: $(repoRoot) 46 | 47 | - pwsh: | 48 | Import-Module $(repoRoot)/build.psm1 -Force 49 | Install-Dotnet 50 | 51 | $runtime = '$(Runtime)' 52 | Write-Verbose "Building with Runtime: $runtime" -Verbose 53 | Start-Build -Runtime $runtime -Configuration Release -Clean -NotIncludeModule -Verbose 54 | 55 | $outputJson = '$(repoRoot)/_build_output_.json' 56 | if (Test-Path $outputJson) { 57 | $result = Get-Content $outputJson | ConvertFrom-Json 58 | Write-Verbose "App path: $($result.App)" -Verbose 59 | 60 | $artifactName = 'macBuild-$(Architecture)' 61 | # Since we are using custom pool for macOS, we need to use artifact.upload to publish the artifacts 62 | Write-Host "##vso[artifact.upload containerfolder=$artifactName;artifactname=$artifactName]$($result.App)" 63 | } 64 | displayName: 'Build macOS' 65 | env: 66 | ob_restore_phase: true 67 | 68 | - template: /.pipelines/templates/finalize.yml@self 69 | 70 | - job: sign_${{ parameters.architecture }} 71 | displayName: Sign_macOS_${{ parameters.architecture }} 72 | condition: succeeded() 73 | dependsOn: build_${{ parameters.architecture }} 74 | pool: 75 | type: windows 76 | variables: 77 | - name: NugetSecurityAnalysisWarningLevel 78 | value: none 79 | - group: DotNetPrivateBuildAccess 80 | - group: certificate_logical_to_actual 81 | - name: ob_outputDirectory 82 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 83 | - name: repoRoot 84 | value: $(Build.SourcesDirectory)\AIShell 85 | - name: ob_sdl_codeSignValidation_enabled 86 | value: true 87 | - name: ob_sdl_tsa_configFile 88 | value: $(repoRoot)\.config\tsaoptions.json 89 | - name: Architecture 90 | value: ${{ parameters.architecture }} 91 | - name: Runtime 92 | value: 'osx-$(Architecture)' 93 | - name: ob_sdl_codeql_compiled_enabled 94 | value: false 95 | - name: ob_sdl_sbom_packageName 96 | value: 'AIShell.MacOS.$(Architecture)' 97 | 98 | steps: 99 | - checkout: self 100 | clean: true 101 | env: 102 | ob_restore_phase: true 103 | 104 | - pwsh: | 105 | Get-ChildItem Env: 106 | displayName: Capture environment variables 107 | env: 108 | ob_restore_phase: true 109 | 110 | - template: /.pipelines/templates/update-nuget-config.yml@self 111 | parameters: 112 | repoRoot: $(repoRoot) 113 | 114 | - pwsh: | 115 | Import-Module $(repoRoot)/build.psm1 -Force 116 | Install-Dotnet 117 | Start-Build -Configuration Release -Clean -NotIncludeModule -Verbose 118 | displayName: 'Build again to get correct SBOM' 119 | env: 120 | ob_restore_phase: true 121 | 122 | - task: DownloadPipelineArtifact@2 123 | inputs: 124 | artifact: 'macBuild-$(Architecture)' 125 | path: '$(Pipeline.Workspace)\macApp' 126 | displayName: Download app build 127 | 128 | - pwsh: | 129 | Get-ChildItem '$(Pipeline.Workspace)\macApp' -Recurse 130 | displayName: 'List Downloaded Artifacts' 131 | # Diagnostics is not critical it passes every time it runs 132 | continueOnError: true 133 | 134 | - template: /.pipelines/templates/sign-files.yml@self 135 | parameters: 136 | appPath: '$(Pipeline.Workspace)\macApp' 137 | repoRoot: '$(repoRoot)' 138 | 139 | - template: /.pipelines/templates/finalize.yml@self 140 | -------------------------------------------------------------------------------- /.pipelines/templates/mac-package.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | architecture: x64 3 | 4 | jobs: 5 | - job: package_${{ parameters.architecture }} 6 | displayName: Package macOS ${{ parameters.architecture }} 7 | condition: succeeded() 8 | pool: 9 | type: linux 10 | isCustom: true 11 | name: Azure Pipelines 12 | vmImage: 'macOS-latest' 13 | 14 | variables: 15 | - name: HOMEBREW_NO_ANALYTICS 16 | value: 1 17 | - name: runCodesignValidationInjection 18 | value: false 19 | - name: nugetMultiFeedWarnLevel 20 | value: none 21 | - name: NugetSecurityAnalysisWarningLevel 22 | value: none 23 | - name: skipNugetSecurityAnalysis 24 | value: true 25 | - group: DotNetPrivateBuildAccess 26 | - name: ob_sdl_sbom_enabled 27 | value: true 28 | - name: ob_sdl_codeql_compiled_enabled 29 | value: false 30 | - name: ob_outputDirectory 31 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 32 | - name: ob_sdl_binskim_enabled 33 | value: true 34 | - name: repoRoot 35 | value: $(Build.SourcesDirectory) 36 | - name: Architecture 37 | value: ${{ parameters.architecture }} 38 | 39 | steps: 40 | - checkout: self 41 | clean: true 42 | 43 | - pwsh: | 44 | Get-ChildItem -Path env: 45 | displayName: Capture environment 46 | 47 | - download: AIShellBuildPipeline 48 | artifact: drop_macos_sign_$(Architecture) 49 | 50 | - pwsh: | 51 | $signedFilesPath = "$(Pipeline.Workspace)/AIShellBuildPipeline/drop_macos_sign_$(Architecture)" 52 | Write-Verbose -Verbose "Downloaded artifacts:" 53 | Get-ChildItem $signedFilesPath -Recurse | Out-String -Width 500 -Stream 54 | 55 | $version = (Get-Item "$signedFilesPath/AIShell.Abstraction.dll").VersionInfo.ProductVersion 56 | $vstsCommandString = "vso[task.setvariable variable=PackageVersion]$version" 57 | Write-Host ("sending " + $vstsCommandString) 58 | Write-Host "##$vstsCommandString" 59 | displayName: 'List downloaded files' 60 | # Diagnostics is not critical it passes every time it runs 61 | continueOnError: true 62 | 63 | - pwsh: | 64 | Import-Module $(repoRoot)/tools/packaging -Force 65 | $signedFilesPath = "$(Pipeline.Workspace)/AIShellBuildPipeline/drop_macos_sign_$(Architecture)" 66 | 67 | Write-Verbose -Message "Checking 'aish' exists in '$signedFilesPath'" -Verbose 68 | if (-not (Test-Path $signedFilesPath/aish)) { 69 | throw "aish not found in $signedFilesPath" 70 | } 71 | 72 | $macosRuntime = "osx-$(Architecture)" 73 | $outDir = "$(Pipeline.Workspace)/AIShellBuildPipeline/pkgs" 74 | $null = New-Item -ItemType Directory $outDir 75 | 76 | New-TarballPackage -PackageSourcePath $signedFilesPath -Version $(PackageVersion) -Architecture $(Architecture) -CurrentLocation $outDir -Force 77 | $tarPkgNameFilter = "AIShell-*$macosRuntime.tar.gz" 78 | $tarPkgPath = Get-ChildItem -Path $outDir -Filter $tarPkgNameFilter -Recurse -File | ForEach-Object FullName 79 | Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$tarPkgPath" 80 | displayName: 'Package $(Architecture)' 81 | -------------------------------------------------------------------------------- /.pipelines/templates/module-package.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: package 3 | displayName: Package AIShell module 4 | condition: succeeded() 5 | pool: 6 | type: linux 7 | 8 | variables: 9 | - name: runCodesignValidationInjection 10 | value: false 11 | - name: nugetMultiFeedWarnLevel 12 | value: none 13 | - name: NugetSecurityAnalysisWarningLevel 14 | value: none 15 | - name: skipNugetSecurityAnalysis 16 | value: true 17 | - group: DotNetPrivateBuildAccess 18 | - group: certificate_logical_to_actual 19 | - name: ob_sdl_sbom_enabled 20 | value: true 21 | - name: ob_outputDirectory 22 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 23 | - name: repoRoot 24 | value: $(Build.SourcesDirectory)/AIShell 25 | - name: ob_sdl_binskim_enabled 26 | value: true 27 | - name: ob_sdl_tsa_configFile 28 | value: $(repoRoot)/.config/tsaoptions.json 29 | - name: ob_signing_setup_enabled 30 | value: true 31 | - name: ob_sdl_codeql_compiled_enabled 32 | value: false 33 | 34 | steps: 35 | - checkout: self 36 | clean: true 37 | env: 38 | ob_restore_phase: true 39 | 40 | - pwsh: | 41 | Get-ChildItem -Path env: 42 | displayName: Capture environment 43 | env: 44 | ob_restore_phase: true 45 | 46 | - download: AIShellBuildPipeline 47 | artifact: drop_module_sign 48 | displayName: Download signed artifacts 49 | env: 50 | ob_restore_phase: true 51 | 52 | - pwsh: | 53 | $signedFilePath = "$(Pipeline.Workspace)/AIShellBuildPipeline/drop_module_sign" 54 | Write-Verbose -Verbose "Downloaded artifacts: " 55 | Get-ChildItem $signedFilePath -Recurse | Out-String -Width 500 -Stream 56 | 57 | $moduleFolder = "$(Pipeline.Workspace)/AIShellBuildPipeline/AIShell" 58 | $null = New-Item -ItemType Directory -Path $moduleFolder -Force 59 | 60 | Write-Verbose -Verbose "Delete SBOM files ..." 61 | Remove-Item -Path "$signedFilePath/_manifest" -Recurse -Force 62 | 63 | Write-Verbose -Verbose "Move module files to the 'AIShell' module folder: " 64 | Copy-Item -Path "$signedFilePath/*" -Destination $moduleFolder -Recurse -Force -Verbose 65 | Get-ChildItem $moduleFolder -Recurse | Out-String -Width 500 -Stream 66 | displayName: 'List downloaded files' 67 | # Diagnostics is not critical it passes every time it runs 68 | continueOnError: true 69 | env: 70 | ob_restore_phase: true 71 | 72 | - pwsh: | 73 | $moduleFolder = "$(Pipeline.Workspace)/AIShellBuildPipeline/AIShell" 74 | $nugetPath = "$(Pipeline.Workspace)/AIShellBuildPipeline/NuGetPackage" 75 | 76 | New-Item -Path $nugetPath -ItemType Directory > $null 77 | $vstsCommandString = "vso[task.setvariable variable=NugetPath]$nugetPath" 78 | Write-Host ("sending " + $vstsCommandString) 79 | Write-Host "##$vstsCommandString" 80 | 81 | try { 82 | $RepoName = "PSRLLocal" 83 | Register-PSResourceRepository -Name $RepoName -Uri $nugetPath -Trusted 84 | Publish-PSResource -Repository $RepoName -Path $moduleFolder -SkipModuleManifestValidate 85 | } finally { 86 | Unregister-PSResourceRepository -Name $RepoName -ErrorAction SilentlyContinue 87 | } 88 | Get-ChildItem -Path $nugetPath | Out-String -Width 500 -Stream 89 | displayName: 'Create the NuGet package' 90 | env: 91 | ob_restore_phase: true 92 | 93 | - task: onebranch.pipeline.signing@1 94 | displayName: Sign module nupkg 95 | inputs: 96 | command: 'sign' 97 | signing_profile: external_distribution 98 | files_to_sign: '*.nupkg' 99 | search_root: $(NugetPath) 100 | 101 | - task: CopyFiles@2 102 | displayName: Upload module nupkg 103 | inputs: 104 | SourceFolder: $(NugetPath) 105 | Contents: '**/*' 106 | TargetFolder: $(ob_outputDirectory) 107 | -------------------------------------------------------------------------------- /.pipelines/templates/nupkg-package.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: package 3 | displayName: Package NuPkgs 4 | condition: succeeded() 5 | pool: 6 | type: windows 7 | 8 | variables: 9 | - name: runCodesignValidationInjection 10 | value: false 11 | - name: nugetMultiFeedWarnLevel 12 | value: none 13 | - name: NugetSecurityAnalysisWarningLevel 14 | value: none 15 | - name: skipNugetSecurityAnalysis 16 | value: true 17 | - name: ob_outputDirectory 18 | value: '$(Build.ArtifactStagingDirectory)\ONEBRANCH_ARTIFACT' 19 | - name: repoRoot 20 | value: $(Build.SourcesDirectory)\AIShell 21 | - name: ob_sdl_binskim_enabled 22 | value: true 23 | - name: ob_sdl_tsa_configFile 24 | value: $(repoRoot)\.config\tsaoptions.json 25 | - group: mscodehub-feed-read-general 26 | - group: mscodehub-feed-read-akv 27 | - group: DotNetPrivateBuildAccess 28 | - name: ob_sdl_sbom_enabled 29 | value: true 30 | - name: ob_sdl_codeql_compiled_enabled 31 | value: false 32 | 33 | steps: 34 | - checkout: self 35 | clean: true 36 | env: 37 | ob_restore_phase: true 38 | 39 | - pwsh: | 40 | Get-ChildItem -Path env: 41 | displayName: Capture environment 42 | env: 43 | ob_restore_phase: true 44 | 45 | - download: AIShellBuildPipeline 46 | artifact: drop_windows_build_x64 47 | displayName: 'Download drop_windows_build_x64' 48 | env: 49 | ob_restore_phase: true 50 | 51 | - pwsh: | 52 | $signedFilesPath = "$(Pipeline.Workspace)\AIShellBuildPipeline\drop_windows_build_x64" 53 | Write-Verbose -Verbose "Downloaded artifacts: " 54 | Get-ChildItem $signedFilesPath -Recurse 55 | 56 | $version = (Get-Item "$signedFilesPath\AIShell.Abstraction.dll").VersionInfo.ProductVersion 57 | $vstsCommandString = "vso[task.setvariable variable=PackageVersion]$version" 58 | Write-Host ("sending " + $vstsCommandString) 59 | Write-Host "##$vstsCommandString" 60 | displayName: 'Capture download artifacts' 61 | env: 62 | ob_restore_phase: true 63 | 64 | - task: NuGetToolInstaller@1 65 | displayName: 'Install NuGet.exe' 66 | env: 67 | ob_restore_phase: true 68 | 69 | - pwsh: | 70 | Import-Module $(repoRoot)\tools\packaging -Force 71 | $signedFilesPath = "$(Pipeline.Workspace)\AIShellBuildPipeline\drop_windows_build_x64" 72 | $outDir = "$(Pipeline.Workspace)\AIShellBuildPipeline\pkgs" 73 | 74 | $null = New-Item -ItemType Directory $outDir 75 | New-NugetPackage -PackageSourcePath $signedFilesPath -Version $(PackageVersion) -PackageDestinationPath $outDir 76 | 77 | $vstsCommandString = "vso[task.setvariable variable=PackagePath]$outDir" 78 | Write-Host ("sending " + $vstsCommandString) 79 | Write-Host "##$vstsCommandString" 80 | displayName: 'Create NuGet Package for single file' 81 | env: 82 | ob_restore_phase: true 83 | 84 | - task: onebranch.pipeline.signing@1 85 | displayName: Sign nupkg files 86 | inputs: 87 | command: 'sign' 88 | cp_code: 'CP-401405' 89 | files_to_sign: '**\*.nupkg' 90 | search_root: $(PackagePath) 91 | 92 | - pwsh: | 93 | if (-not (Test-Path '$(ob_outputDirectory)')) { 94 | New-Item -ItemType Directory -Path '$(ob_outputDirectory)' -Force 95 | } 96 | 97 | Write-Verbose -Verbose "Copying nupkgs to output directory" 98 | Get-ChildItem -Path $(PackagePath) -Recurse | Copy-Item -Destination '$(ob_outputDirectory)' -Force -Verbose 99 | displayName: Copy artifacts to output directory 100 | 101 | - pwsh: | 102 | $nupkgOutputPath = '$(ob_outputDirectory)' 103 | Get-ChildItem -Path $nupkgOutputPath | Out-String | Write-Verbose -Verbose 104 | displayName: List artifacts 105 | -------------------------------------------------------------------------------- /.pipelines/templates/release-msix-bundle.yml: -------------------------------------------------------------------------------- 1 | jobs: 2 | - job: CreateMSIXBundle 3 | displayName: Create .msixbundle file 4 | pool: 5 | type: windows 6 | 7 | variables: 8 | - group: msixTools 9 | - group: 'Azure Blob variable group' 10 | - name: ob_outputDirectory 11 | value: '$(Build.ArtifactStagingDirectory)\ONEBRANCH_ARTIFACT' 12 | 13 | steps: 14 | - download: AIShellPackagePipeline 15 | artifact: drop_windows_package_arm64 16 | displayName: Download arm64 msix 17 | patterns: '**/*.msix' 18 | 19 | - download: AIShellPackagePipeline 20 | artifact: drop_windows_package_x64 21 | displayName: Download x64 msix 22 | patterns: '**/*.msix' 23 | 24 | - download: AIShellPackagePipeline 25 | artifact: drop_windows_package_x86 26 | displayName: Download x86 msix 27 | patterns: '**/*.msix' 28 | 29 | - pwsh: | 30 | $cmd = Get-Command makeappx.exe -ErrorAction Ignore 31 | if ($cmd) { 32 | Write-Verbose -Verbose 'makeappx available in PATH' 33 | $exePath = $cmd.Source 34 | } else { 35 | $toolsDir = '$(Pipeline.Workspace)\releasePipeline\tools' 36 | New-Item $toolsDir -Type Directory -Force > $null 37 | Invoke-RestMethod -Uri '$(makeappUrl)' -OutFile "$toolsDir\makeappx.zip" 38 | Expand-Archive "$toolsDir\makeappx.zip" -DestinationPath "$toolsDir\makeappx" -Force 39 | $exePath = "$toolsDir\makeappx\makeappx.exe" 40 | 41 | Write-Verbose -Verbose 'makeappx was installed:' 42 | Get-ChildItem -Path $toolsDir -Recurse 43 | } 44 | 45 | $vstsCommandString = "vso[task.setvariable variable=MakeAppxPath]$exePath" 46 | Write-Host "sending " + $vstsCommandString 47 | Write-Host "##$vstsCommandString" 48 | displayName: Install makeappx tool 49 | retryCountOnTaskFailure: 1 50 | 51 | - pwsh: | 52 | $sourceDir = '$(Pipeline.Workspace)\releasePipeline\msix' 53 | $null = New-Item -Path $sourceDir -ItemType Directory -Force 54 | 55 | $msixFiles = Get-ChildItem -Path "$(Pipeline.Workspace)\AIShellPackagePipeline\*.msix" -Recurse 56 | foreach ($msixFile in $msixFiles) { 57 | $null = Copy-Item -Path $msixFile.FullName -Destination $sourceDir -Force -Verbose 58 | } 59 | 60 | $file = Get-ChildItem $sourceDir | Select-Object -First 1 61 | $prefix = ($file.BaseName -split "-win")[0] 62 | $pkgName = "$prefix.msixbundle" 63 | Write-Verbose -Verbose "Creating $pkgName" 64 | 65 | $makeappx = '$(MakeAppxPath)' 66 | $outputDir = "$sourceDir\output" 67 | New-Item $outputDir -Type Directory -Force > $null 68 | & $makeappx bundle /d $sourceDir /p "$outputDir\$pkgName" 69 | 70 | Get-ChildItem -Path $sourceDir -Recurse | Out-String -Width 500 -Stream 71 | 72 | $vstsCommandString = "vso[task.setvariable variable=BundleDir]$outputDir" 73 | Write-Host "sending " + $vstsCommandString 74 | Write-Host "##$vstsCommandString" 75 | displayName: Create MsixBundle 76 | retryCountOnTaskFailure: 1 77 | 78 | - pwsh: | 79 | if (-not (Test-Path '$(ob_outputDirectory)')) { 80 | New-Item -ItemType Directory -Path '$(ob_outputDirectory)' -Force 81 | } 82 | 83 | Write-Verbose -Verbose "Copying msixbundle to output directory" 84 | if ($env:BundleDir) { 85 | $bundleFile = (Get-Item "$env:BundleDir\*.msixbundle").FullName 86 | Copy-Item -Path $bundleFile -Destination '$(ob_outputDirectory)' -Force -Verbose 87 | } 88 | else{ 89 | throw "BundleDir not found" 90 | } 91 | displayName: Copy msixbundle to output directory 92 | -------------------------------------------------------------------------------- /.pipelines/templates/release-publish-module.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: publish 3 | default: false 4 | type: boolean 5 | 6 | jobs: 7 | - job: ModulePublish 8 | displayName: Publish to PSGallery 9 | pool: 10 | type: release 11 | os: windows 12 | templateContext: 13 | inputs: 14 | - input: pipelineArtifact 15 | pipeline: AIShellPackagePipeline 16 | artifactName: drop_module_package 17 | variables: 18 | # Disable SBOM, signing, and codeQL for this job 19 | - name: ob_sdl_sbom_enabled 20 | value: false 21 | - name: ob_signing_setup_enabled 22 | value: false 23 | - name: ob_sdl_codeql_compiled_enabled 24 | value: false 25 | 26 | steps: 27 | - task: PowerShell@2 28 | inputs: 29 | targetType: 'inline' 30 | script: | 31 | Get-ChildItem '$(Pipeline.Workspace)/*.nupkg' -recurse 32 | displayName: List nupkg package 33 | 34 | - task: NuGetCommand@2 35 | displayName: Push AIShell module to PSGallery feed 36 | condition: and(ne('${{ parameters.publish }}', 'false'), succeeded()) 37 | inputs: 38 | command: push 39 | packagesToPush: '$(Pipeline.Workspace)/*.nupkg' 40 | nuGetFeedType: external 41 | publishFeedCredentials: PowerShellGallery-dongbow 42 | -------------------------------------------------------------------------------- /.pipelines/templates/release-publish-nuget.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: publish 3 | default: false 4 | type: boolean 5 | 6 | jobs: 7 | - job: NuGetPublish 8 | displayName: Publish to NuGet 9 | condition: succeeded() 10 | pool: 11 | type: release 12 | os: windows 13 | templateContext: 14 | inputs: 15 | - input: pipelineArtifact 16 | pipeline: AIShellPackagePipeline 17 | artifactName: drop_nupkg_package 18 | variables: 19 | # Disable SBOM, signing, and codeQL for this job 20 | - name: ob_sdl_sbom_enabled 21 | value: false 22 | - name: ob_signing_setup_enabled 23 | value: false 24 | - name: ob_sdl_codeql_compiled_enabled 25 | value: false 26 | 27 | steps: 28 | - task: PowerShell@2 29 | inputs: 30 | targetType: 'inline' 31 | script: | 32 | Get-ChildItem '$(Pipeline.Workspace)/*.nupkg' -recurse 33 | displayName: List nupkg package 34 | 35 | - task: NuGetCommand@2 36 | displayName: 'NuGet push' 37 | condition: and(ne('${{ parameters.publish }}', 'false'), succeeded()) 38 | inputs: 39 | command: push 40 | packagesToPush: '$(Pipeline.Workspace)/*.nupkg' 41 | nuGetFeedType: external 42 | publishFeedCredentials: PowerShellNuGetOrgPush 43 | -------------------------------------------------------------------------------- /.pipelines/templates/sign-files.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | appPath: '' 3 | repoRoot: '' 4 | 5 | steps: 6 | - pwsh: | 7 | $appPath = '${{ parameters.appPath }}' 8 | $repoRoot = '${{ parameters.repoRoot }}' 9 | $singing1P = "$(Pipeline.Workspace)/singing1P" 10 | 11 | Import-Module $repoRoot/build.psm1 -Force 12 | Copy-1PFilesToSign -SourceRoot $appPath -TargetRoot $singing1P 13 | displayName: 'Prepare 1st-party files for signing' 14 | 15 | - task: onebranch.pipeline.signing@1 16 | displayName: Sign 1st-party files 17 | inputs: 18 | command: 'sign' 19 | signing_profile: external_distribution 20 | files_to_sign: '**\*.psd1;**\*.psm1;**\*.ps1xml;**\*.ps1;**\*.dll;**\*.exe' 21 | search_root: $(Pipeline.Workspace)/singing1P 22 | 23 | - pwsh: | 24 | $appPath = '${{ parameters.appPath }}' 25 | $repoRoot = '${{ parameters.repoRoot }}' 26 | $singing1P = "$(Pipeline.Workspace)/singing1P" 27 | 28 | Import-Module $repoRoot/build.psm1 -Force 29 | Copy-SignedFileBack -SourceRoot $singing1P -TargetRoot $appPath 30 | displayName: 'Copy signed 1st-party files back' 31 | 32 | - pwsh: | 33 | $appPath = '${{ parameters.appPath }}' 34 | $repoRoot = '${{ parameters.repoRoot }}' 35 | $singing3P = "$(Pipeline.Workspace)/singing3P" 36 | 37 | Import-Module $repoRoot/build.psm1 -Force 38 | Copy-3PFilesToSign -SourceRoot $appPath -TargetRoot $singing3P 39 | displayName: 'Prepare 3rd-party files for signing' 40 | 41 | - task: onebranch.pipeline.signing@1 42 | displayName: Sign 3rd-party files 43 | inputs: 44 | command: 'sign' 45 | signing_profile: $(msft_3rd_party_cert_id) 46 | files_to_sign: '**\*.dll;**\*.exe' 47 | search_root: $(Pipeline.Workspace)/singing3P 48 | 49 | - pwsh: | 50 | $appPath = '${{ parameters.appPath }}' 51 | $repoRoot = '${{ parameters.repoRoot }}' 52 | $singing3P = "$(Pipeline.Workspace)/singing3P" 53 | 54 | Import-Module $repoRoot/build.psm1 -Force 55 | Copy-SignedFileBack -SourceRoot $singing3P -TargetRoot $appPath 56 | displayName: 'Copy signed 3rd-party files back' 57 | 58 | - task: CopyFiles@2 59 | displayName: "Upload signed files" 60 | inputs: 61 | SourceFolder: ${{ parameters.appPath }} 62 | Contents: '**\*' 63 | TargetFolder: $(ob_outputDirectory) 64 | 65 | - template: /.pipelines/templates/finalize.yml@self 66 | -------------------------------------------------------------------------------- /.pipelines/templates/update-nuget-config.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: "repoRoot" 3 | default: $(REPOROOT) 4 | 5 | steps: 6 | - task: NuGetAuthenticate@1 7 | displayName: Install Azure Artifacts Credential Provider 8 | inputs: 9 | forceReinstallCredentialProvider: true 10 | 11 | - pwsh: | 12 | Import-Module ${{ parameters.repoRoot }}/build.psm1 -Force 13 | Write-Verbose -Verbose "Running: Set-NuGetSourceCred -UserName '$(AzDevopsFeedUserNameKVPAT)' -ClearTextPAT '$(powershellPackageReadPat)'" 14 | Set-NuGetSourceCred -UserName '$(AzDevopsFeedUserNameKVPAT)' -ClearTextPAT '$(powershellPackageReadPat)' 15 | displayName: 'Add AzFeed credential' 16 | env: 17 | ob_restore_phase: true 18 | 19 | - pwsh: | 20 | Get-ChildItem ${{ parameters.repoRoot }}/nuget.config -Recurse | Foreach-Object { 21 | Write-Verbose -Verbose "--- START $($_.fullname) ---" 22 | Get-Content $_.fullname | Out-String -width 500 -Stream | write-Verbose -Verbose 23 | Write-Verbose -Verbose "--- END $($_.fullname) ---" 24 | } 25 | displayName: 'Capture all nuget.config files' 26 | env: 27 | ob_restore_phase: true 28 | -------------------------------------------------------------------------------- /.pipelines/templates/wait-for-approval.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | - name: displayName 3 | type: string 4 | - name: instructions 5 | type: string 6 | - name: jobName 7 | type: string 8 | default: approval 9 | - name: timeoutInMinutes 10 | type: number 11 | # 2 days 12 | default: 2880 13 | - name: onTimeout 14 | type: string 15 | default: 'reject' 16 | values: 17 | - resume 18 | - reject 19 | - name: dependsOnJob 20 | type: string 21 | default: '' 22 | 23 | jobs: 24 | - job: ${{ parameters.jobName }} 25 | dependsOn: ${{ parameters.dependsOnJob }} 26 | displayName: ${{ parameters.displayName }} 27 | pool: 28 | type: agentless 29 | timeoutInMinutes: 4320 # job times out in 3 days 30 | steps: 31 | - task: ManualValidation@0 32 | displayName: ${{ parameters.displayName }} 33 | timeoutInMinutes: ${{ parameters.timeoutInMinutes }} 34 | inputs: 35 | instructions: ${{ parameters.instructions }} 36 | onTimeout: ${{ parameters.onTimeout }} 37 | -------------------------------------------------------------------------------- /.pipelines/templates/windows-build.yml: -------------------------------------------------------------------------------- 1 | parameters: 2 | architecture: 'x64' 3 | 4 | jobs: 5 | - job: build_${{ parameters.architecture }} 6 | displayName: Build Windows ${{ parameters.architecture }} 7 | condition: succeeded() 8 | pool: 9 | type: windows 10 | variables: 11 | - name: runCodesignValidationInjection 12 | value: false 13 | - name: NugetSecurityAnalysisWarningLevel 14 | value: none 15 | - group: DotNetPrivateBuildAccess 16 | - group: certificate_logical_to_actual 17 | - name: ob_outputDirectory 18 | value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' 19 | - name: repoRoot 20 | value: $(Build.SourcesDirectory)\AIShell 21 | - name: ob_sdl_codeSignValidation_enabled 22 | value: false 23 | - name: ob_sdl_binskim_enabled 24 | value: true 25 | - name: ob_sdl_tsa_configFile 26 | value: $(repoRoot)\.config\tsaoptions.json 27 | - name: Architecture 28 | value: ${{ parameters.architecture }} 29 | - name: Runtime 30 | value: 'win-$(Architecture)' 31 | - name: ob_sdl_sbom_packageName 32 | value: 'AIShell.Windows.${{ parameters.architecture }}' 33 | #CodeQL tasks added manually to workaround signing failures 34 | - name: ob_sdl_codeql_compiled_enabled 35 | value: false 36 | 37 | steps: 38 | - checkout: self 39 | clean: true 40 | env: 41 | ob_restore_phase: true 42 | 43 | - pwsh: | 44 | Get-ChildItem Env: 45 | displayName: Capture environment variables 46 | env: 47 | ob_restore_phase: true 48 | 49 | - template: /.pipelines/templates/update-nuget-config.yml@self 50 | parameters: 51 | repoRoot: $(repoRoot) 52 | 53 | # Add CodeQL Init task right before your 'Build' step. 54 | - task: CodeQL3000Init@0 55 | env: 56 | ob_restore_phase: true 57 | inputs: 58 | Enabled: true 59 | Language: csharp 60 | 61 | - pwsh: | 62 | Import-Module $(repoRoot)/build.psm1 -Force 63 | Install-Dotnet 64 | 65 | $runtime = '$(Runtime)' 66 | Write-Verbose "Building with Runtime: $runtime" -Verbose 67 | Start-Build -Runtime $runtime -Configuration Release -Clean -NotIncludeModule -Verbose 68 | 69 | $outputJson = '$(repoRoot)/_build_output_.json' 70 | if (Test-Path $outputJson) { 71 | $result = Get-Content $outputJson | ConvertFrom-Json 72 | Write-Verbose "App path: $($result.App)" -Verbose 73 | 74 | $vstsCommandString = "vso[task.setvariable variable=AppDir]$($result.App)" 75 | Write-Host ("sending " + $vstsCommandString) 76 | Write-Host "##$vstsCommandString" 77 | } 78 | displayName: 'Build Windows-$(Architecture)' 79 | env: 80 | ob_restore_phase: true 81 | 82 | # Add CodeQL Finalize task right after your 'Build' step. 83 | - task: CodeQL3000Finalize@0 84 | env: 85 | ob_restore_phase: true 86 | 87 | - pwsh: | 88 | Get-ChildItem $(AppDir) | Out-String -width 500 -Stream 89 | displayName: 'List build artifacts' 90 | env: 91 | ob_restore_phase: true 92 | 93 | - template: /.pipelines/templates/sign-files.yml@self 94 | parameters: 95 | appPath: '$(AppDir)' 96 | repoRoot: '$(repoRoot)' 97 | 98 | - template: /.pipelines/templates/finalize.yml@self 99 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Attach", 9 | "type": "coreclr", 10 | "request": "attach" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Microsoft Corporation 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. -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) 11 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to AI Shell 2 | 3 | Thank you for your interest in contributing to AI Shell! We aren't currently accepting pull 4 | requests, but we highly value your contributions in other forms. 5 | 6 | ## Reporting Issues 7 | 8 | Issues are a great way to contribute to the project. By creating a detailed report, you help us 9 | understand and prioritize what needs attention. Whether it's a bug or a feature request, your 10 | reports play a crucial role in improving AI Shell. 11 | 12 | ### How to Report an Issue 13 | 14 | Before creating an issue, please do the following: 15 | 16 | 1. **Search for existing issues.** To avoid duplicates, please check to see if someone else has 17 | reported the same issue. 18 | 2. **Check if the issue has been fixed.** Try to reproduce the issue using the latest `main` branch 19 | to see if it has already been addressed. 20 | 21 | If you've determined that your issue is unique and current, you can proceed to create a new issue. 22 | Please follow the issue templates provided. 23 | 24 | ## Other Ways to Contribute 25 | 26 | While we aren't accepting pull requests, there are other ways to contribute: 27 | 28 | - Reviewing documentation for typos or clarity 29 | - Participating in discussions on issues 30 | - Providing use cases or additional information on existing issues 31 | -------------------------------------------------------------------------------- /docs/FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently Asked Questions 2 | 3 | This page provides help with common questions about AIS hell. 4 | 5 | ## What is AI Shell? 6 | 7 | **AI Shell** is a platform that provides a framework for developers to build their own AI 8 | Agents and assistance providers for an AI Shell. Agents provide the user experience for the LLM and are 9 | deeply connected to PowerShell 7. For more about the architecture, see the 10 | [shell/README][01]. 11 | 12 | ## What are agents? 13 | 14 | An agent is a library that implements the user interface that talks to a specific language 15 | model or other assistance provider. Users can interact with these agents in a conversational manner, 16 | using natural language, to get the desired output or assistance. Currently, these are the supported 17 | agents: 18 | 19 | Agent README files: 20 | 21 | - [`openai-gpt`][04] 22 | - [`ollama`][02] 23 | - [`interpreter`][03] 24 | 25 | An assistance provider is an agent that provides user assistance without using a language 26 | model or AI engine. 27 | 28 | ## What operating systems are supported? 29 | 30 | We have tested on macOS and Windows operating systems. **AI Shell** may work on linux but we 31 | haven't tested it can't guarantee that all features will work as expected. 32 | 33 | ## How do I get a split pane experience in my Terminal? 34 | 35 | The ability to run `aish` in a split pane depends on the capabilities of your terminal. For example, 36 | Windows Terminal can be split by running the following command: `wt -w 0 sp`. Refer to the 37 | documentation for your terminal application to see if it supports this feature. 38 | 39 | > [!NOTE] 40 | > Not all terminal applications support this feature. 41 | 42 | 43 | [01]: ../shell/README.md 44 | [02]: ../shell/agents/AIShell.Ollama.Agent/README.md 45 | [03]: ../shell/agents/AIShell.Interpreter.Agent/README.md 46 | [04]: ../shell/agents/AIShell.OpenAI.Agent/README.md 47 | -------------------------------------------------------------------------------- /docs/SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin) and [PowerShell](https://github.com/PowerShell). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /docs/SUPPORT.md: -------------------------------------------------------------------------------- 1 | ## Microsoft support policy 2 | 3 | Support for this project is limited to the following resources: 4 | 5 | This project uses GitHub issues to track bugs and feature requests. To avoid duplicates, please 6 | search existing [issues][04] before filing new issues. If there is no existing issue, file your bug 7 | or feature request as a [new issue][05]. 8 | 9 | ## Read the existing documentation 10 | 11 | For other help and questions please see: 12 | 13 | - [Repository README][01] 14 | - [AI Shell architecture][02] 15 | - [FAQ][03] 16 | - Agent README files: 17 | - [`openai-gpt`][08] 18 | - [`interpreter`][07] 19 | 20 | 21 | [01]: ../README.md 22 | [02]: ../shell/README.md 23 | [03]: ./FAQ.md 24 | [04]: https://github.com/PowerShell/AIShell/issues 25 | [05]: https://github.com/PowerShell/AIShell/issues/new/choose 26 | [07]: ../shell/agents/AIShell.Interpreter.Agent/README.md 27 | [08]: ../shell/agents/AIShell.OpenAI.Agent/README.md 28 | -------------------------------------------------------------------------------- /docs/cmdlets/AIShell.md: -------------------------------------------------------------------------------- 1 | --- 2 | Module Name: AIShell 3 | Module Guid: ecb8bee0-59b9-4dae-9d7b-a990b480279a 4 | Download Help Link: 5 | Help Version: 0.1.0 6 | ms.date: 06/17/2024 7 | Locale: en-US 8 | --- 9 | 10 | # AIShell Module 11 | 12 | ## Description 13 | 14 | This is a module to create a deeper connection between PowerShell 7 and AIShell. This module creates 15 | a communication channel between PowerShell and AIShell to allow for sharing of information like 16 | queries, errors and results from AIShell. 17 | 18 | ## AIShell Cmdlets 19 | 20 | ### [Invoke-AIShell](Invoke-AIShell.md) 21 | 22 | When an AIShell session is available, this cmdlet sends a query to the AIShell window. Results are 23 | shown in the AIShell window. 24 | 25 | ### [Resolve-Error](Resolve-Error.md) 26 | 27 | Cmdlet to take the last error in the current session and send it to the open AIShell window for 28 | resolution. 29 | 30 | ### [Start-AIShell](Start-AIShell.md) 31 | 32 | Starts an AIShell session in a split pane window of Windows Terminal and iTerm2 with a connected 33 | communication channel to the PowerShell session that started it. 34 | -------------------------------------------------------------------------------- /docs/cmdlets/Invoke-AIShell.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: AIShell.Integration.dll-Help.xml 3 | Module Name: AIShell 4 | online version: 5 | ms.date: 06/17/2024 6 | schema: 2.0.0 7 | --- 8 | 9 | # Invoke-AIShell 10 | 11 | ## SYNOPSIS 12 | When an AIShell session is available, this cmdlet sends a query to the AIShell window. Results are 13 | shown in the AIShell window. 14 | 15 | ## SYNTAX 16 | 17 | ### Default (Default) 18 | 19 | ``` 20 | Invoke-AIShell [-Query] [-Agent ] [[-Context] ] [] 21 | ``` 22 | 23 | ### Clipboard 24 | 25 | ``` 26 | Invoke-AIShell [-Query] [-Agent ] [-ContextFromClipboard] [] 27 | ``` 28 | 29 | ## DESCRIPTION 30 | 31 | This cmdlet sends a query to the open AIShell agent and results are shown in the AIShell window. 32 | 33 | ## EXAMPLES 34 | 35 | ### Example 1 - Send a query to the AIShell agent 36 | 37 | ```powershell 38 | Start-AIShell 39 | Invoke-AIShell -Query "How do I list out the 5 most CPU intensive processes?" 40 | ``` 41 | 42 | This example sends a query, "How do I list out the 5 most CPU intensive processes?" to the AIShell 43 | agent. Responses are given in the AIShell window. 44 | 45 | ## PARAMETERS 46 | 47 | ### -Agent 48 | 49 | Specifies the agent to use in the current AIShell session. If not specified, AIShell uses the 50 | currently selected agent. 51 | 52 | ```yaml 53 | Type: System.String 54 | Parameter Sets: (All) 55 | Aliases: 56 | 57 | Required: False 58 | Position: Named 59 | Default value: None 60 | Accept pipeline input: False 61 | Accept wildcard characters: False 62 | ``` 63 | 64 | ### -Context 65 | 66 | Additional context information you want to send to the AIShell agent. 67 | 68 | ```yaml 69 | Type: System.Management.Automation.PSObject 70 | Parameter Sets: Default 71 | Aliases: 72 | 73 | Required: False 74 | Position: 1 75 | Default value: None 76 | Accept pipeline input: True (ByValue) 77 | Accept wildcard characters: False 78 | ``` 79 | 80 | ### -ContextFromClipboard 81 | 82 | Use the content in your clipboard as context information for the AIShell agent. 83 | 84 | ```yaml 85 | Type: System.Management.Automation.SwitchParameter 86 | Parameter Sets: Clipboard 87 | Aliases: 88 | 89 | Required: True 90 | Position: Named 91 | Default value: None 92 | Accept pipeline input: False 93 | Accept wildcard characters: False 94 | ``` 95 | 96 | ### -Query 97 | 98 | The user input to send to the AIShell agent. 99 | 100 | ```yaml 101 | Type: System.String 102 | Parameter Sets: (All) 103 | Aliases: 104 | 105 | Required: True 106 | Position: 0 107 | Default value: None 108 | Accept pipeline input: False 109 | Accept wildcard characters: False 110 | ``` 111 | 112 | ### CommonParameters 113 | 114 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 115 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 116 | -WarningAction, and -WarningVariable. For more information, see 117 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 118 | 119 | ## INPUTS 120 | 121 | ### System.Management.Automation.PSObject 122 | 123 | ## OUTPUTS 124 | 125 | ### System.Object 126 | 127 | ## NOTES 128 | 129 | PowerShell includes the following alias for this cmdlet: 130 | 131 | - All platforms: 132 | - `askai` 133 | 134 | ## RELATED LINKS 135 | 136 | [Start-AIShell](Start-AIShell.md) 137 | 138 | [Resolve-Error](Resolve-Error.md) 139 | -------------------------------------------------------------------------------- /docs/cmdlets/Resolve-Error.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: AIShell.Integration.dll-Help.xml 3 | Module Name: AIShell 4 | online version: 5 | ms.date: 06/17/2024 6 | schema: 2.0.0 7 | --- 8 | 9 | # Resolve-Error 10 | 11 | ## SYNOPSIS 12 | Cmdlet to take the last error in the current session and send it to the open AIShell window for 13 | resolution. 14 | 15 | ## SYNTAX 16 | 17 | ``` 18 | Resolve-Error [-Agent ] [-IncludeOutputFromClipboard] [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | 23 | When an error occurs in the current session, this cmdlet sends the error to the AIShell agent for 24 | resolution. The command sends the full error object to the current AIShell agent session, which 25 | attempts to provide a resolution. 26 | 27 | ## EXAMPLES 28 | 29 | ### Example 1 - Resolves the last error 30 | 31 | ```powershell 32 | PS> Start-AIShell 33 | #User receives an error 34 | 35 | PS> Resolve-Error 36 | ``` 37 | 38 | This example shows how to ask AIShell to resolve the last error that occurred in the current AIShell 39 | session. AIShell analyzes the error and attempts to provide a solution in the AIShell agent window. 40 | 41 | ## PARAMETERS 42 | 43 | ### -Agent 44 | 45 | Specifies the agent to use in the current AIShell session. If not specified, AIShell uses the 46 | currently selected agent. 47 | 48 | ```yaml 49 | Type: System.String 50 | Parameter Sets: (All) 51 | Aliases: 52 | 53 | Required: False 54 | Position: Named 55 | Default value: None 56 | Accept pipeline input: False 57 | Accept wildcard characters: False 58 | ``` 59 | 60 | ### -IncludeOutputFromClipboard 61 | 62 | When this parameter is specified, the output copied to the clipboard is included in the error sent 63 | to AIShell. 64 | 65 | ```yaml 66 | Type: System.Management.Automation.SwitchParameter 67 | Parameter Sets: (All) 68 | Aliases: 69 | 70 | Required: False 71 | Position: Named 72 | Default value: None 73 | Accept pipeline input: False 74 | Accept wildcard characters: False 75 | ``` 76 | 77 | ### CommonParameters 78 | 79 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 80 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 81 | -WarningAction, and -WarningVariable. For more information, see 82 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 83 | 84 | ## INPUTS 85 | 86 | ### None 87 | 88 | ## OUTPUTS 89 | 90 | ### System.Object 91 | 92 | ## NOTES 93 | 94 | PowerShell includes the following alias for this cmdlet: 95 | 96 | - All platforms: 97 | - `fixit` 98 | 99 | ## RELATED LINKS 100 | 101 | [Invoke-AIShell](Invoke-AIShell.md) 102 | 103 | [Start-AIShell](Start-AIShell.md) 104 | -------------------------------------------------------------------------------- /docs/cmdlets/Start-AIShell.md: -------------------------------------------------------------------------------- 1 | --- 2 | external help file: AIShell.Integration.dll-Help.xml 3 | Module Name: AIShell 4 | online version: 5 | ms.date: 06/17/2024 6 | schema: 2.0.0 7 | --- 8 | 9 | # Start-AIShell 10 | 11 | ## SYNOPSIS 12 | Starts an AIShell session in a split pane window of Windows Terminal and iTerm2 with a connected 13 | communication channel to the PowerShell session that started it. 14 | 15 | ## SYNTAX 16 | 17 | ``` 18 | Start-AIShell [-Path ] [] 19 | ``` 20 | 21 | ## DESCRIPTION 22 | 23 | Starts an AIShell session in a split pane window of Windows Terminal and iTerm2. The AIShell session 24 | is started in the right pane of the terminal window. The left pane is the current shell session. You 25 | must use these windows to interact with the AIShell session. 26 | 27 | ## EXAMPLES 28 | 29 | ### Example 1 - Start an AIShell session 30 | 31 | ```powershell 32 | Start-AIShell 33 | ``` 34 | 35 | ### Example 2 - Start an AIShell session with a specific path 36 | 37 | ```powershell 38 | Start-AIShell -PATH C:\Users\aish.exe 39 | ``` 40 | 41 | ## PARAMETERS 42 | 43 | ### -Path 44 | 45 | By default, the cmdlet looks for the `aish` executable in the locations listed in the `$env:PATH` 46 | environment variable. Use this parameter to specify an alternate location for the `aish` executable. 47 | 48 | ```yaml 49 | Type: System.String 50 | Parameter Sets: (All) 51 | Aliases: 52 | 53 | Required: False 54 | Position: Named 55 | Default value: None 56 | Accept pipeline input: False 57 | Accept wildcard characters: False 58 | ``` 59 | 60 | ### CommonParameters 61 | 62 | This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, 63 | -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, 64 | -WarningAction, and -WarningVariable. For more information, see 65 | [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). 66 | 67 | ## INPUTS 68 | 69 | ### None 70 | 71 | ## OUTPUTS 72 | 73 | ### System.Object 74 | 75 | ## NOTES 76 | 77 | PowerShell includes the following alias for this cmdlet: 78 | 79 | - All platforms: 80 | - `aish` 81 | 82 | ## RELATED LINKS 83 | 84 | [Invoke-AIShell](Invoke-AIShell.md) 85 | 86 | [Resolve-Error](Resolve-Error.md) 87 | -------------------------------------------------------------------------------- /docs/development/AzureOAIDeployment/main.bicep: -------------------------------------------------------------------------------- 1 | @description('This is the name of your AI Service Account') 2 | param aiserviceaccountname string = '' 3 | 4 | @description('Custom domain name for the endpoint') 5 | param customDomainName string = '' 6 | 7 | @description('Name of the deployment ') 8 | param modeldeploymentname string = '' 9 | 10 | @description('The model being deployed') 11 | param model string = 'gpt-4' 12 | 13 | @description('Version of the model being deployed') 14 | param modelversion string = 'turbo-2024-04-09' 15 | 16 | @description('Capacity for specific model used') 17 | param capacity int = 80 18 | 19 | @description('Location for all resources.') 20 | param location string = resourceGroup().location 21 | 22 | @allowed([ 23 | 'S0' 24 | ]) 25 | param sku string = 'S0' 26 | 27 | resource openAIService 'Microsoft.CognitiveServices/accounts@2024-10-01' = { 28 | name: aiserviceaccountname 29 | location: location 30 | identity: { 31 | type: 'SystemAssigned' 32 | } 33 | sku: { 34 | name: sku 35 | } 36 | kind: 'AIServices' 37 | properties: { 38 | customSubDomainName: customDomainName 39 | } 40 | } 41 | 42 | resource azopenaideployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = { 43 | parent: openAIService 44 | name: modeldeploymentname 45 | properties: { 46 | model: { 47 | format: 'OpenAI' 48 | name: model 49 | version: modelversion 50 | } 51 | } 52 | sku: { 53 | name: 'Standard' 54 | capacity: capacity 55 | } 56 | } 57 | 58 | output openAIServiceEndpoint string = openAIService.properties.endpoint 59 | -------------------------------------------------------------------------------- /docs/media/AIShell-overall-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/AIShell-overall-arch.png -------------------------------------------------------------------------------- /docs/media/DemoGIFs/Invoke-AIShell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/Invoke-AIShell.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/Resolve-error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/Resolve-error.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/azure-agent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/azure-agent.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/insert-code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/insert-code.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/predictiveintellisensedemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/predictiveintellisensedemo.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/standalone-startup.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/standalone-startup.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/start-aishell.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/start-aishell.gif -------------------------------------------------------------------------------- /docs/media/DemoGIFs/switch-agents.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/DemoGIFs/switch-agents.gif -------------------------------------------------------------------------------- /docs/media/Icons/AIShellIconPNG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/docs/media/Icons/AIShellIconPNG.png -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.409" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/AIShell.Abstraction.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | AIShell.Abstraction 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | // General Information about an assembly is controlled through the following 4 | // set of attributes. Change these attribute values to modify the information 5 | // associated with an assembly. 6 | 7 | [assembly: InternalsVisibleTo("AIShell.Kernel")] 8 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/CommandBase.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine.Builder; 2 | using System.CommandLine.Parsing; 3 | using System.CommandLine; 4 | 5 | namespace AIShell.Abstraction; 6 | 7 | public abstract class CommandBase : Command, IDisposable 8 | { 9 | private static readonly string[] s_helpAlias = new[] { "-h", "--help" }; 10 | 11 | /// 12 | /// The constructor that a derived type has to implement. 13 | /// 14 | /// Name of the command. 15 | /// Description of the command, which will be used to show help information. 16 | protected CommandBase(string name, string description = null) 17 | : base(name, description) 18 | { 19 | _parser = null; 20 | } 21 | 22 | private Parser _parser; 23 | 24 | /// 25 | /// Gets the implementation to interact with AIShell. 26 | /// 27 | public IShell Shell { internal set; get; } 28 | 29 | /// 30 | /// Gets the source of the command. 31 | /// 32 | internal string Source { set; get; } 33 | 34 | /// 35 | /// Gets the parser to parse and invoke this command. 36 | /// 37 | internal Parser Parser => 38 | _parser ??= new CommandLineBuilder(this) 39 | .UseHelp(s_helpAlias) 40 | .UseSuggestDirective() 41 | .UseTypoCorrections() 42 | .UseParseErrorReporting() 43 | .Build(); 44 | 45 | /// 46 | /// Dispose the command. 47 | /// 48 | public void Dispose() 49 | { 50 | Dispose(true); 51 | GC.SuppressFinalize(this); 52 | } 53 | 54 | /// 55 | /// The actual implementation to dispose the command. 56 | /// 57 | /// 58 | /// True indicates it's called by the method; 59 | /// False indicates it's called by the runtime from inside the finalizer. 60 | /// 61 | protected virtual void Dispose(bool disposing) 62 | { 63 | // The default implementation is non-op. 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/IRenderElement.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace AIShell.Abstraction; 4 | 5 | public interface IRenderElement 6 | { 7 | string Name { get; } 8 | string Value(T obj); 9 | } 10 | 11 | public sealed class PropertyElement : IRenderElement 12 | { 13 | private readonly string _propertyName; 14 | private readonly PropertyInfo _propertyInfo; 15 | 16 | public PropertyElement(string propertyName) 17 | { 18 | ArgumentException.ThrowIfNullOrEmpty(propertyName); 19 | 20 | _propertyName = propertyName; 21 | _propertyInfo = typeof(T).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); 22 | 23 | if (_propertyInfo is null || !_propertyInfo.CanRead) 24 | { 25 | throw new ArgumentException($"'{propertyName}' is not a public instance property or it's write-only.", nameof(propertyName)); 26 | } 27 | } 28 | 29 | public PropertyElement(PropertyInfo property) 30 | { 31 | ArgumentNullException.ThrowIfNull(property); 32 | 33 | Type type = typeof(T); 34 | if (type != property.ReflectedType) 35 | { 36 | throw new ArgumentException($"The passed-in property is not retrieved from the target type '{type.FullName}'.", nameof(property)); 37 | } 38 | 39 | if (!property.CanRead) 40 | { 41 | throw new ArgumentException($"The property '{property.Name}' is write-only.", nameof(property)); 42 | } 43 | 44 | _propertyName = property.Name; 45 | _propertyInfo = property; 46 | } 47 | 48 | public string Name => _propertyName; 49 | public string Value(T source) 50 | { 51 | ArgumentNullException.ThrowIfNull(source); 52 | return _propertyInfo.GetValue(source)?.ToString(); 53 | } 54 | } 55 | 56 | public sealed class KeyValueElement : IRenderElement 57 | where T : IDictionary 58 | { 59 | private readonly string _key; 60 | 61 | public KeyValueElement(string key) 62 | { 63 | ArgumentException.ThrowIfNullOrEmpty(key); 64 | _key = key; 65 | } 66 | 67 | public string Name => _key; 68 | public string Value(T source) 69 | { 70 | ArgumentNullException.ThrowIfNull(source); 71 | return source.TryGetValue(_key, out string value) ? value : null; 72 | } 73 | } 74 | 75 | public sealed class CustomElement : IRenderElement 76 | { 77 | private readonly string _label; 78 | private readonly Func _valueFunc; 79 | 80 | public CustomElement(string label, Func valueFunc) 81 | { 82 | ArgumentException.ThrowIfNullOrEmpty(label); 83 | ArgumentNullException.ThrowIfNull(valueFunc); 84 | 85 | _label = label; 86 | _valueFunc = valueFunc; 87 | } 88 | 89 | public string Name => _label; 90 | public string Value(T source) 91 | { 92 | ArgumentNullException.ThrowIfNull(source); 93 | return _valueFunc(source); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/IShell.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Abstraction; 2 | 3 | /// 4 | /// The shell interface to interact with the AIShell. 5 | /// 6 | public interface IShell 7 | { 8 | /// 9 | /// The host of the AIShell. 10 | /// 11 | IHost Host { get; } 12 | 13 | /// 14 | /// Indicates whether the bi-directional channel with an application (e.g. a PowerShell session) has been established. 15 | /// 16 | bool ChannelEstablished { get; } 17 | 18 | /// 19 | /// The token to indicate cancellation when `Ctrl+c` is pressed by user. 20 | /// 21 | CancellationToken CancellationToken { get; } 22 | 23 | /// 24 | /// Extracts code blocks that are surrounded by code fences from the passed-in markdown text. 25 | /// 26 | /// The markdown text. 27 | /// A list of code blocks or null if there is no code block. 28 | List ExtractCodeBlocks(string text, out List sourceInfos); 29 | 30 | // TODO: 31 | // - methods to run code: python, command-line, powershell, node-js. 32 | // - methods to communicate with shell client. 33 | } 34 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/IStreamRender.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Abstraction; 2 | 3 | /// 4 | /// Represents a code block from a markdown text. 5 | /// 6 | public record CodeBlock(string Code, string Language); 7 | 8 | /// 9 | /// Represents the source metadata information of a code block extracted from a given markdown text. 10 | /// 11 | /// The start index of the code block within the text. 12 | /// The end index of the code block within the text. 13 | /// Number of spaces for indentation used by the code block. 14 | public record SourceInfo(int Start, int End, int Indents); 15 | 16 | public interface IStreamRender : IDisposable 17 | { 18 | string AccumulatedContent { get; } 19 | List CodeBlocks { get; } 20 | void Refresh(string newChunk); 21 | } 22 | -------------------------------------------------------------------------------- /shell/AIShell.Abstraction/UserAction.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Abstraction; 2 | 3 | public enum UserAction 4 | { 5 | /// 6 | /// Code was copied by user. 7 | /// 8 | CodeCopy, 9 | 10 | /// 11 | /// Code was saved by user. 12 | /// 13 | CodeSave, 14 | 15 | /// 16 | /// Code was posted to the command-line shell by user. 17 | /// 18 | CodePost, 19 | 20 | /// 21 | /// User ran the 'like' command. 22 | /// 23 | Like, 24 | 25 | /// 26 | /// User ran the 'dislike' command. 27 | /// 28 | Dislike, 29 | 30 | /// 31 | /// User ran the 'retry' command. 32 | /// 33 | Retry, 34 | } 35 | 36 | public abstract class UserActionPayload 37 | { 38 | public UserAction Action { get; } 39 | 40 | protected UserActionPayload(UserAction action) 41 | { 42 | Action = action; 43 | } 44 | } 45 | 46 | public sealed class CodePayload : UserActionPayload 47 | { 48 | public string Code { get; } 49 | 50 | public CodePayload(UserAction action, string code) 51 | : base(action) 52 | { 53 | ArgumentException.ThrowIfNullOrEmpty(code); 54 | Code = code; 55 | } 56 | } 57 | 58 | public sealed class LikePayload : UserActionPayload 59 | { 60 | public bool ShareConversation { get; } 61 | 62 | public LikePayload(bool share) 63 | : base(UserAction.Like) 64 | { 65 | ShareConversation = share; 66 | } 67 | } 68 | 69 | public sealed class DislikePayload : UserActionPayload 70 | { 71 | public bool ShareConversation { get; } 72 | public string ShortFeedback { get; } 73 | public string LongFeedback { get; } 74 | 75 | public DislikePayload(bool share, string shortFeedback, string longFeedback) 76 | : base(UserAction.Dislike) 77 | { 78 | ArgumentException.ThrowIfNullOrEmpty(shortFeedback); 79 | ShareConversation = share; 80 | ShortFeedback = shortFeedback; 81 | LongFeedback = longFeedback; 82 | } 83 | } 84 | 85 | public sealed class RetryPayload : UserActionPayload 86 | { 87 | public string LastQuery { get; } 88 | 89 | public RetryPayload(string lastQuery) 90 | : base(UserAction.Retry) 91 | { 92 | ArgumentException.ThrowIfNullOrEmpty(lastQuery); 93 | LastQuery = lastQuery; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /shell/AIShell.App/AIShell.App.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Exe 6 | aish 7 | true 8 | true 9 | true 10 | win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64; 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /shell/AIShell.Integration/AIShell.Integration.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | AIShell.Integration 6 | 7 | 8 | false 9 | en-US 10 | 11 | 12 | 13 | 14 | contentFiles 15 | All 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | Always 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /shell/AIShell.Integration/AIShell.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | RootModule = 'AIShell.psm1' 3 | NestedModules = @("AIShell.Integration.dll") 4 | ModuleVersion = '1.0.4' 5 | GUID = 'ECB8BEE0-59B9-4DAE-9D7B-A990B480279A' 6 | Author = 'Microsoft Corporation' 7 | CompanyName = 'Microsoft Corporation' 8 | Copyright = '(c) Microsoft Corporation. All rights reserved.' 9 | Description = 'Integration with the AIShell to provide intelligent shell experience' 10 | PowerShellVersion = '7.4.6' 11 | PowerShellHostName = 'ConsoleHost' 12 | FunctionsToExport = @() 13 | CmdletsToExport = @('Start-AIShell','Invoke-AIShell','Resolve-Error') 14 | VariablesToExport = '*' 15 | AliasesToExport = @('aish', 'askai', 'fixit') 16 | HelpInfoURI = 'https://aka.ms/aishell-help' 17 | PrivateData = @{ PSData = @{ Prerelease = 'preview4'; ProjectUri = 'https://github.com/PowerShell/AIShell' } } 18 | } 19 | -------------------------------------------------------------------------------- /shell/AIShell.Integration/AIShell.psm1: -------------------------------------------------------------------------------- 1 | if ($IsMacOS -and $env:TERM_PROGRAM -ne "iTerm.app") { 2 | throw "The AIShell module requires iTerm2 to work properly. Please install and run from the iTerm2 terminal." 3 | } 4 | 5 | $module = Get-Module -Name PSReadLine 6 | if ($null -eq $module -or $module.Version -lt [version]"2.4.2") { 7 | throw "The PSReadLine v2.4.2-beta2 or higher is required for the AIShell module to work properly." 8 | } 9 | 10 | $runspace = $Host.Runspace 11 | if ($null -eq $runspace) { 12 | throw "Failed to import the module because '`$Host.Runspace' unexpectedly returns null.`nThe host details:`n$($Host | Out-String -Width 120)" 13 | } 14 | 15 | ## Create the channel singleton when loading the module. 16 | $null = [AIShell.Integration.Channel]::CreateSingleton($runspace, [Microsoft.PowerShell.PSConsoleReadLine]) 17 | -------------------------------------------------------------------------------- /shell/AIShell.Integration/Commands/InvokeAishCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Management.Automation; 3 | using AIShell.Abstraction; 4 | 5 | namespace AIShell.Integration.Commands; 6 | 7 | [Alias("askai")] 8 | [Cmdlet(VerbsLifecycle.Invoke, "AIShell", DefaultParameterSetName = "Default")] 9 | public class InvokeAIShellCommand : PSCmdlet 10 | { 11 | private const string DefaultSet = "Default"; 12 | private const string ClipboardSet = "Clipboard"; 13 | private const string PostCodeSet = "PostCode"; 14 | private const string CopyCodeSet = "CopyCode"; 15 | private const string ExitSet = "Exit"; 16 | 17 | /// 18 | /// Sets and gets the query to be sent to AIShell 19 | /// 20 | [Parameter(Mandatory = true, ValueFromRemainingArguments = true, ParameterSetName = DefaultSet)] 21 | [Parameter(Mandatory = true, ValueFromRemainingArguments = true, ParameterSetName = ClipboardSet)] 22 | public string[] Query { get; set; } 23 | 24 | /// 25 | /// Sets and gets the agent to use for the query. 26 | /// 27 | [Parameter(ParameterSetName = DefaultSet)] 28 | [Parameter(ParameterSetName = ClipboardSet)] 29 | [ValidateNotNullOrEmpty] 30 | public string Agent { get; set; } 31 | 32 | /// 33 | /// Sets and gets the context information for the query. 34 | /// 35 | [Parameter(ValueFromPipeline = true, ParameterSetName = DefaultSet)] 36 | public PSObject Context { get; set; } 37 | 38 | /// 39 | /// Indicates getting context information from clipboard. 40 | /// 41 | [Parameter(Mandatory = true, ParameterSetName = ClipboardSet)] 42 | public SwitchParameter ContextFromClipboard { get; set; } 43 | 44 | /// 45 | /// Indicates running '/code post' from the AIShell. 46 | /// 47 | [Parameter(ParameterSetName = PostCodeSet)] 48 | public SwitchParameter PostCode { get; set; } 49 | 50 | /// 51 | /// Indicates running '/code copy' from the AIShell. 52 | /// 53 | [Parameter(ParameterSetName = CopyCodeSet)] 54 | public SwitchParameter CopyCode { get; set; } 55 | 56 | /// 57 | /// Indicates running '/exit' from the AIShell. 58 | /// 59 | [Parameter(ParameterSetName = ExitSet)] 60 | public SwitchParameter Exit { get; set; } 61 | 62 | private List _contextObjects; 63 | 64 | protected override void ProcessRecord() 65 | { 66 | if (Context is null) 67 | { 68 | return; 69 | } 70 | 71 | _contextObjects ??= []; 72 | _contextObjects.Add(Context); 73 | } 74 | 75 | protected override void EndProcessing() 76 | { 77 | string message, context = null; 78 | 79 | switch (ParameterSetName) 80 | { 81 | case PostCodeSet: 82 | message = "/code post"; 83 | break; 84 | case CopyCodeSet: 85 | message = "/code copy"; 86 | break; 87 | case ExitSet: 88 | message = "/exit"; 89 | break; 90 | default: 91 | Collection results = null; 92 | if (_contextObjects is not null) 93 | { 94 | using PowerShell pwsh = PowerShell.Create(RunspaceMode.CurrentRunspace); 95 | results = pwsh 96 | .AddCommand("Out-String") 97 | .AddParameter("InputObject", _contextObjects) 98 | .Invoke(); 99 | } 100 | else if (ContextFromClipboard) 101 | { 102 | using PowerShell pwsh = PowerShell.Create(RunspaceMode.CurrentRunspace); 103 | results = pwsh 104 | .AddCommand("Get-Clipboard") 105 | .AddParameter("Raw") 106 | .Invoke(); 107 | } 108 | 109 | context = results?.Count > 0 ? results[0] : null; 110 | message = string.Join(' ', Query); 111 | break; 112 | } 113 | 114 | Channel.Singleton.PostQuery(new PostQueryMessage(message, context, Agent)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /shell/AIShell.Integration/ErrorFeedback.cs: -------------------------------------------------------------------------------- 1 | using System.Management.Automation; 2 | using System.Management.Automation.Subsystem; 3 | using System.Management.Automation.Subsystem.Feedback; 4 | using System.Text; 5 | using AIShell.Abstraction; 6 | 7 | namespace AIShell.Integration; 8 | 9 | public sealed class ErrorFeedback : IFeedbackProvider 10 | { 11 | internal const string GUID = "10A13623-CE5E-4808-8346-1DEC831C24BB"; 12 | 13 | private readonly Guid _guid; 14 | 15 | internal ErrorFeedback() 16 | { 17 | _guid = new Guid(GUID); 18 | SubsystemManager.RegisterSubsystem(SubsystemKind.FeedbackProvider, this); 19 | } 20 | 21 | Dictionary ISubsystem.FunctionsToDefine => null; 22 | 23 | public Guid Id => _guid; 24 | 25 | public string Name => "AIShell"; 26 | 27 | public string Description => "Provide feedback for errors by leveraging AI agents running in AIShell."; 28 | 29 | public FeedbackTrigger Trigger => FeedbackTrigger.Error; 30 | 31 | public FeedbackItem GetFeedback(FeedbackContext context, CancellationToken token) 32 | { 33 | // The trigger we listen to is 'Error', so 'LastError' won't be null. 34 | Channel channel = Channel.Singleton; 35 | if (channel.CheckConnection(blocking: false, out _)) 36 | { 37 | string query = CreateQueryForError(context.CommandLine, context.LastError, channel); 38 | PostQueryMessage message = new(query, context: null, agent: null); 39 | channel.PostQuery(message); 40 | 41 | return new FeedbackItem(header: "Check the sidecar for suggestions from AI.", actions: null); 42 | } 43 | 44 | return null; 45 | } 46 | 47 | internal static string CreateQueryForError(string commandLine, ErrorRecord lastError, Channel channel) 48 | { 49 | Exception exception = lastError.Exception; 50 | StringBuilder sb = new StringBuilder(capacity: 100) 51 | .Append( 52 | $""" 53 | Running the command line `{commandLine}` in PowerShell v{channel.PSVersion} failed. 54 | Please try to explain the failure and suggest the right fix. 55 | The error details can be found below in the markdown format. 56 | """) 57 | .Append("\n\n") 58 | .Append("# Error Details\n") 59 | .Append("## Exception Messages\n") 60 | .Append($"{exception.GetType().FullName}: {exception.Message}\n"); 61 | 62 | exception = exception.InnerException; 63 | if (exception is not null) 64 | { 65 | sb.Append("Inner Exceptions:\n"); 66 | do 67 | { 68 | sb.Append($" - {exception.GetType().FullName}: {exception.Message}\n"); 69 | exception = exception.InnerException; 70 | } 71 | while (exception is not null); 72 | } 73 | 74 | string positionMessage = lastError.InvocationInfo?.PositionMessage; 75 | if (!string.IsNullOrEmpty(positionMessage)) 76 | { 77 | sb.Append("## Error Position\n").Append(positionMessage).Append('\n'); 78 | } 79 | 80 | return sb.ToString(); 81 | } 82 | 83 | internal void Unregister() 84 | { 85 | SubsystemManager.UnregisterSubsystem(_guid); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /shell/AIShell.Integration/README.md: -------------------------------------------------------------------------------- 1 | # Shell Integration Module 2 | 3 | The `AIShell` module is a PowerShell module that creates a connection between PowerShell 7 and the 4 | AIShell launched in a side car of your terminal. The module provide deep integration with the 5 | interactive features of PowerShell 7, such as Predictive IntelliSense. It also enables the cross 6 | pane communication between the AIShell agent and the PowerShell that sends queries, errors, and 7 | results between the two shells. The following images shows the current capabilities: 8 | 9 | ![Shell Integration Module](../../docs/media/ShellIntegrationDemo.gif) 10 | 11 | ## Installation and Usage 12 | 13 | This module gets built when you run the `build.ps1` script in the root of the repository. The build 14 | script writes the module to the `out/debug/module` directory. To import the module, run the 15 | following command: 16 | 17 | ```powershell 18 | Import-Module .\out\debug\module\AIShell 19 | ``` 20 | 21 | The module contains the following cmdlets with their respective aliases: 22 | 23 | - `Start-AIShell` - alias `aish` 24 | - `Resolve-Error` - alias `fixit` 25 | - `Invoke-AIShell` - alias `askai` 26 | 27 | ### Start-AIShell 28 | 29 | This cmdlet starts an AIShell session in a split pane window of Windows Terminal or iTerm2 with a 30 | connected communication channel to the PowerShell session that started it. This is necessary to do 31 | to get any of the shell integration features highlighted in the demo above. 32 | 33 | ### Resolve-Error 34 | 35 | When you encounter and error in your working shell and are unsure what to do, instead of copying and 36 | pasting the error message to the AIShell agent, you can run this cmdlet to send the error to the 37 | agent for resolution. This sends the entire error object to the agent for analysis and resolution. 38 | The `Start-AIShell` cmdlet must be run before this cmdlet can be used. 39 | 40 | ### Invoke-AIShell 41 | 42 | This cmdlet allows you to send a query to the AIShell agent to execute. This is useful in case you 43 | do not want to switch between the two panes but want to send a query to the agent. The 44 | `Start-AIShell` cmdlet must be run before this cmdlet can be used. 45 | 46 | ### /code post 47 | 48 | One of the built in chat commands we have in AI Shell is the `/code` command. This command allows 49 | you to interact with the code suggested by the assistance provider in an agent. There is a 50 | subcommand for this command called `post` that copies the code to the working shell and 51 | Predictive IntelliSense buffer of your working shell. Meaning you can run the code in your working 52 | shell and get Predictive IntelliSense for the steps provided by the agent. 53 | 54 | ![Predictive IntelliSense Demo](../../docs/media/AIShellPredictiveIntelliSenseDemo.gif) 55 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/AIShell.Kernel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | AIShell.Kernel 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | // General Information about an assembly is controlled through the following 4 | // set of attributes. Change these attribute values to modify the information 5 | // associated with an assembly. 6 | 7 | [assembly: InternalsVisibleTo("aish")] 8 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/ClearCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | 4 | namespace AIShell.Kernel.Commands; 5 | 6 | internal sealed class ClearCommand : CommandBase 7 | { 8 | public ClearCommand() 9 | : base("clear", "Clear the screen.") 10 | { 11 | this.SetHandler(ClearAction); 12 | this.AddAlias("cls"); 13 | } 14 | 15 | private void ClearAction() 16 | { 17 | Console.Clear(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/CommandRunner.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using System.CommandLine.Parsing; 3 | using AIShell.Abstraction; 4 | 5 | namespace AIShell.Kernel.Commands; 6 | 7 | internal class CommandRunner 8 | { 9 | internal const string Core = "Core"; 10 | 11 | private readonly Shell _shell; 12 | private readonly Dictionary _commands; 13 | 14 | /// 15 | /// Available commands. 16 | /// 17 | internal Dictionary Commands => _commands; 18 | 19 | /// 20 | /// Creates an instance of . 21 | /// 22 | internal CommandRunner(Shell shell) 23 | { 24 | _shell = shell; 25 | _commands = new(StringComparer.OrdinalIgnoreCase); 26 | 27 | var buildin = new CommandBase[] 28 | { 29 | new AgentCommand(), 30 | new ClearCommand(), 31 | new CodeCommand(), 32 | new DislikeCommand(), 33 | new ExitCommand(), 34 | new LikeCommand(), 35 | new RefreshCommand(), 36 | new RetryCommand(), 37 | new HelpCommand(), 38 | //new RenderCommand(), 39 | }; 40 | 41 | LoadCommands(buildin, Core); 42 | } 43 | 44 | /// 45 | /// Load commands into the runner. 46 | /// 47 | /// 48 | /// 49 | internal void LoadCommands(IEnumerable commands, string agentName) 50 | { 51 | if (commands is null) 52 | { 53 | return; 54 | } 55 | 56 | foreach (CommandBase command in commands) 57 | { 58 | command.Shell = _shell; 59 | command.Source = agentName; 60 | 61 | // The 'command.Name' is included in the 'command.Aliases' collection. 62 | // TODO: need to think about how to handle command names/aliases collision. 63 | // We don't handle collision today -- it will throw when collision happens. 64 | foreach (string alias in command.Aliases) 65 | { 66 | _commands.Add(alias, command); 67 | } 68 | } 69 | } 70 | 71 | /// 72 | /// Unload angent commands from the runner. 73 | /// 74 | internal void UnloadAgentCommands() 75 | { 76 | var agentCommands = new List(); 77 | foreach (var command in _commands.Values) 78 | { 79 | if (command.Source is Core) 80 | { 81 | continue; 82 | } 83 | 84 | agentCommands.Add(command); 85 | } 86 | 87 | foreach (var command in agentCommands) 88 | { 89 | // The 'command.Name' is included in the 'command.Aliases' collection. 90 | // TODO: need to update accordingly when we handle command names/aliases collision. 91 | foreach (string alias in command.Aliases) 92 | { 93 | _commands.Remove(alias); 94 | } 95 | 96 | command.Dispose(); 97 | } 98 | } 99 | 100 | /// 101 | /// Resolve the given command name. 102 | /// 103 | /// 104 | /// The corresponding command or null if the name cannot be resolved 105 | /// 106 | internal CommandBase ResolveCommand(string name) 107 | { 108 | return _commands.TryGetValue(name, out CommandBase value) ? value : null; 109 | } 110 | 111 | /// 112 | /// Invoke the given command line. 113 | /// 114 | /// The command line to run, which may include flags and arguments. 115 | /// 116 | internal void InvokeCommand(string commandLine) 117 | { 118 | int index = commandLine.IndexOf(' '); 119 | string commandName = index is -1 ? commandLine : commandLine[..index]; 120 | 121 | CommandBase command = ResolveCommand(commandName) 122 | ?? throw new AIShellException($"The term '{commandName}' is not recognized as a name of a command."); 123 | 124 | command.Parser.Invoke(commandLine); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/DislikeCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | 4 | namespace AIShell.Kernel.Commands; 5 | 6 | internal sealed class DislikeCommand : FeedbackCommand 7 | { 8 | private readonly List _choices; 9 | 10 | public DislikeCommand() 11 | : base("dislike", "Dislike the last response and send feedback.") 12 | { 13 | _choices = ["Inaccurate", "Irrelevant", "Offensive or inappropriate", "Too slow to response", "Other"]; 14 | this.SetHandler(DislikeAction); 15 | } 16 | 17 | private void DislikeAction() 18 | { 19 | var shell = (Shell)Shell; 20 | Host host = shell.Host; 21 | 22 | if (shell.LastAgent is null) 23 | { 24 | host.WriteErrorLine("No previous response available to rate on."); 25 | return; 26 | } 27 | 28 | try 29 | { 30 | host.WriteLine("Thanks for your feedback. Please share more about your experience.\n"); 31 | string shortFeedback = host 32 | .PromptForSelectionAsync("Was the response:", _choices, cancellationToken: shell.CancellationToken) 33 | .GetAwaiter() 34 | .GetResult(); 35 | 36 | host.MarkupLine($"The response was: [teal]{shortFeedback}[/]\n"); 37 | string longFeedback = host 38 | .PromptForTextAsync("What went wrong? ", optional: true, choices: null, shell.CancellationToken) 39 | .GetAwaiter() 40 | .GetResult(); 41 | 42 | host.WriteLine(); 43 | string prompt = GetPromptForHistorySharing(shell.LastAgent.Impl); 44 | bool share = AskForHistorySharingAsync(prompt, shell.CancellationToken) 45 | .GetAwaiter().GetResult(); 46 | 47 | host.WriteLine("\nThanks again for your feedback!\n"); 48 | shell.OnUserAction(new DislikePayload(share, shortFeedback, longFeedback)); 49 | } 50 | catch (OperationCanceledException) 51 | { 52 | // User pressed 'Ctrl+c', likely because they are just trying out the command. 53 | host.WriteLine("\n"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/ExitCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | 4 | namespace AIShell.Kernel.Commands; 5 | 6 | internal sealed class ExitCommand : CommandBase 7 | { 8 | public ExitCommand() 9 | : base("exit", "Exit the interactive session.") 10 | { 11 | this.SetHandler(ExitAction); 12 | } 13 | 14 | private void ExitAction() 15 | { 16 | var shellImpl = (Shell)Shell; 17 | shellImpl.Exit = true; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/HelpCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | 4 | namespace AIShell.Kernel.Commands; 5 | 6 | internal sealed class HelpCommand : CommandBase 7 | { 8 | public HelpCommand() 9 | : base("help", "Show all available commands.") 10 | { 11 | this.SetHandler(HelpAction); 12 | } 13 | 14 | private void HelpAction() 15 | { 16 | var shellImpl = (Shell)Shell; 17 | var host = shellImpl.Host; 18 | 19 | var commands = shellImpl.CommandRunner.Commands; 20 | var list = commands.Values.Distinct().Order(new CommandComparer()).ToList(); 21 | 22 | host.WriteLine(); 23 | host.MarkupLine("[bold white]-[/] Type then press [bold olive underline]Enter[/] to chat with the chosen AI agent."); 24 | host.MarkupLine("[bold white]-[/] All available commands are listed in the table below."); 25 | 26 | var elements = new IRenderElement[] 27 | { 28 | new CustomElement("Name", c => $"/{c.Name}"), 29 | new PropertyElement(nameof(Description)), 30 | new CustomElement("Source", c => c.Source), 31 | }; 32 | 33 | shellImpl.Host.RenderTable(list, elements); 34 | host.MarkupLine($"Learn more at [link]https://aka.ms/AIShell[/].\n"); 35 | } 36 | 37 | private class CommandComparer : IComparer 38 | { 39 | public int Compare(CommandBase x, CommandBase y) 40 | { 41 | if (x == y) return 0; 42 | if (x is null) return -1; 43 | if (y is null) return 1; 44 | 45 | if (x.Source == y.Source) return string.Compare(x.Name, y.Name); 46 | if (x.Source != CommandRunner.Core) return -1; 47 | if (y.Source != CommandRunner.Core) return 1; 48 | 49 | // Should be unreachable here. 50 | throw new NotImplementedException(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/LikeCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | using Spectre.Console; 4 | 5 | namespace AIShell.Kernel.Commands; 6 | 7 | internal abstract class FeedbackCommand : CommandBase 8 | { 9 | protected FeedbackCommand(string name, string description) 10 | : base(name, description) 11 | { 12 | } 13 | 14 | protected static string GetPromptForHistorySharing(ILLMAgent agent) 15 | { 16 | string product = agent.Company is null 17 | ? $"the agent [green]{agent.Name}[/]" 18 | : $"{agent.Company} products and services"; 19 | 20 | string privacy = null; 21 | if (agent.LegalLinks is not null) 22 | { 23 | string privacyText = "Privacy statement"; 24 | bool hasLink = agent.LegalLinks.TryGetValue(privacyText, out string link); 25 | if (!hasLink) 26 | { 27 | privacyText = "Privacy"; 28 | hasLink = agent.LegalLinks.TryGetValue(privacyText, out link); 29 | } 30 | 31 | privacy = hasLink ? $" ([link={link.EscapeMarkup()}]{privacyText.EscapeMarkup()}[/])" : null; 32 | } 33 | 34 | return $"Would you like to share the conversation history to help further improve {product}?{privacy}"; 35 | } 36 | 37 | /// 38 | /// The `TextPrompt.ChoiceStyle` doesn't work with `Style.Plain`, which looks like a bug. 39 | /// So, we have to build the choices and default value into the prompt. 40 | /// 41 | protected static async Task AskForHistorySharingAsync(string promptText, CancellationToken cancellationToken) 42 | { 43 | var comparer = StringComparer.CurrentCultureIgnoreCase; 44 | 45 | var prompt = new TextPrompt(promptText, comparer) 46 | .PromptStyle(new Style(Color.Teal)) 47 | .InvalidChoiceMessage("[red]Please select one of the available options[/]") 48 | .ValidationErrorMessage("[red]Please select one of the available options[/]") 49 | .DefaultValue('y') 50 | .AddChoice('y') 51 | .AddChoice('n'); 52 | 53 | var result = await prompt.ShowAsync(AnsiConsole.Console, cancellationToken).ConfigureAwait(false); 54 | return comparer.Compare('y'.ToString(), result.ToString()) == 0; 55 | } 56 | } 57 | 58 | internal sealed class LikeCommand : FeedbackCommand 59 | { 60 | public LikeCommand() 61 | : base("like", "Like the last response and send feedback.") 62 | { 63 | this.SetHandler(LikeAction); 64 | } 65 | 66 | private void LikeAction() 67 | { 68 | var shell = (Shell)Shell; 69 | Host host = shell.Host; 70 | 71 | if (shell.LastAgent is null) 72 | { 73 | host.WriteErrorLine("No previous response available to rate on."); 74 | return; 75 | } 76 | 77 | try 78 | { 79 | host.WriteLine("Great! Thank you for the feedback!\n"); 80 | string prompt = GetPromptForHistorySharing(shell.LastAgent.Impl); 81 | bool share = AskForHistorySharingAsync(prompt, shell.CancellationToken) 82 | .GetAwaiter().GetResult(); 83 | 84 | host.WriteLine(); 85 | shell.OnUserAction(new LikePayload(share)); 86 | } 87 | catch (OperationCanceledException) 88 | { 89 | // User pressed 'Ctrl+c', likely because they are just trying out the command. 90 | host.WriteLine("\n"); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/RefreshCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | 4 | namespace AIShell.Kernel.Commands; 5 | 6 | internal sealed class RefreshCommand : CommandBase 7 | { 8 | public RefreshCommand() 9 | : base("refresh", "Start a new chat session.") 10 | { 11 | this.SetHandler(RefreshAction); 12 | } 13 | 14 | private void RefreshAction() 15 | { 16 | Console.Write("\x1b[2J"); 17 | Console.SetCursorPosition(0, Console.WindowTop); 18 | 19 | var shell = (Shell)Shell; 20 | shell.ShowBanner(); 21 | shell.ShowLandingPage(); 22 | shell.ActiveAgent.Impl.RefreshChatAsync(Shell, force: true).GetAwaiter().GetResult(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/RenderCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Text.Json; 3 | using System.CommandLine; 4 | using AIShell.Abstraction; 5 | 6 | namespace AIShell.Kernel.Commands; 7 | 8 | internal sealed class RenderCommand : CommandBase 9 | { 10 | public RenderCommand() 11 | : base("render", "Render a markdown file, for diagnosis purpose.") 12 | { 13 | var file = new Argument("file", "The file path to save the code to."); 14 | var append = new Option("--streaming", "Render in the streaming manner."); 15 | 16 | AddArgument(file); 17 | AddOption(append); 18 | this.SetHandler(RenderAction, file, append); 19 | } 20 | 21 | private void RenderAction(string path, bool streaming) 22 | { 23 | var host = Shell.Host; 24 | 25 | if (string.IsNullOrEmpty(path)) 26 | { 27 | host.WriteErrorLine($"Please specify a file path for rendering its content."); 28 | return; 29 | } 30 | 31 | path = Utils.ResolveTilde(path); 32 | FileInfo file = new(path); 33 | 34 | string fullName = file.FullName; 35 | if (Directory.Exists(fullName)) 36 | { 37 | host.WriteErrorLine($"The specified path '{fullName}' points to an existing directory. Please specify a file path instead."); 38 | return; 39 | } 40 | 41 | try 42 | { 43 | using FileStream stream = file.OpenRead(); 44 | 45 | if (streaming) 46 | { 47 | using var streamingRender = host.NewStreamRender(CancellationToken.None); 48 | string ext = Path.GetExtension(file.Name); 49 | 50 | if (string.Equals(ext, ".json", StringComparison.OrdinalIgnoreCase)) 51 | { 52 | // Handle JSON file specially as we assume it contains all chunks stored in a string array. 53 | string[] words = JsonSerializer.Deserialize(stream); 54 | foreach (string word in words) 55 | { 56 | streamingRender.Refresh(word); 57 | } 58 | } 59 | else 60 | { 61 | using StreamReader reader = new(stream, Encoding.Default); 62 | string text = reader.ReadToEnd(); 63 | string[] words = text.Split(' '); 64 | foreach (string word in words) 65 | { 66 | streamingRender.Refresh(word + " "); 67 | } 68 | } 69 | } 70 | else 71 | { 72 | using StreamReader reader = new(stream, Encoding.Default); 73 | string text = reader.ReadToEnd(); 74 | host.RenderFullResponse(text); 75 | } 76 | } 77 | catch (Exception e) 78 | { 79 | host.WriteErrorLine(e.Message); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Command/RetryCommand.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using AIShell.Abstraction; 3 | 4 | namespace AIShell.Kernel.Commands; 5 | 6 | internal sealed class RetryCommand : CommandBase 7 | { 8 | public RetryCommand() 9 | : base("retry", "Regenerate a new response for the last query.") 10 | { 11 | this.SetHandler(RetryAction); 12 | } 13 | 14 | private void RetryAction() 15 | { 16 | var shell = (Shell)Shell; 17 | 18 | if (shell.LastQuery is null) 19 | { 20 | shell.Host.WriteErrorLine("No previous query available."); 21 | return; 22 | } 23 | 24 | shell.Regenerate = true; 25 | shell.OnUserAction(new RetryPayload(shell.LastQuery)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Exception.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Kernel; 2 | 3 | public enum ExceptionHandlerAction 4 | { 5 | Stop, 6 | Continue, 7 | } 8 | 9 | public sealed class AIShellException : Exception 10 | { 11 | public ExceptionHandlerAction HandlerAction { get; } 12 | 13 | public AIShellException(string message) 14 | : this(message, ExceptionHandlerAction.Continue, innerException: null) 15 | { 16 | } 17 | 18 | public AIShellException(string message, Exception innerException) 19 | : this(message, ExceptionHandlerAction.Continue, innerException) 20 | { 21 | } 22 | 23 | public AIShellException(string message, ExceptionHandlerAction action, Exception innerException = null) 24 | : base(message, innerException) 25 | { 26 | ArgumentException.ThrowIfNullOrEmpty(message); 27 | HandlerAction = action; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/LLMAgent.cs: -------------------------------------------------------------------------------- 1 | using AIShell.Abstraction; 2 | using Spectre.Console; 3 | 4 | namespace AIShell.Kernel; 5 | 6 | internal class LLMAgent 7 | { 8 | internal ILLMAgent Impl { get; } 9 | internal AgentAssemblyLoadContext LoadContext { get; } 10 | internal string Prompt { set; get; } 11 | 12 | internal LLMAgent(ILLMAgent agent, AgentAssemblyLoadContext loadContext) 13 | { 14 | Impl = agent; 15 | LoadContext = loadContext; 16 | Prompt = $"@{agent.Name}"; 17 | } 18 | 19 | internal void Display(Host host, string description = null) 20 | { 21 | // Display the description of the agent. 22 | string desc = description ?? Impl.Description; 23 | host.MarkupLine(desc.EscapeMarkup()); 24 | 25 | // Display properties of the agent. 26 | if (Impl.AgentInfo is null || Impl.AgentInfo.Count is 0) 27 | { 28 | host.WriteLine(); 29 | } 30 | else 31 | { 32 | host.RenderList(Impl.AgentInfo); 33 | } 34 | 35 | // Display up to 3 sample queries from the agent. 36 | if (Impl.SampleQueries is not null && Impl.SampleQueries.Count > 0) 37 | { 38 | int count = 0; 39 | host.MarkupLine("[teal]Try one of these sample queries:[/]\n"); 40 | foreach (string query in Impl.SampleQueries) 41 | { 42 | count++; 43 | host.MarkupLine($" [italic]\"{query.EscapeMarkup()}\"[/]"); 44 | 45 | if (count is 3) 46 | { 47 | break; 48 | } 49 | } 50 | 51 | host.WriteLine(); 52 | } 53 | 54 | // Display any legal links from the agent. 55 | if (Impl.LegalLinks is not null && Impl.LegalLinks.Count > 0) 56 | { 57 | bool first = true; 58 | foreach (var pair in Impl.LegalLinks) 59 | { 60 | if (first) 61 | { 62 | first = false; 63 | } 64 | else 65 | { 66 | host.Write(" | "); 67 | } 68 | 69 | host.Markup($"[link={pair.Value.EscapeMarkup()}]{pair.Key.EscapeMarkup()}[/]"); 70 | } 71 | 72 | host.WriteLine("\n"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Render/MarkdownRender.cs: -------------------------------------------------------------------------------- 1 | using Markdig; 2 | using Markdown.VT; 3 | using AIShell.Abstraction; 4 | 5 | namespace AIShell.Kernel; 6 | 7 | internal class CodeBlockVisitor : IVTRenderVisitor 8 | { 9 | internal CodeBlockVisitor() 10 | { 11 | CodeBlocks = []; 12 | } 13 | 14 | internal List CodeBlocks { get; } 15 | 16 | internal void Reset() 17 | { 18 | CodeBlocks.Clear(); 19 | } 20 | 21 | public void VisitCodeBlock(string code, string language) 22 | { 23 | if (string.IsNullOrWhiteSpace(code)) 24 | { 25 | return; 26 | } 27 | 28 | CodeBlocks.Add(new CodeBlock(code.Replace("\r\n", "\n"), language)); 29 | } 30 | } 31 | 32 | internal class MarkdownRender 33 | { 34 | private readonly VTRenderer _vtRender; 35 | private readonly MarkdownPipeline _pipeline; 36 | private readonly StringWriter _stringWriter; 37 | private readonly CodeBlockVisitor _visitor; 38 | 39 | internal MarkdownRender() 40 | { 41 | _stringWriter = new StringWriter(); 42 | _visitor = new CodeBlockVisitor(); 43 | _vtRender = new VTRenderer(_stringWriter, new PSMarkdownOptionInfo(), _visitor); 44 | _pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); 45 | } 46 | 47 | internal List GetAllCodeBlocks() 48 | { 49 | var code = _visitor.CodeBlocks; 50 | return code.Count > 0 ? code : null; 51 | } 52 | 53 | internal CodeBlock GetLastCodeBlock() 54 | { 55 | var code = _visitor.CodeBlocks; 56 | return code.Count > 0 ? code[^1] : null; 57 | } 58 | 59 | internal string RenderText(string text) 60 | { 61 | try 62 | { 63 | // Reset the visitor before rendering a new markdown text. 64 | _visitor.Reset(); 65 | return Markdig.Markdown.Convert(text, _vtRender, _pipeline).ToString(); 66 | } 67 | finally 68 | { 69 | // Clear the 'StringWriter' so that the next rendering can start fresh. 70 | _stringWriter.GetStringBuilder().Clear(); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Setting.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace AIShell.Kernel; 5 | 6 | /// 7 | /// Settings for AIShell. 8 | /// 9 | internal class Setting 10 | { 11 | public string DefaultAgent { set; get; } 12 | public bool UseClipboardContent { set; get; } 13 | 14 | internal static Setting Load() 15 | { 16 | FileInfo file = new(Utils.AppConfigFile); 17 | if (file.Exists) 18 | { 19 | try 20 | { 21 | using var stream = file.OpenRead(); 22 | return JsonSerializer.Deserialize(stream, SourceGenerationContext.Default.Setting); 23 | } 24 | catch (Exception e) 25 | { 26 | Console.WriteLine($""" 27 | Failed to load the configuration file '{Utils.AppConfigFile}' with the following error: 28 | {e.Message} 29 | 30 | Proceeding with the default configuration ... 31 | 32 | """); 33 | } 34 | } 35 | 36 | return new Setting(); 37 | } 38 | } 39 | 40 | /// 41 | /// Use source generation to serialize and deserialize the setting file. 42 | /// Both metadata-based and serialization-optimization modes are used to gain the best performance. 43 | /// 44 | [JsonSourceGenerationOptions( 45 | WriteIndented = true, 46 | AllowTrailingCommas = true, 47 | PropertyNameCaseInsensitive = true, 48 | ReadCommentHandling = JsonCommentHandling.Skip, 49 | UseStringEnumConverter = true)] 50 | [JsonSerializable(typeof(Setting))] 51 | internal partial class SourceGenerationContext : JsonSerializerContext { } 52 | -------------------------------------------------------------------------------- /shell/AIShell.Kernel/Utility/ShellArgs.cs: -------------------------------------------------------------------------------- 1 | using AIShell.Abstraction; 2 | 3 | namespace AIShell.Kernel; 4 | 5 | /// 6 | /// Arguments for the AI shell. 7 | /// 8 | public sealed class ShellArgs(ShellWrapper shellWrapper, string channel) 9 | { 10 | /// 11 | /// Gets the named pipe used to setup communication between aish and the command-line shell. 12 | /// 13 | public string Channel { get; } = string.IsNullOrEmpty(channel) ? null : channel; 14 | 15 | /// 16 | /// Gets the shell wrapper configuration. 17 | /// 18 | public ShellWrapper ShellWrapper { get; } = shellWrapper; 19 | } 20 | -------------------------------------------------------------------------------- /shell/Markdown.VT/ColorCode.VT/Parser/Bash.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using ColorCode.Common; 5 | 6 | namespace ColorCode.VT; 7 | 8 | public class Bash : ILanguage 9 | { 10 | public string Id => "bash"; 11 | public string Name => "bash"; 12 | public string CssClassName => "bash"; 13 | public string FirstLinePattern => null; 14 | 15 | internal const string BashCommentScope = "Bash Comment"; 16 | 17 | public IList Rules => 18 | new List 19 | { 20 | new LanguageRule( 21 | @"(#.*?)\r?$", 22 | new Dictionary 23 | { 24 | {1, BashCommentScope} 25 | }), 26 | 27 | new LanguageRule( 28 | @"'[^\n]*?'", 29 | new Dictionary 30 | { 31 | {0, ScopeName.String} 32 | }), 33 | 34 | new LanguageRule( 35 | @"""[^\n]*?(? 37 | { 38 | {0, ScopeName.String} 39 | }), 40 | 41 | // match the first word of a line in a multi-line string as the command name. 42 | new LanguageRule( 43 | @"(?m)^\s*(\w+)", 44 | new Dictionary 45 | { 46 | {1, ScopeName.PowerShellCommand} 47 | }), 48 | 49 | // match options like '-word' 50 | new LanguageRule( 51 | @"\s(-\w+)", 52 | new Dictionary 53 | { 54 | {1, ScopeName.PowerShellParameter} 55 | }), 56 | 57 | // match options like '--word', '--word-word', and '--word-word-word', but not '--word-' or '--word-word-'. 58 | // Also match potential value for the option that is specified in the form of '--word=value', but we don't 59 | // capture the value part because it should be rendered as plain text, and our real purpose is to not let 60 | // the value part to be matched by other rules. 61 | new LanguageRule( 62 | @"\s(--(?:\w+-)*\w+)(?:=(?:\w+-)*\w+)?", 63 | new Dictionary 64 | { 65 | {1, ScopeName.PowerShellParameter} 66 | }), 67 | 68 | // match variable like '$word', '$digit', '$word_word' and etc. 69 | new LanguageRule( 70 | @"\$\w+", 71 | new Dictionary 72 | { 73 | {0, ScopeName.PowerShellVariable} 74 | }), 75 | }; 76 | 77 | public bool HasAlias(string lang) 78 | { 79 | switch (lang.ToLower()) 80 | { 81 | case "sh": 82 | case "shell": 83 | case "azurecli": 84 | return true; 85 | default: 86 | return false; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /shell/Markdown.VT/ColorCode.VT/Parser/Json.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using ColorCode.Common; 5 | 6 | namespace ColorCode.VT; 7 | 8 | public class Json : ILanguage 9 | { 10 | private const string Regex_String = @"""(?:[^""\\]|\\.)*"""; 11 | private const string Regex_Number = @"-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][-+]?[0-9]+)?"; 12 | 13 | public string Id 14 | { 15 | get { return LanguageId.Json; } 16 | } 17 | 18 | public string Name 19 | { 20 | get { return "JSON"; } 21 | } 22 | 23 | public string CssClassName 24 | { 25 | get { return "json"; } 26 | } 27 | 28 | public string FirstLinePattern 29 | { 30 | get { return null; } 31 | } 32 | 33 | public IList Rules 34 | { 35 | get 36 | { 37 | return new List 38 | { 39 | new LanguageRule( 40 | $@"[,\{{]\s*({Regex_String})\s*:", 41 | new Dictionary 42 | { 43 | {1, ScopeName.JsonKey} 44 | }), 45 | new LanguageRule( 46 | Regex_String, 47 | new Dictionary 48 | { 49 | {0, ScopeName.JsonString} 50 | }), 51 | new LanguageRule( 52 | Regex_Number, 53 | new Dictionary 54 | { 55 | {0, ScopeName.JsonNumber} 56 | }), 57 | new LanguageRule( 58 | @"\b(true|false|null)\b", 59 | new Dictionary 60 | { 61 | {1, ScopeName.JsonConst} 62 | }), 63 | }; 64 | } 65 | } 66 | 67 | public bool HasAlias(string lang) 68 | { 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Markdown.VT.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Markdown.VT 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Blocks/HeadingBlockRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for headings. 10 | /// 11 | internal class HeadingBlockRenderer : VTObjectRenderer 12 | { 13 | private const string VTReset = "\x1b[0m"; 14 | private const string VTStyle = "\x1b[38;5;208m"; 15 | 16 | protected override void Write(VTRenderer renderer, HeadingBlock obj) 17 | { 18 | if (obj.Parent is MarkdownDocument) 19 | { 20 | renderer.WriteLine(); 21 | } 22 | else 23 | { 24 | renderer.EnsureLine(); 25 | } 26 | 27 | string prevStyle = renderer.CurrentStyle; 28 | renderer.CurrentStyle = VTStyle; 29 | 30 | try 31 | { 32 | renderer 33 | .Write(VTStyle) 34 | .Write(new string('#', obj.Level)) 35 | .Write(' ') 36 | .WriteLeafInline(obj) 37 | .Write(VTReset); 38 | 39 | if (prevStyle is not null) 40 | { 41 | renderer.Write(prevStyle); 42 | } 43 | } 44 | finally 45 | { 46 | renderer.CurrentStyle = prevStyle; 47 | } 48 | 49 | renderer.EnsureLine(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Blocks/ListBlockRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for list blocks. 10 | /// 11 | internal class ListBlockRenderer : VTObjectRenderer 12 | { 13 | private const string Bullet = "\u2022 "; 14 | 15 | protected override void Write(VTRenderer renderer, ListBlock obj) 16 | { 17 | bool isTopLevel = obj.Parent is not ListItemBlock; 18 | if (isTopLevel) 19 | { 20 | renderer.WriteLine(); 21 | } 22 | else 23 | { 24 | renderer.EnsureLine(); 25 | renderer.PushIndentAndUpdateWidth(VTRenderer.DefaultIndent); 26 | } 27 | 28 | int index = 1; 29 | if (obj.IsOrdered && int.TryParse(obj.OrderedStart, out int value)) 30 | { 31 | index = value; 32 | } 33 | 34 | for (int i = 0; i < obj.Count; i++) 35 | { 36 | var listItem = (ListItemBlock) obj[i]; 37 | var prefix = obj.IsOrdered ? $"{index++}. " : Bullet; 38 | 39 | if (i > 0) 40 | { 41 | // Separate list items with an empty line. 42 | renderer.WriteLine(); 43 | } 44 | 45 | renderer.Write(prefix).WriteChildren(listItem); 46 | } 47 | 48 | renderer.EnsureLine(); 49 | 50 | if (!isTopLevel) 51 | { 52 | renderer.PopIndentAndUpdateWidth(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Blocks/ListItemBlockRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System.Threading; 5 | using Markdig.Syntax; 6 | 7 | namespace Markdown.VT; 8 | 9 | /// 10 | /// Renderer for adding VT100 escape sequences for items in a list block. 11 | /// 12 | internal class ListItemBlockRenderer : VTObjectRenderer 13 | { 14 | protected override void Write(VTRenderer renderer, ListItemBlock obj) 15 | { 16 | if (obj.Parent is ListBlock parent) 17 | { 18 | if (!parent.IsOrdered) 19 | { 20 | foreach (var line in obj) 21 | { 22 | RenderWithIndent(renderer, line, parent.BulletType, 0); 23 | } 24 | } 25 | } 26 | } 27 | 28 | private static void RenderWithIndent(VTRenderer renderer, MarkdownObject block, char listBullet, int indentLevel) 29 | { 30 | // Indent left by 2 for each level on list. 31 | string indent = Padding(indentLevel * 2); 32 | 33 | if (block is ParagraphBlock paragraphBlock) 34 | { 35 | renderer.Write(indent).Write(listBullet).Write(" ").Write(paragraphBlock.Inline); 36 | } 37 | else 38 | { 39 | // If there is a sublist, the block is a ListBlock instead of ParagraphBlock. 40 | if (block is ListBlock subList) 41 | { 42 | foreach (var subListItem in subList) 43 | { 44 | if (subListItem is ListItemBlock subListItemBlock) 45 | { 46 | foreach (var line in subListItemBlock) 47 | { 48 | // Increment indent level for sub list. 49 | RenderWithIndent(renderer, line, listBullet, indentLevel + 1); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | } 56 | 57 | // Typical padding is at most a screen's width, any more than that and we won't bother caching. 58 | private const int IndentCacheMax = 120; 59 | 60 | private static readonly string[] IndentCache = new string[IndentCacheMax]; 61 | 62 | internal static string Padding(int countOfSpaces) 63 | { 64 | if (countOfSpaces >= IndentCacheMax) 65 | { 66 | return new string(' ', countOfSpaces); 67 | } 68 | 69 | var result = IndentCache[countOfSpaces]; 70 | 71 | if (result == null) 72 | { 73 | Interlocked.CompareExchange(ref IndentCache[countOfSpaces], new string(' ', countOfSpaces), comparand: null); 74 | result = IndentCache[countOfSpaces]; 75 | } 76 | 77 | return result; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Blocks/ParagraphBlockRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for paragraphs. 10 | /// 11 | internal class ParagraphBlockRenderer : VTObjectRenderer 12 | { 13 | protected override void Write(VTRenderer renderer, ParagraphBlock obj) 14 | { 15 | if (obj.Parent is MarkdownDocument) 16 | { 17 | // Write an empty line if it's a top-level paragraph. 18 | renderer.WriteLine(); 19 | } 20 | else if (obj.Parent is ListItemBlock listItem) 21 | { 22 | // When the current paragraph is in the middle of a 'ListItemBlock' 23 | // that contains multiple items, then we write an empty line if the 24 | // previous item is not a paragraph, e.g. when the previous item is 25 | // a 'CodeBlock'. 26 | int i = 0; 27 | for (; i < listItem.Count; i++) 28 | { 29 | if (listItem[i] == obj) 30 | { 31 | break; 32 | } 33 | } 34 | 35 | if (i > 0 && listItem[i - 1] is not ParagraphBlock) 36 | { 37 | renderer.WriteLine(); 38 | } 39 | } 40 | 41 | if (obj.Column > 0) 42 | { 43 | renderer.PushIndentAndUpdateWidth(new string(' ', obj.Column)); 44 | } 45 | 46 | renderer.WriteLeafInline(obj); 47 | renderer.EnsureLine(); 48 | 49 | if (obj.Column > 0) 50 | { 51 | renderer.PopIndentAndUpdateWidth(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Blocks/QuoteBlockRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for quote blocks. 10 | /// 11 | internal class QuoteBlockRenderer : VTObjectRenderer 12 | { 13 | private const char QuoteChar = '\u2502'; 14 | 15 | protected override void Write(VTRenderer renderer, QuoteBlock obj) 16 | { 17 | renderer.EnsureLine(); 18 | renderer.PushIndentAndUpdateWidth($"{VTRenderer.DefaultIndent}{QuoteChar} "); 19 | 20 | renderer.WriteChildren(obj); 21 | 22 | renderer.PopIndentAndUpdateWidth(); 23 | renderer.EnsureLine(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Blocks/ThematicBreakRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Alexandre Mutel. All rights reserved. 2 | // This file is licensed under the BSD-Clause 2 license. 3 | // See the license.txt file in the project root for more information. 4 | 5 | using Markdig.Syntax; 6 | 7 | namespace Markdown.VT; 8 | 9 | /// 10 | /// A HTML renderer for a . 11 | /// 12 | /// 13 | public class ThematicBreakRenderer : VTObjectRenderer 14 | { 15 | private const string VTReset = "\x1b[0m"; 16 | private const string Style = "\x1b[38;5;240m"; 17 | 18 | protected override void Write(VTRenderer renderer, ThematicBreakBlock obj) 19 | { 20 | renderer.WriteLine(); 21 | renderer.WriteLine($"{Style}--------{VTReset}"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Extensions/VTTableRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Text; 7 | using Spectre.Console; 8 | using Spectre.Console.Advanced; 9 | 10 | using Table = Markdig.Extensions.Tables.Table; 11 | using TableRow = Markdig.Extensions.Tables.TableRow; 12 | using TableCell = Markdig.Extensions.Tables.TableCell; 13 | 14 | namespace Markdown.VT; 15 | 16 | /// 17 | /// A VT100 renderer for a 18 | /// 19 | /// 20 | public class VTTableRenderer : VTObjectRenderer 21 | { 22 | private readonly IAnsiConsole _myConsole; 23 | 24 | public VTTableRenderer() 25 | { 26 | _myConsole = AnsiConsole.Create( 27 | new AnsiConsoleSettings 28 | { 29 | Ansi = AnsiSupport.Detect, 30 | ColorSystem = ColorSystemSupport.Detect, 31 | Out = new AnsiConsoleOutput(Console.Out), 32 | } 33 | ); 34 | } 35 | 36 | protected override void Write(VTRenderer renderer, Table table) 37 | { 38 | var spectreTable = new Spectre.Console.Table() 39 | .LeftAligned() 40 | .MinimalBorder(); 41 | 42 | var sb = new StringBuilder(); 43 | var newWriter = new StringWriter(sb); 44 | var origWriter = renderer.Writer; 45 | var origUseMarkup = renderer.UseSpectreMarkup; 46 | 47 | try 48 | { 49 | renderer.Writer = newWriter; 50 | renderer.UseSpectreMarkup = true; 51 | 52 | int rowIndex = -1; 53 | foreach (TableRow row in table) 54 | { 55 | if (!row.IsHeader) 56 | { 57 | spectreTable.AddEmptyRow(); 58 | rowIndex++; 59 | } 60 | 61 | for (int i = 0; i < row.Count; i++) 62 | { 63 | var cell = (TableCell)row[i]; 64 | renderer.Write(cell); 65 | 66 | newWriter.Flush(); 67 | string cellContent = sb.ToString().Trim(); 68 | sb.Clear(); 69 | 70 | if (row.IsHeader) 71 | { 72 | spectreTable.AddColumn($"[green bold]{cellContent}[/]"); 73 | } 74 | else 75 | { 76 | spectreTable.Rows.Update(rowIndex, i, new Markup(cellContent)); 77 | } 78 | } 79 | } 80 | } 81 | finally 82 | { 83 | renderer.Writer = origWriter; 84 | renderer.UseSpectreMarkup = origUseMarkup; 85 | } 86 | 87 | int start = 0; 88 | int consoleWidth = AnsiConsole.Profile.Width; 89 | int indentWidth = renderer.GetIndentWidth(); 90 | 91 | _myConsole.Profile.Width = consoleWidth - indentWidth; 92 | string result = _myConsole.ToAnsi(spectreTable); 93 | if (OperatingSystem.IsWindows()) 94 | { 95 | // Normalize line endings to be LF only. 96 | result = result.Replace("\r\n", "\n", StringComparison.Ordinal); 97 | } 98 | 99 | renderer.WriteLine(); 100 | while (true) 101 | { 102 | if (start == result.Length) 103 | { 104 | break; 105 | } 106 | 107 | int nlIndex = result.IndexOf('\n', start); 108 | int length = nlIndex is -1 ? result.Length - start : nlIndex - start + 1; 109 | var span = result.AsSpan(start, length); 110 | if (!span.IsWhiteSpace()) 111 | { 112 | renderer.Write(span.Trim('\n')); 113 | // Call 'WriteLine' explicitly to make sure the indentation is applied. 114 | renderer.WriteLine(); 115 | } 116 | 117 | if (nlIndex is -1) 118 | { 119 | break; 120 | } 121 | 122 | start = nlIndex + 1; 123 | } 124 | 125 | renderer.EnsureLine(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/AutolinkInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Alexandre Mutel. All rights reserved. 2 | // This file is licensed under the BSD-Clause 2 license. 3 | // See the license.txt file in the project root for more information. 4 | 5 | using Markdig.Syntax.Inlines; 6 | 7 | namespace Markdown.VT; 8 | 9 | /// 10 | /// A HTML renderer for an . 11 | /// 12 | /// 13 | public class AutolinkInlineRenderer : VTObjectRenderer 14 | { 15 | public string HyperlinkInVT(string text, string url) 16 | { 17 | return $"\x1b]8;;{url}\x1b\\{text}\x1b]8;;\x1b\\"; 18 | } 19 | 20 | protected override void Write(VTRenderer renderer, AutolinkInline obj) 21 | { 22 | if (!obj.IsEmail) 23 | { 24 | renderer.Write(obj.Url); 25 | return; 26 | } 27 | 28 | renderer.Write(HyperlinkInVT(obj.Url, $"mailtto:{obj.Url}")); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/CodeInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax.Inlines; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for inline code elements. 10 | /// 11 | internal class CodeInlineRenderer : VTObjectRenderer 12 | { 13 | // Use extended color for background. 14 | // See https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#extended-colors 15 | private const string VTStyle = "\x1b[39;48;5;236m"; 16 | private const string VTReset = "\x1b[0m"; 17 | private const string MarkupStyle = "[default on rgb(48,48,48)]"; 18 | private const string MarkupReset = "[/]"; 19 | 20 | protected override void Write(VTRenderer renderer, CodeInline obj) 21 | { 22 | bool useMarkup = renderer.UseSpectreMarkup; 23 | 24 | renderer.Write(useMarkup ? MarkupStyle : VTStyle); 25 | renderer.Write(' '); 26 | renderer.Write(obj.ContentSpan); 27 | renderer.Write(' '); 28 | renderer.Write(useMarkup ? MarkupReset : VTReset); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/DelimiterInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Alexandre Mutel. All rights reserved. 2 | // This file is licensed under the BSD-Clause 2 license. 3 | // See the license.txt file in the project root for more information. 4 | 5 | using Markdig.Syntax.Inlines; 6 | 7 | namespace Markdown.VT; 8 | 9 | /// 10 | /// A HTML renderer for a . 11 | /// 12 | /// 13 | public class DelimiterInlineRenderer : VTObjectRenderer 14 | { 15 | protected override void Write(VTRenderer renderer, DelimiterInline obj) 16 | { 17 | renderer.Write(obj.ToLiteral()); 18 | renderer.WriteChildren(obj); 19 | } 20 | } -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/EmphasisInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax.Inlines; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for bold and italics elements. 10 | /// 11 | internal class EmphasisInlineRenderer : VTObjectRenderer 12 | { 13 | private const string VTBold = "\x1b[1m"; 14 | private const string VTItalic = "\x1b[3m"; 15 | private const string VTReset = "\x1b[0m"; 16 | 17 | private const string MarkupBold = "[bold]"; 18 | private const string MarkupItalic = "[italic]"; 19 | private const string MarkupReset = "[/]"; 20 | 21 | protected override void Write(VTRenderer renderer, EmphasisInline obj) 22 | { 23 | bool useMarkup = renderer.UseSpectreMarkup; 24 | string prevStyle = renderer.CurrentStyle; 25 | string currStyle = obj.DelimiterCount is 2 26 | ? (useMarkup ? MarkupBold : VTBold) 27 | : (useMarkup ? MarkupItalic : VTItalic); 28 | 29 | try 30 | { 31 | if (!useMarkup) 32 | { 33 | renderer.CurrentStyle = currStyle; 34 | } 35 | 36 | renderer.Write(currStyle).WriteChildren(obj); 37 | renderer.Write(useMarkup ? MarkupReset : VTReset); 38 | 39 | if (!useMarkup && prevStyle is not null) 40 | { 41 | renderer.Write(prevStyle); 42 | } 43 | } 44 | finally 45 | { 46 | if (!useMarkup) 47 | { 48 | renderer.CurrentStyle = prevStyle; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/LeafInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax.Inlines; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for leaf elements like plain text in paragraphs. 10 | /// 11 | internal class LeafInlineRenderer : VTObjectRenderer 12 | { 13 | protected override void Write(VTRenderer renderer, LeafInline obj) 14 | { 15 | // If the next sibling is null, then this is the last line in the paragraph. 16 | // Add new line character at the end. 17 | // Else just write without newline at the end. 18 | if (obj.NextSibling == null) 19 | { 20 | renderer.WriteLine(obj.ToString()); 21 | } 22 | else 23 | { 24 | renderer.Write(obj.ToString()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/LineBreakInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax.Inlines; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for line breaks. 10 | /// 11 | internal class LineBreakInlineRenderer : VTObjectRenderer 12 | { 13 | protected override void Write(VTRenderer renderer, LineBreakInline obj) 14 | { 15 | if (renderer.IsLastInContainer) 16 | { 17 | return; 18 | } 19 | 20 | // Treat soft break as hard break in terminal, for better readability. 21 | renderer.WriteLine(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/LinkInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Syntax.Inlines; 5 | 6 | namespace Markdown.VT; 7 | 8 | /// 9 | /// Renderer for adding VT100 escape sequences for links. 10 | /// 11 | internal class LinkInlineRenderer : VTObjectRenderer 12 | { 13 | private const string VTImageLabelStyle = "\x1b[38;5;243m"; 14 | private const string VTImageUrlStyle = "\x1b[38;5;212;4m"; 15 | private const string VTLinkStyle = "\x1b[38;5;35;1m"; 16 | private const string VTReset = "\x1b[0m"; 17 | 18 | private const string MarkupImageLabelStyle = "[rgb(118,118,118)]"; 19 | private const string MarkupImageUrlStyle = "[underline rgb(255,135,215)]"; 20 | private const string MarkupLinkStyle = "[bold rgb(0,175,95)]"; 21 | private const string MarkupReset = "[/]"; 22 | 23 | public string HyperlinkInVT(string text, string url) 24 | { 25 | return $"\x1b]8;;{url}\x1b\\{text}\x1b]8;;\x1b\\"; 26 | } 27 | 28 | public string HyperlinkInMarkup(string text, string url) 29 | { 30 | return $"[link={url}]{text}[/]"; 31 | } 32 | 33 | protected override void Write(VTRenderer renderer, LinkInline obj) 34 | { 35 | string url = obj.GetDynamicUrl is not null ? obj.GetDynamicUrl() ?? obj.Url : obj.Url; 36 | string prevStyle = renderer.CurrentStyle; 37 | bool useMarkup = renderer.UseSpectreMarkup; 38 | 39 | if (obj.IsImage) 40 | { 41 | if (obj.Label is null) 42 | { 43 | renderer 44 | .Write(useMarkup ? MarkupImageUrlStyle : VTImageUrlStyle) 45 | .Write(url) 46 | .Write(useMarkup ? MarkupReset : VTReset); 47 | } 48 | else 49 | { 50 | renderer 51 | .Write(useMarkup ? MarkupImageLabelStyle : VTImageLabelStyle) 52 | .Write("Image: ") 53 | .Write(useMarkup ? HyperlinkInMarkup(obj.Label, url) : HyperlinkInVT(obj.Label, url)) 54 | .Write(useMarkup ? MarkupReset : VTReset); 55 | } 56 | } 57 | else if (useMarkup) 58 | { 59 | renderer 60 | .Write(MarkupLinkStyle) 61 | .Write($"[link={url}]") 62 | .WriteChildren(obj); 63 | renderer 64 | .Write(MarkupReset) 65 | .Write(MarkupReset); 66 | } 67 | else 68 | { 69 | renderer 70 | .Write(VTLinkStyle) 71 | .Write($"\x1b]8;;{url}\x1b\\") 72 | .WriteChildren(obj); 73 | renderer 74 | .Write($"\x1b]8;;\x1b\\") 75 | .Write(VTReset); 76 | } 77 | 78 | if (!useMarkup && prevStyle is not null) 79 | { 80 | renderer.Write(prevStyle); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/Inlines/LiteralInlineRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Alexandre Mutel. All rights reserved. 2 | // This file is licensed under the BSD-Clause 2 license. 3 | // See the license.txt file in the project root for more information. 4 | 5 | using Markdig.Syntax.Inlines; 6 | using Spectre.Console; 7 | 8 | namespace Markdown.VT; 9 | 10 | /// 11 | /// A HTML renderer for a . 12 | /// 13 | /// 14 | public class LiteralInlineRenderer : VTObjectRenderer 15 | { 16 | protected override void Write(VTRenderer renderer, LiteralInline obj) 17 | { 18 | if (renderer.UseSpectreMarkup) 19 | { 20 | string content = obj.Content.ToString(); 21 | renderer.Write(content.EscapeMarkup()); 22 | } 23 | else 24 | { 25 | renderer.Write(ref obj.Content); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /shell/Markdown.VT/Render/VTObjectRenderer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT License. 3 | 4 | using Markdig.Renderers; 5 | using Markdig.Syntax; 6 | 7 | namespace Markdown.VT; 8 | 9 | /// 10 | /// A base class for VT rendering and Markdown objects. 11 | /// 12 | /// The element type of the renderer. 13 | public abstract class VTObjectRenderer : MarkdownObjectRenderer where T : MarkdownObject 14 | { 15 | } 16 | -------------------------------------------------------------------------------- /shell/README.md: -------------------------------------------------------------------------------- 1 | # AI Shell architecture 2 | 3 | ai-shell 4 | 5 | ### AIShell.Abstraction 6 | 7 | This project will be released as a NuGet package. It contains all the interfaces for defining an 8 | agent plugin that interacts with AI Shell. 9 | 10 | This abstraction layer includes: 11 | 12 | - `IShell`: Represents a proxy of the AI shell. 13 | - `IHost`: Represents a proxy of the shell host. 14 | - `IRenderElement`: Represents the `header/value` or `label/value` pairs for rendering objects in 15 | table or list format. 16 | - `IStreamRender`: Represents a special render for rendering streaming response. 17 | - `ILLMAgent`: Represents an agent plugin. 18 | - `IOrchestrator`: Derives `ILLMAgent`. Represents a special agent that can route a query to the 19 | most suitable agent. 20 | - `ICodeAnalyzer`: Derives `ILLMAgent`. Represents a special agent that can analyze code for 21 | security concerns. 22 | - `CommandBase`: represents a command that an agent can register to the shell when being loaded. 23 | 24 | The most important interface method in `ILLMAgent` is `Task Chat(string input, IShell shell)`, 25 | which will be called by the shell when a query comes from the user. It gives extreme flexibility to 26 | the implementation of an agent. An agent can do whatever it wants targeting an arbitrary AI backend 27 | and render the output using the utilities provided by `IShell`. 28 | 29 | An agent plugin is responsible for managing its own chat history. 30 | 31 | ### AIShell.Kernel 32 | 33 | This is the implementation of AI Shell. It consists of the following components: 34 | 35 | - ReadLine 36 | - Renders (mardown render, stream render, paging render) 37 | - Plugin management 38 | - Host (a range of utility methods for writing output and interactive prompting) 39 | - Command runner and built-in commands 40 | - Utilities (clipboard, tab completion, predictive intellisense) 41 | - Code execution for `python`, `powershell`, `cmd`, and `bash` (to enable agents to do function 42 | calling with LLM. **not-yet-started**) 43 | - Shell integration (deep integration with the command-line shell. **not-yet-started**) 44 | - Configuration (colors, key bindings, and etc. **not-yet-started**) 45 | 46 | ### AIShell.App 47 | 48 | This is a thin wrapper over `AIShell.Kernel` to create an executable. The initial idea is to 49 | make `AIShell.Kernel` a library, so it can be used or hosted by other applications. We can 50 | easily merge `AIShell.App` into `AIShell.Kernel` if this idea no longer make sense. 51 | -------------------------------------------------------------------------------- /shell/ReadLine/Accessibility.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Microsoft.PowerShell.Internal 8 | { 9 | internal class Accessibility 10 | { 11 | internal static bool IsScreenReaderActive() 12 | { 13 | bool returnValue = false; 14 | 15 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 16 | { 17 | PlatformWindows.SystemParametersInfo(PlatformWindows.SPI_GETSCREENREADER, 0, ref returnValue, 0); 18 | } 19 | 20 | return returnValue; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shell/ReadLine/Completion.vi.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | using System; 6 | 7 | namespace Microsoft.PowerShell 8 | { 9 | public partial class PSConsoleReadLine 10 | { 11 | /// 12 | /// Ends the current edit group, if needed, and invokes TabCompleteNext. 13 | /// 14 | public static void ViTabCompleteNext(ConsoleKeyInfo? key = null, object arg = null) 15 | { 16 | if (_singleton._editGroupStart >= 0) 17 | { 18 | _singleton._groupUndoHelper.EndGroup(); 19 | } 20 | TabCompleteNext(key, arg); 21 | } 22 | 23 | /// 24 | /// Ends the current edit group, if needed, and invokes TabCompletePrevious. 25 | /// 26 | public static void ViTabCompletePrevious(ConsoleKeyInfo? key = null, object arg = null) 27 | { 28 | if (_singleton._editGroupStart >= 0) 29 | { 30 | _singleton._groupUndoHelper.EndGroup(); 31 | } 32 | TabCompletePrevious(key, arg); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /shell/ReadLine/DisplayBlockBase.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | using System; 6 | using Microsoft.PowerShell.Internal; 7 | 8 | namespace Microsoft.PowerShell 9 | { 10 | public partial class PSConsoleReadLine 11 | { 12 | private class DisplayBlockBase 13 | { 14 | internal PSConsoleReadLine Singleton; 15 | internal int Top; 16 | 17 | protected void MoveCursorDown(int cnt) 18 | { 19 | IConsole console = Singleton._console; 20 | while (cnt-- > 0) 21 | { 22 | console.Write("\n"); 23 | } 24 | } 25 | 26 | protected void AdjustForActualScroll(int scrollCnt) 27 | { 28 | if (scrollCnt > 0) 29 | { 30 | Top -= scrollCnt; 31 | _singleton._initialY -= scrollCnt; 32 | _savedCursorTop -= scrollCnt; 33 | } 34 | } 35 | 36 | protected void AdjustForPossibleScroll(int cnt) 37 | { 38 | IConsole console = Singleton._console; 39 | var scrollCnt = console.CursorTop + cnt + 1 - console.BufferHeight; 40 | if (scrollCnt > 0) 41 | { 42 | Top -= scrollCnt; 43 | _singleton._initialY -= scrollCnt; 44 | _savedCursorTop -= scrollCnt; 45 | } 46 | } 47 | 48 | protected void MoveCursorToStartDrawingPosition(IConsole console) 49 | { 50 | // Calculate the coord to place the cursor at the end of current input. 51 | Point bufferEndPoint = Singleton.ConvertOffsetToPoint(Singleton._buffer.Length); 52 | // Top must be initialized before any possible adjustment by 'AdjustForPossibleScroll' or 'AdjustForActualScroll', 53 | // otherwise its value would be corrupted and cause rendering issue. 54 | Top = bufferEndPoint.Y + 1; 55 | 56 | if (bufferEndPoint.Y == console.BufferHeight) 57 | { 58 | // The input happens to end at the very last cell of the current buffer, so 'bufferEndPoint' is pointing to 59 | // the first cell at one line below the current buffer, and thus we need to scroll up the buffer. 60 | console.SetCursorPosition(console.BufferWidth - 1, console.BufferHeight - 1); 61 | // We scroll the buffer by 2 lines here, so the cursor is placed at the start of the first line after 'bufferEndPoint'. 62 | MoveCursorDown(2); 63 | bufferEndPoint.Y -= 2; 64 | AdjustForActualScroll(2); 65 | } 66 | else 67 | { 68 | // Move the cursor to the end of our input. 69 | console.SetCursorPosition(bufferEndPoint.X, bufferEndPoint.Y); 70 | // Move cursor to the start of the first line after our input (after 'bufferEndPoint'). 71 | AdjustForPossibleScroll(1); 72 | MoveCursorDown(1); 73 | } 74 | } 75 | 76 | private int _savedCursorLeft; 77 | private int _savedCursorTop; 78 | 79 | public void SaveCursor() 80 | { 81 | IConsole console = Singleton._console; 82 | _savedCursorLeft = console.CursorLeft; 83 | _savedCursorTop = console.CursorTop; 84 | } 85 | 86 | public void RestoreCursor() => Singleton._console.SetCursorPosition(_savedCursorLeft, _savedCursorTop); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /shell/ReadLine/Disposable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.PowerShell 4 | { 5 | internal sealed class Disposable : IDisposable 6 | { 7 | private Action m_onDispose; 8 | 9 | internal static readonly Disposable NonOp = new Disposable(); 10 | 11 | private Disposable() 12 | { 13 | m_onDispose = null; 14 | } 15 | 16 | public Disposable(Action onDispose) 17 | { 18 | m_onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose)); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | if (m_onDispose != null) 24 | { 25 | m_onDispose(); 26 | m_onDispose = null; 27 | } 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /shell/ReadLine/HistoryQueue.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Diagnostics.CodeAnalysis; 9 | 10 | namespace Microsoft.PowerShell 11 | { 12 | [ExcludeFromCodeCoverage] 13 | internal sealed class QueueDebugView 14 | { 15 | private readonly HistoryQueue _queue; 16 | 17 | [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] 18 | public T[] Items => this._queue.ToArray(); 19 | 20 | public QueueDebugView(HistoryQueue queue) 21 | { 22 | this._queue = queue ?? throw new ArgumentNullException(nameof(queue)); 23 | } 24 | } 25 | 26 | [DebuggerDisplay("Count = {" + nameof(Count) + "}")] 27 | [DebuggerTypeProxy(typeof(QueueDebugView<>))] 28 | internal class HistoryQueue 29 | { 30 | private readonly T[] _array; 31 | private int _head; 32 | private int _tail; 33 | 34 | public HistoryQueue(int capacity) 35 | { 36 | Debug.Assert(capacity > 0); 37 | _array = new T[capacity]; 38 | _head = _tail = Count = 0; 39 | } 40 | 41 | public void Clear() 42 | { 43 | for (int i = 0; i < Count; i++) 44 | { 45 | this[i] = default(T); 46 | } 47 | _head = _tail = Count = 0; 48 | } 49 | 50 | public bool Contains(T item) 51 | { 52 | return IndexOf(item) != -1; 53 | } 54 | 55 | public int Count { get; private set; } 56 | 57 | public int IndexOf(T item) 58 | { 59 | // REVIEW: should we use case insensitive here? 60 | var eqComparer = EqualityComparer.Default; 61 | for (int i = 0; i < Count; i++) 62 | { 63 | if (eqComparer.Equals(this[i], item)) 64 | { 65 | return i; 66 | } 67 | } 68 | 69 | return -1; 70 | } 71 | 72 | public void Enqueue(T item) 73 | { 74 | if (Count == _array.Length) 75 | { 76 | Dequeue(); 77 | } 78 | _array[_tail] = item; 79 | _tail = (_tail + 1) % _array.Length; 80 | Count += 1; 81 | } 82 | 83 | public T Dequeue() 84 | { 85 | Debug.Assert(Count > 0); 86 | 87 | T obj = _array[_head]; 88 | _array[_head] = default(T); 89 | _head = (_head + 1) % _array.Length; 90 | Count -= 1; 91 | return obj; 92 | } 93 | 94 | public T[] ToArray() 95 | { 96 | var result = new T[Count]; 97 | if (Count > 0) 98 | { 99 | if (_head < _tail) 100 | { 101 | Array.Copy(_array, _head, result, 0, Count); 102 | } 103 | else 104 | { 105 | Array.Copy(_array, _head, result, 0, _array.Length - _head); 106 | Array.Copy(_array, 0, result, _array.Length - _head, _tail); 107 | } 108 | } 109 | return result; 110 | } 111 | 112 | [ExcludeFromCodeCoverage] 113 | public T this[int index] 114 | { 115 | get 116 | { 117 | Debug.Assert(index >= 0 && index < Count); 118 | return _array[(_head + index) % _array.Length]; 119 | } 120 | set 121 | { 122 | Debug.Assert(index >= 0 && index < Count); 123 | _array[(_head + index) % _array.Length] = value; 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /shell/ReadLine/PSReadLine.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Library 7 | ReadLine 8 | $(NoWarn);CA1416 9 | true 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /shell/ReadLine/Position.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Microsoft.PowerShell 4 | { 5 | public partial class PSConsoleReadLine 6 | { 7 | /// 8 | /// Returns the position of the beginning of line 9 | /// starting from the specified "current" position. 10 | /// 11 | /// The position in the current logical line. 12 | private static int GetBeginningOfLinePos(int current) 13 | { 14 | int i = Math.Max(0, current); 15 | while (i > 0) 16 | { 17 | if (_singleton._buffer[--i] == '\n') 18 | { 19 | i += 1; 20 | break; 21 | } 22 | } 23 | 24 | return i; 25 | } 26 | 27 | /// 28 | /// Returns the position of the beginning of line 29 | /// for the 0-based specified line number. 30 | /// 31 | private static int GetBeginningOfNthLinePos(int lineIndex) 32 | { 33 | System.Diagnostics.Debug.Assert(lineIndex >= 0 || lineIndex < _singleton.GetLogicalLineCount()); 34 | 35 | var nth = 0; 36 | var index = 0; 37 | var result = 0; 38 | 39 | for (; index < _singleton._buffer.Length; index++) 40 | { 41 | if (nth == lineIndex) 42 | { 43 | result = index; 44 | break; 45 | } 46 | 47 | if (_singleton._buffer[index] == '\n') 48 | { 49 | nth++; 50 | } 51 | } 52 | 53 | if (nth == lineIndex) 54 | { 55 | result = index; 56 | } 57 | 58 | 59 | return result; 60 | } 61 | 62 | /// 63 | /// Returns the position of the end of the logical line 64 | /// as specified by the "current" position. 65 | /// 66 | /// 67 | /// 68 | private static int GetEndOfLogicalLinePos(int current) 69 | { 70 | var newCurrent = current; 71 | 72 | for (var position = current; position < _singleton._buffer.Length; position++) 73 | { 74 | if (_singleton._buffer[position] == '\n') 75 | { 76 | break; 77 | } 78 | 79 | newCurrent = position; 80 | } 81 | 82 | return newCurrent; 83 | } 84 | 85 | /// 86 | /// Returns the position of the end of the logical line 87 | /// for the 0-based specified line number. 88 | /// 89 | private static int GetEndOfNthLogicalLinePos(int lineIndex) 90 | { 91 | return GetEndOfLogicalLinePos( 92 | GetBeginningOfNthLinePos(lineIndex)); 93 | } 94 | 95 | /// 96 | /// Returns the position of the first non whitespace character in 97 | /// the current logical line as specified by the "current" position. 98 | /// 99 | /// The position in the current logical line. 100 | private static int GetFirstNonBlankOfLogicalLinePos(int current) 101 | { 102 | var beginningOfLine = GetBeginningOfLinePos(current); 103 | 104 | var newCurrent = beginningOfLine; 105 | 106 | while (newCurrent < _singleton._buffer.Length && IsVisibleBlank(newCurrent)) 107 | { 108 | newCurrent++; 109 | } 110 | 111 | return newCurrent; 112 | } 113 | 114 | private static bool IsVisibleBlank(int newCurrent) 115 | { 116 | var c = _singleton._buffer[newCurrent]; 117 | 118 | // [:blank:] of vim's pattern matching behavior 119 | // defines blanks as SPACE and TAB characters. 120 | 121 | return c == ' ' || c == '\t'; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /shell/ReadLine/StringBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace Microsoft.PowerShell 5 | { 6 | internal class Range 7 | { 8 | internal Range(int offset, int count) 9 | { 10 | Offset = offset; 11 | Count = count; 12 | } 13 | internal int Offset { get; } 14 | internal int Count { get; } 15 | } 16 | 17 | internal static class StringBuilderLinewiseExtensions 18 | { 19 | /// 20 | /// Determines the offset and the length of the fragment 21 | /// in the specified buffer that corresponds to a 22 | /// given number of lines starting from the specified line index 23 | /// 24 | /// 25 | /// 26 | /// 27 | internal static Range GetRange(this StringBuilder buffer, int lineIndex, int lineCount) 28 | { 29 | // this method considers lines by the first '\n' character from the previous line 30 | // up until the last non new-line character of the current line. 31 | // 32 | // buffer: line 0\nline 1\nline 2[...]\nline n 33 | // lines: 0....._1......_2......[...]_3...... 34 | 35 | var length = buffer.Length; 36 | 37 | var startPosition = 0; 38 | var startPositionIdentified = false; 39 | 40 | var endPosition = length - 1; 41 | 42 | var currentLine = 0; 43 | 44 | if (lineIndex == 0) 45 | { 46 | startPosition = 0; 47 | startPositionIdentified = true; 48 | } 49 | 50 | for (var position = 0; position < length; position++) 51 | { 52 | if (buffer[position] == '\n') 53 | { 54 | currentLine++; 55 | 56 | if (!startPositionIdentified && currentLine == lineIndex) 57 | { 58 | startPosition = position; 59 | startPositionIdentified = true; 60 | } 61 | 62 | if (currentLine == lineIndex + lineCount) 63 | { 64 | endPosition = position - 1; 65 | break; 66 | } 67 | } 68 | } 69 | 70 | return new Range( 71 | startPosition, 72 | endPosition - startPosition + 1 73 | ); 74 | } 75 | } 76 | 77 | internal static class StringBuilderPredictionExtensions 78 | { 79 | internal static StringBuilder EndColorSection(this StringBuilder buffer, string selectionHighlighting) 80 | { 81 | return buffer.Append(VTColorUtils.AnsiReset).Append(selectionHighlighting); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /shell/ReadLine/UndoRedo.vi.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | using System; 6 | 7 | namespace Microsoft.PowerShell 8 | { 9 | public partial class PSConsoleReadLine 10 | { 11 | private class GroupUndoHelper 12 | { 13 | public Action _instigator; 14 | public object _instigatorArg; 15 | 16 | public GroupUndoHelper() 17 | { 18 | _instigator = null; 19 | _instigatorArg = null; 20 | } 21 | 22 | public void StartGroup(Action instigator, object instigatorArg) 23 | { 24 | _instigator = instigator; 25 | _instigatorArg = instigatorArg; 26 | _singleton.StartEditGroup(); 27 | } 28 | 29 | public void Clear() 30 | { 31 | _instigator = null; 32 | _instigatorArg = null; 33 | } 34 | 35 | public void EndGroup() 36 | { 37 | if (_singleton._editGroupStart >= 0) 38 | { 39 | _singleton.EndEditGroup(_instigator, _instigatorArg); 40 | } 41 | Clear(); 42 | } 43 | } 44 | private readonly GroupUndoHelper _groupUndoHelper = new GroupUndoHelper(); 45 | 46 | /// 47 | /// Undo all previous edits for line. 48 | /// 49 | public static void UndoAll(ConsoleKeyInfo? key = null, object arg = null) 50 | { 51 | if (_singleton._undoEditIndex > 0) 52 | { 53 | while (_singleton._undoEditIndex > 0) 54 | { 55 | _singleton._edits[_singleton._undoEditIndex - 1].Undo(); 56 | _singleton._undoEditIndex--; 57 | } 58 | _singleton.Render(); 59 | } 60 | else 61 | { 62 | Ding(); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /shell/ReadLine/VisualEditing.vi.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | 9 | namespace Microsoft.PowerShell 10 | { 11 | public partial class PSConsoleReadLine 12 | { 13 | /// 14 | /// Edit the command line in a text editor specified by $env:EDITOR or $env:VISUAL. 15 | /// 16 | public static void ViEditVisually(ConsoleKeyInfo? key = null, object arg = null) 17 | { 18 | string editor = GetPreferredEditor(); 19 | if (string.IsNullOrWhiteSpace(editor)) 20 | { 21 | Ding(); 22 | return; 23 | } 24 | 25 | var tempPowerShellFile = GetTemporaryPowerShellFile(); 26 | using (FileStream fs = File.OpenWrite(tempPowerShellFile)) 27 | { 28 | using (TextWriter tw = new StreamWriter(fs)) 29 | { 30 | tw.Write(_singleton._buffer.ToString()); 31 | } 32 | } 33 | 34 | var si = new ProcessStartInfo(editor, $"\"{tempPowerShellFile}\"") 35 | { 36 | UseShellExecute = false, 37 | RedirectStandardError = false, 38 | RedirectStandardInput = false, 39 | RedirectStandardOutput = false 40 | }; 41 | var pi = _singleton.CallPossibleExternalApplication(() => Process.Start(si)); 42 | if (pi != null) 43 | { 44 | pi.WaitForExit(); 45 | InvokePrompt(); 46 | _singleton.ProcessViVisualEditing(tempPowerShellFile); 47 | } 48 | else 49 | { 50 | Ding(); 51 | } 52 | } 53 | 54 | private static string GetTemporaryPowerShellFile() 55 | { 56 | string filename; 57 | do 58 | { 59 | filename = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".ps1"); 60 | } while (File.Exists(filename) || Directory.Exists(filename)); 61 | 62 | return filename; 63 | } 64 | 65 | private void ProcessViVisualEditing(string tempFileName) 66 | { 67 | string editedCommand; 68 | using (TextReader tr = File.OpenText(tempFileName)) 69 | { 70 | editedCommand = tr.ReadToEnd(); 71 | } 72 | File.Delete(tempFileName); 73 | 74 | if (!string.IsNullOrWhiteSpace(editedCommand)) 75 | { 76 | while (editedCommand.Length > 0 && char.IsWhiteSpace(editedCommand[editedCommand.Length - 1])) 77 | { 78 | editedCommand = editedCommand.Substring(0, editedCommand.Length - 1); 79 | } 80 | editedCommand = editedCommand.Replace(Environment.NewLine, "\n"); 81 | Replace(0, _buffer.Length, editedCommand); 82 | _current = _buffer.Length; 83 | if (_options.EditMode == EditMode.Vi) _current -= 1; 84 | Render(); 85 | } 86 | } 87 | 88 | private static string GetPreferredEditor() 89 | { 90 | var editor = Environment.GetEnvironmentVariable("VISUAL"); 91 | return !string.IsNullOrWhiteSpace(editor) 92 | ? editor 93 | : Environment.GetEnvironmentVariable("EDITOR"); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /shell/ReadLine/Words.cs: -------------------------------------------------------------------------------- 1 | /********************************************************************++ 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | --********************************************************************/ 4 | 5 | namespace Microsoft.PowerShell 6 | { 7 | public partial class PSConsoleReadLine 8 | { 9 | private enum FindTokenMode 10 | { 11 | CurrentOrNext, 12 | Next, 13 | Previous, 14 | } 15 | 16 | private bool InWord(int index, string wordDelimiters) 17 | { 18 | char c = _buffer[index]; 19 | return InWord(c, wordDelimiters); 20 | } 21 | 22 | private bool InWord(char c, string wordDelimiters) 23 | { 24 | return !char.IsWhiteSpace(c) && wordDelimiters.IndexOf(c) < 0; 25 | } 26 | 27 | /// 28 | /// Find the end of the current/next word as defined by wordDelimiters and whitespace. 29 | /// 30 | private int FindForwardWordPoint(string wordDelimiters) 31 | { 32 | int i = _current; 33 | if (i == _buffer.Length) 34 | { 35 | return i; 36 | } 37 | 38 | if (!InWord(i, wordDelimiters)) 39 | { 40 | // Scan to end of current non-word region 41 | while (i < _buffer.Length) 42 | { 43 | if (InWord(i, wordDelimiters)) 44 | { 45 | break; 46 | } 47 | i += 1; 48 | } 49 | } 50 | while (i < _buffer.Length) 51 | { 52 | if (!InWord(i, wordDelimiters)) 53 | { 54 | break; 55 | } 56 | i += 1; 57 | } 58 | return i; 59 | } 60 | 61 | /// 62 | /// Find the start of the next word. 63 | /// 64 | private int FindNextWordPoint(string wordDelimiters) 65 | { 66 | int i = _singleton._current; 67 | if (i == _singleton._buffer.Length) 68 | { 69 | return i; 70 | } 71 | 72 | if (InWord(i, wordDelimiters)) 73 | { 74 | // Scan to end of current word region 75 | while (i < _singleton._buffer.Length) 76 | { 77 | if (!InWord(i, wordDelimiters)) 78 | { 79 | break; 80 | } 81 | i += 1; 82 | } 83 | } 84 | 85 | while (i < _singleton._buffer.Length) 86 | { 87 | if (InWord(i, wordDelimiters)) 88 | { 89 | break; 90 | } 91 | i += 1; 92 | } 93 | return i; 94 | } 95 | 96 | /// 97 | /// Find the beginning of the previous word. 98 | /// 99 | private int FindBackwardWordPoint(string wordDelimiters) 100 | { 101 | int i = _current - 1; 102 | if (i < 0) 103 | { 104 | return 0; 105 | } 106 | 107 | if (!InWord(i, wordDelimiters)) 108 | { 109 | // Scan backwards until we are at the end of the previous word. 110 | while (i > 0) 111 | { 112 | if (InWord(i, wordDelimiters)) 113 | { 114 | break; 115 | } 116 | i -= 1; 117 | } 118 | } 119 | while (i > 0) 120 | { 121 | if (!InWord(i, wordDelimiters)) 122 | { 123 | i += 1; 124 | break; 125 | } 126 | i -= 1; 127 | } 128 | return i; 129 | } 130 | 131 | 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/AIShell.Interpreter.Agent.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | true 7 | 8 | 9 | false 10 | 11 | 12 | 13 | 14 | false 15 | None 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | false 29 | 30 | runtime 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/ExecutionService/Languages/PowerShell.cs: -------------------------------------------------------------------------------- 1 | using AIShell.Abstraction; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | 5 | namespace AIShell.Interpreter.Agent; 6 | 7 | /// 8 | /// This class is used to execute powershell code on the local machine. It inherits most functionality 9 | /// from the SubprocessLanguage class while implementing the PreprocessCode method for non-function calling 10 | /// AIs. 11 | /// 12 | internal class PowerShell: SubprocessLanguage 13 | { 14 | internal PowerShell() : base() 15 | { 16 | // -NoProfile prevents the profile from loading and -file - reads the code from stdin 17 | StartCmd = ["pwsh", "-NoProfile -Command -"]; 18 | VersionCmd = ["pwsh", "--version"]; 19 | } 20 | 21 | protected override string PreprocessCode(string code) 22 | { 23 | string try_catch_code = @$" 24 | try {{ 25 | $ErrorActionPreference = 'Stop' 26 | {code.AsSpan().TrimEnd()} 27 | }} 28 | catch {{ 29 | $e = $_.Exception 30 | $msg = $e.GetType().FullName + "": "" + $e.Message 31 | $indent = """" 32 | while ($e.InnerException) {{ 33 | $e = $e.InnerException 34 | $indent += ""---> "" 35 | $msg += ""`n"" + $indent + $e.GetType().FullName + "": "" + $e.Message 36 | }} 37 | [Console]::Error.WriteLine($msg) 38 | [Console]::Error.WriteLine($_.InvocationInfo.PositionMessage) 39 | }} finally {{ 40 | Write-Host '##end_of_execution##' 41 | }} 42 | "; 43 | return try_catch_code; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/ExecutionService/Languages/Python.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Interpreter.Agent; 2 | 3 | /// 4 | /// This class is used to execute Python code on the local machine. It inherits most functionality 5 | /// from the SubprocessLanguage class while implementing the PreprocessCode method for non-function calling 6 | /// AIs. 7 | /// 8 | internal class Python: SubprocessLanguage 9 | { 10 | internal Python() : base() 11 | { 12 | string python = OperatingSystem.IsWindows() ? "python" : "python3"; 13 | 14 | // -q doesn't print the banner 15 | // -i runs the code in interactive mode 16 | // -u unbuffered binary stdout and stderr 17 | // Without these flags, the output is buffered and we can't read it until the process ends 18 | StartCmd = [ python, "-qui" ]; 19 | VersionCmd = [ python, "-V" ]; 20 | } 21 | 22 | protected override string PreprocessCode(string code) 23 | { 24 | // 1. Since code is inserted via string interpolation into an indented try block every subsequent line of 25 | // code needs an extra indent to be valid Python syntax. 26 | // 2. If code throws an error, the error message is printed to stderr for proper user prompt generation. 27 | // 3. Did not use the finally block to print `##end_of_execution##` because errors enountered 28 | // by Python parser are not caught by the try block. 29 | 30 | string try_catch_code = 31 | $@" 32 | import sys 33 | import traceback 34 | try: 35 | {code.Replace("\n", "\n ")} 36 | except Exception: 37 | print(traceback.format_exc(), file=sys.stderr) 38 | 39 | print('##end_of_execution##') 40 | "; 41 | return try_catch_code; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/ExecutionService/OutputData.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Interpreter.Agent; 2 | 3 | internal enum OutputType 4 | { 5 | Output, 6 | Error, 7 | End, 8 | Interrupt, 9 | } 10 | 11 | internal class OutputData 12 | { 13 | internal OutputType Type { get; set; } 14 | internal string Content { get; set; } 15 | 16 | internal OutputData(OutputType type, string content) 17 | { 18 | Type = type; 19 | Content = content; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/Utility/DataPacket.cs: -------------------------------------------------------------------------------- 1 | using Azure.AI.OpenAI; 2 | 3 | namespace AIShell.Interpreter.Agent; 4 | 5 | /// 6 | /// This class is used to encapsulate data that is sent between Agent and CodeExeuctionService. 7 | /// Using the Azure.AI.OpenAI ChatRole. 8 | /// 9 | public class DataPacket(ChatRole Role, string Content) 10 | { 11 | public readonly ChatRole role = Role; 12 | public string content = Content; 13 | } 14 | 15 | public class InternalChatResultsPacket(string AIResponse, string ToolResponse, string language = "", string code = "") 16 | { 17 | public readonly string aiResponse = AIResponse ?? "No response from AI."; 18 | public readonly bool wasResponseCancelled = AIResponse?.Contains("AI response cancelled.") ?? false; 19 | 20 | public string toolResponse = ToolResponse; 21 | public readonly bool wasToolCancelled = ToolResponse.Contains("Tool call was cancelled."); 22 | public readonly bool didUserRun = !ToolResponse.Contains("User chose not to run code."); 23 | public readonly bool wasCodeGiven = !ToolResponse.Contains("No code was given."); 24 | public bool wasThereAnError = ToolResponse.Contains("Error"); 25 | 26 | public readonly string Language = language; 27 | public readonly string Code = code; 28 | 29 | public void SetToolResponse(string response) 30 | { 31 | toolResponse = response; 32 | } 33 | 34 | public void SetError(bool error) 35 | { 36 | wasThereAnError = error; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/Utility/TaskCompletionChatPrompts.cs: -------------------------------------------------------------------------------- 1 | namespace AIShell.Interpreter.Agent; 2 | 3 | /// 4 | /// Summary description for Class1 5 | /// 6 | public static class TaskCompletionChatPrompts 7 | { 8 | 9 | public static readonly Dictionary prompts = new Dictionary 10 | { 11 | // Repeating same code 12 | { "SameError", "\nYou have already told me to fix the error. Please provide more information.\n"}, 13 | // Error handling Function Calling Model 14 | { "ErrorFunctionsBased", "\nPlease check the ChatRequestToolMessage for the error output from the code. If the code needs user input then say EXACTLY 'Please provide more information'. If it is a Python syntax error try adding a blank line after an indentation is complete.\n" }, 15 | // Error handling Text Based Model 16 | { "ErrorTextBased", "\nPlease check the following for the error output from the code. If the code needs user input then say EXACTLY 'Please provide more information'. If it is a Python syntax error try adding a blank line after an indentation is complete.\nCode output:\n\n" }, 17 | // Output response for Function Calling Model 18 | { "OutputFunctionBased", "\nPlease check the ChatRequestToolMessage for output for the code. If this is not what you were expecting then please fix the code. " + 19 | "If it is what you were expecting please move on to the next step and only the next step. If the task is done say " + 20 | "EXACTLY 'The task is done.'\n"}, 21 | // Output response for Text Based Model 22 | { "OutputTextBased", "\nPlease check the following for output for the code. If this is not what you were expecting then please fix the code. " + 23 | "If it is what you were expecting please move on to the next step and only the next step. If the task is done say " + 24 | "EXACTLY 'The task is done.'\n Code output:\n\n"}, 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/Utility/ToolResponsePacket.cs: -------------------------------------------------------------------------------- 1 | using Azure.AI.OpenAI; 2 | 3 | namespace AIShell.Interpreter.Agent; 4 | 5 | /// 6 | /// Summarizes the content of a response from the tool into booleans. 7 | /// 8 | public class ToolResponsePacket : DataPacket 9 | { 10 | public bool IsLanguageSupported { get; private set; } 11 | public bool IsLanguageOnPath { get; private set; } 12 | public bool Error { get; private set; } 13 | public readonly string Language; 14 | public readonly string Code; 15 | public string ToolId; 16 | public string Content { get; private set; } 17 | 18 | public ToolResponsePacket(string language, string code) : base(ChatRole.Tool, "") 19 | { 20 | Language = language; 21 | Code = code; 22 | Content = ""; 23 | } 24 | 25 | public ToolResponsePacket(string content, string language, string code) : base(ChatRole.Tool, content) 26 | { 27 | Language = language; 28 | Code = code; 29 | Content = ""; 30 | SetContent(content); 31 | } 32 | public void SetToolId(string toolId) 33 | { 34 | ToolId = toolId; 35 | } 36 | 37 | public void SetError(bool isError) 38 | { 39 | Error = isError; 40 | } 41 | 42 | public void SetContent(string content) 43 | { 44 | Content += content; 45 | if (Content.StartsWith("Language not supported.")) 46 | { 47 | IsLanguageSupported = false; 48 | } 49 | else 50 | { 51 | IsLanguageSupported = true; 52 | } 53 | if (Content.StartsWith("Language not found on path.")) 54 | { 55 | IsLanguageOnPath = false; 56 | } 57 | else 58 | { 59 | IsLanguageOnPath = true; 60 | } 61 | } 62 | 63 | public void ResetContent(string content) 64 | { 65 | Content = content; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/assets/Interpreter-Agent-Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/shell/agents/AIShell.Interpreter.Agent/assets/Interpreter-Agent-Architecture.png -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/assets/InterpreterAgentDemoSpedUp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/shell/agents/AIShell.Interpreter.Agent/assets/InterpreterAgentDemoSpedUp.gif -------------------------------------------------------------------------------- /shell/agents/AIShell.Interpreter.Agent/assets/InterpreterAgentFlowChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/shell/agents/AIShell.Interpreter.Agent/assets/InterpreterAgentFlowChart.png -------------------------------------------------------------------------------- /shell/agents/AIShell.Ollama.Agent/AIShell.Ollama.Agent.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | true 7 | true 8 | 9 | 10 | false 11 | 12 | 13 | 14 | 15 | false 16 | None 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | false 27 | 28 | runtime 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /shell/agents/AIShell.Ollama.Agent/README.md: -------------------------------------------------------------------------------- 1 | # Ollama Plugin 2 | 3 | This agent is used to interact with a language model running locally or remotely by utilizing the Ollama API. 4 | Before using this agent locally you need to have Ollama installed and running. 5 | 6 | ## Pre-requisites to using the agent locally 7 | 8 | - Install [Ollama](https://github.com/ollama/ollama) 9 | - Install a [Ollama model](https://github.com/ollama/ollama?tab=readme-ov-file#model-library), we suggest using the `phi3` model as it is set as the default model in the code 10 | - [Start the Ollama API server](https://github.com/ollama/ollama?tab=readme-ov-file#start-ollama) 11 | 12 | ## Configuration 13 | 14 | To configure the agent, run `/agent config ollama` to open up the setting file in your default editor, and then update the file based on the following example. 15 | 16 | ```jsonc 17 | { 18 | // To use Ollama API service: 19 | // 1. Install Ollama: `winget install Ollama.Ollama` 20 | // 2. Start Ollama API server: `ollama serve` 21 | // 3. Install Ollama model: `ollama pull phi3` 22 | 23 | // Declare predefined model configurations 24 | "Presets": [ 25 | { 26 | "Name": "PowerShell Expert", 27 | "Description": "A ollama agent with expertise in PowerShell scripting and command line utilities.", 28 | "ModelName": "phi3", 29 | "SystemPrompt": "You are a helpful and friendly assistant with expertise in PowerShell scripting and command line." 30 | } 31 | ], 32 | 33 | // Declare Ollama endpoint 34 | "Endpoint": "http://localhost:11434", 35 | // Enable Ollama streaming 36 | "Stream": false, 37 | // Specify the default preset to use 38 | "DefaultPreset": "PowerShell Expert" 39 | } 40 | ``` 41 | -------------------------------------------------------------------------------- /shell/agents/AIShell.OpenAI.Agent/AIShell.OpenAI.Agent.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | true 7 | 8 | 9 | false 10 | 11 | 12 | 13 | 14 | false 15 | None 16 | 17 | 18 | 19 | 20 | false 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | false 35 | 36 | runtime 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /shell/agents/AIShell.OpenAI.Agent/Command.cs: -------------------------------------------------------------------------------- 1 | using System.CommandLine; 2 | using System.CommandLine.Completions; 3 | using AIShell.Abstraction; 4 | 5 | namespace AIShell.OpenAI.Agent; 6 | 7 | internal sealed class GPTCommand : CommandBase 8 | { 9 | private readonly OpenAIAgent _agnet; 10 | 11 | public GPTCommand(OpenAIAgent agent) 12 | : base("gpt", "Command for GPT management within the 'openai-gpt' agent.") 13 | { 14 | _agnet = agent; 15 | 16 | var use = new Command("use", "Specify a GPT to use, or choose one from the available GPTs."); 17 | var useGPT = new Argument( 18 | name: "GPT", 19 | getDefaultValue: () => null, 20 | description: "Name of a GPT.").AddCompletions(GPTNameCompleter); 21 | use.AddArgument(useGPT); 22 | use.SetHandler(UseGPTAction, useGPT); 23 | 24 | var list = new Command("list", "List a specific GPT, or all available GPTs."); 25 | var listGPT = new Argument( 26 | name: "GPT", 27 | getDefaultValue: () => null, 28 | description: "Name of a GPT.").AddCompletions(GPTNameCompleter); 29 | list.AddArgument(listGPT); 30 | list.SetHandler(ListGPTAction, listGPT); 31 | 32 | AddCommand(list); 33 | AddCommand(use); 34 | } 35 | 36 | private void ListGPTAction(string name) 37 | { 38 | IHost host = Shell.Host; 39 | 40 | // Reload the setting file if needed. 41 | _agnet.ReloadSettings(); 42 | 43 | Settings setting = _agnet.Settings; 44 | 45 | if (setting is null || setting.GPTs.Count is 0) 46 | { 47 | host.WriteErrorLine("No GPT instance defined."); 48 | return; 49 | } 50 | 51 | if (string.IsNullOrEmpty(name)) 52 | { 53 | setting.ListAllGPTs(host); 54 | return; 55 | } 56 | 57 | try 58 | { 59 | _agnet.Settings.ShowOneGPT(host, name); 60 | } 61 | catch (InvalidOperationException ex) 62 | { 63 | string availableGPTNames = GPTNamesAsString(); 64 | host.WriteErrorLine($"{ex.Message} Available GPT(s): {availableGPTNames}."); 65 | } 66 | } 67 | 68 | private void UseGPTAction(string name) 69 | { 70 | // Reload the setting file if needed. 71 | _agnet.ReloadSettings(); 72 | 73 | var setting = _agnet.Settings; 74 | var host = Shell.Host; 75 | 76 | if (setting is null || setting.GPTs.Count is 0) 77 | { 78 | host.WriteErrorLine("No GPT instance defined."); 79 | return; 80 | } 81 | 82 | try 83 | { 84 | GPT chosenGPT = string.IsNullOrEmpty(name) 85 | ? host.PromptForSelectionAsync( 86 | title: "[orange1]Please select a [Blue]GPT[/] to use[/]:", 87 | choices: setting.GPTs, 88 | converter: GPTName, 89 | CancellationToken.None).GetAwaiter().GetResult() 90 | : setting.GetGPTByName(name); 91 | 92 | setting.UseGPT(chosenGPT); 93 | _agnet.UpdateDescription(); 94 | host.MarkupLine($"Using the agent [green]{chosenGPT.Name}[/]:"); 95 | host.WriteLine(_agnet.Description); 96 | } 97 | catch (InvalidOperationException ex) 98 | { 99 | string availableGPTNames = GPTNamesAsString(); 100 | host.WriteErrorLine($"{ex.Message} Available GPT(s): {availableGPTNames}."); 101 | } 102 | } 103 | 104 | private static string GPTName(GPT gpt) => gpt.Name; 105 | private IEnumerable GPTNameCompleter(CompletionContext context) => _agnet.Settings?.GPTs.Select(GPTName) ?? []; 106 | private string GPTNamesAsString() => string.Join(", ", GPTNameCompleter(null)); 107 | } 108 | -------------------------------------------------------------------------------- /shell/agents/AIShell.OpenAI.Agent/README.md: -------------------------------------------------------------------------------- 1 | # OpenAI Agent 2 | 3 | This agent is designed to provide a flexible and user-friendly platform for interacting with OpenAI 4 | services, either the public OpenAI service or a private deployment of the Azure OpenAI service, 5 | through one or more custom defined GPT instances. 6 | 7 | For more information about this agent, see the [OpenAI Agent][01] documentation. 8 | 9 | 10 | [01]: https://learn.microsoft.com/powershell/utility-modules/aishell/how-to/agent-openai 11 | -------------------------------------------------------------------------------- /shell/agents/AIShell.PhiSilica.Agent/AIShell.PhiSilica.Agent.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Library 4 | net8.0-windows10.0.26100.0 5 | enable 6 | CS8305 7 | AnyCPU 8 | None 9 | true 10 | true 11 | true 12 | win-arm64 13 | true 14 | 15 | 16 | 17 | 18 | 19 | false 20 | 21 | runtime 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /shell/agents/AIShell.PhiSilica.Agent/PhiSilicaAgent.cs: -------------------------------------------------------------------------------- 1 | using AIShell.Abstraction; 2 | using Microsoft.Windows.AI; 3 | using Microsoft.Windows.AI.Generative; 4 | 5 | namespace AIShell.PhiSilica.Agent; 6 | 7 | public sealed partial class PhiSilicaAgent : ILLMAgent 8 | { 9 | private readonly Task _initTask; 10 | private LanguageModel _model; 11 | 12 | public string Name => "PhiSilica"; 13 | public string Description => "This is the AI Shell Agent for talking to the inbox Phi Silica model on Copilot+ PCs."; 14 | public string SettingFile => null; 15 | 16 | public IEnumerable GetCommands() => null; 17 | public bool CanAcceptFeedback(UserAction action) => false; 18 | public Task RefreshChatAsync(IShell shell, bool force) => Task.CompletedTask; 19 | public void OnUserAction(UserActionPayload actionPayload) { } 20 | public void Initialize(AgentConfig config) { } 21 | public void Dispose() { } 22 | 23 | public PhiSilicaAgent() 24 | { 25 | // Start the initialization for AI feature and model on a background thread. 26 | _initTask = Task.Run(InitFeatureAndModelAsync); 27 | } 28 | 29 | private async Task InitFeatureAndModelAsync() 30 | { 31 | AIFeatureReadyState state = LanguageModel.GetReadyState(); 32 | if (state is AIFeatureReadyState.NotSupportedOnCurrentSystem) 33 | { 34 | throw new PlatformNotSupportedException("The Phi Silica feature is not supported on current system."); 35 | } 36 | 37 | if (state is AIFeatureReadyState.DisabledByUser) 38 | { 39 | throw new PlatformNotSupportedException("The Phi Silica feature is currently disabled."); 40 | } 41 | 42 | if (state is AIFeatureReadyState.EnsureNeeded) 43 | { 44 | // Initialize the WinRT runtime. 45 | AIFeatureReadyResult result = await LanguageModel.EnsureReadyAsync(); 46 | // Do not proceed if it failed to get the feature ready. 47 | if (result.Status is not AIFeatureReadyResultState.Success) 48 | { 49 | throw new InvalidOperationException(result.ErrorDisplayText, result.Error); 50 | } 51 | } 52 | 53 | _model = await LanguageModel.CreateAsync(); 54 | } 55 | 56 | public async Task ChatAsync(string input, IShell shell) 57 | { 58 | IHost host = shell.Host; 59 | 60 | try 61 | { 62 | // Wait for the init task to finish. Once it's finished, calling this again is a non-op. 63 | await _initTask; 64 | } 65 | catch (Exception e) 66 | { 67 | host.WriteErrorLine(e.Message); 68 | if (e is InvalidOperationException && e.InnerException is not null) 69 | { 70 | host.WriteErrorLine(e.InnerException.StackTrace); 71 | } 72 | else if (e is not PlatformNotSupportedException) 73 | { 74 | // Show stack trace for non-PNS exception. 75 | host.WriteErrorLine(e.StackTrace); 76 | } 77 | 78 | return false; 79 | } 80 | 81 | var result = await host.RunWithSpinnerAsync( 82 | status: "Thinking ...", 83 | func: async () => await _model.GenerateResponseAsync(input) 84 | ); 85 | 86 | if (result is not null && !string.IsNullOrEmpty(result.Text)) 87 | { 88 | host.RenderFullResponse(result.Text); 89 | } 90 | else 91 | { 92 | host.WriteErrorLine("No response received from the language model."); 93 | } 94 | 95 | return true; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /shell/agents/Microsoft.Azure.Agent/Microsoft.Azure.Agent.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | true 7 | 8 | 9 | false 10 | 11 | 12 | 13 | 14 | false 15 | None 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | contentFiles 25 | All 26 | 27 | 28 | 29 | 30 | 31 | 32 | false 33 | 34 | runtime 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /shell/agents/Microsoft.Azure.Agent/README.md: -------------------------------------------------------------------------------- 1 | # Copilot in Azure Agent 2 | 3 | This agent is designed to connect you to the [Copilot in Azure][03] experience directly from your command 4 | line. It provides assistance for Azure CLI commands, Azure PowerShell commands, and general Azure 5 | knowledge. To use this agent, you need to sign in to Azure using the az login command from Azure 6 | CLI. For more details about this agent please see, [Copilot in Azure Agent][01]. 7 | 8 | ![Copilot in Azure Agent][02] 9 | 10 | 11 | 12 | [01]: https://learn.microsoft.com/powershell/utility-modules/aishell/how-to/agent-azure 13 | [02]: ../../../docs/media/DemoGIFs/azure-agent.gif 14 | [03]: https://azure.microsoft.com/products/copilot -------------------------------------------------------------------------------- /shell/nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /shell/shell.common.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AI Shell 5 | Microsoft Corporation 6 | (c) Microsoft Corporation. 7 | 8 | net8.0 9 | enable 10 | 12.0 11 | 1.0.0-preview.4 12 | 13 | true 14 | true 15 | true 16 | en-US 17 | false 18 | 19 | 20 | 21 | 22 | false 23 | None 24 | true 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tools/assets/AppxManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | $DISPLAYNAME$ 16 | Microsoft Corporation 17 | assets\StoreLogo.png 18 | disabled 19 | disabled 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 | -------------------------------------------------------------------------------- /tools/assets/Square150x150Logo-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square150x150Logo-Preview.png -------------------------------------------------------------------------------- /tools/assets/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square150x150Logo.png -------------------------------------------------------------------------------- /tools/assets/Square44x44Logo-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square44x44Logo-Preview.png -------------------------------------------------------------------------------- /tools/assets/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square44x44Logo.png -------------------------------------------------------------------------------- /tools/assets/Square44x44Logo.targetsize-48-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square44x44Logo.targetsize-48-Preview.png -------------------------------------------------------------------------------- /tools/assets/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /tools/assets/Square44x44Logo.targetsize-48_altform-unplated-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square44x44Logo.targetsize-48_altform-unplated-Preview.png -------------------------------------------------------------------------------- /tools/assets/Square44x44Logo.targetsize-48_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/Square44x44Logo.targetsize-48_altform-unplated.png -------------------------------------------------------------------------------- /tools/assets/StoreLogo-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/StoreLogo-Preview.png -------------------------------------------------------------------------------- /tools/assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PowerShell/AIShell/b591ed9e41be27a69c37e68a6168ec89c9f3bedb/tools/assets/StoreLogo.png -------------------------------------------------------------------------------- /tools/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "AgentsToInclude": "*" 3 | } 4 | -------------------------------------------------------------------------------- /tools/packaging/packaging.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | RootModule = "packaging.psm1" 3 | GUID = "27f898a4-1fcd-4704-8fd1-16894ab80090" 4 | Author = "AIShell" 5 | CompanyName = "Microsoft Corporation" 6 | Copyright = "Copyright (c) Microsoft Corporation." 7 | ModuleVersion = "1.0.0" 8 | PowerShellVersion = "7.2" 9 | CmdletsToExport = @() 10 | FunctionsToExport = @( 11 | 'New-NugetPackage' 12 | 'New-NuSpec' 13 | 'Get-ProjectPackageInformation' 14 | 'New-TarballPackage' 15 | 'New-ZipPackage' 16 | 'New-MSIXPackage' 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /tools/packaging/packaging.strings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | NuspecTemplate = @' 3 | 4 | 5 | 6 | AIShell.Abstraction 7 | {0} 8 | Microsoft 9 | https://github.com/PowerShell/AIShell 10 | false 11 | MIT 12 | https://licenses.nuget.org/MIT 13 | The abstraction layer SDK for building a plugin agent for AIShell. 14 | © Microsoft Corporation. All rights reserved. 15 | AIShell 16 | en-US 17 | 18 | 19 | 20 | 21 | 22 | '@ 23 | } 24 | --------------------------------------------------------------------------------