Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
  • Loading branch information
steve02081504 committed Feb 12, 2025
0 parents commit 7406034
Show file tree
Hide file tree
Showing 8 changed files with 739 additions and 0 deletions.
70 changes: 70 additions & 0 deletions README.md
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 使用示例](https://github.com/user-attachments/assets/93afee48-93d4-42c7-a5e0-b7f5c93bdee9)

## 安装

使用这个命令安装 `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)
```
110 changes: 110 additions & 0 deletions assists/esh-assist.ps1
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
51 changes: 51 additions & 0 deletions assists/main.ps1
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
}
159 changes: 159 additions & 0 deletions assists/pwsh-assist.ps1
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
}
Loading

0 comments on commit 7406034

Please sign in to comment.