[
  {
    "path": ".editorconfig",
    "content": "﻿# To learn more about .editorconfig see https://aka.ms/editorconfigdocs\n###############################\n# Core EditorConfig Options   #\n###############################\nroot = true\n\n# All files\n[*]\nindent_style = space\n\n# Code files\n[*.{cs,csx,vb,vbx}]\nindent_size = 4\ninsert_final_newline = true\ncharset = utf-8\n\n[*.md]\ncharset = utf-8\n\n###############################\n# .NET Coding Conventions     #\n###############################\n[*.{cs,vb}]\n# Organize usings\ndotnet_sort_system_directives_first = true\n# this. preferences\ndotnet_style_qualification_for_field = false:silent\ndotnet_style_qualification_for_property = false:silent\ndotnet_style_qualification_for_method = false:silent\ndotnet_style_qualification_for_event = false:silent\n# Language keywords vs BCL types preferences\ndotnet_style_predefined_type_for_locals_parameters_members = true:silent\ndotnet_style_predefined_type_for_member_access = true:silent\n# Parentheses preferences\ndotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent\ndotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent\n# Modifier preferences\ndotnet_style_require_accessibility_modifiers = for_non_interface_members:silent\ndotnet_style_readonly_field = true:suggestion\n# Expression-level preferences\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_explicit_tuple_names = true:suggestion\ndotnet_style_null_propagation = true:suggestion\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent\ndotnet_style_prefer_inferred_tuple_names = true:suggestion\ndotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion\ndotnet_style_prefer_auto_properties = true:silent\ndotnet_style_prefer_conditional_expression_over_assignment = true:silent\ndotnet_style_prefer_conditional_expression_over_return = true:silent\n###############################\n# Naming Conventions          #\n###############################\n# Style Definitions\ndotnet_naming_style.pascal_case_style.capitalization             = pascal_case\n# Use PascalCase for constant fields  \ndotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.constant_fields_should_be_pascal_case.symbols  = constant_fields\ndotnet_naming_rule.constant_fields_should_be_pascal_case.style    = pascal_case_style\ndotnet_naming_symbols.constant_fields.applicable_kinds            = field\ndotnet_naming_symbols.constant_fields.applicable_accessibilities  = *\ndotnet_naming_symbols.constant_fields.required_modifiers          = const\n###############################\n# C# Coding Conventions       #\n###############################\n[*.cs]\n# var preferences\ncsharp_style_var_for_built_in_types = true:silent\ncsharp_style_var_when_type_is_apparent = true:silent\ncsharp_style_var_elsewhere = true:silent\n# Expression-bodied members\ncsharp_style_expression_bodied_methods = false:silent\ncsharp_style_expression_bodied_constructors = false:silent\ncsharp_style_expression_bodied_operators = false:silent\ncsharp_style_expression_bodied_properties = true:silent\ncsharp_style_expression_bodied_indexers = true:silent\ncsharp_style_expression_bodied_accessors = true:silent\n# Pattern matching preferences\ncsharp_style_pattern_matching_over_is_with_cast_check = true:suggestion\ncsharp_style_pattern_matching_over_as_with_null_check = true:suggestion\n# Null-checking preferences\ncsharp_style_throw_expression = true:suggestion\ncsharp_style_conditional_delegate_call = true:suggestion\n# Modifier preferences\ncsharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion\n# Expression-level preferences\ncsharp_prefer_braces = true:silent\ncsharp_style_deconstructed_variable_declaration = true:suggestion\ncsharp_prefer_simple_default_expression = true:suggestion\ncsharp_style_pattern_local_over_anonymous_function = true:suggestion\ncsharp_style_inlined_variable_declaration = true:suggestion\n###############################\n# C# Formatting Rules         #\n###############################\n# New line preferences\ncsharp_new_line_before_open_brace = all\ncsharp_new_line_before_else = true\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_object_initializers = true\ncsharp_new_line_before_members_in_anonymous_types = true\ncsharp_new_line_between_query_expression_clauses = true\n# Indentation preferences\ncsharp_indent_case_contents = true\ncsharp_indent_switch_labels = true\ncsharp_indent_labels = flush_left\n# Space preferences\ncsharp_space_after_cast = false\ncsharp_space_after_keywords_in_control_flow_statements = true\ncsharp_space_between_method_call_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_parameter_list_parentheses = false\ncsharp_space_between_parentheses = false\ncsharp_space_before_colon_in_inheritance_clause = true\ncsharp_space_after_colon_in_inheritance_clause = true\ncsharp_space_around_binary_operators = before_and_after\ncsharp_space_between_method_declaration_empty_parameter_list_parentheses = false\ncsharp_space_between_method_call_name_and_opening_parenthesis = false\ncsharp_space_between_method_call_empty_parameter_list_parentheses = false\n# Wrapping preferences\ncsharp_preserve_single_line_statements = true\ncsharp_preserve_single_line_blocks = true"
  },
  {
    "path": ".gitattributes",
    "content": "###############################################################################\n# Set default behavior to automatically normalize line endings.\n###############################################################################\n* text=auto\n\n*.sh text eol=lf\n\n###############################################################################\n# Set default behavior for command prompt diff.\n#\n# This is need for earlier builds of msysgit that does not have it on by\n# default for csharp files.\n# Note: This is only used by command line\n###############################################################################\n#*.cs     diff=csharp\n\n###############################################################################\n# Set the merge driver for project and solution files\n#\n# Merging from the command prompt will add diff markers to the files if there\n# are conflicts (Merging from VS is not affected by the settings below, in VS\n# the diff markers are never inserted). Diff markers may cause the following \n# file extensions to fail to load in VS. An alternative would be to treat\n# these files as binary and thus will always conflict and require user\n# intervention with every merge. To do so, just uncomment the entries below\n###############################################################################\n#*.sln       merge=binary\n#*.csproj    merge=binary\n#*.vbproj    merge=binary\n#*.vcxproj   merge=binary\n#*.vcproj    merge=binary\n#*.dbproj    merge=binary\n#*.fsproj    merge=binary\n#*.lsproj    merge=binary\n#*.wixproj   merge=binary\n#*.modelproj merge=binary\n#*.sqlproj   merge=binary\n#*.wwaproj   merge=binary\n\n###############################################################################\n# behavior for image files\n#\n# image files are treated as binary by default.\n###############################################################################\n#*.jpg   binary\n#*.png   binary\n#*.gif   binary\n\n###############################################################################\n# diff behavior for common document formats\n# \n# Convert binary document formats to text before diffing them. This feature\n# is only available from the command line. Turn it on by uncommenting the \n# entries below.\n###############################################################################\n#*.doc   diff=astextplain\n#*.DOC   diff=astextplain\n#*.docx  diff=astextplain\n#*.DOCX  diff=astextplain\n#*.dot   diff=astextplain\n#*.DOT   diff=astextplain\n#*.pdf   diff=astextplain\n#*.PDF   diff=astextplain\n#*.rtf   diff=astextplain\n#*.RTF   diff=astextplain\n"
  },
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: Build-Development\n\non:\n  push:\n    branches:\n      - main\n      - master\n  pull_request:\n    types:\n      - opened\n      - synchronize\n\njobs:\n  Build:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [windows-latest, ubuntu-latest, macos-latest]\n    env:\n      DOTNET_NOLOGO: true\n    steps:\n    - uses: actions/checkout@v1\n    - uses: actions/setup-dotnet@v1\n      with:\n        dotnet-version: '5.0.x'\n\n    # Build\n    - run: dotnet restore\n    - run: dotnet build -c Release\n\n    # Run Unit tests\n    - run: dotnet test -c Release --no-build --logger trx --results-directory $GITHUB_WORKSPACE/artifacts\n\n    # Packaging\n    - name: dotnet pack\n      run: dotnet pack -c Release --no-build -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output $GITHUB_WORKSPACE/artifacts\n      shell: bash\n\n    # Upload & Publish\n    - uses: actions/upload-artifact@master\n      with:\n        name: Packages\n        path: artifacts"
  },
  {
    "path": ".github/workflows/release.yaml",
    "content": "name: Build-Release\n\non:\n  push:\n    tags:\n      - v*\n\njobs:\n  Release:\n    if: \"contains(github.ref, 'refs/tags')\"\n    runs-on: ubuntu-latest\n    env:\n      DOTNET_NOLOGO: true\n    steps:\n    - uses: actions/checkout@v1\n    - uses: actions/setup-dotnet@v1\n      with:\n        dotnet-version: '5.0.x'\n\n    - name: \"Set VersionSuffix for Preview\"\n      if: \"contains(github.ref, 'refs/tags') && contains(github.ref, 'preview')\"\n      run: |\n        echo \"VERSION_SUFFIX=preview.`date '+%Y%m%d-%H%M%S'`+${GITHUB_SHA:0:6}\" >> $GITHUB_ENV\n    - name: \"Get git tag\"\n      if: \"contains(github.ref, 'refs/tags')\"\n      run: echo \"GIT_TAG=${GITHUB_REF#refs/tags/}\" >> $GITHUB_ENV\n\n    # Build\n    - run: dotnet restore\n    - run: dotnet build -c Release\n\n    # Packaging\n    - name: dotnet pack\n      run: dotnet pack -c Release --no-build --version-suffix \"$VERSION_SUFFIX\" -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg --output $GITHUB_WORKSPACE/artifacts\n\n    # Upload & Publish\n    - uses: actions/upload-artifact@master\n      with:\n        name: Packages\n        path: artifacts\n\n    - name: \"Push to NuGet.org\"\n      run: |\n        dotnet nuget push \"$GITHUB_WORKSPACE/artifacts/*.nupkg\" --skip-duplicate -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n.DS_Store\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\n[Aa][Rr][Mm]/\n[Aa][Rr][Mm]64/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n*.snupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!?*.[Cc]ache/\n\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n*- Backup*.rdl\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n# BeatPulse healthcheck temp database\nhealthchecksdb\n\n# .NET Launch Profiles\nlaunchSettings.json"
  },
  {
    "path": "Chell.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.30114.105\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Chell\", \"src\\Chell\\Chell.csproj\", \"{6BE659EC-A00D-4148-B19D-B5478DE001FA}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Chell.Run\", \"src\\Chell.Run\\Chell.Run.csproj\", \"{893D8C70-3C13-47D6-987F-C099C6161D7E}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Chell.Tests\", \"tests\\Chell.Tests\\Chell.Tests.csproj\", \"{1FBAA8ED-438E-498C-AB1F-29429550DC21}\"\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Solution Items\", \"Solution Items\", \"{EBB92712-D878-48FD-8E31-E577AABDC0FB}\"\n\tProjectSection(SolutionItems) = preProject\n\t\t.gitignore = .gitignore\n\t\t.github\\workflows\\build.yaml = .github\\workflows\\build.yaml\n\t\tDirectory.Build.props = Directory.Build.props\n\t\tREADME.md = README.md\n\t\t.github\\workflows\\release.yaml = .github\\workflows\\release.yaml\n\tEndProjectSection\nEndProject\nProject(\"{2150E333-8FDC-42A3-9474-1A3956D46DE8}\") = \"Samples\", \"Samples\", \"{AF454F92-8B84-446B-B0E2-9BA8887B09CC}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"GettingStarted.Basic.Windows\", \"samples\\GettingStarted.Basic.Windows\\GettingStarted.Basic.Windows.csproj\", \"{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}\"\nEndProject\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"GettingStarted.Basic.Unix\", \"samples\\GettingStarted.Basic.Unix\\GettingStarted.Basic.Unix.csproj\", \"{69DC1056-843E-4980-908A-5DB4ADA95460}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x64 = Debug|x64\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x64 = Release|x64\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x64.Build.0 = Release|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{6BE659EC-A00D-4148-B19D-B5478DE001FA}.Release|x86.Build.0 = Release|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x64.Build.0 = Release|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{893D8C70-3C13-47D6-987F-C099C6161D7E}.Release|x86.Build.0 = Release|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x64.Build.0 = Release|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{1FBAA8ED-438E-498C-AB1F-29429550DC21}.Release|x86.Build.0 = Release|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x64.Build.0 = Release|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5}.Release|x86.Build.0 = Release|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x64.ActiveCfg = Debug|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x64.Build.0 = Debug|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x64.ActiveCfg = Release|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x64.Build.0 = Release|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(NestedProjects) = preSolution\n\t\t{9BE2CB2C-D6F2-429A-A00F-CF30CAEF28B5} = {AF454F92-8B84-446B-B0E2-9BA8887B09CC}\n\t\t{69DC1056-843E-4980-908A-5DB4ADA95460} = {AF454F92-8B84-446B-B0E2-9BA8887B09CC}\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {5B16EB0E-5342-4D93-9FA2-8327FF69A276}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Directory.Build.props",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <VersionPrefix>1.0.0</VersionPrefix>\n    <LangVersion>latest</LangVersion>\n    <Nullable>enable</Nullable>\n    <GenerateDocumentationFile>true</GenerateDocumentationFile>\n    <NoWarn>$(NoWarn);1591;1587;1574</NoWarn>\n\n    <!-- NuGet Package Information -->\n    <Description>Write scripts with the power of .NET. Provides a shell script-like (bash, cmd, ...) experience to .NET application.</Description>\n    <Authors>Mayuki Sawatari</Authors>\n    <Copyright>Copyright © Mayuki Sawatari</Copyright>\n    <PackageProjectUrl>https://github.com/mayuki/Chell</PackageProjectUrl>\n    <RepositoryUrl>https://github.com/mayuki/Chell</RepositoryUrl>\n    <!--<PackageIcon>icon.png</PackageIcon>-->\n    <PackageTags>CommandLine Shell Process</PackageTags>\n    <PackageLicenseExpression>MIT</PackageLicenseExpression>\n\n  </PropertyGroup>\n</Project>\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright © Mayuki Sawatari <mayuki@misuzilla.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE."
  },
  {
    "path": "README.md",
    "content": "# Chell\nWrite scripts with the power of C# and .NET.\n\nChell is a library and execution tool for providing a shell script-like (bash, cmd, ...) experience to .NET applications.\n\n```csharp\nvar branch = await Run($\"git branch --show-current\");\nawait Run($\"git archive {branch} -o {branch}.zip\");\n```\n\n.NET applications are great for complex tasks, but executing processes can be boring. Chell brings the experience closer to shell scripting. This library is heavily influenced by [google/zx](https://github.com/google/zx).\n\n## When should I use Chell?\n- **Write better shell scripts**: Write a complex script and use the power of .NET and C#\n- **Write multi-platform shell scripts**: As an alternative to scripts that work on multiple platforms\n- **Run a process quickly in your app**: As .NET library for easy handling of process launch and output\n- **All developers in the project are .NET developers**: 🙃\n\nOf course, if the shell script is already working fine and you don't have any problems, then there is no need to use Chell.\n\n## Chell at a glance\nUsing Chell makes the code feel more like a script by taking advantage of C# 9's top-level statements and C# 6's `using static`.\n\n```csharp\n// Chell.Exports exposes a variety of functions and properties\nusing Chell;\nusing static Chell.Exports;\n```\n```csharp\n// Move the current directory with Cd method\nCd(\"/tmp\");\n\n// Dispose the return value of Cd method to return to the previous directory\nusing (Cd(\"/usr/local/bin\"))\n{\n    // The current directory is \"/usr/local/bin\".\n}\n// The current directory is \"/\" again.\n```\n```csharp\n// You can run the process by passing a string to Run method\nawait Run($\"ls -lFa\");\n```\n```csharp\n// An interpolated string passed to Run method will be escaped and expanded if it is an array\nvar newDirs = new [] { \"foo\", \"bar\", \"my app\", \"your;app\" };\nawait Run($\"mkdir {newDirs}\"); // $ mkdir foo bar \"my app\" \"your;app\"\n```\n```csharp\n// Run method returns the result object of the command (ProcessOutput class)\nvar result = await Run($\"ls -lFa\");\n// You can read stdout & stderr line by line\nforeach (var line in result)\n{\n   Echo(line);\n}\n\n// Allows to get stdout & stderr with implicit conversion to `string`\nstring output = result;\n// You can also get stdout as bytes (ReadOnlyMemory<byte>)\nvar binary = result.OutputBinary;\n```\n```csharp\n// Provides convenient extension methods for parsing JSON.\nvar images = await Run($\"docker image ls --format {\"{{json .}}\"}\").SuppressConsoleOutputs();\nforeach (var image in images.AsJsonLines(new { Repository = \"\", ID = \"\", Tag = \"\"}))\n{\n    Echo(image);\n}\n// $ docker image ls --format \"{{json .}}\"\n// { Repository = mcr.microsoft.com/dotnet/sdk, ID = b160c8f3dbd6, Tag = 5.0 }\n// { Repository = <none>, ID = 3ee645b4a3bd, Tag = <none> }\n```\n```csharp\n// Standard input/output of process tasks can be connected by pipes\nawait (Run($\"ls -lFa\") | Run($\"grep dotnet\"));\n// The difference with `await (Run($\"ls -lFa | grep dotnet\"));` is that the shell can pipe or not.\n\n// You can also specify a Stream as input or output\n// Write ffmpeg output to a Stream.\nawait (Run($\"ffmpeg ...\") | destinationStream);\n// Write a Stream to ffmpeg process.\nawait (srcStream | Run($\"ffmpeg ...\"));\n```\n\nJust want to make it easy for your app to handle processes? If you don't use `Chell.Exports`, you won't get any unnecessary methods or properties, and you'll get the same functions by `new Run(...)`.\n\n```csharp\nusing Chell;\nvar result = await new Run($\"ls -lF\");\n```\n\nWant to run it like a scripting language? Install [Chell.Run](#chellrun), and you can run it like a script.\n\n```bash\n% dotnet tool install -g Chell.Run\n\n% chell -e \"Echo(DateTime.Now)\"\n9/1/2021 0:00:00 PM\n\n% cat <<__EOF__ > MyScript.cs\nvar dirs = new [] { \"foo bar\", \"baz\" };\nawait Run($\"mkdir {dirs}\");\nawait Run($\"ls -l\");\n__EOF__\n\n% chell MyScript.cs\n$ mkdir \"foo bar\" \"baz\"\n$ ls -l\ntotal 8\ndrwxr-xr-x 2 mayuki mayuki 4096 Sep  1 00:00  baz/\ndrwxr-xr-x 2 mayuki mayuki 4096 Sep  1 00:00 'foo bar'/\n```\n\n\n## Features\n- Automatic shell character escaping and array expansion\n- Stream and Process Pipes\n- Provide utilities and shortcuts useful for scripting.\n- Simple shell script-like execution tools\n- Multi-platform (Windows, Linux, macOS)\n- LINQPad friendly\n\n## Install\n```\ndotnet package add Chell\n```\n### Requirements\n.NET Standard 2.1, .NET 5 or higher\n\n## Chell.Exports\nChell.Exports class exposes a variety of utilities and shortcuts to make writing feel like shell scripting. It is recommended to include this class in your scripts with `static using`.\n\n### Methods (Functions)\n#### `Run`\nStarts a process using the specified command-line and returns a `ProcessTask`.\n\n```csharp\nawait Run($\"ls -lF\");\n\n// The followings are equivalent to calling Run method\nawait (Run)$\"ls -lF\";\nawait new Run($\"ls -lF\");\n```\n\nThe process will be launched asynchronously and can wait for completion by `await`. And you can `await` to get a `ProcessOutput` object with its output.\n\nIf the exit code of the process returns non-zero, it will throw an exception. To suppress this exception, see `NoThrow`.\n\nAn interpolated string passed to Run method will be escaped and expanded if it is an array.\n\n```csharp\nvar newDirs = new [] { \"foo\", \"bar\", \"my app\", \"your;app\" };\nawait Run($\"mkdir {newDirs}\"); // equivalent to `mkdir foo bar \"my app\" \"your;app\"`\n```\n\nYou can also pass an execution options (`ProcessTaskOptions`) to Run method.\n\n```csharp\nawait Run($\"ping -t localhost\", new ProcessTaskOptions(\n    workingDirectory: @\"C:\\Windows\",\n    timeout: TimeSpan.FromSeconds(1)\n));\n```\n\n#### `Cd(string)`\n```csharp\nCd(\"/usr/local/bin\"); // equivalent to `Environment.CurrentDirectory = \"/usr/local/bin\";`\n```\n\nDispose the return value of `Cd` method to return to the previous directory.\n\n```csharp\nCd(\"/\"); // The current directory is \"/\".\nusing (Cd(\"/usr/local/bin\"))\n{\n    // The current directory is \"/usr/local/bin\".\n}\n// The current directory is \"/\" again.\n```\n\n#### `Mkdirp(string path)`\nSame as `mkdir -p`. Creates a new directory and any necessary sub-directories in the specified path.\n\n#### `Dump<T>(T value)`\nFormats the object and write it to the console.\n\n```csharp\nDump(new { Foo = 123, Bar = \"Baz\" }); // => \"{ Foo = 123, Bar = \"Baz\" }\"\n```\n\n#### `Which(string name)`, `TryWhich(string name, out string path)`\nReturns a path of the specified command.\n\n```csharp\nvar dotnetPath = Which(\"dotnet\");\nawait Run($\"{dotnetPath} run\");\n```\n\n#### `Echo(object message = default)`\n`Echo` method is equivalent to Console.WriteLine.\n\n```csharp\nEcho(\"Hello World!\"); // equivalent to Console.WriteLine(\"Hello World!\");\n```\n\n#### `Sleep(int duration)`, `Sleep(TimeSpan duration)`\nReturns a Task that waits for the specified duration.\n\n```csharp\nawait Sleep(10); // Sleep for 10 seconds.\n```\n\n#### `Exit(int exitCode)`\nTerminates the application with an exit code.\n\n```csharp\nExit(1);\n```\n\n### Properties\n\n#### `Env.Vars`\n\nExposes the environment variables as `IDictionary<string, string>`.\n\n```csharp\nEnv.Vars[\"PATH\"] = Env.Vars[\"PATH\"] + \":/path/to/\";\n```\n\n#### `Env.IsWindows`\nReturns whether the running operating system is Windows or not. If it returns `false`, the operating system is Linux or macOS.\n\n```csharp\nif (Env.IsWindows) { /* Something to do for Windows */ }\n```\n\n#### `Env.Shell`\nSpecify explicitly which shell to use, or set to not use a shell.\n\n```csharp\nEnv.Shell.UseBash();\nEnv.Shell.NoUseShell();\nEnv.Shell.UseCmd();\n```\n\n#### `Env.Verbosity`\nSets or gets the output level when executing a command/process.\n\n- `Verbosity.All`: Displays both the command line and the output of the command\n- `Verbosity.CommandLine`: Displays the command line\n- `Verbosity.Output`: Displays the output of the command\n- `Verbosity.Silent`: No display\n\n#### `Env.ProcessTimeout`\n\nSets the timeout for running the process. The default value is `0` (disabled).\n\n```csharp\nEnv.ProcessTimeout = TimeSpan.FromSecond(1);\n\n// OperationCanceledException will be thrown after 1s.\nawait Run($\"ping -t localhost\");\n```\n\n#### `Arguments`\nGets the arguments passed to the current application.\n\n```csharp\n// $ myapp foo bar baz => new [] { \"foo\", \"bar\", \"baz\" };\nforeach (var arg in Arguments) { /* ... */ }\n```\n#### `CurrentDirectory`, `ExecutableDirectory`, `ExecutableName`, `ExecutablePath`\nGets the current directory and the application directory or name or path.\n\n```csharp\n// C:\\> cd C:\\Users\\Alice\n// C:\\Users\\Alice> Downloads\\MyApp.exe\n\nEcho(CurrentDirectory); // C:\\Users\\Alice\nEcho(ExecutableDirectory); // C:\\Users\\Alice\\Downloads\nEcho(ExecutableName); // MyApp.exe\nEcho(ExecutablePath); // C:\\Users\\Alice\\Downloads\\MyApp.exe\n```\n\n#### `HomeDirectory`\nGets the path of the current user's home directory.\n\n```csharp\n// Windows: C:/Users/<UserName>\n// Linux: /home/<UserName>\n// macOS: /Users/<UserName>\nEcho(HomeDirectory);\n```\n\n#### `StdIn`, `StdOut`, `StdErr`\nProvides the wrapper with methods useful for reading and writing to the standard input/output/error streams.\n\n```csharp\n// Reads data from standard input.\nawait StdIn.ReadToEndAsync();\n\n// Writes data to standard output or error.\nStdOut.WriteLine(\"FooBar\");\nStdErr.WriteLine(\"Oops!\");\n```\n\n## ProcessTask class\nRepresents the execution task of the process started by `Run`.\n\n\n### `Pipe`\nConnects the standard output of the process to another `ProcessTask` or `Stream`.\n\n```csharp\nawait (Run($\"ls -lF\") | Run($\"grep .dll\"));\n\n// The followings are equivalent to using '|'.\nvar procTask1 = Run($\"ls -lF\");\nvar procTask2 = Run($\"grep .dll\");\nprocTask1.Pipe(procTask2);\n```\n\nA `Stream` can also be passed to Pipe. If the ProcessTask has connected to the `Stream`, it will not write to `ProcessOutput`.\n\n```csharp\nvar memStream = new MemoryStream();\nawait Run($\"ls -lF\").Pipe(memStream);\n```\n\n### `ConnectStreamToStandardInput`\nConnects the Stream to the standard input of the process. The method can be called only once before the process starts.\n\n```csharp\nawait (myStream | Run($\"grep .dll\"));\n\n// The followings are equivalent to using '|'.\nvar procTask = Run($\"grep .dll\");\nprocTask.ConnectStreamToStandardInput(myStream);\n```\n\n### `NoThrow`\nSuppresses exception throwing when the exit code is non-zero.\n\n```csharp\nawait Run($\"AppReturnsExitCodeNonZero\").NoThrow();\n```\n\n### `SuppressConsoleOutputs`\nSuppresses the writing of command execution results to the standard output.\n\n```csharp\n// equivalent to \"Env.Verbosity = Verbosity.Silent\" or pipe to null.\nawait Run($\"ls -lF\").SuppressConsoleOutputs();\n\n```\n### `ExitCode`\nReturns a `Task` to get the exit code of the process. This is equivalent to waiting for a `ProcessTask` with `NoThrow`.\n\n```csharp\nvar proc = Run($\"ls -lF\");\nif (await proc.ExitCode != 0)\n{\n    ...\n}\n\n// equivalent to `(await Run($\"ls -lF\").NoThrow()).ExitCode`\n```\n\n## ProcessOutput class\nProvides the results of the process execution.\n\n### `Combined`, `CombinedBinary`\nGets the combined standard output and error as a string or byte array.\n\n### `Output`, `OutputBinary`\nGets the standard output as a string or byte array.\n\n### `Error`, `ErrorBinary`\nGets the standard error as a string or byte array.\n\n### `AsLines(bool trimEnd = false)`, `GetEnumerator()`\nGets the combined standard output and error as a per-line `IEnumerable<string>`.\n\n```csharp\n// equivalent to `foreach (var line in procOutput.AsLines())`\nforeach (var line in procOutput) { ... }\n```\n\n### `ToString()`\nThe method equivalent to `Combined` property.\n\n### `ExitCode`\nGets the exit code of the process.\n\n## Utilities and shortcuts\nChell.Exports class also exposes a variety of useful utilities and shortcuts to libraries.\n\n### `Prompt`\nPrompts the user for input and gets it.\n\n```csharp\nvar name = await Prompt(\"What's your name? \");\n```\n\n### `Chalk`: Kokuban: Terminal string styling\nProvides a shortcut to [mayuki/Kokuban](https://github.com/mayuki/Kokuban). You can easily style the text on the terminal.\n\n```csharp\n// \"Error: \" will be colored.\nEcho((Chalk.Red + \"Error: \") + \"Something went wrong.\");\n```\n\n### `Glob`\nProvides a shortcut to `Microsoft.Extensions.FileSystemGlobbing`.\n\n- `Glob(params string[] patterns)`\n- `Glob(string baseDir, string[] patterns)`\n\n```csharp\n// Glob patterns starting with '!' will be treated as excludes.\nforeach (var path in Glob(\"**/*.cs\", \"!**/*.vb\"))\n{\n  ...\n}\n```\n\n### JSON serialize/deserialize (System.Text.Json)\nProvides shortcuts to `System.Text.Json`.\n\n- `ToJson<T>(T obj)`\n```csharp\nvar obj = new { Name = \"Alice\", Age = 18 };\nvar json = ToJson(obj);\nEcho(json); // {\"Name\":\"Alice\",\"Age\":18}\n```\n\n- `FromJson<T>(string json)`\n- `FromJson<T>(string json, T shape)`\n```csharp\nvar json = \"{ \\\"foo\\\": 123 }\";\nvar obj = FromJson(json, new { Foo = 0 });\nDump(obj); // { Foo = 123 }\n```\n\n- `AsJson`\n- `AsJsonLines`\n```csharp\nusing Chell;\nvar output = await Run($\"docker image ls --format {\"{{json .}}\"}\");\nforeach (var image in output.AsJsonLines(new { Repository = \"\", ID = \"\", Tag = \"\"}))\n{\n    // ...\n}\n```\n```csharp\nusing Chell;\nvar output = await Run($\"kubectl version --client -o json\");\nvar obj = output.AsJson(new { clientVersion = new { major = \"\", minor = \"\", gitVersion = \"\" } });\nEcho(obj); // { clientVersion = { major = 1, minor = 21, gitVersion = v1.21.2 } }\n```\n\n### HTTP acccess (System.Net.Http)\nProvides shortcuts to `System.Net.Http.HttpClient`.\n\n- `FetchAsync`\n- `FetchByteArrayAsync`\n- `FetchStreamAsync`\n- `FetchStringAsync`\n\n## Chell as a Library\nChell can also be used as a utility library to run processes.\n\nIf you don't use `Chell.Exports`, you won't get any unnecessary methods or properties, and you can use `Run` and `ChellEnvironment`, `Exports` class.\n\n```csharp\nusing Chell;\n\nvar results = await new Run($\"ls -lF\");\n\n// ChellEnvironment.Current is equivalent to `Env` on `Chell.Exports`.\nConsole.WriteLine(ChellEnvironment.Current.ExecutablePath);\nConsole.WriteLine(ChellEnvironment.Current.ExecutableName);\nConsole.WriteLine(ChellEnvironment.Current.Arguments);\nConsole.WriteLine(ChellEnvironment.Current.Vars[\"PATH\"]);\n```\n\n## Chell.Run\nChell.Run executes the input source code in an environment where Chell and some libraries are available.\n\nIt does not perform any NuGet package resolution, so we recommend creating a typical C# project if you need to handle such complexities.\n\n```\n$ dotnet tool install -g Chell.Run\n```\n```bash\n$ chell -e \"Echo(123);\"\n$ chell <<EOF\nvar result = await Run($\"ls -lF\");\nEcho(\"Hello World!\");\nEOF\n```\n\nChell.Run implicitly has some namespace imports and library references and can be used out-of-the-box.\n\nImplicitly specified `using`s:\n\n- System\n- System.Collections\n- System.Collections.Generic\n- System.Diagnostics\n- System.IO\n- System.Text\n- System.Text.RegularExpressions\n- System.Linq\n- System.Threading\n- System.Threading.Tasks\n- Chell\n- Chell.Extensions\n- Chell.Exports (using static)\n\nAdditional referenced libraries:\n- Mono.Options\n- Sharprompt\n- Cocona.Lite\n\n## Chell ❤ LINQPad\nLINQPad is often used to do tasks in place of shell scripts. Chell can help you in this case as well.\n\nChell knows about LINQPad, so `Dump` method is replaced by LINQPad's `Dump`, and standard output and errors are displayed in the results without problems.\n\n![image](https://user-images.githubusercontent.com/9012/131680759-6f138ceb-457b-489f-b17d-e36688f13346.png)\n\n## License\n\n```\nMIT License\n\nCopyright © Mayuki Sawatari <mayuki@misuzilla.org>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n```\n"
  },
  {
    "path": "samples/GettingStarted.Basic.Unix/GettingStarted.Basic.Unix.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net5.0</TargetFramework>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Chell\\Chell.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/GettingStarted.Basic.Unix/Program.cs",
    "content": "using System;\nusing System.Linq;\nusing static Chell.Exports;\n\n// Starts a process.\n// The array will be expanded and the elements will be escaped\nvar dirs = new[] { \"/\", \"/usr\", \"/bin\" };\nvar results = await Run($\"ls -l {dirs}\");\n\n// Enumerates the results to retrieve the standard output line by line.\nforeach (var line in results)\n{\n    Echo($\"Result> {line}\");\n}\nEcho();\n\n// Built-in Variables\nEcho((Chalk.Green + \"ExecutableName: \") + string.Join(' ', ExecutableName));\nEcho((Chalk.Green + \"ExecutableDirectory: \") + string.Join(' ', ExecutableDirectory));\nEcho((Chalk.Green + \"Arguments: \") + string.Join(' ', Arguments));\nEcho((Chalk.Green + \"CurrentDirectory: \") + string.Join(' ', CurrentDirectory));\nEcho();\n\n// Environment Variables\nEcho((Chalk.Green + \"Env.Vars[\\\"PATH\\\"]: \") + Env.Vars[\"PATH\"]);\nEcho();\n\n// Standard Input/Error as Stream + Utility methods.\nStdOut.WriteLine(\"Hello World!\");\nStdErr.WriteLine(\"Hello World! (Error)\");\nEcho();\n\n// Get the data from network and pipe it to the process\nawait (await FetchByteArrayAsync(\"http://www.example.com/\") | Run(\"grep title\"));\n\n\n// Temporarily change the current directory.\nusing (Cd(\"/\"))\n{\n    await Run($\"dir\");\n}\n\nExit(1);\n"
  },
  {
    "path": "samples/GettingStarted.Basic.Windows/GettingStarted.Basic.Windows.csproj",
    "content": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFramework>net5.0</TargetFramework>\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Chell\\Chell.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "samples/GettingStarted.Basic.Windows/Program.cs",
    "content": "using System;\nusing System.Linq;\nusing Chell;\nusing static Chell.Exports;\n\n// Starts a process.\n// The array will be expanded and the elements will be escaped\nvar dirs = new[] { @\"C:\\Windows\\Microsoft.NET\", @\"C:\\Program Files\" };\nvar results = await Run($\"dir {dirs}\"); // \n\n// Enumerates the results to retrieve the standard output line by line.\nforeach (var line in results.Where(x => x.Contains(\"Windows\")))\n{\n    Echo($\"Result> {line}\");\n}\nEcho();\n\n// Built-in Variables\nEcho((Chalk.Green + \"ExecutableName: \") + string.Join(' ', ExecutableName));\nEcho((Chalk.Green + \"ExecutableDirectory: \") + string.Join(' ', ExecutableDirectory));\nEcho((Chalk.Green + \"Arguments: \") + string.Join(' ', Arguments));\nEcho((Chalk.Green + \"CurrentDirectory: \") + string.Join(' ', CurrentDirectory));\nEcho();\n\n// Environment Variables\nEcho((Chalk.Green + \"Env.Vars[\\\"PATH\\\"]: \") + Env.Vars[\"PATH\"]);\nEcho();\n\n// Standard Input/Error as Stream + Utility methods.\nStdOut.WriteLine(\"Hello World!\");\nStdErr.WriteLine(\"Hello World! (Error)\");\nEcho();\n\n// Get the data from network and pipe it to the process\nawait (await FetchByteArrayAsync(\"http://www.example.com/\") | Run(\"findstr title\"));\n\n// Temporarily change the current directory.\nusing (Cd(\"C:\\\\Users\"))\n{\n    await Run($\"dir\");\n}\n\nExit(1);\n"
  },
  {
    "path": "src/.editorconfig",
    "content": "###############################\n# C# Nullability              #\n###############################\n[*.cs]\n# CS8618: Non-nullable field is uninitialized. Consider declaring as nullable.\ndotnet_diagnostic.CS8618.severity = error\n# CS8604: Possible null reference argument.\ndotnet_diagnostic.CS8604.severity = error\n# CS8629: Nullable value type may be null.\ndotnet_diagnostic.CS8629.severity = error\n# CS8600: Converting null literal or possible null value to non-nullable type.\ndotnet_diagnostic.CS8600.severity = error\n# CS8603: Possible null reference return.\ndotnet_diagnostic.CS8603.severity = error\n# CS8610: Nullability of reference types in type of parameter doesn't match overridden member.\ndotnet_diagnostic.CS8610.severity = error\n# CS8625: Cannot convert null literal to non-nullable reference type.\ndotnet_diagnostic.CS8625.severity = error\n# CS8606: Possible null reference assignment to iteration variable\ndotnet_diagnostic.CS8606.severity = error\n# CS8602: Dereference of a possibly null reference.\ndotnet_diagnostic.CS8602.severity = error\n# CS8601: Possible null reference assignment.\ndotnet_diagnostic.CS8601.severity = error\n# CS8614: Nullability of reference types in type of parameter doesn't match implicitly implemented member.\ndotnet_diagnostic.CS8614.severity = error"
  },
  {
    "path": "src/Chell/Chell.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFrameworks>netstandard2.1;netcoreapp3.1;net5.0</TargetFrameworks>\n    <LangVersion>latest</LangVersion>\n    <Nullable>enable</Nullable>\n    <IsPackable>true</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Kokuban\" Version=\"0.1.0\" />\n    <PackageReference Include=\"Microsoft.Extensions.FileSystemGlobbing\" Version=\"5.0.0\" />\n    <PackageReference Include=\"System.IO.Pipelines\" Version=\"5.0.0\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"$(TargetFramework) == 'netstandard2.1' or $(TargetFramework) == 'netcoreapp3.1'\">\n    <PackageReference Include=\"System.Text.Json\" Version=\"5.0.0\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <None Update=\"Extensions\\ProcessTaskExtensions.tt\">\n      <Generator>TextTemplatingFileGenerator</Generator>\n      <LastGenOutput>ProcessTaskExtensions.Generated.cs</LastGenOutput>\n    </None>\n    <None Update=\"IO\\ChellWritableStream.tt\">\n      <Generator>TextTemplatingFileGenerator</Generator>\n      <LastGenOutput>ChellWritableStream.Generated.cs</LastGenOutput>\n    </None>\n  </ItemGroup>\n\n  <ItemGroup>\n    <Service Include=\"{508349b6-6b84-4df5-91f0-309beebad82d}\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <Compile Update=\"Extensions\\ProcessTaskExtensions.Generated.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>ProcessTaskExtensions.tt</DependentUpon>\n    </Compile>\n    <Compile Update=\"IO\\ChellWritableStream.Generated.cs\">\n      <DesignTime>True</DesignTime>\n      <AutoGen>True</AutoGen>\n      <DependentUpon>ChellWritableStream.tt</DependentUpon>\n    </Compile>\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Chell/ChellEnvironment.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Pipes;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing Chell.Internal;\nusing Chell.IO;\nusing Chell.Shell;\n\nnamespace Chell\n{\n    [Flags]\n    public enum ChellVerbosity\n    {\n        /// <summary>\n        /// By default, no output is written to the console.\n        /// </summary>\n        Silent = 0,\n\n        /// <summary>\n        /// Writes a executing command line to the console.\n        /// </summary>\n        CommandLine = 1 << 0,\n\n        /// <summary>\n        /// Writes a command output to the console.\n        /// </summary>\n        ConsoleOutputs = 1 << 1,\n\n        /// <summary>\n        /// Writes all command lines and command outputs.\n        /// </summary>\n        Full = CommandLine | ConsoleOutputs,\n\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        Debug = Full | 1 << 31,\n    }\n\n    public class ChellEnvironment\n    {\n        public static ChellEnvironment Current { get; set; } = new ChellEnvironment();\n        \n        private string[] _arguments;\n        private string? _executablePath;\n        private string _executableName;\n        private string _executableDirectory;\n\n        public ChellEnvironment()\n        {\n            var args = Environment.GetCommandLineArgs();\n            var path = args[0];\n            _arguments = args.Skip(1).ToArray();\n            _executablePath = path;\n            _executableName = Path.GetFileName(path);\n            _executableDirectory = Path.GetDirectoryName(path)!;\n        }\n\n        /// <summary>\n        /// Gets or sets the verbosity.\n        /// </summary>\n        public ChellVerbosity Verbosity { get; set; } = ChellVerbosity.Full;\n\n        public ShellExecutorProvider Shell { get; } = new ShellExecutorProvider();\n        public IConsoleProvider Console { get; set; } =\n            LINQPadHelper.RunningOnLINQPad\n                ? new LINQPadConsoleProvider()\n                : SystemConsoleProvider.Instance;\n\n        /// <summary>\n        /// Gets the identifier for the current application process.\n        /// </summary>\n        public int ProcessId =>\n#if NET5_0_OR_GREATER\n            Environment.ProcessId\n#else\n            Process.GetCurrentProcess().Id\n#endif\n        ;\n\n        /// <summary>\n        /// Gets whether the current application is running on Windows.\n        /// </summary>\n        public bool IsWindows => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n\n        /// <summary>\n        /// Gets the command line arguments. like <c>args</c> of a entry point.\n        /// </summary>\n        public IReadOnlyList<string> Arguments => _arguments;\n\n        /// <summary>\n        /// Gets the path of the executing application. like <c>argv[0]</c>. (e.g. C:\\\\Path\\To\\App.exe, /path/to/app)\n        /// </summary>\n        /// <remarks>\n        /// The path may be null when running a inline script.\n        /// </remarks>\n        public string? ExecutablePath => _executablePath;\n\n        /// <summary>\n        /// Gets the name of the executing application. like <c>argv[0]</c>. (e.g. App.exe, app)\n        /// </summary>\n        public string ExecutableName => _executableName;\n\n        /// <summary>\n        /// Gets the directory of the executing application. like <c>argv[0]</c>. (e.g. C:\\\\Path\\To, /path/to)\n        /// </summary>\n        public string ExecutableDirectory => _executableDirectory;\n\n        /// <summary>\n        /// Gets or sets the path of the current working directory.\n        /// </summary>\n        public string CurrentDirectory\n        {\n            get => Environment.CurrentDirectory;\n            set => Environment.CurrentDirectory = value;\n        }\n\n        /// <summary>\n        /// Gets the path of the current user's home directory.\n        /// </summary>\n        public string HomeDirectory => Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);\n\n        /// <summary>\n        /// Gets the environment variables as <see cref=\"IDictionary{String, String}\"/> representation.\n        /// </summary>\n        public IDictionary<string, string> Vars { get; } = new EnvironmentVariables();\n\n        /// <summary>\n        /// Gets the standard input stream.\n        /// </summary>\n        public ChellReadableStream StdIn => new ChellReadableStream(this.Console.OpenStandardInput(), this.Console.InputEncoding);\n\n        /// <summary>\n        /// Gets the standard output stream.\n        /// </summary>\n        public ChellWritableStream StdOut => new ChellWritableStream(this.Console.OpenStandardOutput(), this.Console.OutputEncoding);\n\n        /// <summary>\n        /// Gets the standard output stream.\n        /// </summary>\n        public ChellWritableStream StdErr => new ChellWritableStream(this.Console.OpenStandardError(), this.Console.OutputEncoding);\n\n        /// <summary>\n        /// Gets or sets the default timeout for the process. The value affects the current application. The default value is <see cref=\"TimeSpan.Zero\"/>.\n        /// </summary>\n        /// <remarks>\n        /// If the value is <see cref=\"TimeSpan.Zero\"/> or <see cref=\"TimeSpan.MaxValue\"/>, the process will not be timed out.\n        /// </remarks>\n        public TimeSpan ProcessTimeout { get; set; } = TimeSpan.Zero;\n\n        [EditorBrowsable(EditorBrowsableState.Never)]\n        public void SetCommandLineArgs(string? executablePath, string executableName, string executableDirectory, string[] args)\n        {\n            _arguments = args.ToArray();\n            _executableName = executableName;\n            _executablePath = executablePath;\n            _executableDirectory = executableDirectory;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/CommandLineString.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Diagnostics;\n\nnamespace Chell\n{\n    /// <summary>\n    /// Workaround for string/FormattableString overload issues\n    /// </summary>\n    [EditorBrowsable(EditorBrowsableState.Never)]\n    [DebuggerDisplay(\"CommandLineString: String={StringValue,nq}; FormattableString={FormattableStringValue,nq}\")]\n    public readonly struct CommandLineString\n    {\n        public string? StringValue { get; }\n        public FormattableString? FormattableStringValue { get; }\n\n        public CommandLineString(string value)\n        {\n            StringValue = value;\n            FormattableStringValue = null;\n        }\n\n        public CommandLineString(FormattableString value)\n        {\n            StringValue = null;\n            FormattableStringValue = value;\n        }\n\n        public static implicit operator CommandLineString(string value)\n        {\n            return new CommandLineString(value);\n        }\n\n        public static implicit operator CommandLineString(FormattableString value)\n        {\n            return new CommandLineString(value);\n        }\n    }\n}"
  },
  {
    "path": "src/Chell/Exports.cs",
    "content": "using Microsoft.Extensions.FileSystemGlobbing;\nusing Microsoft.Extensions.FileSystemGlobbing.Abstractions;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Text.Json;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Chell.Internal;\nusing Chell.IO;\nusing Kokuban;\nusing Kokuban.AnsiEscape;\n\nnamespace Chell\n{\n    public static class Exports\n    {\n        public static class Verbosity\n        {\n            /// <summary>\n            /// By default, no output is written to the console.\n            /// </summary>\n            public const ChellVerbosity Silent = ChellVerbosity.Silent;\n\n            /// <summary>\n            /// Writes a executing command line to the console.\n            /// </summary>\n            public const ChellVerbosity CommandLine = ChellVerbosity.CommandLine;\n\n            /// <summary>\n            /// Writes a command output to the console.\n            /// </summary>\n            public const ChellVerbosity ConsoleOutputs = ChellVerbosity.ConsoleOutputs;\n\n            /// <summary>\n            /// Writes all command lines and command outputs.\n            /// </summary>\n            public const ChellVerbosity Full = ChellVerbosity.Full;\n        }\n\n        /// <summary>\n        /// Gets the current environment.\n        /// </summary>\n        public static ChellEnvironment Env => ChellEnvironment.Current;\n\n        /// <summary>\n        /// Gets the standard input stream.\n        /// </summary>\n        public static ChellReadableStream StdIn => Env.StdIn;\n\n        /// <summary>\n        /// Gets the standard output stream.\n        /// </summary>\n        public static ChellWritableStream StdOut => Env.StdOut;\n\n        /// <summary>\n        /// Gets the standard error stream.\n        /// </summary>\n        public static ChellWritableStream StdErr => Env.StdErr;\n\n        /// <summary>\n        /// Gets the command line arguments. like <c>args</c> of a entry point.\n        /// </summary>\n        public static IReadOnlyList<string> Arguments => Env.Arguments;\n\n        /// <summary>\n        /// Gets the path of the executing application. like <c>argv[0]</c>. (e.g. C:\\\\Path\\To\\App.exe, /path/to/app)\n        /// </summary>\n        /// <remarks>\n        /// The path may be null when running a inline script.\n        /// </remarks>\n        public static string? ExecutablePath => Env.ExecutablePath;\n\n        /// <summary>\n        /// Gets the name of the executing application. like <c>argv[0]</c>. (e.g. App.exe, app)\n        /// </summary>\n        public static string ExecutableName => Env.ExecutableName;\n\n        /// <summary>\n        /// Gets the directory of the executing application. like <c>argv[0]</c>. (e.g. C:\\\\Path\\To, /path/to)\n        /// </summary>\n        public static string ExecutableDirectory => Env.ExecutableDirectory;\n\n        /// <summary>\n        /// Gets or sets the path of the current working directory.\n        /// </summary>\n        public static string CurrentDirectory\n        {\n            get => Env.CurrentDirectory;\n            set => Env.CurrentDirectory = value;\n        }\n\n        /// <summary>\n        /// Gets the identifier for the current application process.\n        /// </summary>\n        public static int ProcessId => Env.ProcessId;\n\n        /// <summary>\n        /// Gets the Kokuban ANSI style builder to decorate texts.\n        /// </summary>\n        public static AnsiStyle Chalk => Kokuban.Chalk.Create(KokubanOptions.Default);\n\n        /// <summary>\n        /// Starts the process task with the specified command line.\n        /// </summary>\n        /// <param name=\"commandLine\"></param>\n        /// <param name=\"options\"></param>\n        /// <returns></returns>\n        public static ProcessTask Run(FormattableString commandLine, ProcessTaskOptions? options = default)\n            => new ProcessTask(commandLine, options);\n\n        /// <summary>\n        /// Starts the process task with the specified command line.\n        /// </summary>\n        /// <param name=\"commandLine\"></param>\n        /// <param name=\"options\"></param>\n        /// <returns></returns>\n        public static ProcessTask Run(CommandLineString commandLine, ProcessTaskOptions? options = default)\n            => new ProcessTask(commandLine, options);\n\n        /// <summary>\n        /// Starts the process task with the specified command line.\n        /// </summary>\n        /// <param name=\"inputStream\">The data to be passed to the standard input of the process.</param>\n        /// <param name=\"commandLine\"></param>\n        /// <param name=\"options\"></param>\n        /// <returns></returns>\n        public static ProcessTask Run(Stream inputStream, FormattableString commandLine, ProcessTaskOptions? options = default)\n            => new ProcessTask(inputStream, commandLine, options);\n\n        /// <summary>\n        /// Starts the process task with the specified command line.\n        /// </summary>\n        /// <param name=\"inputStream\">The data to be passed to the standard input of the process.</param>\n        /// <param name=\"commandLine\"></param>\n        /// <param name=\"options\"></param>\n        /// <returns></returns>\n        public static ProcessTask Run(Stream inputStream, CommandLineString commandLine, ProcessTaskOptions? options = default)\n            => new ProcessTask(inputStream, commandLine, options);\n\n        /// <summary>\n        /// Starts the process task with the specified command line.\n        /// </summary>\n        /// <param name=\"inputData\">The data to be passed to the standard input of the process.</param>\n        /// <param name=\"commandLine\"></param>\n        /// <param name=\"options\"></param>\n        /// <returns></returns>\n        public static ProcessTask Run(ReadOnlyMemory<byte> inputData, FormattableString commandLine, ProcessTaskOptions? options = default)\n            => new ProcessTask(inputData, commandLine, options);\n\n        /// <summary>\n        /// Starts the process task with the specified command line.\n        /// </summary>\n        /// <param name=\"inputData\">The data to be passed to the standard input of the process.</param>\n        /// <param name=\"commandLine\"></param>\n        /// <param name=\"options\"></param>\n        /// <returns></returns>\n        public static ProcessTask Run(ReadOnlyMemory<byte> inputData, CommandLineString commandLine, ProcessTaskOptions? options = default)\n            => new ProcessTask(inputData, commandLine, options);\n\n        /// <summary>\n        /// Writes the message to the console.\n        /// </summary>\n        /// <param name=\"message\"></param>\n        public static void Echo(object? message = default)\n            => ChellEnvironment.Current.Console.Out.WriteLine(message);\n\n        /// <summary>\n        /// Writes the object details to the console.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"obj\"></param>\n        public static void Dump<T>(T obj)\n            => ObjectDumper.Dump(obj);\n\n        /// <summary>\n        /// Converts the object to a JSON.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        public static string ToJson<T>(T obj, JsonSerializerOptions? options = default)\n            => JsonSerializer.Serialize<T>(obj, options);\n\n        /// <summary>\n        /// Converts the JSON to an object.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        public static T? FromJson<T>(string json, T shape)\n            => FromJson<T>(json);\n\n        /// <summary>\n        /// Converts the JSON to an object.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        public static T? FromJson<T>(string json)\n            => Chell.Extensions.StringExtensions.AsJson<T>(json);\n\n        /// <summary>\n        /// Changes the current directory to the specified path.\n        /// </summary>\n        /// <remarks>\n        /// Dispose the return value to return to the previous directory.\n        /// </remarks>\n        /// <param name=\"path\"></param>\n        public static IDisposable Cd(string path)\n            => new ChangeDirectoryScope(path);\n\n        private class ChangeDirectoryScope : IDisposable\n        {\n            private readonly string _previousCurrentDirectory;\n\n            public ChangeDirectoryScope(string newCurrentDirectory)\n            {\n                _previousCurrentDirectory = Environment.CurrentDirectory;\n                ChangeDirectory(newCurrentDirectory);\n            }\n\n            public void Dispose()\n            {\n                ChangeDirectory(_previousCurrentDirectory);\n            }\n\n            private void ChangeDirectory(string path)\n            {\n                CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $\"cd {path}\");\n                Environment.CurrentDirectory = path;\n            }\n        }\n\n        /// <summary>\n        /// Sleeps for the specified time.\n        /// </summary>\n        /// <param name=\"timeSpan\"></param>\n        /// <returns></returns>\n        public static Task Sleep(TimeSpan timeSpan)\n            => Task.Delay(timeSpan);\n\n        /// <summary>\n        /// Sleeps for the specified time.\n        /// </summary>\n        /// <param name=\"seconds\"></param>\n        /// <returns></returns>\n        public static Task Sleep(int seconds)\n            => Task.Delay(TimeSpan.FromSeconds(seconds));\n\n        /// <summary>\n        /// Get the task to ignore the exception and return <see cref=\"ProcessOutput\"/>.\n        /// </summary>\n        /// <param name=\"task\"></param>\n        /// <returns></returns>\n        public static Task<ProcessOutput> NoThrow(ProcessTask task)\n            => task.NoThrow();\n\n        /// <summary>\n        /// Terminates the current process with specified exit code.\n        /// </summary>\n        /// <param name=\"exitCode\"></param>\n        public static void Exit(int exitCode = 0)\n            => Environment.Exit(exitCode);\n\n        /// <summary>\n        /// Creates a new directory and any necessary sub-directories in the specified path.\n        /// </summary>\n        /// <param name=\"path\"></param>\n        public static void Mkdirp(string path)\n            => Directory.CreateDirectory(path);\n\n        /// <summary>\n        /// Fetches the content of the specified URL using GET method.\n        /// </summary>\n        /// <param name=\"requestUri\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        public static Task<HttpResponseMessage> FetchAsync(string requestUri, CancellationToken cancellationToken = default)\n        {\n            CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $\"{nameof(FetchAsync)} {requestUri}\");\n            var httpClient = new HttpClient();\n            return httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);\n        }\n\n        /// <summary>\n        /// Fetches the content of the specified URL as string using GET method.\n        /// </summary>\n        /// <param name=\"requestUri\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        public static async Task<string> FetchStringAsync(string requestUri, CancellationToken cancellationToken = default)\n        {\n            CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $\"{nameof(FetchStringAsync)} {requestUri}\");\n            var httpClient = new HttpClient();\n            var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);\n            res.EnsureSuccessStatusCode();\n\n#if NET5_0_OR_GREATER\n            return await res.Content.ReadAsStringAsync(cancellationToken);\n#else\n            return await res.Content.ReadAsStringAsync();\n#endif\n        }\n\n        /// <summary>\n        /// Fetches the content of the specified URL as byte[] using GET method.\n        /// </summary>\n        /// <param name=\"requestUri\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        public static async Task<byte[]> FetchByteArrayAsync(string requestUri, CancellationToken cancellationToken = default)\n        {\n            CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $\"{nameof(FetchByteArrayAsync)} {requestUri}\");\n            var httpClient = new HttpClient();\n            var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);\n            res.EnsureSuccessStatusCode();\n\n#if NET5_0_OR_GREATER\n            return await res.Content.ReadAsByteArrayAsync(cancellationToken);\n#else\n            return await res.Content.ReadAsByteArrayAsync();\n#endif\n        }\n\n        /// <summary>\n        /// Fetches the content of the specified URL as Stream using GET method.\n        /// </summary>\n        /// <param name=\"requestUri\"></param>\n        /// <param name=\"cancellationToken\"></param>\n        /// <returns></returns>\n        public static async Task<Stream> FetchStreamAsync(string requestUri, CancellationToken cancellationToken = default)\n        {\n            CommandLineHelper.WriteCommandLineToConsole(ChellEnvironment.Current.Console, $\"{nameof(FetchStreamAsync)} {requestUri}\");\n            var httpClient = new HttpClient();\n            var res = await httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken);\n            res.EnsureSuccessStatusCode();\n\n#if NET5_0_OR_GREATER\n            return await res.Content.ReadAsStreamAsync(cancellationToken);\n#else\n            return await res.Content.ReadAsStreamAsync();\n#endif\n        }\n\n        /// <summary>\n        /// Gets the full path to a command, similar to the `which` command on Unix.\n        /// </summary>\n        /// <param name=\"commandName\"></param>\n        /// <returns></returns>\n        public static string Which(string commandName)\n            => Internal.Which.TryGetPath(commandName, out var matchedPath)\n                ? matchedPath\n                : throw new FileNotFoundException($\"Command '{commandName}' is not found.\");\n\n        /// <summary>\n        /// Gets the full path to a command, similar to the `which` command on Unix.\n        /// </summary>\n        /// <param name=\"commandName\"></param>\n        /// <param name=\"matchedPath\"></param>\n        /// <returns></returns>\n        public static bool TryWhich(string commandName, out string matchedPath)\n            => Internal.Which.TryGetPath(commandName, out matchedPath);\n\n        /// <summary>\n        /// Enumerates paths under the current directory that match the specified glob pattern.\n        /// </summary>\n        /// <remarks>\n        /// A glob pattern accepts <c>*</c> and <c>**</c> (e.g. <c>**/*.cs</c>). If the specify a pattern is started with '!', it will be treated as an excluded pattern.\n        /// </remarks>\n        /// <param name=\"patterns\"></param>\n        /// <returns></returns>\n        public static IEnumerable<string> Glob(params string[] patterns)\n            => Glob(Environment.CurrentDirectory, patterns);\n\n        /// <summary>\n        /// Enumerates paths under the specified directory that match the specified glob pattern.\n        /// </summary>\n        /// <remarks>\n        /// A glob pattern accepts <c>*</c> and <c>**</c> (e.g. <c>**/*.cs</c>). If the specify a pattern is started with '!', it will be treated as an excluded pattern.\n        /// </remarks>\n        /// <param name=\"baseDir\"></param>\n        /// <param name=\"patterns\"></param>\n        /// <returns></returns>\n        public static IEnumerable<string> Glob(string baseDir, string[] patterns)\n        {\n            var matcher = new Matcher();\n\n            foreach (var pattern in patterns)\n            {\n                if (pattern.StartsWith(\"!\"))\n                {\n                    matcher.AddExclude(pattern.Substring(1));\n                }\n                else\n                {\n                    matcher.AddInclude(pattern);\n                }\n            }\n\n            var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDir)));\n            return result.Files\n                .Select(x => Path.GetFullPath(Path.Combine(baseDir, x.Stem))); // NOTE: Microsoft.Extensions.FileSystemGlobbing 5.0.0 does not reflect the root directory in `Path`.\n        }\n\n        /// <summary>\n        /// Displays the message and reads lines entered by the user from the console.\n        /// </summary>\n        /// <param name=\"message\"></param>\n        /// <returns></returns>\n        public static async Task<string?> Prompt(string message)\n        {\n            Console.Write(message);\n            return await Console.In.ReadLineAsync();\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Extensions/ChellExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\nusing Chell.Internal;\n\nnamespace Chell.Extensions\n{\n    public static class ChellExtensions\n    {\n        /// <summary>\n        /// Writes a object details to the output.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"value\"></param>\n        /// <returns></returns>\n        public static T Dump<T>(this T value)\n        {\n            return ObjectDumper.Dump(value);\n        }\n\n        /// <summary>\n        /// Writes a object details to the output.\n        /// </summary>\n        /// <typeparam name=\"T\"></typeparam>\n        /// <param name=\"task\"></param>\n        /// <returns></returns>\n        public static async Task<T> Dump<T>(this Task<T> task)\n        {\n            var result = await task.ConfigureAwait(false);\n            ObjectDumper.Dump(result);\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Extensions/ProcessOutputExtensions.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Text.Json;\n\nnamespace Chell\n{\n    public static class ProcessOutputExtensions\n    {\n        /// <summary>\n        /// Enumerates lines by converting them to objects as JSON.\n        /// </summary>\n        /// <remarks>\n        /// Converts to the anonymous type specified in <see cref=\"shape\"/> argument.\n        /// </remarks>\n        public static IEnumerable<T?> AsJsonLines<T>(this ProcessOutput processOutput, T shape, bool skipEmptyLine = true, JsonSerializerOptions? options = null)\n            => Chell.Extensions.StringExtensions.AsJsonLines<T>(processOutput.AsLines(), skipEmptyLine, options);\n\n        /// <summary>\n        /// Enumerates lines by converting them to objects as JSON.\n        /// </summary>\n        public static IEnumerable<T?> AsJsonLines<T>(this ProcessOutput processOutput, bool skipEmptyLine = true, JsonSerializerOptions? options = null)\n            => Chell.Extensions.StringExtensions.AsJsonLines<T>(processOutput.AsLines(), skipEmptyLine, options);\n\n        /// <summary>\n        /// Converts the output string to an object as JSON.\n        /// </summary>\n        /// <remarks>\n        /// Converts to the anonymous type specified in <see cref=\"shape\"/> argument.\n        /// </remarks>\n        public static T? AsJson<T>(this ProcessOutput processOutput, T shape, JsonSerializerOptions? options = null)\n            => AsJson<T>(processOutput, options);\n\n        /// <summary>\n        /// Converts the output string to an object as JSON.\n        /// </summary>\n        public static T? AsJson<T>(this ProcessOutput processOutput, JsonSerializerOptions? options = null)\n            => JsonSerializer.Deserialize<T>(processOutput.ToString(), options);\n    }\n}"
  },
  {
    "path": "src/Chell/Extensions/ProcessTaskExtensions.Generated.cs",
    "content": "﻿/// <auto-generated />\nusing System.Threading.Tasks;\n\nnamespace Chell\n{\n    public static partial class ProcessTaskExtensions\n    {\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2)> GetAwaiter(this (ProcessTask T1, ProcessTask T2) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2)> WhenAllAsync(ProcessTask t1, ProcessTask t2)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2).ConfigureAwait(false);\n                return (results[0], results[1]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3).ConfigureAwait(false);\n                return (results[0], results[1], results[2]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4, t5).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3], results[4]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4, t5, t6).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3], results[4], results[5]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4, t5, t6, t7).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3], results[4], results[5], results[6]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7, ProcessTask T8) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7, ProcessTask t8)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4, t5, t6, t7, t8).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3], results[4], results[5], results[6], results[7]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7, tasks.T8).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7, ProcessTask T8, ProcessTask T9) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7, ProcessTask t8, ProcessTask t9)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4, t5, t6, t7, t8, t9).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3], results[4], results[5], results[6], results[7], results[8]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7, tasks.T8, tasks.T9).GetAwaiter();\n        }\n        public static System.Runtime.CompilerServices.TaskAwaiter<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9, ProcessOutput Result10)> GetAwaiter(this (ProcessTask T1, ProcessTask T2, ProcessTask T3, ProcessTask T4, ProcessTask T5, ProcessTask T6, ProcessTask T7, ProcessTask T8, ProcessTask T9, ProcessTask T10) tasks)\n        {\n            static async Task<(ProcessOutput Result1, ProcessOutput Result2, ProcessOutput Result3, ProcessOutput Result4, ProcessOutput Result5, ProcessOutput Result6, ProcessOutput Result7, ProcessOutput Result8, ProcessOutput Result9, ProcessOutput Result10)> WhenAllAsync(ProcessTask t1, ProcessTask t2, ProcessTask t3, ProcessTask t4, ProcessTask t5, ProcessTask t6, ProcessTask t7, ProcessTask t8, ProcessTask t9, ProcessTask t10)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10).ConfigureAwait(false);\n                return (results[0], results[1], results[2], results[3], results[4], results[5], results[6], results[7], results[8], results[9]);\n            }\n            return WhenAllAsync(tasks.T1, tasks.T2, tasks.T3, tasks.T4, tasks.T5, tasks.T6, tasks.T7, tasks.T8, tasks.T9, tasks.T10).GetAwaiter();\n        }\n\n    }\n}"
  },
  {
    "path": "src/Chell/Extensions/ProcessTaskExtensions.cs",
    "content": "﻿using System.Threading.Tasks;\n\nnamespace Chell\n{\n    public static partial class ProcessTaskExtensions\n    {\n    }\n}"
  },
  {
    "path": "src/Chell/Extensions/ProcessTaskExtensions.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n<#@ output extension=\".Generated.cs\" #>\n/// <auto-generated />\nusing System.Threading.Tasks;\n\nnamespace Chell\n{\n    public static partial class ProcessTaskExtensions\n    {\n<# for (var i = 2; i <= 10; i++) {\n       var inTuple = string.Join(\", \", Enumerable.Range(1, i).Select(x => $\"ProcessTask T{x}\"));\n       var outTuple = string.Join(\", \", Enumerable.Range(1, i).Select(x => $\"ProcessOutput Result{x}\"));\n#>\n        public static System.Runtime.CompilerServices.TaskAwaiter<(<#= outTuple #>)> GetAwaiter(this (<#= inTuple #>) tasks)\n        {\n            static async Task<(<#= outTuple #>)> WhenAllAsync(<#= string.Join(\", \", Enumerable.Range(1, i).Select(x => $\"ProcessTask t{x}\")) #>)\n            {\n                var results = await Task.WhenAll<ProcessOutput>(<#= string.Join(\", \", Enumerable.Range(1, i).Select(x => $\"t{x}\")) #>).ConfigureAwait(false);\n                return (<#= string.Join(\", \", Enumerable.Range(0, i).Select(x => $\"results[{x}]\")) #>);\n            }\n            return WhenAllAsync(<#= string.Join(\", \", Enumerable.Range(1, i).Select(x => $\"tasks.T{x}\")) #>).GetAwaiter();\n        }\n<# } #>\n\n    }\n}"
  },
  {
    "path": "src/Chell/Extensions/StringExtensions.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.Json;\nusing System.Threading.Tasks;\n\nnamespace Chell.Extensions\n{\n    public static class StringExtensions\n    {\n        /// <summary>\n        /// Enumerates lines by converting them to objects as JSON.\n        /// </summary>\n        /// <remarks>\n        /// Converts to the anonymous type specified in <see cref=\"shape\"/> argument.\n        /// </remarks>\n        public static IEnumerable<T?> AsJsonLines<T>(this IEnumerable<string> lines, T shape, bool skipEmptyLine = true, JsonSerializerOptions? options = null)\n        {\n            return AsJsonLines<T>(lines, skipEmptyLine, options);\n        }\n\n        /// <summary>\n        /// Enumerates lines by converting them to objects as JSON.\n        /// </summary>\n        public static IEnumerable<T?> AsJsonLines<T>(this IEnumerable<string> lines, bool skipEmptyLine = true, JsonSerializerOptions? options = null)\n        {\n            return lines.Where(x => !skipEmptyLine || !string.IsNullOrWhiteSpace(x)).Select(x => JsonSerializer.Deserialize<T>(x, options));\n        }\n\n        /// <summary>\n        /// Converts the output string to an object as JSON.\n        /// </summary>\n        /// <remarks>\n        /// Converts to the anonymous type specified in <see cref=\"shape\"/> argument.\n        /// </remarks>\n        public static T? AsJson<T>(this string json, T shape, JsonSerializerOptions? options = null)\n            => AsJson<T>(json, options);\n\n        /// <summary>\n        /// Converts the output string to an object as JSON.\n        /// </summary>\n        public static T? AsJson<T>(this string json, JsonSerializerOptions? options = null)\n            => JsonSerializer.Deserialize<T>(json.ToString(), options);\n    }\n}\n"
  },
  {
    "path": "src/Chell/IO/ChellWrappedStream.cs",
    "content": "﻿using System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Chell.IO\n{\n    public partial class ChellWritableStream : ChellWrappedStream\n    {\n        private readonly StreamWriter _writer;\n\n        public ChellWritableStream(Stream baseStream, Encoding encoding)\n            : base(baseStream)\n        {\n            _writer = new StreamWriter(baseStream, encoding);\n            _writer.AutoFlush = true;\n        }\n\n        public void Write(byte[] value) => BaseStream.Write(value);\n        public new void Write(ReadOnlySpan<byte> value) => BaseStream.Write(value);\n        public ValueTask WriteAsync(byte[] value, CancellationToken cancellationToken = default) => BaseStream.WriteAsync(value, cancellationToken);\n        public new ValueTask WriteAsync(ReadOnlyMemory<byte> value, CancellationToken cancellationToken = default) => BaseStream.WriteAsync(value, cancellationToken);\n\n        protected override void Dispose(bool disposing)\n        {\n            _writer.Dispose();\n            base.Dispose(disposing);\n        }\n\n        public override async ValueTask DisposeAsync()\n        {\n            await _writer.DisposeAsync();\n            await base.DisposeAsync();\n        }\n    }\n\n    public partial class ChellReadableStream : ChellWrappedStream\n    {\n        private readonly StreamReader _reader;\n\n        public ChellReadableStream(Stream baseStream, Encoding encoding)\n            : base(baseStream)\n        {\n            _reader = new StreamReader(baseStream, encoding);\n        }\n\n        public async Task<byte[]> ReadAllBytesAsync(CancellationToken cancellationToken = default)\n        {\n            var bufferWriter = new ArrayBufferWriter<byte>();\n            while (true)\n            {\n                cancellationToken.ThrowIfCancellationRequested();\n                var readLen = await BaseStream.ReadAsync(bufferWriter.GetMemory(1024 * 32), cancellationToken);\n                if (readLen == 0)\n                {\n                    return bufferWriter.WrittenMemory.ToArray();\n                }\n                bufferWriter.Advance(readLen);\n            }\n        }\n\n        public byte[] ReadAllBytes()\n        {\n            var bufferWriter = new ArrayBufferWriter<byte>();\n            while (true)\n            {\n                var readLen = BaseStream.Read(bufferWriter.GetSpan(1024 * 32));\n                if (readLen == 0)\n                {\n                    return bufferWriter.WrittenMemory.ToArray();\n                }\n                bufferWriter.Advance(readLen);\n            }\n        }\n\n        public async Task<string> ReadToEndAsync()\n        {\n            return await _reader.ReadToEndAsync();\n        }\n\n        public string ReadToEnd()\n        {\n            return _reader.ReadToEnd();\n        }\n\n        public IEnumerable<string> ReadAllLines()\n        {\n            while (!_reader.EndOfStream)\n            {\n                var line = _reader.ReadLine();\n                if (line is null) yield break;\n\n                yield return line;\n            }\n        }\n\n        public async IAsyncEnumerable<string> ReadAllLinesAsync()\n        {\n            while (!_reader.EndOfStream)\n            {\n                var line = await _reader.ReadLineAsync();\n                if (line is null) yield break;\n\n                yield return line;\n            }\n        }\n\n        protected override void Dispose(bool disposing)\n        {\n            _reader.Dispose();\n            base.Dispose(disposing);\n        }\n    }\n\n    public abstract class ChellWrappedStream : Stream\n    {\n        private readonly Stream _baseStream;\n\n        protected Stream BaseStream => _baseStream;\n\n        protected ChellWrappedStream(Stream baseStream)\n        {\n            _baseStream = baseStream;\n        }\n\n        #region Stream Implementation\n        public override void Flush()\n        {\n            _baseStream.Flush();\n        }\n\n        public override int Read(byte[] buffer, int offset, int count)\n        {\n            return _baseStream.Read(buffer, offset, count);\n        }\n\n        public override int Read(Span<byte> buffer)\n        {\n            return _baseStream.Read(buffer);\n        }\n\n        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)\n        {\n            return _baseStream.ReadAsync(buffer, offset, count, cancellationToken);\n        }\n\n        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())\n        {\n            return _baseStream.ReadAsync(buffer, cancellationToken);\n        }\n\n        public override long Seek(long offset, SeekOrigin origin)\n        {\n            return _baseStream.Seek(offset, origin);\n        }\n\n        public override void SetLength(long value)\n        {\n            _baseStream.SetLength(value);\n        }\n\n        public override void Write(byte[] buffer, int offset, int count)\n        {\n            _baseStream.Write(buffer, offset, count);\n        }\n\n        public override void Write(ReadOnlySpan<byte> buffer)\n        {\n            _baseStream.Write(buffer);\n        }\n\n        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)\n        {\n            return _baseStream.WriteAsync(buffer, offset, count, cancellationToken);\n        }\n\n        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = new CancellationToken())\n        {\n            return _baseStream.WriteAsync(buffer, cancellationToken);\n        }\n\n        public override bool CanRead => _baseStream.CanRead;\n\n        public override bool CanSeek => _baseStream.CanSeek;\n\n        public override bool CanWrite => _baseStream.CanWrite;\n\n        public override long Length => _baseStream.Length;\n\n        public override long Position\n        {\n            get => _baseStream.Position;\n            set => _baseStream.Position = value;\n        }\n        #endregion\n    }\n}\n"
  },
  {
    "path": "src/Chell/IO/ChellWritableStream.Generated.cs",
    "content": "﻿#nullable enable\n/// <auto-generated />\nusing System;\nusing System.IO;\nusing System.Threading.Tasks;\n\nnamespace Chell.IO\n{\n    public partial class ChellWritableStream\n    {\n        public void Write(string? value) => _writer.Write(value);\n        public void WriteLine(string? value) => _writer.WriteLine(value);\n        public Task WriteAsync(string? value) => _writer.WriteAsync(value);\n        public Task WriteLineAsync(string? value) => _writer.WriteLineAsync(value);\n        public void Write(char value) => _writer.Write(value);\n        public void WriteLine(char value) => _writer.WriteLine(value);\n        public Task WriteAsync(char value) => _writer.WriteAsync(value);\n        public Task WriteLineAsync(char value) => _writer.WriteLineAsync(value);\n        public void Write(char[]? value) => _writer.Write(value);\n        public void WriteLine(char[]? value) => _writer.WriteLine(value);\n        public Task WriteAsync(char[]? value) => _writer.WriteAsync(value);\n        public Task WriteLineAsync(char[]? value) => _writer.WriteLineAsync(value);\n        public void Write(object? value) => _writer.Write(value);\n        public void WriteLine(object? value) => _writer.WriteLine(value);\n        public void Write(double value) => _writer.Write(value);\n        public void WriteLine(double value) => _writer.WriteLine(value);\n        public void Write(float value) => _writer.Write(value);\n        public void WriteLine(float value) => _writer.WriteLine(value);\n        public void Write(long value) => _writer.Write(value);\n        public void WriteLine(long value) => _writer.WriteLine(value);\n        public void Write(int value) => _writer.Write(value);\n        public void WriteLine(int value) => _writer.WriteLine(value);\n        public void Write(ReadOnlySpan<char> value) => _writer.Write(value);\n        public void WriteLine(ReadOnlySpan<char> value) => _writer.WriteLine(value);\n        public Task WriteAsync(ReadOnlyMemory<char> value) => _writer.WriteAsync(value);\n        public Task WriteLineAsync(ReadOnlyMemory<char> value) => _writer.WriteLineAsync(value);\n    }\n}"
  },
  {
    "path": "src/Chell/IO/ChellWritableStream.tt",
    "content": "﻿<#@ template debug=\"false\" hostspecific=\"false\" language=\"C#\" #>\n<#@ assembly name=\"System.Core\" #>\n<#@ import namespace=\"System.Linq\" #>\n<#@ import namespace=\"System.Text\" #>\n<#@ import namespace=\"System.Collections.Generic\" #>\n<#@ output extension=\".Generated.cs\" #>\n<#\nvar types = new []\n{\n    (0, \"string?\"),\n    (0, \"char\"),\n    (0, \"char[]?\"),\n    (1, \"object?\"),\n    (1, \"double\"),\n    (1, \"float\"),\n    (1, \"long\"),\n    (1, \"int\"),\n    (1, \"ReadOnlySpan<char>\"),\n    (2, \"ReadOnlyMemory<char>\"),\n};\n#>\n#nullable enable\n/// <auto-generated />\nusing System;\nusing System.IO;\nusing System.Threading.Tasks;\n\nnamespace Chell.IO\n{\n    public partial class ChellWritableStream\n    {\n<# foreach (var (target, type) in types) { #>\n<#   if (target == 0 || target == 1) { #>\n        public void Write(<#= type #> value) => _writer.Write(value);\n        public void WriteLine(<#= type #> value) => _writer.WriteLine(value);\n<#   } #>\n<#   if (target == 0 || target == 2) { #>\n        public Task WriteAsync(<#= type #> value) => _writer.WriteAsync(value);\n        public Task WriteLineAsync(<#= type #> value) => _writer.WriteLineAsync(value);\n<#   } #>\n<# } #>\n    }\n}"
  },
  {
    "path": "src/Chell/IO/IConsoleProvider.cs",
    "content": "using System.Collections.Generic;\nusing System.IO;\nusing System.Text;\n\nnamespace Chell.IO\n{\n    public interface IConsoleProvider\n    {\n        Stream OpenStandardInput();\n        Stream OpenStandardOutput();\n        Stream OpenStandardError();\n        Encoding InputEncoding { get; }\n        Encoding OutputEncoding { get; }\n        bool IsInputRedirected { get; }\n        bool IsOutputRedirected { get; }\n        bool IsErrorRedirected { get; }\n\n        TextWriter Out { get; }\n        TextWriter Error { get; }\n    }\n}\n"
  },
  {
    "path": "src/Chell/IO/LINQPadConsoleProvider.cs",
    "content": "using System;\nusing System.Buffers;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Chell.IO\n{\n    public class LINQPadConsoleProvider : IConsoleProvider\n    {\n        private readonly Pipe _pipe;\n\n        public Encoding InputEncoding => Console.InputEncoding;\n        public Encoding OutputEncoding => Console.OutputEncoding;\n        public bool IsInputRedirected => Console.IsInputRedirected;\n        public bool IsOutputRedirected => Console.IsOutputRedirected;\n        public bool IsErrorRedirected => Console.IsErrorRedirected;\n\n        public TextWriter Out { get; }\n        public TextWriter Error { get; }\n\n        public LINQPadConsoleProvider()\n        {\n            _pipe = new Pipe();\n            Out = new PipeTextWriter(_pipe.Writer, OutputEncoding);\n            Error = new PipeTextWriter(_pipe.Writer, OutputEncoding);\n\n            var reader = new StreamReader(_pipe.Reader.AsStream());\n            _ = Task.Run(async () =>\n            {\n                Memory<char> buffer = new char[1024];\n                while (true)\n                {\n                    var read = await reader.ReadAsync(buffer, default);\n                    if (read != 0)\n                    {\n                        Console.Out.Write(buffer.Span.Slice(0, read));\n                    }\n                }\n            });\n        }\n\n        public Stream OpenStandardInput()\n            => Console.OpenStandardInput();\n\n        public Stream OpenStandardOutput()\n            => _pipe.Writer.AsStream(leaveOpen: true);\n\n        public Stream OpenStandardError()\n            => _pipe.Writer.AsStream(leaveOpen: true);\n\n        private class PipeTextWriter : TextWriter\n        {\n            private readonly PipeWriter _writer;\n            public override Encoding Encoding { get; }\n\n            public PipeTextWriter(PipeWriter writer, Encoding encoding)\n            {\n                _writer = writer;\n                Encoding = encoding;\n            }\n\n            public override void Write(char value)\n            {\n                Span<byte> buffer = stackalloc byte[4];\n                Span<char> c = stackalloc char[1];\n                c[0] = value;\n\n                var written = Encoding.GetBytes(c, buffer);\n                _writer.Write(buffer.Slice(0, written));\n                _ = _writer.FlushAsync();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/IO/SystemConsoleProvider.cs",
    "content": "using System;\nusing System.IO;\nusing System.Text;\n\nnamespace Chell.IO\n{\n    /// <summary>\n    /// Encapsulates console intrinsic members as object.\n    /// </summary>\n    public sealed class SystemConsoleProvider : IConsoleProvider\n    {\n        public static IConsoleProvider Instance { get; } = new SystemConsoleProvider();\n\n        private SystemConsoleProvider()\n        {}\n\n        public Stream OpenStandardInput()\n            => Console.OpenStandardInput();\n\n        public Stream OpenStandardOutput()\n            => Console.OpenStandardOutput();\n\n        public Stream OpenStandardError()\n            => Console.OpenStandardError();\n\n        public Encoding InputEncoding => Console.InputEncoding;\n        public Encoding OutputEncoding => Console.OutputEncoding;\n\n        public bool IsInputRedirected => Console.IsInputRedirected;\n        public bool IsOutputRedirected => Console.IsOutputRedirected;\n        public bool IsErrorRedirected => Console.IsErrorRedirected;\n\n        public TextWriter Out => Console.Out;\n        public TextWriter Error => Console.Error;\n    }\n}\n"
  },
  {
    "path": "src/Chell/Internal/CommandLineHelper.cs",
    "content": "using System;\nusing System.Collections;\nusing System.Linq;\nusing Chell.IO;\nusing Chell.Shell;\nusing Kokuban;\n\n// ReSharper disable CoVariantArrayConversion\n\nnamespace Chell.Internal\n{\n    internal class CommandLineHelper\n    {\n        public static void WriteCommandLineToConsole(IConsoleProvider console, string commandLine, ChellVerbosity? verbosity = default)\n        {\n            verbosity ??= ChellEnvironment.Current.Verbosity;\n            if (verbosity.Value.HasFlag(ChellVerbosity.CommandLine))\n            {\n                var parts = commandLine.Split(' ', 2);\n                if (LINQPadHelper.RunningOnLINQPad)\n                {\n                    LINQPadHelper.WriteRawHtml(\n                        $\"<span style=\\\"font-weight:bold;\\\">$ <span style=\\\"color:green\\\">{EscapeHtml(parts[0])}</span>{EscapeHtml((parts.Length > 1 ? \" \" + parts[1] : string.Empty))}</span>\");\n                }\n                else\n                {\n                    console.Out.WriteLine(\"$ \" + (Chalk.BrightGreen + parts[0]) + (parts.Length > 1 ? \" \" + parts[1] : string.Empty));\n                }\n            }\n\n            static string EscapeHtml(string s)\n                => s.Replace(\"&\", \"&amp;\").Replace(\"\\\"\", \"&quot;\").Replace(\"<\", \"&gt;\");\n        }\n\n        public static string Expand(FormattableString commandLine, IShellExecutor shellExecutor)\n        {\n            return string.Format(commandLine.Format.Trim(), commandLine.GetArguments().Select(x =>\n            {\n                return x switch\n                {\n                    ProcessOutput procOutput => shellExecutor.Escape(procOutput.Output.TrimEnd('\\n')),\n                    string s => shellExecutor.Escape(s),\n                    IEnumerable enumerable => string.Join(\" \", enumerable.OfType<object>().Select(y => shellExecutor.Escape(y.ToString() ?? string.Empty))),\n                    null => string.Empty,\n                    _ => shellExecutor.Escape(x.ToString() ?? string.Empty),\n                };\n            }).ToArray());\n        }\n\n        public static (string Command, string Arguments) Parse(FormattableString commandLine)\n        {\n            var (command, argumentsFormat) = Parse(commandLine.Format.Trim());\n            return (command, string.Format(argumentsFormat, commandLine.GetArguments().Select(x =>\n            {\n                if (x is IEnumerable enumerable)\n                {\n                    return string.Join(\" \", enumerable.OfType<object>().Select(y => Escape(y.ToString() ?? string.Empty)));\n                }\n                return Escape(x?.ToString() ?? string.Empty);\n            }).ToArray()));\n        }\n\n        public static (string Command, string Arguments) Parse(string commandLine)\n        {\n            if (commandLine.StartsWith(\"\\\"\"))\n            {\n                var pos = commandLine.IndexOf('\"', 1);\n                if (pos == -1)\n                {\n                    throw new InvalidOperationException(\"Invalid Command\");\n                }\n                return (Command: commandLine.Substring(1, pos), Arguments: commandLine.Substring(pos));\n            }\n            else\n            {\n                var parts = commandLine.Split(' ', 2);\n                return (Command: parts[0], Arguments: parts.Length == 1 ? string.Empty : parts[1]);\n            }\n        }\n\n        public static string Escape(string v)\n            => $\"\\\"{v.Replace(\"`\", \"``\").Replace(\"\\\"\", \"`\\\"\")}\\\"\";\n    }\n}\n"
  },
  {
    "path": "src/Chell/Internal/EnvironmentVariables.cs",
    "content": "﻿using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\n\nnamespace Chell.Internal\n{\n    internal class EnvironmentVariables : IDictionary<string, string>\n    {\n        public IEnumerator<KeyValuePair<string, string>> GetEnumerator()\n        {\n            return Environment.GetEnvironmentVariables()\n                .OfType<DictionaryEntry>()\n                .Select(x => KeyValuePair.Create((string)x.Key, (string)(x.Value ?? string.Empty)))\n                .GetEnumerator();\n        }\n\n        IEnumerator IEnumerable.GetEnumerator()\n        {\n            return GetEnumerator();\n        }\n\n        public void Add(KeyValuePair<string, string> item)\n        {\n            Environment.SetEnvironmentVariable(item.Key, item.Value);\n        }\n\n        public void Clear()\n        {\n            throw new NotSupportedException();\n        }\n\n        public bool Contains(KeyValuePair<string, string> item)\n        {\n            return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(item.Key));\n        }\n\n        public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)\n        {\n            throw new NotSupportedException();\n        }\n\n        public bool Remove(KeyValuePair<string, string> item)\n        {\n            if (Contains(item))\n            {\n                Environment.SetEnvironmentVariable(item.Key, null);\n                return true;\n            }\n\n            return false;\n        }\n\n        public int Count => Environment.GetEnvironmentVariables().Count;\n        public bool IsReadOnly => false;\n\n        public void Add(string key, string value)\n        {\n            Environment.SetEnvironmentVariable(key, value);\n        }\n\n        public bool ContainsKey(string key)\n        {\n            return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(key));\n        }\n\n        public bool Remove(string key)\n        {\n            if (ContainsKey(key))\n            {\n                Environment.SetEnvironmentVariable(key, null);\n                return true;\n            }\n\n            return false;\n        }\n\n        public bool TryGetValue(string key, out string value)\n        {\n            var tmpValue = Environment.GetEnvironmentVariable(key);\n            if (string.IsNullOrWhiteSpace(tmpValue))\n            {\n                value = string.Empty;\n                return false;\n            }\n\n            value = tmpValue;\n            return true;\n        }\n\n        public string this[string key]\n        {\n            get => TryGetValue(key, out var value) ? value : string.Empty;\n            set => Add(key, value);\n        }\n\n        public ICollection<string> Keys\n            => Environment.GetEnvironmentVariables().OfType<DictionaryEntry>().Select(x => (string)x.Key).ToArray();\n        public ICollection<string> Values\n            => Environment.GetEnvironmentVariables().OfType<DictionaryEntry>().Select(x => (string)(x.Value ?? string.Empty)).ToArray();\n    }\n}"
  },
  {
    "path": "src/Chell/Internal/LINQPadHelper.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Chell.Internal\n{\n    internal class LINQPadHelper\n    {\n        private static bool? _runningOnLINQPad;\n        internal static bool RunningOnLINQPad => _runningOnLINQPad ??= Type.GetType(\"LINQPad.Util, LINQPad.Runtime\") != null;\n\n        public static void WriteRawHtml(string html)\n        {\n            Debug.Assert(RunningOnLINQPad);\n\n            var t = Type.GetType(\"LINQPad.Util, LINQPad.Runtime\");\n            if (t != null)\n            {\n                var obj = t.InvokeMember(\"RawHtml\", BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, new[] {html});\n                ObjectDumper.Dump(obj);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Internal/ObjectDumper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Text.Encodings.Web;\nusing System.Text.Json;\nusing System.Text.Unicode;\nusing System.Threading.Tasks;\n\nnamespace Chell.Internal\n{\n    internal static class ObjectDumper\n    {\n        public static T Dump<T>(T obj)\n            => DumpMethodCache<T>.Method(obj);\n\n        private static class DumpMethodCache\n        {\n            public static MethodInfo? LINQPadDumpMethod { get; }\n\n            static DumpMethodCache()\n            {\n                var linqPadExtensionsType = Type.GetType(\"LINQPad.Extensions, LINQPad.Runtime\");\n                if (linqPadExtensionsType != null)\n                {\n                    LINQPadDumpMethod = linqPadExtensionsType.GetMethods()\n                        .FirstOrDefault(x => x.Name == \"Dump\" && x.GetParameters().Length == 1);\n                }\n            }\n        }\n\n        private static class DumpMethodCache<T>\n        {\n            public static Func<T, T> Method { get; }\n\n            static DumpMethodCache()\n            {\n                if (DumpMethodCache.LINQPadDumpMethod != null)\n                {\n                    var closedDumpMethod = DumpMethodCache.LINQPadDumpMethod.MakeGenericMethod(typeof(T));\n                    Method = (Func<T, T>)closedDumpMethod.CreateDelegate(typeof(Func<T, T>));\n                }\n                else\n                {\n                    Method = x =>\n                    {\n                        Console.WriteLine(JsonSerializer.Serialize(x, new JsonSerializerOptions() { Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), WriteIndented = true }));\n                        return x;\n                    };\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Internal/OutputSink.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Chell.Internal\n{\n    internal class OutputSink : IDisposable, IAsyncDisposable\n    {\n        private readonly Encoding _encoding;\n        private readonly Pipe _outputPipe;\n        private readonly Pipe _errorPipe;\n        private readonly MemoryStream _outputBuffer;\n        private readonly MemoryStream _errorBuffer;\n        private readonly MemoryStream _combinedBuffer;\n        private readonly CancellationTokenSource _cancellationTokenSource;\n        private readonly Task _readWriteTaskOutput;\n        private readonly Task _readWriteTaskError;\n\n        internal PipeWriter OutputWriter => _outputPipe.Writer;\n        internal PipeWriter ErrorWriter => _errorPipe.Writer;\n\n        public ReadOnlyMemory<byte> OutputBinary => new ReadOnlyMemory<byte>(_outputBuffer.GetBuffer(), 0, (int)_outputBuffer.Length);\n        public ReadOnlyMemory<byte> ErrorBinary => new ReadOnlyMemory<byte>(_errorBuffer.GetBuffer(), 0, (int)_errorBuffer.Length);\n        public ReadOnlyMemory<byte> CombinedBinary => new ReadOnlyMemory<byte>(_combinedBuffer.GetBuffer(), 0, (int)_combinedBuffer.Length);\n        public string Output => (_outputBuffer is { Length: > 0} s) ? _encoding.GetString(OutputBinary.Span) : string.Empty;\n        public string Error => (_errorBuffer is { Length: > 0 } s) ? _encoding.GetString(ErrorBinary.Span) : string.Empty;\n        public string Combined => (_combinedBuffer is { Length: > 0 } s) ? _encoding.GetString(CombinedBinary.Span) : string.Empty;\n\n        public OutputSink(Encoding encoding)\n        {\n            _encoding = encoding;\n            _outputPipe = new Pipe();\n            _errorPipe = new Pipe();\n            _cancellationTokenSource = new CancellationTokenSource();\n\n            _outputBuffer = new MemoryStream();\n            _errorBuffer = new MemoryStream();\n            _combinedBuffer = new MemoryStream();\n\n            _readWriteTaskOutput = RunReadWriteLoopAsync(_outputPipe.Reader, _outputBuffer, _cancellationTokenSource.Token);\n            _readWriteTaskError = RunReadWriteLoopAsync(_errorPipe.Reader, _errorBuffer, _cancellationTokenSource.Token);\n        }\n\n        public async Task CompleteAsync()\n        {\n            await _outputPipe.Writer.CompleteAsync().ConfigureAwait(false);\n            await _errorPipe.Writer.CompleteAsync().ConfigureAwait(false);\n\n            try\n            {\n                _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5));\n                await _readWriteTaskOutput.ConfigureAwait(false);\n                await _readWriteTaskError.ConfigureAwait(false);\n            }\n            catch (OperationCanceledException)\n            {\n            }\n        }\n\n        private async Task RunReadWriteLoopAsync(PipeReader reader, Stream dest, CancellationToken cancellationToken)\n        {\n            while (true)\n            {\n                cancellationToken.ThrowIfCancellationRequested();\n\n                var result = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);\n\n                if (result.Buffer.IsSingleSegment)\n                {\n                    dest.Write(result.Buffer.FirstSpan);\n                    _combinedBuffer.Write(result.Buffer.FirstSpan);\n                }\n                else\n                {\n                    foreach (var segment in result.Buffer)\n                    {\n                        dest.Write(segment.Span);\n                        _combinedBuffer.Write(segment.Span);\n                    }\n                }\n\n                reader.AdvanceTo(result.Buffer.End);\n\n                if (result.IsCanceled || result.IsCompleted)\n                {\n                    return;\n                }\n            }\n        }\n\n        public void Dispose()\n        {\n            try\n            {\n                CompleteAsync().Wait();\n            }\n            catch { }\n        }\n\n        public async ValueTask DisposeAsync()\n        {\n            try\n            {\n                await CompleteAsync().ConfigureAwait(false);\n            }\n            catch { }\n        }\n    }\n}"
  },
  {
    "path": "src/Chell/Internal/StandardInput.cs",
    "content": "﻿using System;\nusing System.Threading;\n\nnamespace Chell.Internal\n{\n    internal class StandardInput\n    {\n        private static readonly Lazy<StreamPipe> _pipe;\n        internal static StreamPipe Pipe => _pipe.Value;\n\n        static StandardInput()\n        {\n            _pipe = new Lazy<StreamPipe>(() => new StreamPipe(Console.OpenStandardInput()), LazyThreadSafetyMode.ExecutionAndPublication);\n        }\n    }\n}"
  },
  {
    "path": "src/Chell/Internal/StreamPipe.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Chell.Internal\n{\n    internal class StreamPipe\n    {\n        private readonly Pipe _pipe;\n        private readonly Stream _baseStream;\n        private readonly Task _copyTask;\n        private readonly Task _readerTask;\n        private readonly CancellationTokenSource _cancellationTokenSourceCopyStreamToPipe;\n        private readonly CancellationTokenSource _cancellationTokenSource;\n        private readonly object _syncLock = new object();\n        private readonly List<object> _destinations = new List<object>();\n        private readonly TaskCompletionSource<bool> _destinationsReady = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);\n        private bool _shutdown;\n\n        public StreamPipe(Stream baseStream)\n        {\n            _pipe = new Pipe(new PipeOptions());\n            _baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));\n            _cancellationTokenSource = new CancellationTokenSource();\n            _cancellationTokenSourceCopyStreamToPipe = new CancellationTokenSource();\n\n            _copyTask = CopyStreamToPipeAsync(_cancellationTokenSourceCopyStreamToPipe.Token);\n            _readerTask = RunReadLoopAsync(_cancellationTokenSource.Token);\n        }\n\n        private async Task CopyStreamToPipeAsync(CancellationToken cancellationToken)\n        {\n            try\n            {\n                await _baseStream.CopyToAsync(_pipe.Writer, cancellationToken).ConfigureAwait(false);\n                await _pipe.Writer.CompleteAsync().ConfigureAwait(false);\n            }\n            catch (Exception ex)\n            {\n                await _pipe.Writer.CompleteAsync(ex).ConfigureAwait(false);\n            }\n        }\n\n        public async Task CompleteAsync()\n        {\n            _shutdown = true;\n            _cancellationTokenSource.CancelAfter(1000);\n            _cancellationTokenSourceCopyStreamToPipe.CancelAfter(1000);\n\n            try\n            {\n                await _copyTask.ConfigureAwait(false);\n                await _readerTask.ConfigureAwait(false);\n            }\n            catch (OperationCanceledException)\n            {\n            }\n        }\n\n        public void Ready()\n        {\n            _destinationsReady.TrySetResult(true);\n        }\n\n        public StreamPipe Connect(Stream stream)\n        {\n            lock (_syncLock)\n            {\n                _destinations.Add(stream ?? throw new ArgumentNullException(nameof(stream)));\n            }\n            return this;\n        }\n        public StreamPipe Connect(PipeWriter writer)\n        {\n            lock (_syncLock)\n            {\n                _destinations.Add(writer ?? throw new ArgumentNullException(nameof(writer)));\n            }\n            return this;\n        }\n\n        public StreamPipe Disconnect(Stream stream)\n        {\n            lock (_syncLock)\n            {\n                _destinations.Remove(stream ?? throw new ArgumentNullException(nameof(stream)));\n            }\n            return this;\n        }\n\n        public StreamPipe Disconnect(PipeWriter writer)\n        {\n            lock (_syncLock)\n            {\n                _destinations.Remove(writer ?? throw new ArgumentNullException(nameof(writer)));\n            }\n            return this;\n        }\n\n        private async Task RunReadLoopAsync(CancellationToken cancellationToken)\n        {\n            var cancellationTokenTask = new TaskCompletionSource<bool>();\n            await using var cancellationTokenRegistration = cancellationToken.Register(() => cancellationTokenTask.TrySetCanceled(cancellationToken));\n\n            while (true)\n            {\n                cancellationToken.ThrowIfCancellationRequested();\n\n                // Wait for the destination to be available.\n                object[] dests;\n                lock (_syncLock)\n                {\n                    dests = _destinations.ToArray();\n                }\n                while (dests.Length == 0)\n                {\n                    if (_shutdown) return;\n                    cancellationToken.ThrowIfCancellationRequested();\n\n                    // Wait for signal to start sending to the destinations.\n                    // This will wait only once after the process starts.\n                    await Task.WhenAny(cancellationTokenTask.Task, _destinationsReady.Task).ConfigureAwait(false);\n\n                    cancellationToken.ThrowIfCancellationRequested();\n\n                    lock (_syncLock)\n                    {\n                        dests = _destinations.ToArray();\n                    }\n\n                    if (dests.Length == 0)\n                    {\n                        await Task.Yield();\n                    }\n                }\n\n                // Reads from the pipe reader.\n                var result = await _pipe.Reader.ReadAsync(cancellationToken).ConfigureAwait(false);\n\n                // Get the current destination again.\n                // If there is no destination after reading, do not advance the pipe, and go to the top of the loop.\n                lock (_syncLock)\n                {\n                    var destsCurrent = _destinations.ToArray();\n                    if (!dests.SequenceEqual(destsCurrent))\n                    {\n                        dests = destsCurrent;\n                        if (dests.Length == 0)\n                        {\n                            _pipe.Reader.AdvanceTo(result.Buffer.Start);\n                            continue;\n                        }\n                    }\n                }\n\n                // Writes to the destinations.\n                await Task.WhenAll(dests.Select(async x =>\n                {\n                    var writeTask = x switch\n                    {\n                        Stream stream => WriteAsync(stream, result, cancellationToken),\n                        PipeWriter writer => WriteAsync(writer, result, cancellationToken),\n                        _ => throw new NotSupportedException()\n                    };\n\n                    // NOTE: The destination may be closed first.\n                    //       When the destination is closed, the task throws an IOException (Broken pipe).\n                    try\n                    {\n                        await writeTask.ConfigureAwait(false);\n                    }\n                    catch (IOException)\n                    {\n                    }\n                })).ConfigureAwait(false);\n\n                _pipe.Reader.AdvanceTo(result.Buffer.End);\n\n                if (result.IsCanceled || result.IsCompleted)\n                {\n                    return;\n                }\n            }\n        }\n\n        private static async ValueTask WriteAsync(Stream stream, ReadResult result, CancellationToken cancellationToken)\n        {\n            if (!result.Buffer.IsEmpty)\n            {\n                if (result.Buffer.IsSingleSegment)\n                {\n                    await stream.WriteAsync(result.Buffer.First, cancellationToken).ConfigureAwait(false);\n                }\n                else\n                {\n                    foreach (var segment in result.Buffer)\n                    {\n                        await stream.WriteAsync(segment, cancellationToken).ConfigureAwait(false);\n                    }\n                }\n                await stream.FlushAsync(cancellationToken).ConfigureAwait(false);\n\n            }\n\n            if (result.IsCompleted || result.IsCanceled)\n            {\n                stream.Close();\n            }\n        }\n\n        private static async ValueTask WriteAsync(PipeWriter writer, ReadResult result, CancellationToken cancellationToken)\n        {\n            if (!result.Buffer.IsEmpty)\n            {\n                if (result.Buffer.IsSingleSegment)\n                {\n                    await writer.WriteAsync(result.Buffer.First, cancellationToken).ConfigureAwait(false);\n                }\n                else\n                {\n                    foreach (var segment in result.Buffer)\n                    {\n                        await writer.WriteAsync(segment, cancellationToken).ConfigureAwait(false);\n                    }\n                }\n                await writer.FlushAsync(cancellationToken).ConfigureAwait(false);\n\n            }\n\n            if (result.IsCompleted || result.IsCanceled)\n            {\n                await writer.CompleteAsync().ConfigureAwait(false);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Internal/Which.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Chell.Internal\n{\n    public static class Which\n    {\n        public static bool TryGetPath(string commandName, out string matchedPath)\n        {\n            var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n            var paths = (Environment.GetEnvironmentVariable(\"PATH\") ?? string.Empty).Split(isWindows ? ';' : ':');\n            var pathExts = Array.Empty<string>();\n\n            if (isWindows)\n            {\n                paths = paths.Prepend(Environment.CurrentDirectory).ToArray();\n                pathExts = (Environment.GetEnvironmentVariable(\"PATHEXT\") ?? \".COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC\").Split(';');\n            }\n\n            foreach (var path in paths)\n            {\n                // /path/to/foo.ext\n                foreach (var ext in pathExts)\n                {\n                    var fullPath = Path.Combine(path, $\"{commandName}{ext}\");\n                    if (File.Exists(fullPath))\n                    {\n                        matchedPath = fullPath;\n                        return true;\n                    }\n                }\n\n                // /path/to/foo\n                {\n                    var fullPath = Path.Combine(path, commandName);\n                    if (File.Exists(fullPath))\n                    {\n                        matchedPath = fullPath;\n                        return true;\n                    }\n                }\n            }\n\n            matchedPath = string.Empty;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/ProcessOutput.cs",
    "content": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Text;\nusing Chell.Internal;\n\nnamespace Chell\n{\n    /// <summary>\n    /// Provides the outputs and results from the process.\n    /// </summary>\n    /// <remarks>\n    /// If the output is redirected or piped, it will not be captured.\n    /// </remarks>\n    public class ProcessOutput : IEnumerable<string>\n    {\n        /// <summary>\n        /// Gets the standard outputs as <see cref=\"String\"/>.\n        /// </summary>\n        public string Output => Sink.Output;\n\n        /// <summary>\n        /// Gets the standard errors as <see cref=\"String\"/>.\n        /// </summary>\n        public string Error => Sink.Error;\n\n        /// <summary>\n        /// Gets the standard outputs and standard errors as <see cref=\"String\"/>.\n        /// </summary>\n        public string Combined => Sink.Combined;\n\n        /// <summary>\n        /// Gets the standard outputs as <see cref=\"byte\"/> sequence.\n        /// </summary>\n        public ReadOnlyMemory<byte> OutputBinary => Sink.OutputBinary;\n\n        /// <summary>\n        /// Gets the standard errors as <see cref=\"byte\"/> sequence.\n        /// </summary>\n        public ReadOnlyMemory<byte> ErrorBinary => Sink.ErrorBinary;\n\n        /// <summary>\n        /// Gets the standard outputs and standard errors as <see cref=\"byte\"/> sequence.\n        /// </summary>\n        public ReadOnlyMemory<byte> CombinedBinary => Sink.CombinedBinary;\n\n        /// <summary>\n        /// Get the exit code when the process terminated.\n        /// </summary>\n        public int ExitCode { get; internal set; }\n\n        internal OutputSink Sink { get; }\n\n        public ProcessOutput(Encoding encoding)\n        {\n            Sink = new OutputSink(encoding);\n        }\n\n        public static implicit operator string(ProcessOutput processOutput)\n            => processOutput.ToString();\n\n        /// <summary>\n        /// Gets the standard outputs and standard errors as <see cref=\"String\"/>.\n        /// </summary>\n        public override string ToString() => Combined;\n\n        public IEnumerator<string> GetEnumerator()\n            => AsLines().GetEnumerator();\n\n        IEnumerator IEnumerable.GetEnumerator()\n            => GetEnumerator();\n\n        /// <summary>\n        /// Gets the standard outputs and standard errors as lines.\n        /// </summary>\n        public IEnumerable<string> AsLines(bool trimEnd = false)\n            => (trimEnd ? Combined.TrimEnd('\\n', '\\r') : Combined).Split(new[] { \"\\r\\n\", \"\\n\" }, StringSplitOptions.None);\n    }\n}\n"
  },
  {
    "path": "src/Chell/ProcessTask.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.ExceptionServices;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Chell.Internal;\n\nnamespace Chell\n{\n    /// <summary>\n    /// Represents the execution task of a process.\n    /// </summary>\n    public class ProcessTask \n    {\n        private static readonly TimeSpan ProcessStartScheduledDelay = TimeSpan.FromMilliseconds(250);\n        private static int _idSequence = 0;\n\n        private readonly int _id;\n        private readonly DateTimeOffset _startedAt;\n        private readonly Lazy<Task<ProcessOutput>> _taskLazy;\n        private readonly ProcessOutput _output;\n        private readonly ProcessTaskOptions _options;\n        private readonly CancellationTokenSource _processCancellation;\n        private readonly CancellationTokenRegistration _processCancellationRegistration;\n\n        private readonly object _syncLock = new object();\n        private string? _processName;\n        private Process? _process;\n        private ExceptionDispatchInfo? _processException;\n\n        private Stream? _stdInStream;\n        private StreamPipe? _stdOutPipe;\n        private StreamPipe? _stdErrorPipe;\n        private bool _piped;\n        private bool _hasStandardIn;\n        private bool _suppressPipeToConsole;\n        private bool _suppressExceptionExitCodeNonZero;\n\n        /// <summary>\n        /// Gets the process of the task. If the process is not started yet, the property returns <value>null</value>.\n        /// </summary>\n        public Process? Process => _process;\n\n        /// <summary>\n        /// Gets the command line string of the task.\n        /// </summary>\n        public string CommandLine { get; }\n\n        /// <summary>\n        /// Gets the command name of the task.\n        /// </summary>\n        /// <remarks>\n        /// This value is passed to <see cref=\"ProcessStartInfo.FileName\"/>.\n        /// </remarks>\n        public string Command { get; }\n\n        /// <summary>\n        /// Gets the arguments of the task.\n        /// </summary>\n        /// <remarks>\n        /// The value is passed to <see cref=\"ProcessStartInfo.Arguments\"/>. It may be escaped or quoted.\n        /// </remarks>\n        public string Arguments { get; }\n\n        /// <summary>\n        /// Gets the previous task of the task if piped.\n        /// </summary>\n        public ProcessTask? PreviousTask { get; private set; }\n\n        public ProcessTask(FormattableString commandLine, ProcessTaskOptions? options = default)\n            : this(default(Stream?), commandLine, options)\n        { }\n        public ProcessTask(Stream? inputStream, FormattableString commandLine, ProcessTaskOptions? options = default)\n            : this(inputStream, CommandLineHelper.Expand(commandLine, options?.ShellExecutor ?? ChellEnvironment.Current.Shell.Executor), options)\n        { }\n        public ProcessTask(ReadOnlyMemory<byte> inputData, FormattableString commandLine, ProcessTaskOptions? options = default)\n            : this(new MemoryStream(inputData.ToArray()), CommandLineHelper.Expand(commandLine, options?.ShellExecutor ?? ChellEnvironment.Current.Shell.Executor), options)\n        { }\n\n        // NOTE: The overload of `string commandLine` cannot be made public due to restrictions.\n        public ProcessTask(CommandLineString commandLine, ProcessTaskOptions? options = default)\n            : this(default(Stream?), commandLine, options)\n        { }\n        public ProcessTask(Stream? inputStream, CommandLineString commandLine, ProcessTaskOptions? options = default)\n            : this(inputStream, commandLine.StringValue ?? CommandLineHelper.Expand(commandLine.FormattableStringValue ?? throw new InvalidOperationException(\"The command line string cannot be null.\"), options?.ShellExecutor ?? ChellEnvironment.Current.Shell.Executor), options)\n        { }\n        public ProcessTask(ReadOnlyMemory<byte> inputData, CommandLineString commandLine, ProcessTaskOptions? options = default)\n            : this(new MemoryStream(inputData.ToArray()), commandLine.StringValue ?? CommandLineHelper.Expand(commandLine.FormattableStringValue ?? throw new InvalidOperationException(\"The command line string cannot be null.\"), options?.ShellExecutor ?? ChellEnvironment.Current.Shell.Executor), options)\n        { }\n        private ProcessTask(Stream? inputStream, string commandLine, ProcessTaskOptions? options)\n            : this(inputStream, commandLine, (options?.ShellExecutor ?? ChellEnvironment.Current.Shell.Executor).GetCommandAndArguments(commandLine), options)\n        { }\n\n        public ProcessTask(string command, string arguments, ProcessTaskOptions? options = default)\n            : this(default(Stream?), command, arguments, options)\n        { }\n        public ProcessTask(Stream? inputStream, string command, string arguments, ProcessTaskOptions? options = default)\n            : this(inputStream, $\"{command} {arguments}\", (command, arguments), options)\n        { }\n        public ProcessTask(ReadOnlyMemory<byte> inputData, string command, string arguments, ProcessTaskOptions? options = default)\n            : this(new MemoryStream(inputData.ToArray()), $\"{command} {arguments}\", (command, arguments), options)\n        { }\n\n        private ProcessTask(Stream? inputStream, string commandLine, (string Command, string Arguments) commandAndArguments, ProcessTaskOptions? options = default)\n        {\n            _startedAt = DateTimeOffset.Now;\n            _options = options ?? new ProcessTaskOptions();\n            _id = Interlocked.Increment(ref _idSequence);\n            _processCancellation = new CancellationTokenSource();\n            _processCancellationRegistration = _processCancellation.Token.Register(() =>\n            {\n                WriteDebugTrace($\"ProcessTimedOut: Pid={_process?.Id}; StartedAt={_startedAt}; Elapsed={DateTimeOffset.Now - _startedAt}\");\n#if NET5_0_OR_GREATER || NETCOREAPP3_0 || NETCOREAPP3_1\n                _process?.Kill(true);\n#else\n                // TODO: .NET Standard 2.0 or 2.1 does not support kill child processes.\n                _process?.Kill();\n#endif\n            });\n\n            _output = new ProcessOutput(_options.ShellExecutor.Encoding);\n            _taskLazy = new Lazy<Task<ProcessOutput>>(AsTaskCore, LazyThreadSafetyMode.ExecutionAndPublication);\n            _suppressPipeToConsole = !_options.Verbosity.HasFlag(ChellVerbosity.ConsoleOutputs);\n\n            CommandLine = commandLine ?? throw new ArgumentNullException(nameof(commandLine));\n            (Command, Arguments) = commandAndArguments;\n\n            if (inputStream != null)\n            {\n                // Set the stdin stream and start a process immediately.\n                ConnectStreamToStandardInput(inputStream);\n            }\n            else if (_options.RedirectStandardInput)\n            {\n                // Enable stdin redirection and start a process immediately.\n                RedirectStandardInput();\n            }\n            else\n            {\n                // Delay startup to allow time to configure the stdin stream.\n                // If a Task is requested (e.g. `await`, `AsTask`, `Pipe` ...), it will be started immediately.\n                _ = ScheduleStartProcessAsync();\n            }\n\n            WriteDebugTrace($\"Created: {commandLine}\");\n\n            async Task ScheduleStartProcessAsync()\n            {\n                await Task.Delay(ProcessStartScheduledDelay).ConfigureAwait(false);\n                EnsureProcess();\n            }\n        }\n        \n        public static ProcessTask operator |(ProcessTask a, FormattableString b)\n            => a.Pipe(new ProcessTask(b));\n        public static ProcessTask operator |(ProcessTask a, Stream b)\n            => a.Pipe(b);\n        public static ProcessTask operator |(ProcessTask a, ProcessTask b)\n            => a.Pipe(b);\n\n        public static ProcessTask operator |(Stream a, ProcessTask b)\n        {\n            b.ConnectStreamToStandardInput(a);\n            return b;\n        }\n        public static ProcessTask operator |(ReadOnlyMemory<byte> a, ProcessTask b)\n        {\n            b.ConnectStreamToStandardInput(new MemoryStream(a.ToArray()));\n            return b;\n        }\n\n        public static implicit operator Task(ProcessTask task)\n            => task.AsTask();\n        public static implicit operator Task<ProcessOutput>(ProcessTask task)\n            => task.AsTask();\n\n        /// <summary>\n        /// Gets the output of the process as Task.\n        /// </summary>\n        /// <returns></returns>\n        public async Task<ProcessOutput> AsTask()\n        {\n            try\n            {\n                return await _taskLazy.Value.ConfigureAwait(false);\n            }\n            catch when (_suppressExceptionExitCodeNonZero)\n            {\n                return _output;\n            }\n        }\n\n        public System.Runtime.CompilerServices.TaskAwaiter<ProcessOutput> GetAwaiter()\n        {\n            return AsTask().GetAwaiter();\n        }\n\n        /// <summary>\n        /// Gets the exit code of the process as Task.\n        /// </summary>\n        public Task<int> ExitCode\n        {\n            get\n            {\n                if (_taskLazy.Value.IsCompleted)\n                {\n                    return Task.FromResult(_output.ExitCode);\n                }\n\n                return _taskLazy.Value.ContinueWith(x => _output.ExitCode);\n            }\n        }\n\n        /// <summary>\n        /// Configures the task to ignore the exception when the process returns exit code with non-zero.\n        /// </summary>\n        /// <returns></returns>\n        public ProcessTask NoThrow()\n        {\n            _suppressExceptionExitCodeNonZero = true;\n            return this;\n        }\n\n        public override string ToString()\n        {\n            return PreviousTask != null ? $\"{PreviousTask} | {CommandLine}\" : $\"{CommandLine}\";\n        }\n\n        /// <summary>\n        /// Enables standard output redirection of the process.\n        /// </summary>\n        /// <remarks>\n        /// Redirecting standard input must be done before the process has started. You can also use a <see cref=\"ProcessTaskOptions\"/> that is guaranteed to enable redirection while creating a <see cref=\"ProcessTask\"/>.\n        /// </remarks>\n        public void RedirectStandardInput(bool immediateLaunchProcess = true)\n        {\n            WriteDebugTrace($\"RedirectStandardInput: immediateLaunchProcess={immediateLaunchProcess}\");\n            lock (_syncLock)\n            {\n                if (_process != null)\n                {\n                    throw new InvalidOperationException(\"The process has already been started. Redirecting standard input must be done before the process has started.\");\n                }\n\n                _hasStandardIn = true;\n            }\n\n            if (immediateLaunchProcess)\n            {\n                EnsureProcess();\n            }\n        }\n\n        /// <summary>\n        /// Connects a stream to the standard input of the process.\n        /// </summary>\n        /// <remarks>\n        /// Connecting standard input must be done before the process has started. You can also use a constructor argument that is guaranteed to receive a stream.\n        /// </remarks>\n        /// <param name=\"stream\"></param>\n        public void ConnectStreamToStandardInput(Stream stream)\n        {\n            lock (_syncLock)\n            {\n                if (_stdInStream != null) throw new InvalidOperationException(\"The standard input has already connected to the process.\");\n                _stdInStream = stream ?? throw new ArgumentNullException(nameof(stream));\n            }\n\n            RedirectStandardInput();\n        }\n\n        /// <summary>\n        /// Suppresses output to the console if the standard output of the process is not configured.\n        /// </summary>\n        /// <returns></returns>\n        public ProcessTask SuppressConsoleOutputs()\n        {\n            _suppressPipeToConsole = true;\n            return this;\n        }\n\n        /// <summary>\n        /// Pipes the standard output to the stream.\n        /// </summary>\n        /// <param name=\"stream\"></param>\n        /// <returns></returns>\n        public ProcessTask Pipe(Stream stream)\n        {\n            EnsureProcess();\n\n            if (_stdOutPipe != null && _stdErrorPipe != null)\n            {\n                _stdOutPipe.Connect(stream);\n            }\n\n            _piped = true;\n\n            ReadyPipe();\n            return this;\n        }\n\n        /// <summary>\n        /// Pipes the standard output to the another process.\n        /// </summary>\n        /// <param name=\"nextProcess\"></param>\n        /// <returns></returns>\n        public ProcessTask Pipe(ProcessTask nextProcess)\n        {\n            // First, enable standard input redirection before starting a process for the next ProcessTask.\n            nextProcess.RedirectStandardInput(immediateLaunchProcess: false);\n\n            // Second, start a process.\n            EnsureProcess();\n\n            // Third, start a process for the next ProcessTask.\n            nextProcess.EnsureProcess();\n\n            if (_stdOutPipe != null && _stdErrorPipe != null)\n            {\n                if (nextProcess.Process != null)\n                {\n                    WriteDebugTrace($\"Pipe: {Process!.Id} -> {nextProcess.Process.Id}\");\n                    _stdOutPipe.Connect(nextProcess.Process.StandardInput.BaseStream ?? Stream.Null);\n                }\n            }\n\n            nextProcess.PreviousTask = this;\n            _piped = true;\n\n            ReadyPipe();\n            return nextProcess;\n        }\n\n        private void EnsureProcess()\n        {\n            lock (_syncLock)\n            {\n                if (_process is null && _processException is null)\n                {\n                    StartProcess();\n                }\n            }\n        }\n\n        private void StartProcess()\n        {\n            Debug.Assert(_process is null);\n            Debug.Assert(_processException is null);\n\n            // Enable only when stdin is redirected or has input stream.\n            // If RedirectStandardInput or CreateNoWindow is set to 'true', a process will not be interactive.\n            var procStartInfo = new ProcessStartInfo\n            {\n                FileName = Command,\n                Arguments = Arguments,\n                UseShellExecute = false,\n                CreateNoWindow = _options.Console.IsInputRedirected,\n                RedirectStandardOutput = true,\n                RedirectStandardInput = _options.Console.IsInputRedirected || _hasStandardIn,\n                RedirectStandardError = true,\n                WorkingDirectory = _options.WorkingDirectory ?? string.Empty,\n            };\n\n            if (_options.Verbosity.HasFlag(ChellVerbosity.CommandLine))\n            {\n                CommandLineHelper.WriteCommandLineToConsole(_options.Console, CommandLine, _options.Verbosity);\n            }\n\n            try\n            {\n                _processName = procStartInfo.FileName;\n                _process = Process.Start(procStartInfo)!;\n                WriteDebugTrace($\"Process.Start: Pid={_process.Id}; HasStandardIn={_hasStandardIn}; StandardIn={_stdInStream}; IsInputRedirected={Console.IsInputRedirected}\");\n\n                // Set a timeout if the option has non-zero or max-value.\n                if (_options.Timeout != TimeSpan.MaxValue && _options.Timeout != TimeSpan.Zero)\n                {\n                    _processCancellation.CancelAfter(_options.Timeout);\n                }\n\n                // Connect the stream to the standard input.\n                if (_stdInStream != null)\n                {\n                    _ = CopyCoreAsync(_stdInStream, _process.StandardInput.BaseStream);\n                }\n\n                _stdOutPipe = new StreamPipe(Process?.StandardOutput.BaseStream ?? Stream.Null);\n                _stdErrorPipe = new StreamPipe(Process?.StandardError.BaseStream ?? Stream.Null);\n            }\n            catch (Exception e)\n            {\n                _processException = ExceptionDispatchInfo.Capture(e);\n            }\n\n            static async Task CopyCoreAsync(Stream src, Stream dest)\n            {\n                await UnbufferedCopyToAsync(src, dest).ConfigureAwait(false);\n                dest.Close();\n            }\n        }\n\n        private void ReadyPipe()\n        {\n            WriteDebugTrace($\"ReadyPipe: Pid={Process?.Id}; Piped={_piped}; _stdOutPipe={_stdOutPipe}; _stdErrorPipe={_stdErrorPipe}\");\n\n            if (!_piped)\n            {\n                if (!_suppressPipeToConsole)\n                {\n                    _stdOutPipe?.Connect(_options.Console.OpenStandardOutput());\n                    _stdErrorPipe?.Connect(_options.Console.OpenStandardError());\n                }\n                _stdOutPipe?.Connect(_output.Sink.OutputWriter);\n                _stdErrorPipe?.Connect(_output.Sink.ErrorWriter);\n            }\n\n            _stdOutPipe?.Ready();\n            _stdErrorPipe?.Ready();\n        }\n\n        private static async Task UnbufferedCopyToAsync(Stream src, Stream dest, CancellationToken cancellationToken = default)\n        {\n            var buffer = new byte[80 * 1024];\n            while (true)\n            {\n                var read = await src.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);\n                if (read == 0)\n                {\n                    return;\n                }\n\n                await dest.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false);\n                await dest.FlushAsync(cancellationToken).ConfigureAwait(false);\n            }\n        }\n\n        private async Task ThrowIfParentTaskHasThrownProcessException(bool awaitForComplete)\n        {\n            if (PreviousTask != null)\n            {\n                // First, throw an exception for the parent task.\n                await PreviousTask.ThrowIfParentTaskHasThrownProcessException(awaitForComplete).ConfigureAwait(false);\n\n                // Second, Start the process.\n                PreviousTask.EnsureProcess();\n\n                // Third, If the process is failed to start, the task will be Faulted state immediately. We should throw an exception of prev task here.\n                var t = PreviousTask.AsTask();\n                if (t.IsFaulted)\n                {\n                    await t.ConfigureAwait(false);\n                }\n\n                if (!t.IsCompleted && awaitForComplete)\n                {\n                    await t.ConfigureAwait(false);\n                }\n            }\n        }\n\n        private async Task<ProcessOutput> AsTaskCore()\n        {\n            await ThrowIfParentTaskHasThrownProcessException(awaitForComplete:false).ConfigureAwait(false);\n\n            EnsureProcess();\n\n            if (Process is {} proc)\n            {\n                if (_stdOutPipe is null || _stdErrorPipe is null) throw new InvalidOperationException();\n\n                // If we have no stdin stream and Console's StandardInput is redirected, connects them automatically.\n                var connectStdInToPipe = _options.EnableAutoWireStandardInput && !_hasStandardIn && _options.Console.IsInputRedirected /*Non-Interactive*/;\n                if (connectStdInToPipe)\n                {\n                    StandardInput.Pipe.Connect(proc.StandardInput.BaseStream);\n                    StandardInput.Pipe.Ready();\n                }\n\n                ReadyPipe();\n\n\n                try\n                {\n#if NET5_0_OR_GREATER\n                    await proc.WaitForExitAsync().ConfigureAwait(false);\n#else\n                    await Task.Run(() => proc.WaitForExit()).ConfigureAwait(false);\n#endif\n                }\n                finally\n                {\n                    _processCancellationRegistration.Dispose();\n                }\n\n                _output.ExitCode = proc.ExitCode;\n\n                WriteDebugTrace($\"ProcessExited: Pid={proc.Id}; ExitCode={proc.ExitCode}\");\n\n                if (connectStdInToPipe)\n                {\n                    StandardInput.Pipe.Disconnect(proc.StandardInput.BaseStream);\n                }\n\n                // Flush output streams/pipes\n                WriteDebugTrace($\"Pipe/Sink.CompleteAsync: Pid={proc.Id}\");\n                await _stdOutPipe.CompleteAsync().ConfigureAwait(false);\n                await _stdErrorPipe.CompleteAsync().ConfigureAwait(false);\n                await _output.Sink.CompleteAsync().ConfigureAwait(false);\n                WriteDebugTrace($\"Pipe/Sink.CompleteAsync:Done: Pid={proc.Id}\");\n\n                await ThrowIfParentTaskHasThrownProcessException(awaitForComplete: true).ConfigureAwait(false);\n\n                if (_output.ExitCode != 0)\n                {\n                    var ex = new ProcessTaskException(_processName ?? \"Unknown\", proc.Id, this, _output);\n                    if (_processCancellation.IsCancellationRequested)\n                    {\n                        throw new OperationCanceledException($\"The process has reached the timeout. (Timeout: {_options.Timeout})\", ex);\n                    }\n                    else\n                    {\n                        throw ex;\n                    }\n                }\n            }\n            else\n            {\n                _output.ExitCode = 127;\n                _processCancellationRegistration.Dispose();\n                if (_processException != null)\n                {\n                    throw new ProcessTaskException(this, _output, _processException.SourceException);\n                }\n                else\n                {\n                    throw new ProcessTaskException(this, _output);\n                }\n            }\n            return _output;\n        }\n\n        [Conditional(\"DEBUG\")]\n        private void WriteDebugTrace(string s)\n        {\n            if (_options.Verbosity.HasFlag(ChellVerbosity.Debug))\n            {\n                _options.Console.Out.WriteLine($\"[DEBUG][{_id}] {s}\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/ProcessTaskException.cs",
    "content": "﻿using System;\n\nnamespace Chell\n{\n    /// <summary>\n    /// Represents an error that occurs during process execution.\n    /// </summary>\n    public class ProcessTaskException : Exception\n    {\n        public ProcessTask ProcessTask { get; }\n        public ProcessOutput Output { get; }\n\n        public ProcessTaskException(string processName, int processId, ProcessTask processTask, ProcessOutput output, Exception? innerException = default)\n            : base($\"Process '{processName}' ({processId}) has exited with exit code {output.ExitCode}. (Executed command: {processTask.Command} {processTask.Arguments})\", innerException)\n        {\n            ProcessTask = processTask;\n            Output = output;\n        }\n\n        public ProcessTaskException(ProcessTask processTask, ProcessOutput output, Exception? innerException = default)\n            : base($\"Failed to start the process. (Executed command: {processTask.Command} {processTask.Arguments})\", innerException)\n        {\n            ProcessTask = processTask;\n            Output = output;\n        }\n    }\n}"
  },
  {
    "path": "src/Chell/ProcessTaskOptions.cs",
    "content": "using System;\nusing Chell.IO;\nusing Chell.Shell;\n\nnamespace Chell\n{\n    public class ProcessTaskOptions\n    {\n        /// <summary>\n        /// Gets or sets whether to enable automatic wiring of standard input to the process. The default value is <value>true</value>.\n        /// </summary>\n        public bool EnableAutoWireStandardInput { get; set; }\n\n        /// <summary>\n        /// Gets or sets to enable standard input redirection. The default value is <value>false</value>.\n        /// </summary>\n        public bool RedirectStandardInput { get; set; }\n\n        /// <summary>\n        /// Gets or sets the shell executor. The default value is <c>ChellEnvironment.Current.Shell.Executor</c>.\n        /// </summary>\n        public IShellExecutor ShellExecutor { get; set; }\n\n        /// <summary>\n        /// Gets or sets the console provider. The default value is <c>ChellEnvironment.Current.Console</c>.\n        /// </summary>\n        public IConsoleProvider Console { get; set; }\n\n        /// <summary>\n        /// Gets or sets the verbosity. The default value is <c>ChellEnvironment.Current.Verbosity</c>.\n        /// </summary>\n        public ChellVerbosity Verbosity { get; set; }\n\n        /// <summary>\n        /// Gets or sets the working directory for the process.\n        /// </summary>\n        public string? WorkingDirectory { get; set; }\n\n        /// <summary>\n        /// Gets or sets the duration to timeout the process. The default value is <c>ChellEnvironment.Current.ProcessTimeout</c>.\n        /// </summary>\n        /// <remarks>\n        /// If the value is <see cref=\"TimeSpan.Zero\"/> or <see cref=\"TimeSpan.MaxValue\"/>, the process will not be timed out.\n        /// </remarks>\n        public TimeSpan Timeout { get; set; }\n\n        public ProcessTaskOptions(\n            bool? redirectStandardInput = default,\n            bool? enableAutoWireStandardInput = default,\n            ChellVerbosity? verbosity = default,\n            IShellExecutor? shellExecutor = default,\n            IConsoleProvider? console = default,\n            string? workingDirectory = default,\n            TimeSpan? timeout = default\n        )\n        {\n            RedirectStandardInput = redirectStandardInput ?? false;\n            EnableAutoWireStandardInput = enableAutoWireStandardInput ?? true;\n            ShellExecutor = shellExecutor ?? ChellEnvironment.Current.Shell.Executor;\n            Console = console ?? ChellEnvironment.Current.Console;\n            Verbosity = verbosity ?? ChellEnvironment.Current.Verbosity;\n            WorkingDirectory = workingDirectory ?? workingDirectory;\n            Timeout = timeout ?? ChellEnvironment.Current.ProcessTimeout;\n        }\n\n        private ProcessTaskOptions(ProcessTaskOptions orig)\n        {\n            RedirectStandardInput = orig.RedirectStandardInput;\n            EnableAutoWireStandardInput = orig.EnableAutoWireStandardInput;\n            ShellExecutor = orig.ShellExecutor;\n            Console = orig.Console;\n            Verbosity = orig.Verbosity;\n            WorkingDirectory = orig.WorkingDirectory;\n            Timeout = orig.Timeout;\n        }\n\n        public ProcessTaskOptions WithRedirectStandardInput(bool redirectStandardInput)\n            => new ProcessTaskOptions(this) { RedirectStandardInput = redirectStandardInput };\n        public ProcessTaskOptions WithEnableAutoWireStandardInput(bool enableAutoWireStandardInput)\n            => new ProcessTaskOptions(this) { EnableAutoWireStandardInput = enableAutoWireStandardInput };\n        public ProcessTaskOptions WithShellExecutor(IShellExecutor shellExecutor)\n            => new ProcessTaskOptions(this) { ShellExecutor = shellExecutor };\n        public ProcessTaskOptions WithVerbosity(ChellVerbosity verbosity)\n            => new ProcessTaskOptions(this) { Verbosity = verbosity };\n        public ProcessTaskOptions WithWorkingDirectory(string? workingDirectory)\n            => new ProcessTaskOptions(this) { WorkingDirectory = workingDirectory };\n        public ProcessTaskOptions WithTimeout(TimeSpan timeout)\n            => new ProcessTaskOptions(this) { Timeout = timeout };\n    }\n}\n"
  },
  {
    "path": "src/Chell/Run.cs",
    "content": "using System;\nusing System.IO;\nusing Chell.Shell;\n\nnamespace Chell\n{\n    /// <summary>\n    /// Short cut for <see cref=\"ProcessTask\"/> to launch a process from a <see cref=\"string\"/> or <see cref=\"FormattableString\"/>.\n    /// </summary>\n    public class Run : ProcessTask\n    {\n        public static implicit operator Run(FormattableString commandLine)\n            => new Run(commandLine);\n        public static implicit operator Run(CommandLineString commandLine)\n            => new Run(commandLine);\n\n        /// <summary>\n        /// Launches a process from a <see cref=\"string\"/>.\n        /// </summary>\n        /// <param name=\"commandLine\"></param>\n        public Run(CommandLineString commandLine) : base(commandLine) { }\n\n        /// <summary>\n        /// Launches a process from a <see cref=\"FormattableString\"/>.\n        /// </summary>\n        /// <remarks>\n        ///  The interpolated string will be escaped and the array will be expanded.\n        /// </remarks>\n        public Run(FormattableString commandLine) : base(commandLine) { }\n\n        /// <summary>\n        /// Launches a process from a <see cref=\"string\"/> and connects the specified <see cref=\"Stream\"/> to the standard input.\n        /// </summary>\n        public Run(Stream inputStream, CommandLineString commandLine) : base(inputStream, commandLine) { }\n\n        /// <summary>\n        /// Launches a process from a <see cref=\"FormattableString\"/> and connects the specified <see cref=\"Stream\"/> to the standard input.\n        /// </summary>\n        /// <remarks>\n        /// The interpolated string will be escaped and the array will be expanded.\n        /// </remarks>\n        /// <param name=\"inputStream\"></param>\n        /// <param name=\"commandLine\"></param>\n        public Run(Stream inputStream, FormattableString commandLine) : base(inputStream, commandLine) { }\n\n        /// <summary>\n        /// Launches a process from a <see cref=\"string\"/> and writes the specified binary data to the standard input.\n        /// </summary>\n        /// <param name=\"inputData\"></param>\n        /// <param name=\"commandLine\"></param>\n        public Run(ReadOnlyMemory<byte> inputData, CommandLineString commandLine) : base(new MemoryStream(inputData.ToArray()), commandLine) { }\n\n        /// <summary>\n        /// Launches a process from a <see cref=\"FormattableString\"/> and writes the specified binary data to the standard input.\n        /// </summary>\n        /// <remarks>\n        /// The interpolated string will be escaped and the array will be expanded.\n        /// </remarks>\n        /// <param name=\"inputData\"></param>\n        /// <param name=\"commandLine\"></param>\n        public Run(ReadOnlyMemory<byte> inputData, FormattableString commandLine) : base(new MemoryStream(inputData.ToArray()), commandLine) { }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Shell/BashShellExecutor.cs",
    "content": "using System.Diagnostics;\nusing System.IO;\nusing System.Text;\nusing System.Text.RegularExpressions;\nusing Chell.Internal;\n\nnamespace Chell.Shell\n{\n    public class BashShellExecutor : IShellExecutor\n    {\n        public static string? AutoDetectedPath { get; set; }\n\n        public string? Path { get; set; } = AutoDetectedPath;\n\n        public Encoding Encoding => Encoding.UTF8;\n\n        public string Prefix { get; set; }\n\n        public (string Command, string Arguments) GetCommandAndArguments(string commandLine)\n            => (Path ?? throw new FileNotFoundException(\"Bash is not found in the PATH.\"), $\"-c \\\"{Prefix}{commandLine}\\\"\");\n\n        // https://unix.stackexchange.com/questions/187651/how-to-echo-single-quote-when-using-single-quote-to-wrap-special-characters-in\n        public string Escape(string value)\n        {\n            if (string.IsNullOrEmpty(value))\n            {\n                return string.Empty;\n            }\n            if (Regex.IsMatch(value, \"^[a-zA-Z0-9_.-/]+$\"))\n            {\n                return value;\n            }\n            return $\"$'{value.Replace(\"\\\\\", \"\\\\\\\\\").Replace(\"'\", \"\\\\'\").Replace(\"\\\"\", \"\\\\\\\"\")}'\";\n        }\n\n        public BashShellExecutor(string? prefix = null)\n        {\n            Prefix = prefix ?? \"set -euo pipefail;\";\n        }\n\n        static BashShellExecutor()\n        {\n            if (Which.TryGetPath(\"bash\", out var bashPath))\n            {\n                AutoDetectedPath = bashPath;\n            }\n            else\n            {\n                AutoDetectedPath = null;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Shell/CmdShellExecutor.cs",
    "content": "using System.Text;\nusing System.Text.RegularExpressions;\n\nnamespace Chell.Shell\n{\n    public class CmdShellExecutor : IShellExecutor\n    {\n        public Encoding Encoding => Encoding.UTF8;\n\n        public (string Command, string Arguments) GetCommandAndArguments(string commandLine)\n            => (\"cmd\", $\"/c \\\"{commandLine}\\\"\");\n\n        public string Escape(string value)\n        {\n            if (string.IsNullOrEmpty(value))\n            {\n                return string.Empty;\n            }\n            if (Regex.IsMatch(value, \"^[a-zA-Z0-9_.-/\\\\\\\\]+$\"))\n            {\n                return value;\n            }\n\n            value = Regex.Replace(value, \"([<>|&^])\", \"^$1\");\n            value = Regex.Replace(value, \"(\\\\\\\\)?\\\"\", x => x.Groups[1].Success ? \"\\\\\\\\\\\\\\\"\" : \"\\\\\\\"\");\n\n            return $\"\\\"{value}\\\"\";\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell/Shell/IShellExecutor.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Chell.Shell\n{\n    public interface IShellExecutor\n    {\n        Encoding Encoding { get; }\n        (string Command, string Arguments) GetCommandAndArguments(string commandLine);\n        string Escape(string value);\n    }\n}\n"
  },
  {
    "path": "src/Chell/Shell/NoUseShellExecutor.cs",
    "content": "﻿using System.Text;\nusing System.Text.RegularExpressions;\nusing Chell.Internal;\n\nnamespace Chell.Shell\n{\n    public class NoUseShellExecutor : IShellExecutor\n    {\n        public Encoding Encoding => Encoding.UTF8;\n\n        public (string Command, string Arguments) GetCommandAndArguments(string commandLine)\n        {\n            return CommandLineHelper.Parse(commandLine);\n        }\n\n        public string Escape(string value)\n            => Regex.IsMatch(value, \"^[a-zA-Z0-9_.-/]+$\")\n                ? value\n                : $\"\\\"{value.Replace(\"`\", \"``\").Replace(\"\\\"\", \"`\\\"\")}\\\"\";\n    }\n}"
  },
  {
    "path": "src/Chell/Shell/ShellExecutorProvider.cs",
    "content": "using System;\nusing System.Runtime.InteropServices;\nusing Chell.Shell;\n\nnamespace Chell.Shell\n{\n    public class ShellExecutorProvider\n    {\n        public IShellExecutor Executor { get; private set; } = GetPlatformPreferredExecutor();\n\n        public void SetExecutor(IShellExecutor shellExecutor)\n        {\n            Executor = shellExecutor ?? throw new ArgumentNullException(nameof(shellExecutor));\n        }\n\n        internal static IShellExecutor GetPlatformPreferredExecutor()\n            => RuntimeInformation.IsOSPlatform(OSPlatform.Windows)\n                ? new CmdShellExecutor()\n                : BashShellExecutor.AutoDetectedPath != null\n                    ? new BashShellExecutor()\n                    : new NoUseShellExecutor();\n    }\n}\n\nnamespace Chell\n{\n    public static class ShellExecutorProviderExtensions\n    {\n        public static void NoUseShell(this ShellExecutorProvider provider)\n        {\n            provider.SetExecutor(new NoUseShellExecutor());\n        }\n        public static void UseBash(this ShellExecutorProvider provider, string? prefix = null)\n        {\n            provider.SetExecutor(new BashShellExecutor(prefix));\n        }\n        public static void UseCmd(this ShellExecutorProvider provider)\n        {\n            provider.SetExecutor(new CmdShellExecutor());\n        }\n        public static void UseDefault(this ShellExecutorProvider provider)\n        {\n            provider.SetExecutor(ShellExecutorProvider.GetPlatformPreferredExecutor());\n        }\n    }\n}\n"
  },
  {
    "path": "src/Chell.Run/Chell.Run.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <OutputType>Exe</OutputType>\n    <TargetFrameworks>netcoreapp3.1;net5.0</TargetFrameworks>\n\n    <Description>Tool to run C# code like a script. Provides a shell script-like (bash, cmd, ...) experience to .NET application.</Description>\n    <PackAsTool>true</PackAsTool>\n    <ToolCommandName>chell</ToolCommandName>\n<!--\n    <PublishReadyToRun>true</PublishReadyToRun>\n    <TieredCompilationQuickJitForLoops>true</TieredCompilationQuickJitForLoops>\n-->\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"Cocona.Lite\" Version=\"1.6.0\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.Scripting\" Version=\"3.11.0\" />\n    <PackageReference Include=\"Microsoft.CodeAnalysis.CSharp.Workspaces\" Version=\"3.11.0\" />\n    <PackageReference Include=\"Mono.Options\" Version=\"6.12.0.148\" />\n    <PackageReference Include=\"Sharprompt\" Version=\"2.3.3\" />\n  </ItemGroup>\n\n  <ItemGroup Condition=\"$(TargetFramework) == 'netcoreapp3.1'\">\n    <PackageReference Include=\"System.Text.Json\" Version=\"5.0.0\" />\n    <PackageReference Include=\"System.Text.Encodings.Web\" Version=\"5.0.1\" />\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\Chell\\Chell.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "src/Chell.Run/Program.cs",
    "content": "using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Cocona;\nusing Microsoft.CodeAnalysis;\nusing Microsoft.CodeAnalysis.CSharp.Scripting;\nusing Microsoft.CodeAnalysis.Scripting;\n\nnamespace Chell.Run\n{\n    partial class Program\n    {\n        static Task Main(string[] args)\n            => CoconaLiteApp.RunAsync<Program>(args);\n\n        public class RunCommandParameterSet : ICommandParameterSet\n        {\n            [Option(\"ref\", new[] { 'r' }, Description = \"Additional reference assembly\")]\n            [HasDefaultValue]\n            public string[]? References { get; set; } = null;\n\n            [Option(\"using\", new[] { 'u' }, Description = \"Additional `using` namespace\")]\n            [HasDefaultValue]\n            public string[]? Usings { get; set; } = null;\n\n            [Option('q')]\n            [HasDefaultValue]\n            public bool Silent { get; set; } = false;\n        }\n\n        [IgnoreUnknownOptions]\n        [Command(Description = \"Chell.Run: Run C# script instantly.\")]\n        public async Task RunAsync(\n            RunCommandParameterSet runParams, \n            [Option('e', Description = \"A one-line program that can be run instantly.\")] string? eval = default,\n            [Argument(Description = \"The path to a script file, or arguments to pass to the script\")] string[]? filenameOrArgs = default\n        )\n        {\n            var fileName = filenameOrArgs is {Length: > 0} ? filenameOrArgs[0] : null;\n\n            // -e \"..\" or --eval \"...\"\n            if (!string.IsNullOrEmpty(eval))\n            {\n                var args = Environment.GetCommandLineArgs();\n                var index = Array.FindIndex(args, x => x == \"-e\" || x == \"--eval\");\n                args = args.Skip(index + 2).ToArray();\n                await RunScriptAsync(\"<Inline>\", Environment.CurrentDirectory, eval, args, runParams);\n            }\n            // Read a script from stdin.\n            else if (fileName == \"-\" || (string.IsNullOrWhiteSpace(fileName) && Console.IsInputRedirected))\n            {\n                // Pass the strings as arguments after '-'.\n                var args = Array.Empty<string>();\n                if (fileName == \"-\")\n                {\n                    args = Environment.GetCommandLineArgs();\n                    var index = Array.IndexOf(args, \"-\");\n                    args = args.Skip(index + 1).ToArray();\n                }\n\n                using var reader = new StreamReader(Console.OpenStandardInput());\n                var code = await reader.ReadToEndAsync();\n                await RunScriptAsync(\"<StdIn>\", Environment.CurrentDirectory, code, args, runParams);\n            }\n            else\n            {\n                if (string.IsNullOrWhiteSpace(fileName))\n                {\n                    throw new CommandExitedException(\"Error: Specify the path or pass the script from standard input.\", -1);\n                }\n                if (!File.Exists(fileName))\n                {\n                    throw new CommandExitedException(\"Error: No such file or directory.\", -1);\n                }\n\n                var ext = Path.GetExtension(fileName);\n                if (ext == \".cs\")\n                {\n                    // Run .cs script file.\n                    var fullPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, fileName));\n\n                    var args = filenameOrArgs?.Skip(1).ToArray() ?? Array.Empty<string>();\n                    await RunScriptAsync(fullPath, Path.GetDirectoryName(fullPath) ?? Environment.CurrentDirectory, await File.ReadAllTextAsync(fileName, Encoding.UTF8), args, runParams);\n                }\n                else\n                {\n                    throw new CommandExitedException(\"Error: The specified file has unknown extension. Chell accepts a filename with `.cs` extension.\", -1);\n                }\n            }\n        }\n\n        private async Task RunScriptAsync(string fileName, string executableDirectory, string content, string[] args, RunCommandParameterSet runParams)\n        {\n            _ = typeof(System.Text.Json.JsonSerializer).Assembly;\n            _ = typeof(Chell.ChellEnvironment).Assembly;\n            _ = typeof(Cocona.CoconaLiteApp).Assembly;\n            _ = typeof(Sharprompt.Prompt).Assembly;\n            _ = typeof(Mono.Options.Command).Assembly;\n\n            var references = AppDomain.CurrentDomain.GetAssemblies()\n                .Distinct()\n                .GroupBy(x => x)\n                .Select(x => x.Last())\n                .Select(x => MetadataReference.CreateFromFile(x.Location));\n            var usings = new[]\n            {\n                \"System\",\n                \"System.Collections\",\n                \"System.Collections.Generic\",\n                \"System.Diagnostics\",\n                \"System.IO\",\n                \"System.Text\",\n                \"System.Text.RegularExpressions\",\n                \"System.Linq\",\n                \"System.Threading\",\n                \"System.Threading.Tasks\",\n                \"Chell\",\n                \"Chell.Extensions\",\n\n                // using static\n                \"Chell.Exports\"\n            }.AsEnumerable();\n\n            var scriptOptions = ScriptOptions.Default\n                .AddImports(usings.Concat(runParams.Usings ?? Array.Empty<string>()))\n                .AddReferences(references)\n                .AddReferences(runParams.References ?? Array.Empty<string>());\n\n            try\n            {\n                if (runParams.Silent)\n                {\n                    ChellEnvironment.Current.Verbosity = ChellVerbosity.Silent;\n                }\n\n                ChellEnvironment.Current.SetCommandLineArgs(fileName, Path.GetFileName(fileName), executableDirectory, args);\n                var script = await CSharpScript.RunAsync(content, scriptOptions);\n            }\n            catch (CompilationErrorException e)\n            {\n                Console.Error.WriteLine($\"{fileName}{e.Message}\");\n            }\n            catch (ProcessTaskException e)\n            {\n                Console.Error.WriteLine(e.Message);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Chell.Tests/Chell.Tests.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\n  <PropertyGroup>\n    <TargetFramework>net5.0</TargetFramework>\n\n    <IsPackable>false</IsPackable>\n  </PropertyGroup>\n\n  <ItemGroup>\n    <PackageReference Include=\"FluentAssertions\" Version=\"6.1.0\" />\n    <PackageReference Include=\"Microsoft.NET.Test.Sdk\" Version=\"16.9.4\" />\n    <PackageReference Include=\"xunit\" Version=\"2.4.1\" />\n    <PackageReference Include=\"xunit.runner.visualstudio\" Version=\"2.4.3\">\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n    <PackageReference Include=\"coverlet.collector\" Version=\"3.0.2\">\n      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>\n      <PrivateAssets>all</PrivateAssets>\n    </PackageReference>\n  </ItemGroup>\n\n  <ItemGroup>\n    <ProjectReference Include=\"..\\..\\src\\Chell\\Chell.csproj\" />\n  </ItemGroup>\n\n</Project>\n"
  },
  {
    "path": "tests/Chell.Tests/ChellEnvironmentTest.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading.Tasks;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Chell.Tests\n{\n    public class ChellEnvironmentTest\n    {\n        [Fact]\n        public void HomeDirectory()\n        {\n            var env = new ChellEnvironment();\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                env.HomeDirectory.Should().Be(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));\n            }\n            else\n            {\n                env.HomeDirectory.Should().Be(Environment.GetEnvironmentVariable(\"HOME\"));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Chell.Tests/CommandLineStringTest.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Chell.Tests\n{\n    public class CommandLineStringTest\n    {\n        [Fact]\n        public void StaticMethodPreferString()\n        {\n            StaticMethodOverloadedTest.A(\"Foo {0}\").Should().Be($\"{nameof(CommandLineString)}: StringValue=Foo {{0}}; FormattableStringValue=\");\n        }\n        [Fact]\n        public void StaticMethodPreferFormattableString()\n        {\n            StaticMethodOverloadedTest.A($\"Foo {0}\").Should().Be($\"{nameof(FormattableString)}: Foo {{0}}; 1\");\n        }\n\n        private static class StaticMethodOverloadedTest\n        {\n            public static string A(CommandLineString s) => $\"{nameof(CommandLineString)}: StringValue={s.StringValue}; FormattableStringValue={s.FormattableStringValue}\";\n            public static string A(FormattableString s) => $\"{nameof(FormattableString)}: {s.Format}; {s.ArgumentCount}\";\n        }\n\n        [Fact]\n        public void InstanceMethodPreferString()\n        {\n            new InstanceMethodOverloadedTest().A(\"Foo {0}\").Should().Be($\"{nameof(CommandLineString)}: StringValue=Foo {{0}}; FormattableStringValue=\");\n        }\n        [Fact]\n        public void InstanceMethodPreferFormattableString()\n        {\n            new InstanceMethodOverloadedTest().A($\"Foo {0}\").Should().Be($\"{nameof(FormattableString)}: Foo {{0}}; 1\");\n        }\n\n        private class InstanceMethodOverloadedTest\n        {\n            public string A(CommandLineString s) => $\"{nameof(CommandLineString)}: StringValue={s.StringValue}; FormattableStringValue={s.FormattableStringValue}\";\n            public string A(FormattableString s) => $\"{nameof(FormattableString)}: {s.Format}; {s.ArgumentCount}\";\n        }\n\n        [Fact]\n        public void ImplicitCastConstructorPreferString()\n        {\n            new ConstructorOverloadedTest(\"Foo {0}\").Result.Should().Be($\"{nameof(CommandLineString)}: StringValue=Foo {{0}}; FormattableStringValue=\");\n        }\n        [Fact]\n        public void ImplicitCastConstructorPreferFormattableString()\n        {\n            new ConstructorOverloadedTest($\"Foo {0}\").Result.Should().Be($\"{nameof(FormattableString)}: Foo {{0}}; 1\");\n        }\n\n        private class ConstructorOverloadedTest\n        {\n            public string Result { get; }\n\n            public ConstructorOverloadedTest(CommandLineString s)\n            {\n                Result = $\"{nameof(CommandLineString)}: StringValue={s.StringValue}; FormattableStringValue={s.FormattableStringValue}\";\n            }\n\n            public ConstructorOverloadedTest(FormattableString s)\n            {\n                Result = $\"{nameof(FormattableString)}: {s.Format}; {s.ArgumentCount}\";\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Chell.Tests/ProcessTaskTest.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.IO.Pipelines;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Chell.IO;\nusing Chell.Shell;\nusing FluentAssertions;\nusing Kokuban;\nusing Xunit;\n\nnamespace Chell.Tests\n{\n    internal static class ProcessTaskTestFixtureExtensions\n    {\n        public static TemporaryAppBuilder.Compilation AddTo(this TemporaryAppBuilder.Compilation compilation,\n            IList<IDisposable> disposables)\n        {\n            disposables.Add(compilation);\n            return compilation;\n        }\n    }\n\n    public class ProcessTaskTestFixture : IDisposable\n    {\n        private readonly TemporaryAppSolutionBuilder _slnBuilder = new TemporaryAppSolutionBuilder();\n        public string EchoArg { get; }\n        public string EchoOutAndErrorArgs { get; }\n        public string HelloWorld { get; }\n        public string ExitCodeNonZero { get; }\n        public string ExitCodeNonZeroWaited { get; }\n        public string WriteCommandLineArgs { get; }\n        public string StandardInputPassThroughText { get; }\n        public string StandardInputPassThroughBinary { get; }\n        public string WriteSleepWriteExit { get; }\n        public string ReadOnce { get; }\n        public string ReadAllLines { get; }\n        public string WriteCurrentDirectory { get; }\n        public string Never { get; }\n\n        public ProcessTaskTestFixture()\n        {\n            EchoArg = _slnBuilder.CreateProject(nameof(EchoArg), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Console.WriteLine(\"\"[\"\" + Environment.GetCommandLineArgs()[1] + \"\"]\"\");\n                \"));\n            EchoOutAndErrorArgs = _slnBuilder.CreateProject(nameof(EchoOutAndErrorArgs), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    using System.Threading.Tasks;\n                    Console.Out.WriteLine(\"\"[\"\" + Environment.GetCommandLineArgs()[1] + \"\"]\"\");\n                    await Task.Delay(100);\n                    Console.Error.WriteLine(\"\"[\"\" + Environment.GetCommandLineArgs()[2] + \"\"]\"\");\n                    await Task.Delay(100);\n                    Console.Out.WriteLine(\"\"[\"\" + Environment.GetCommandLineArgs()[3] + \"\"]\"\");\n                    await Task.Delay(100);\n                    Console.Error.WriteLine(\"\"[\"\" + Environment.GetCommandLineArgs()[4] + \"\"]\"\");\n                \"));\n            HelloWorld = _slnBuilder.CreateProject(nameof(HelloWorld), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Console.WriteLine(\"\"Hello World!\"\");\n                \"));\n            ExitCodeNonZero = _slnBuilder.CreateProject(nameof(ExitCodeNonZero), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Environment.ExitCode = 192;\n                \"));\n            ExitCodeNonZeroWaited = _slnBuilder.CreateProject(nameof(ExitCodeNonZeroWaited), builder => \n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    using System.Threading.Tasks;\n                    await Task.Delay(100);\n                    Environment.ExitCode = 192;\n                \"));\n            WriteCommandLineArgs = _slnBuilder.CreateProject(nameof(WriteCommandLineArgs), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    foreach (var line in Environment.GetCommandLineArgs())\n                    {\n                        Console.WriteLine(line);\n                    }\n                \"));\n            StandardInputPassThroughText = _slnBuilder.CreateProject(nameof(StandardInputPassThroughText), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Console.InputEncoding = System.Text.Encoding.UTF8;\n                    Console.OutputEncoding = System.Text.Encoding.UTF8;\n                    Console.Write(Console.In.ReadToEnd());\n                \"));\n            StandardInputPassThroughBinary = _slnBuilder.CreateProject(nameof(StandardInputPassThroughBinary), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Console.OpenStandardInput().CopyTo(Console.OpenStandardOutput());\n                \"));\n            WriteSleepWriteExit = _slnBuilder.CreateProject(nameof(WriteSleepWriteExit), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    using System.Threading;\n                    Console.WriteLine(\"\"Hello\"\");\n                    Thread.Sleep(1000);\n                    Console.WriteLine(\"\"Hello\"\");\n                \"));\n            ReadOnce = _slnBuilder.CreateProject(nameof(ReadOnce), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Console.WriteLine(Console.ReadLine());\n                \"));\n            ReadAllLines = _slnBuilder.CreateProject(nameof(ReadAllLines), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    while (true)\n                    {\n                        var line = Console.ReadLine();\n                        if (line == null) return;\n                        Console.WriteLine(line);\n                    }\n                \"));\n            WriteCurrentDirectory = _slnBuilder.CreateProject(nameof(WriteCurrentDirectory), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    Console.WriteLine(Environment.CurrentDirectory);\n                \"));\n            Never = _slnBuilder.CreateProject(nameof(Never), builder =>\n                builder.WriteSourceFile(\"Program.cs\", @\"\n                    using System;\n                    using System.Threading;\n                    Console.WriteLine(\"\"Hello\"\");\n                    while (true) { Thread.Sleep(1000); }\n                \"));\n            _slnBuilder.Build();\n        }\n\n        public void Dispose()\n        {\n            _slnBuilder.Dispose();\n        }\n    }\n\n    [Collection(\"ProcessTaskTest\")] // NOTE: Test cases use `Console` and does not run in parallel.\n    public class ProcessTaskTest : IClassFixture<ProcessTaskTestFixture>\n    {\n        private readonly ProcessTaskTestFixture _fixture;\n\n        public ProcessTaskTest(ProcessTaskTestFixture fixture)\n        {\n            _fixture = fixture;\n        }\n\n        private async Task<(string StandardOut, string StandardError)> RunAsync(Func<IConsoleProvider, Task> func)\n        {\n            var fakeConsole = new FakeConsoleProvider();\n\n            await func(fakeConsole);\n\n            return (fakeConsole.GetStandardOutputAsString(), fakeConsole.GetStandardErrorAsString());\n        }\n\n        [Fact]\n        public async Task CommandNotFound_UseShell()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var dummyCommandName = Guid.NewGuid().ToString();\n            var procTask = new ProcessTask($\"{dummyCommandName} --help\");\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                (await procTask.ExitCode).Should().Be(1); // A shell (cmd) will return 1.\n            }\n            else\n            {\n                (await procTask.ExitCode).Should().Be(127); // A shell (bash) will return 127.\n            }\n        }\n\n        [Fact]\n        public async Task CommandNotFound_NoUseShell()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var dummyCommandName = Guid.NewGuid().ToString();\n            var procTask = new ProcessTask($\"{dummyCommandName} --help\", new ProcessTaskOptions(shellExecutor: new NoUseShellExecutor()));\n            (await procTask.ExitCode).Should().Be(127); // System.Diagnostics.Process will return 127.\n        }\n\n        [Fact]\n        public async Task Execute()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var procTask = new ProcessTask($\"{_fixture.HelloWorld}\");\n            (await procTask.ExitCode).Should().Be(0);\n\n            var result = await procTask;\n            result.ExitCode.Should().Be(0);\n            result.Combined.Should().Be(\"Hello World!\"  + Environment.NewLine);\n            result.Output.Should().Be(\"Hello World!\" + Environment.NewLine);\n            result.Error.Should().BeEmpty();\n        }\n\n        [Fact]\n        public async Task ProcessOutputInArgumentShouldBeTrimmed()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var procTask1 = new ProcessTask($\"{_fixture.HelloWorld}\");\n            var result1 = await procTask1;\n            var procTask2 = new ProcessTask($\"{_fixture.EchoArg} {result1}\");\n            var result2 = await procTask2;\n\n            result1.Combined.Should().Be(\"Hello World!\" + Environment.NewLine);\n            result2.Combined.Should().Be(\"[Hello World!]\" + Environment.NewLine);\n        }\n\n        [Fact]\n        public async Task ExpandArguments()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var args = new[] { \"Alice\", \"Karen\", \"Program Files\", @\"C:\\Program Files (x86)\\Microsoft Visual Studio\" };\n            var procTask = new ProcessTask($\"{_fixture.WriteCommandLineArgs} {args}\");\n            var result = await procTask;\n\n            // NOTE: We need skip the first line which is path of the command.\n            result.AsLines(trimEnd: true).Skip(1).Should().BeEquivalentTo(args);\n        }\n\n        [Fact]\n        public async Task ExpandArguments_Escape()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var args = new[] { \"Alice\", \"Karen\", \"Program Files\", @\"C:\\Program Files (x86)\\Microsoft Visual Studio\", \"\\\"\\\\'|<>\" };\n            var procTask = new ProcessTask($\"{_fixture.WriteCommandLineArgs} {args}\");\n            var result = await procTask;\n\n            // NOTE: We need skip the first line which is path of the command.\n            result.AsLines(trimEnd: true).Skip(1).Should().BeEquivalentTo(args);\n        }\n\n        [Fact]\n        public async Task ExitCode()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var procTask = new ProcessTask($\"{_fixture.ExitCodeNonZero}\");\n            (await procTask.ExitCode).Should().Be(192);\n        }\n\n        [Fact]\n        public async Task ExitCode_ThrowIfNonZero()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var procTask = new ProcessTask($\"{_fixture.ExitCodeNonZero}\");\n            await Assert.ThrowsAsync<ProcessTaskException>(async () => await procTask);\n        }\n\n        [Fact]\n        public async Task ProcessOutput_StandardInputPassThroughText()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var memStream = new MemoryStream(Encoding.UTF8.GetBytes(\"Hello, コンニチハ!\\nABCDEFG\"));\n            var procTask = new ProcessTask(memStream, $\"{_fixture.StandardInputPassThroughText}\");\n            var result = await procTask;\n\n            result.Output.TrimEnd().Should().Be(\"Hello, コンニチハ!\\nABCDEFG\");\n        }\n\n        [Fact]\n        public async Task ProcessOutput_StandardInputOutputCombined()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var procTask = new ProcessTask($\"{_fixture.EchoOutAndErrorArgs} Arg0 Arg1 Arg2 Arg3\");\n            var result = await procTask;\n\n            result.Output.TrimEnd().Should().Be(string.Join(Environment.NewLine, \"[Arg0]\", \"[Arg2]\"));\n            result.Error.TrimEnd().Should().Be(string.Join(Environment.NewLine, \"[Arg1]\", \"[Arg3]\"));\n            result.Combined.TrimEnd().Should().Be(string.Join(Environment.NewLine, \"[Arg0]\", \"[Arg1]\", \"[Arg2]\", \"[Arg3]\"));\n        }\n\n\n        [Fact]\n        public async Task ProcessOutput_StandardInputPassThroughBinary()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var memStream = new MemoryStream(Encoding.Unicode.GetBytes(\"Hello, コンニチハ!\\nABCDEFG\"));\n            var procTask = new ProcessTask(memStream, $\"{_fixture.StandardInputPassThroughBinary}\");\n            var result = await procTask;\n\n            result.OutputBinary.ToArray().Should().BeEquivalentTo(memStream.ToArray());\n        }\n\n        [Fact]\n        public async Task Pipe_StandardInputPassThroughBinary()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var data = Encoding.Unicode.GetBytes(\"Hello, コンニチハ!\\nABCDEFG\");\n            var memStream = new MemoryStream(data);\n            var procTask = new ProcessTask(memStream, $\"{_fixture.StandardInputPassThroughBinary}\");\n            var destStream = new MemoryStream();\n            var result = await procTask.Pipe(destStream);\n\n            result.ExitCode.Should().Be(0);\n            result.OutputBinary.Length.Should().Be(0);\n            destStream.ToArray().Should().BeEquivalentTo(data);\n        }\n\n        [Fact]\n        public async Task Pipe_CloseDestFirst()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var srcTask = new ProcessTask($\"{_fixture.WriteSleepWriteExit}\");\n            var destTask = new ProcessTask($\"{_fixture.ReadOnce}\");\n\n            await (srcTask | destTask);\n        }\n\n        [Fact]\n        public async Task Pipe_CloseSrcFirst()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var srcTask = new ProcessTask($\"{_fixture.HelloWorld}\");\n            var destTask = new ProcessTask($\"{_fixture.ReadAllLines}\");\n\n            await (srcTask | destTask);\n        }\n\n        [Fact]\n        public async Task Pipe_ExitCode_NonZero()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var srcTask = new ProcessTask($\"{_fixture.ExitCodeNonZero}\");\n            var destTask = new ProcessTask($\"{_fixture.ReadAllLines}\");\n\n            await Assert.ThrowsAsync<ProcessTaskException>(async () => await (srcTask | destTask));\n        }\n        \n        [Fact]\n        public async Task Pipe_ExitCode_NonZero_ExitTailFirst()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var srcTask = new ProcessTask($\"{_fixture.ExitCodeNonZeroWaited}\");\n            var destTask = new ProcessTask($\"{_fixture.ReadAllLines}\");\n\n            await Assert.ThrowsAsync<ProcessTaskException>(async () => await (srcTask | destTask));\n        }\n\n        [Fact]\n        public async Task Pipe_ExitCode_NonZero_NoThrow()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var srcTask = new ProcessTask($\"{_fixture.ExitCodeNonZero}\");\n            var destTask = new ProcessTask($\"{_fixture.ReadAllLines}\");\n\n            await (srcTask.NoThrow() | destTask);\n        }\n\n        [Fact]\n        public async Task WorkingDirectory()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            {\n                var currentDirectory = Environment.CurrentDirectory;\n                var output = await new ProcessTask($\"{_fixture.WriteCurrentDirectory}\");\n                output.ToString().Trim().Should().Be(currentDirectory);\n            }\n            {\n                var currentDirectory = Environment.CurrentDirectory;\n                var workingDirectory = Path.GetFullPath(Path.Combine(currentDirectory, \"..\"));\n                var output = await new ProcessTask($\"{_fixture.WriteCurrentDirectory}\", new ProcessTaskOptions().WithWorkingDirectory(workingDirectory));\n                output.ToString().Trim().Should().Be(workingDirectory);\n            }\n        }\n\n        [Fact]\n        public async Task ProcessTimeout()\n        {\n            Func<Task> execute = async () =>\n            {\n                using var fakeConsoleScope = new FakeConsoleProviderScope();\n                await Assert.ThrowsAsync<OperationCanceledException>(async () =>\n                {\n                    await new ProcessTask($\"{_fixture.WriteSleepWriteExit}\",\n                        new ProcessTaskOptions().WithTimeout(TimeSpan.FromMilliseconds(300)));\n                });\n            };\n            await execute.Should().CompleteWithinAsync(TimeSpan.FromSeconds(2));\n        }\n\n        [Fact]\n        public async Task ProcessTimeout_Never()\n        {\n            Func<Task> execute = async () =>\n            {\n                using var fakeConsoleScope = new FakeConsoleProviderScope();\n                await Assert.ThrowsAsync<OperationCanceledException>(async () =>\n                {\n                    await new ProcessTask($\"{_fixture.Never}\",\n                        new ProcessTaskOptions().WithTimeout(TimeSpan.FromMilliseconds(300)));\n                });\n            };\n            await execute.Should().CompleteWithinAsync(TimeSpan.FromSeconds(2));\n        }\n\n        [Fact]\n        public async Task Verbosity_Silent()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var (stdOut, stdErr) = await RunAsync(async (console) =>\n            {\n                await new ProcessTask($\"{_fixture.HelloWorld}\", new ProcessTaskOptions(console: console).WithVerbosity(ChellVerbosity.Silent));\n            });\n\n            stdOut.Should().BeEmpty();\n        }\n\n        [Fact]\n        public async Task Verbosity_CommandLine()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var (stdOut, stdErr) = await RunAsync(async (console) =>\n            {\n                await new ProcessTask($\"{_fixture.HelloWorld}\", new ProcessTaskOptions(console: console).WithVerbosity(ChellVerbosity.CommandLine));\n            });\n\n            stdOut.Should().StartWith(\"$ \");\n            stdOut.Should().NotContain(\"Hello World!\");\n        }\n\n        [Fact]\n        public async Task Verbosity_ConsoleOutputs()\n        {\n            using var fakeConsoleScope = new FakeConsoleProviderScope();\n            var (stdOut, stdErr) = await RunAsync(async (console) =>\n            {\n                await new ProcessTask($\"{_fixture.HelloWorld}\", new ProcessTaskOptions(console: console).WithVerbosity(ChellVerbosity.ConsoleOutputs));\n            });\n\n            stdOut.Should().Be(\"Hello World!\" + Environment.NewLine);\n        }\n\n        private class FakeConsoleProviderScope : IDisposable\n        {\n            private readonly KokubanColorMode _origKokubanColorMode;\n            private readonly IConsoleProvider _origConsoleProvider;\n            private readonly FakeConsoleProvider _fakeConsoleProvider;\n\n            public string StdOut => _fakeConsoleProvider.GetStandardOutputAsString();\n            public string StdErr => _fakeConsoleProvider.GetStandardErrorAsString();\n\n            public FakeConsoleProviderScope()\n            {\n                _origKokubanColorMode = KokubanOptions.Default.Mode;\n                _origConsoleProvider = ChellEnvironment.Current.Console;\n                _fakeConsoleProvider = new FakeConsoleProvider();\n\n                KokubanOptions.Default.Mode = KokubanColorMode.None;\n                ChellEnvironment.Current.Console = _fakeConsoleProvider;\n            }\n\n            public void Dispose()\n            {\n                ChellEnvironment.Current.Console = _origConsoleProvider;\n                _fakeConsoleProvider.Dispose();\n                KokubanOptions.Default.Mode = _origKokubanColorMode;\n            }\n        }\n    }\n\n    public class FakeConsoleProvider : IConsoleProvider, IDisposable\n    {\n        private readonly Pipe _outputPipe;\n        private readonly Pipe _errorPipe;\n        private readonly CancellationTokenSource _cts;\n\n        private readonly MemoryStream _input = new MemoryStream();\n        private readonly MemoryStream _output = new MemoryStream();\n        private readonly MemoryStream _error = new MemoryStream();\n\n        public FakeConsoleProvider()\n        {\n            _cts = new CancellationTokenSource();\n            _outputPipe = new Pipe(new PipeOptions(readerScheduler:PipeScheduler.Inline, writerScheduler:PipeScheduler.Inline));\n            _outputPipe.Reader.CopyToAsync(_output, _cts.Token);\n            _errorPipe = new Pipe(new PipeOptions(readerScheduler: PipeScheduler.Inline, writerScheduler: PipeScheduler.Inline));\n            _errorPipe.Reader.CopyToAsync(_error, _cts.Token);\n        }\n\n        public string GetStandardOutputAsString() => Encoding.UTF8.GetString(_output.ToArray());\n        public string GetStandardErrorAsString() => Encoding.UTF8.GetString(_error.ToArray());\n\n        public Stream OpenStandardInput()\n            => _input;\n\n        public Stream OpenStandardOutput()\n            => _outputPipe.Writer.AsStream(leaveOpen: true);\n\n        public Stream OpenStandardError()\n            => _errorPipe.Writer.AsStream(leaveOpen: true);\n\n        public Encoding InputEncoding => new UTF8Encoding(false);\n        public Encoding OutputEncoding => new UTF8Encoding(false);\n        public Encoding ErrorEncoding => new UTF8Encoding(false);\n        public bool IsInputRedirected => false;\n        public bool IsOutputRedirected => false;\n        public bool IsErrorRedirected => false;\n        public TextWriter Out => new StreamWriter(_outputPipe.Writer.AsStream(leaveOpen: true), OutputEncoding, leaveOpen: true) { AutoFlush = true };\n        public TextWriter Error => new StreamWriter(_errorPipe.Writer.AsStream(leaveOpen: true), ErrorEncoding, leaveOpen: true) { AutoFlush = true };\n        public void Dispose()\n        {\n            _cts.Cancel();\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Chell.Tests/Shell/BashShellExecutorTest.cs",
    "content": "using System;\nusing Chell.Shell;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Chell.Tests.Shell\n{\n    public class BashShellExecutorTest\n    {\n        [Fact]\n        public void Escape()\n        {\n            var executor = new BashShellExecutor();\n            executor.Escape(@\"\").Should().Be(@\"\");\n            executor.Escape(@\"foo\").Should().Be(@\"foo\");\n            executor.Escape(@\"123\").Should().Be(@\"123\");\n            executor.Escape(@\"f oo\").Should().Be(@\"$'f oo'\");\n            executor.Escape(@\"b'ar\").Should().Be(@\"$'b\\'ar'\");\n            executor.Escape(@\"b\"\"ar\").Should().Be(@\"$'b\\\"\"ar'\");\n            executor.Escape(@\"a\\b\").Should().Be(@\"$'a\\\\b'\");\n        }\n\n        [Fact]\n        public void GetCommandAndArguments()\n        {\n            {\n                var executor = new BashShellExecutor();\n                executor.Path = \"/bin/bash\";\n                executor.Prefix = \"set -euo pipefail;\";\n                executor.GetCommandAndArguments(\"foo bar\").Should().Be((\"/bin/bash\", \"-c \\\"set -euo pipefail;foo bar\\\"\"));\n            }\n            {\n                var executor = new BashShellExecutor();\n                executor.Path = \"/usr/local/bin/bash\";\n                executor.Prefix = \"\";\n                executor.GetCommandAndArguments(\"foo bar\").Should().Be((\"/usr/local/bin/bash\", \"-c \\\"foo bar\\\"\"));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Chell.Tests/Shell/CmdShellExecutorTest.cs",
    "content": "using System;\nusing Chell.Shell;\nusing FluentAssertions;\nusing Xunit;\n\nnamespace Chell.Tests.Shell\n{\n    public class CmdShellExecutorTest\n    {\n        [Fact]\n        public void Escape()\n        {\n            var executor = new CmdShellExecutor();\n            executor.Escape(@\"\").Should().Be(@\"\");\n            executor.Escape(@\"foo\").Should().Be(@\"foo\");\n            executor.Escape(@\"123\").Should().Be(@\"123\");\n            executor.Escape(@\"Program Files\").Should().Be(@\"\"\"Program Files\"\"\");\n            executor.Escape(@\"b'ar\").Should().Be(@\"\"\"b'ar\"\"\");\n            executor.Escape(@\"b\"\"ar\").Should().Be(@\"\"\"b\\\"\"ar\"\"\");\n            executor.Escape(@\"a\\b\").Should().Be(@\"a\\b\");\n            executor.Escape(@\"^\").Should().Be(@\"\"\"^^\"\"\");\n            executor.Escape(@\"<\").Should().Be(@\"\"\"^<\"\"\");\n            executor.Escape(@\">\").Should().Be(@\"\"\"^>\"\"\");\n            executor.Escape(@\"|\").Should().Be(@\"\"\"^|\"\"\");\n            executor.Escape(@\"&\").Should().Be(@\"\"\"^&\"\"\");\n            executor.Escape(@\"&&\").Should().Be(@\"\"\"^&^&\"\"\");\n\n            // \\  --> \\\n            // \"  --> \"\\\"\"\n            // \\\" --> \"\\\\\\\"\"\n            executor.Escape(@\"\\\").Should().Be(@\"\\\");\n            executor.Escape(@\"\"\"\").Should().Be(@\"\"\"\\\"\"\"\"\");\n            executor.Escape(@\"\\\"\"\").Should().Be(@\"\"\"\\\\\\\"\"\"\"\");\n\n            // \"\\\" --> \"\\\"\\\\\\\"\"\n            executor.Escape(@\"\"\"\\\"\"\").Should().Be(\"\\\"\\\\\\\"\\\\\\\\\\\\\\\"\\\"\");\n\n            // \"\\\"'|<>[] --> \"\\\"\\\\\\\"'^|^<^>[]\"\"\n            executor.Escape(\"\\\"\\\\\\\"'|<>[]\").Should().Be(\"\\\"\\\\\\\"\\\\\\\\\\\\\\\"'^|^<^>[]\\\"\");\n\n            // \"\\'|<> --> \"\\\"\\'^|^<^>\"\n            executor.Escape(\"\\\"\\\\'|<>\").Should().Be(\"\\\"\\\\\\\"\\\\'^|^<^>\\\"\");\n\n            // A B\\C D --> \"A B\\C D\"\n            // A \"B\\C D --> \"A \\\"B\\C D\"\n            executor.Escape(@\"A B\\C D\").Should().Be(@\"\"\"A B\\C D\"\"\");\n            executor.Escape(@\"A \"\"B\\C D\").Should().Be(@\"\"\"A \\\"\"B\\C D\"\"\");\n        }\n\n        [Fact]\n        public void GetCommandAndArguments()\n        {\n            var executor = new CmdShellExecutor();\n            executor.GetCommandAndArguments(\"foo bar\").Should().Be((\"cmd\", \"/c \\\"foo bar\\\"\"));\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Chell.Tests/TemporaryAppBuilder.cs",
    "content": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Chell.Tests\n{\n    public class TemporaryAppSolutionBuilder : IDisposable\n    {\n        private readonly List<TemporaryAppBuilder> _apps;\n        public string BaseDirectory { get; }\n\n        public TemporaryAppSolutionBuilder()\n        {\n            _apps = new List<TemporaryAppBuilder>();\n            BaseDirectory = Path.Combine(Path.GetTempPath(), $\"Chell.Tests-{Guid.NewGuid()}\");\n        }\n\n        public string CreateProject(string projectName, Action<TemporaryAppBuilder> configure)\n        {\n            var builder = TemporaryAppBuilder.Create(BaseDirectory, projectName);\n            _apps.Add(builder);\n            configure(builder);\n            return Path.Combine(BaseDirectory, \"out\", projectName);\n        }\n        \n        public TemporaryAppSolutionBuilder RunInSolutionDirectory(string fileName, string arguments)\n        {\n            var procStartInfo = new ProcessStartInfo()\n            {\n                FileName = fileName,\n                Arguments =  arguments,\n                WorkingDirectory = BaseDirectory,\n                RedirectStandardOutput = true,\n                RedirectStandardError = true,\n            };\n\n            var proc = Process.Start(procStartInfo)!;\n            var standardOutput = proc.StandardOutput.ReadToEnd();\n            var standardError = proc.StandardError.ReadToEnd();\n            proc.WaitForExit();\n\n            if (proc.ExitCode != 0)\n            {\n                throw new InvalidOperationException(string.Join(Environment.NewLine, $\"The process has been exited with code {proc.ExitCode}. (FileName={fileName}, Arguments={arguments}\", \"Output:\", standardOutput, \"Error:\", standardError));\n            }\n\n            return this;\n        }\n        \n        public void Build()\n        {\n            RunInSolutionDirectory(\"dotnet\", \"new sln\");\n            RunInSolutionDirectory(\"dotnet\", $\"sln add {string.Join(\" \", _apps.Select(x => x.ProjectName + \"/src\"))}\");\n            RunInSolutionDirectory(\"dotnet\", \"publish -o out\");\n        }\n        \n        public void Dispose()\n        {\n            Directory.Delete(BaseDirectory, recursive:true);\n        }\n    }\n    public class TemporaryAppBuilder : IDisposable\n    {\n        private bool _disposed;\n\n        public string ProjectName { get; }\n\n        public string BaseDirectory { get; }\n        public string SourceDirectory { get; }\n        public string OutputDirectory { get; }\n\n        private TemporaryAppBuilder(string baseSlnDirectory, string projectName)\n        {\n            ProjectName = projectName;\n            BaseDirectory = Path.Combine(baseSlnDirectory, projectName);\n            SourceDirectory = Path.Combine(BaseDirectory, $\"src\");\n            OutputDirectory = Path.Combine(BaseDirectory, $\"out\");\n        }\n\n        public static TemporaryAppBuilder Create(string baseSlnDirectory, string projectName)\n        {\n            var builder = new TemporaryAppBuilder(baseSlnDirectory, projectName);\n            builder.Initialize();\n            return builder;\n        }\n\n        private void Initialize()\n        {\n            Directory.CreateDirectory(BaseDirectory);\n            Directory.CreateDirectory(SourceDirectory);\n            Directory.CreateDirectory(OutputDirectory);\n\n            // Create .NET Console App project.\n            //RunInSourceDirectory(\"dotnet\", $\"new console -f net5.0 -n {ProjectName} -o .\");\n            // Explicitly use .NET 5 SDK. (AppHost is required for macOS with .NET 5 SDK)\n            WriteSourceFile(\"global.json\",\n                @\"{\n                    \"\"sdk\"\": {\n                        \"\"version\"\": \"\"5.0.100\"\",\n                        \"\"rollForward\"\": \"\"latestFeature\"\"\n                    }\n                }\");\n            WriteSourceFile($\"{ProjectName}.csproj\",\n                @\"<Project Sdk=\"\"Microsoft.NET.Sdk\"\">\n                    <PropertyGroup>\n                        <OutputType>Exe</OutputType>\n                        <TargetFramework>net5.0</TargetFramework>\n                    </PropertyGroup>\n                </Project>\");\n            WriteSourceFile(\"Directory.Build.props\",\n                @\"<Project ToolsVersion=\"\"15.0\"\" xmlns=\"\"http://schemas.microsoft.com/developer/msbuild/2003\"\">\n                    <PropertyGroup>\n                        <UseAppHost>true</UseAppHost>\n                    </PropertyGroup>\n                </Project>\");\n        }\n\n        public TemporaryAppBuilder WriteSourceFile(string fileName, string content)\n        {\n            File.WriteAllText(Path.Combine(SourceDirectory, fileName), content, Encoding.UTF8);\n            return this;\n        }\n\n        public string GetExecutablePath()\n            => Path.Combine(OutputDirectory, ProjectName);\n\n        public Compilation Build()\n        {\n            RunInSourceDirectory(\"dotnet\", $\"publish -o \\\"{OutputDirectory}\\\"\");\n\n            return new Compilation(this, Path.Combine(OutputDirectory, ProjectName));\n        }\n\n        public class Compilation : IDisposable\n        {\n            private readonly TemporaryAppBuilder _builder;\n            public string ExecutablePath { get; }\n\n            public Compilation(TemporaryAppBuilder builder, string executablePath)\n            {\n                _builder = builder;\n                ExecutablePath = executablePath;\n            }\n\n            public void Dispose()\n            {\n                _builder.Dispose();\n            }\n        }\n\n        public TemporaryAppBuilder RunInSourceDirectory(string fileName, string arguments)\n        {\n            var procStartInfo = new ProcessStartInfo()\n            {\n                FileName = fileName,\n                Arguments =  arguments,\n                WorkingDirectory = SourceDirectory,\n                RedirectStandardOutput = true,\n                RedirectStandardError = true,\n            };\n\n            var proc = Process.Start(procStartInfo)!;\n            proc.WaitForExit();\n            var standardOutput = proc.StandardOutput.ReadToEnd();\n            var standardError = proc.StandardError.ReadToEnd();\n\n\n            if (proc.ExitCode != 0)\n            {\n                throw new InvalidOperationException(string.Join(Environment.NewLine, $\"The process has been exited with code {proc.ExitCode}. (FileName={fileName}, Arguments={arguments}\", \"Output:\", standardOutput, \"Error:\", standardError));\n            }\n\n            return this;\n        }\n\n        public void Dispose()\n        {\n            if (_disposed) return;\n\n            Directory.Delete(BaseDirectory, recursive:true);\n\n            _disposed = true;\n        }\n    }\n}\n"
  }
]