-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7406034
Showing
8 changed files
with
739 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# fount-pwsh | ||
|
||
**fount-pwsh** 是一个工具,它可以让你在 PowerShell (pwsh) 或 esh 终端中轻松使用 **Fount**。 | ||
它可以帮你更好地使用 shell,或只是在 shell 里和角色聊天。 | ||
|
||
 | ||
|
||
## 安装 | ||
|
||
使用这个命令安装 `fount-pwsh` 模块: | ||
|
||
```powershell | ||
Install-Module fount-pwsh | ||
``` | ||
|
||
**注意:** `fount-pwsh` 依赖于 [Fount](https://github.com/steve02081504/fount) 工作。 | ||
不过别担心! | ||
如果你还没有安装 Fount,`fount-pwsh` 会在你第一次使用以下命令时 **自动帮你安装 Fount**: | ||
|
||
- `Start-Fount` | ||
- `Set-FountAssist` | ||
- `Install-FountAssist` | ||
|
||
## 配置助手 | ||
|
||
你需要一个支持 **`shellassist` 界面** 的 **Fount 角色**。 | ||
|
||
> [!WARNING] | ||
> **关于 PowerShell 性能** | ||
> | ||
> 在 PowerShell 中,加载 Fount 助手平均需要 **大约 600 毫秒**。 这可能会稍微影响你的 shell 启动速度。 (对于 esh 来说,加载是 **瞬间的**。) | ||
> | ||
> 如果你希望 PowerShell 启动更快,可以考虑将 Fount 助手的加载设置为 **后台加载** (参阅powershell的[事件注册](https://learn.microsoft.com/powershell/module/microsoft.powershell.utility/register-engineevent?view=powershell-7.5)和[OnIdle](https://learn.microsoft.com/dotnet/api/system.management.automation.psengineevent.onidle?view=powershellsdk-7.4.0)事件)。 | ||
**两种方法启用你的助手:** | ||
|
||
**1. 自动配置 (推荐):** | ||
|
||
使用这个命令,可以让 Fount 助手在每次你打开新的 shell 窗口时自动启动并提供帮助: | ||
|
||
```powershell | ||
Install-FountAssist <你的 Fount 用户名> <需要的角色名> | ||
``` | ||
|
||
将 `<你的 Fount 用户名>` 和 `<需要的角色名>` 替换为你实际的 Fount 用户名和角色名称。 | ||
|
||
**2. 手动配置 (适用于高级用户):** | ||
|
||
如果你想手动设置,可以将以下代码添加到你的 **shell profile** 文件中: | ||
|
||
```powershell | ||
Set-FountAssist <你的 Fount 用户名> <需要的角色名> | ||
``` | ||
|
||
## 助手什么时候会出现? | ||
|
||
Fount 助手会在以下这些情况下自动出现: | ||
|
||
- **当你输入了错误的命令时** (如拼写错误)。 | ||
- **当你运行的命令执行失败时** (`$?` 的值为 false)。 | ||
- **当你主动使用 `f` 命令时** | ||
|
||
## 禁用/启用 自动助手 | ||
|
||
你可以使用 `f` 命令来轻松地禁用或启用自动助手功能: | ||
|
||
```powershell | ||
f 0 # 禁用自动助手 (也可以用 f false / f no / f n / f disable) | ||
f 1 # 启用自动助手 (也可以用 f true / f yes / f y / f enable) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
$EshellUI.ExecutionRecorders.Add({ | ||
if ($Global:FountAssist.last_commaned) { | ||
$Global:FountAssist.last_commaned.output = $($ans | Out-String) -join "`n" | ||
$Global:FountAssist.last_commaned.error = $($err | Out-String) -join "`n" | ||
$Global:FountAssist.shellhistory.Add($Global:FountAssist.last_commaned) | Out-Null | ||
} | ||
$Global:FountAssist.last_commaned = @{ | ||
command = $expr_now | ||
time = Get-Date | ||
} | ||
# keep shellhistory's last 30 records | ||
while ($Global:FountAssist.shellhistory.Count -gt 30) { | ||
$Global:FountAssist.shellhistory.RemoveAt(0) | ||
} | ||
$Global:FountAssist.rejected_commands = [System.Collections.ArrayList]@() | ||
}) | Out-Null | ||
function global:f( | ||
[ValidateScript({ | ||
(IsEnable $_) -or (IsDisable $_) -or (!$_) | ||
})] | ||
$switch | ||
) { | ||
if ($switch -ne $null) { | ||
$Global:FountAssist.Enabled = IsEnable $switch | ||
Write-Host "fount assist is $(@('disabled','enabled')[$Global:FountAssist.Enabled])" | ||
return | ||
} | ||
$requst = @{ | ||
charname = $Global:FountAssist.AssistCharname | ||
UserCharname = $Global:FountAssist.UserCharname | ||
shelltype = "esh(like powershell)" | ||
shellhistory = $Global:FountAssist.shellhistory | ||
command_now = $expr_now | ||
command_error = $($global:expr_err_now | Out-String) -join "`n" | ||
rejected_commands = $Global:FountAssist.rejected_commands | ||
chat_scoped_char_memorys = $Global:FountAssist.chat_scoped_char_memorys | ||
pwd = "$pwd" | ||
} | ||
$result = Invoke-FountShell $Global:FountAssist.FountUsername 'shellassist' $requst | ||
if ($result.chat_scoped_char_memorys) { | ||
$Global:FountAssist.chat_scoped_char_memorys = $result.chat_scoped_char_memorys | ||
} | ||
if ($result.shellhistory) { | ||
$Global:FountAssist.shellhistory = [System.Collections.ArrayList]$result.shellhistory | ||
} | ||
|
||
if ($result.content -or $result.recommend_command) { | ||
Write-Host | ||
} | ||
if ($result.content) { | ||
$Global:FountAssist.shellhistory.Add(@{ | ||
name = $result.name | ||
avatar = $result.avatar | ||
role = "char" | ||
content = $result.content | ||
extension = $result.extension | ||
}) | Out-Null | ||
Write-Host $result.content | ||
} | ||
if ($result.recommend_command) { | ||
# 显示并询问是否执行或重新推荐 | ||
Write-Host "use `"" -NoNewline | ||
Write-Host $result.recommend_command -ForegroundColor Yellow -NoNewline | ||
Write-Host "`" as command? " -NoNewline | ||
Write-Host "[" -NoNewline | ||
Write-Host "enter" -ForegroundColor Green -NoNewline | ||
Write-Host "/" -NoNewline | ||
Write-Host "n" -ForegroundColor Blue -NoNewline | ||
Write-Host " (next)/" -NoNewline | ||
Write-Host "ctrl+c" -ForegroundColor Red -NoNewline | ||
Write-Host "]" | ||
[console]::TreatControlCAsInput = $true | ||
while ($true) { | ||
if ([console]::KeyAvailable) { | ||
$key = [console]::ReadKey($true) | ||
if ($key.Key -eq "Enter") { | ||
[console]::TreatControlCAsInput = $false | ||
$EshellUI.AcceptLine($result.recommend_command) | ||
return | ||
} | ||
if ($key.Key -eq "N") { | ||
$Global:FountAssist.rejected_commands.Add($result.recommend_command) | Out-Null | ||
return f | ||
} | ||
if ($key.Key -eq "C" -and $key.Modifiers -eq "Control") { | ||
return | ||
} | ||
} | ||
} | ||
} | ||
} | ||
$EshellUI.ExecutionHandlers.Add({ | ||
if (!$Global:FountAssist.Enabled) { | ||
return | ||
} | ||
#若当前表达式是合法ps脚本但不是合法命令 | ||
if ($global:bad_expr_now -and $global:expr_ast_now) { | ||
f | ||
return '' #终止当前表达式 | ||
} | ||
}) | Out-Null | ||
$EshellUI.AfterExecutionHandlers.Add({ | ||
if (!$Global:FountAssist.Enabled) { | ||
return | ||
} | ||
#若当前表达式退出值不为0且不在白名单中 | ||
if (-not $? -and $Global:FountAssist.NonZeroReturnWhiteList -notcontains (($expr_now -split '\s')[0])) { | ||
f | ||
} | ||
}) | Out-Null |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
$Global:FountAssist = @{ | ||
Enabled = $true | ||
AssistCharname = $null | ||
FountUsername = $null | ||
shellhistory = [System.Collections.ArrayList]@() | ||
rejected_commands = [System.Collections.ArrayList]@() | ||
chat_scoped_char_memorys = @{} | ||
# 以非0返回值指示信息而非错误的命令 | ||
NonZeroReturnWhiteList = [System.Collections.ArrayList]@('robocopy') | ||
} | ||
|
||
$script:FountAssistInstalled = $false | ||
|
||
function Set-FountAssist { | ||
param ( | ||
$FountUsername, | ||
$AssistCharname | ||
) | ||
if ($AssistCharname) { $Global:FountAssist.AssistCharname = $AssistCharname } | ||
if ($FountUsername) { $Global:FountAssist.FountUsername = $FountUsername } | ||
Start-Fount background keepalive runshell $Global:FountAssist.FountUsername preload chars $Global:FountAssist.AssistCharname | ||
if ($script:FountAssistInstalled) { | ||
return | ||
} | ||
if ($EshellUI) { | ||
. $PSScriptRoot/esh-assist.ps1 | ||
} | ||
else { | ||
. $PSScriptRoot/pwsh-assist.ps1 | ||
} | ||
} | ||
|
||
function Install-FountAssist { | ||
param ( | ||
$FountUsername, | ||
$AssistCharname | ||
) | ||
if ($AssistCharname) { $Global:FountAssist.AssistCharname = $AssistCharname } | ||
if ($FountUsername) { $Global:FountAssist.FountUsername = $FountUsername } | ||
if ($EshellUI) { | ||
$EshellUI.FountAssist = "$($Global:FountAssist.FountUsername):$($Global:FountAssist.AssistCharname)" | ||
} | ||
else { | ||
$content = Get-Content $PROFILE -ErrorAction Ignore | ||
$content = $content -split "`n" | Where-Object { $_ -notmatch 'Set-FountAssist' } | ||
$content += "Set-FountAssist $($Global:FountAssist.FountUsername) $($Global:FountAssist.AssistCharname)" | ||
$content = $content -join "`n" | ||
Set-Content $PROFILE $content | ||
} | ||
Set-FountAssist | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
function global:f( | ||
[ValidateScript({ | ||
(IsEnable $_) -or (IsDisable $_) -or (!$_) | ||
})] | ||
$switch | ||
) { | ||
if ($switch -ne $null) { | ||
$Global:FountAssist.Enabled = IsEnable $switch | ||
Write-Host "fount assist is $(@('disabled','enabled')[$Global:FountAssist.Enabled])" | ||
return | ||
} | ||
$requst = @{ | ||
charname = $Global:FountAssist.AssistCharname | ||
UserCharname = $Global:FountAssist.UserCharname | ||
shelltype = "powershell" | ||
shellhistory = $Global:FountAssist.shellhistory | ||
command_now = $Global:FountAssist.last_commaned.command | ||
command_error = $($Error | Select-Object -SkipLast $Global:FountAssist.HistoryErrorCount | Out-String) -join "`n" | ||
rejected_commands = $Global:FountAssist.rejected_commands | ||
chat_scoped_char_memorys = $Global:FountAssist.chat_scoped_char_memorys | ||
pwd = "$pwd" | ||
} | ||
$result = Invoke-FountShell $Global:FountAssist.FountUsername 'shellassist' $requst | ||
if ($result.chat_scoped_char_memorys) { | ||
$Global:FountAssist.chat_scoped_char_memorys = $result.chat_scoped_char_memorys | ||
} | ||
if ($result.shellhistory) { | ||
$Global:FountAssist.shellhistory = [System.Collections.ArrayList]$result.shellhistory | ||
} | ||
|
||
if ($result.content -or $result.recommend_command) { | ||
Write-Host | ||
} | ||
if ($result.content) { | ||
$Global:FountAssist.shellhistory.Add(@{ | ||
name = $result.name | ||
avatar = $result.avatar | ||
role = "char" | ||
content = $result.content | ||
extension = $result.extension | ||
}) | Out-Null | ||
Write-Host $result.content | ||
} | ||
if ($result.recommend_command) { | ||
# 显示并询问是否执行或重新推荐 | ||
Write-Host "use `"" -NoNewline | ||
Write-Host $result.recommend_command -ForegroundColor Yellow -NoNewline | ||
Write-Host "`" as command? " -NoNewline | ||
Write-Host "[" -NoNewline | ||
Write-Host "enter" -ForegroundColor Green -NoNewline | ||
Write-Host "/" -NoNewline | ||
Write-Host "n" -ForegroundColor Blue -NoNewline | ||
Write-Host " (next)/" -NoNewline | ||
Write-Host "ctrl+c" -ForegroundColor Red -NoNewline | ||
Write-Host "]" | ||
[console]::TreatControlCAsInput = $true | ||
while ($true) { | ||
if ([console]::KeyAvailable) { | ||
$key = [console]::ReadKey($true) | ||
if ($key.Key -eq "Enter") { | ||
[console]::TreatControlCAsInput = $false | ||
[Microsoft.PowerShell.PSConsoleReadLine]::AddToHistory($result.recommend_command) | ||
$StartExecutionTime = Get-Date | ||
Invoke-Expression $result.recommend_command | Out-String | Write-Host | ||
$EndExecutionTime = Get-Date | ||
[PSCustomObject](@{ | ||
CommandLine = $result.recommend_command | ||
ExecutionStatus = "Completed" | ||
StartExecutionTime = $StartExecutionTime | ||
EndExecutionTime = $EndExecutionTime | ||
}) | Add-History | ||
return | ||
} | ||
if ($key.Key -eq "N") { | ||
$Global:FountAssist.rejected_commands.Add($result.recommend_command) | Out-Null | ||
f | ||
return | ||
} | ||
if ($key.Key -eq "C" -and $key.Modifiers -eq "Control") { | ||
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() | ||
return | ||
} | ||
} | ||
} | ||
} | ||
} | ||
$Global:FountAssist.HistoryErrorCount = $Error.Count | ||
Set-PSReadLineKeyHandler -Key Enter -ScriptBlock { | ||
$line = $null | ||
$cursor = $null | ||
[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) | ||
|
||
if ($Global:FountAssist.last_commaned) { | ||
$Global:FountAssist.last_commaned.output = $($ans | Out-String) -join "`n" | ||
$Global:FountAssist.last_commaned.error = $($Error | Select-Object -SkipLast $Global:FountAssist.HistoryErrorCount | Out-String) -join "`n" | ||
$Global:FountAssist.shellhistory.Add($Global:FountAssist.last_commaned) | Out-Null | ||
} | ||
|
||
$Global:FountAssist.last_commaned = @{ | ||
command = $line | ||
time = Get-Date | ||
} | ||
|
||
# keep shellhistory's last 30 records | ||
while ($Global:FountAssist.shellhistory.Count -gt 30) { | ||
$Global:FountAssist.shellhistory.RemoveAt(0) | ||
} | ||
$Global:FountAssist.rejected_commands = [System.Collections.ArrayList]@() | ||
$Global:FountAssist.HistoryErrorCount = $Error.Count | ||
|
||
if (!$Global:FountAssist.Enabled) { | ||
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() | ||
return | ||
} | ||
|
||
$parseError = $null | ||
$ast = [System.Management.Automation.Language.Parser]::ParseInput($line, [ref]$null, [ref]$parseError) | ||
|
||
$bad_expr = $false | ||
if ($parseError) { | ||
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() | ||
return | ||
} | ||
$Commands = $ast.FindAll({param($ast) $ast -is [System.Management.Automation.Language.CommandAst] }, $true) | ||
foreach ($Command in $Commands) { | ||
if (!(Get-Command $Command.CommandElements[0] -ErrorAction Ignore)) { | ||
$bad_expr = $true | ||
break | ||
} | ||
} | ||
|
||
#若当前表达式是合法ps脚本但不是合法命令 | ||
if ($bad_expr) { | ||
f | ||
$YIndexBackup = $host.UI.RawUI.CursorPosition.Y | ||
[Microsoft.PowerShell.PSConsoleReadLine]::CancelLine() | ||
Write-Host "`b`b " -NoNewline | ||
try { | ||
if ($host.UI.RawUI.CursorPosition.Y -ne $YIndexBackup) { | ||
$host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates 0, $YIndexBackup | ||
} | ||
} catch { } | ||
Write-Host | ||
} | ||
else { | ||
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() | ||
} | ||
} | ||
if (-not $Global:FountAssist.OriginalPrompt) { | ||
$Global:FountAssist.OriginalPrompt = Get-Content function:\prompt | ||
} | ||
function global:prompt { | ||
if (-not $? -and $Global:FountAssist.NonZeroReturnWhiteList -notcontains (($expr_now -split '\s')[0])) { | ||
if ($Global:FountAssist.Enabled) { | ||
try{ f } catch {} | ||
} | ||
} | ||
& $Global:FountAssist.OriginalPrompt | ||
} |
Oops, something went wrong.