Skip to content

Add PowerShell module #2543

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

ltrzesniewski
Copy link
Contributor

@ltrzesniewski ltrzesniewski commented Jan 12, 2025

This adds PowerShell support 🎉

I built this script around @lzybkr's prototype, so I added him as co-author (I hope that's ok). I wouldn't know where to start without his contribution.

I'm not a PowerShell expert, so this was a nice opportunity to learn some stuff. I think it's ok, but I would appreciate if someone more knowledgeable in the matter could review this though.

It would be nice if other PowerShell users could test this with their configs and report any issues. I wouldn't be surprised if there are some remaining bugs or missing features.

Fixes #84

Installation

If you'd like to test this, you can install the atuin from this PR by running:

cargo install --git https://github.com/ltrzesniewski/atuin.git --branch powershell-pr

Then, add the following to your PowerShell profile file (whose path is in $PROFILE) and restart the shell:

atuin init powershell | Out-String | Invoke-Expression

This requires atuin to be in the path and the PSReadLine module to be installed, which is the case by default.

Tests

I tested this on the following:

  • PowerShell 7.4.6 / PSReadLine 2.3.5 / Windows (the latest one)
  • PowerShell 7.5.1 / PSReadLine 2.3.6 / Windows (the latest one)
  • PowerShell 5.1.22621.4391 / PSReadLine 2.0.0 / Windows (the one shipped with Windows)
  • PowerShell 7.4.6 / PSReadLine 2.3.5 / Ubuntu WSL (strangely, it didn't behave exactly like the Windows version)
  • PowerShell 7.5.1 / PSReadLine 2.3.6 / Ubuntu WSL

I also tested this with and without my custom Oh My Posh prompt. It works fine in both cases, except that since my OMP config contains "newline": true, my prompt is multiline and shifts downwards by a single line on each atuin search -i invocation. This can be adjusted with the $env:ATUIN_POWERSHELL_PROMPT_OFFSET environment variable (e.g. I set mine to -1 to account for the additional prompt line).

Checks

  • I am happy for maintainers to push small adjustments to this PR, to speed up the review cycle
  • I have checked that there are no existing pull requests for the same thing

@Master-Hash
Copy link

Master-Hash commented Jan 12, 2025

I've tested it and everything works well. Now I'm a happy user. Thank you for your work!

@justunsix
Copy link

justunsix commented Jan 13, 2025

Thank you @ltrzesniewski and lzybkr, appreciate the work ❤️

Confirming Successful Test on

  • PowerShell 7.4.6 / PSReadLine 2.3.5 / Windows 11 10.0.26100 N/A Build 26100
  • PowerShell 5.1.26100.2161 / PSReadLine 2.3.6 / Windows 11 10.0.26100 N/A Build 26100

About Tests

Details on test steps and commands run at https://github.com/justunsix/rust-tests/blob/main/atuin-tests/pr2543/README.md

  • How to build from PR branch

  • Commands executed to test

  • Test platform versions

  • Also tested successfully with nushell 0.101.0 in same environment

Error not related to PR

Sharing an error found during testing in case others find it during their testing, though it is a local issue and not related to the PR itself.

The error also occurs when testing with atuin/main in nushell in the same environment, so error
is not caused by changes in the PR.

  • During testing, there was an error running atuin search -i:
Error: migration 20230531212437 was previously applied but has been modified
>>
>> Location:
>>     C:\Users\user1\usr\reference\atuin\crates\atuin-client\src\record\sqlite_store.rs:61:9
Error:: The term 'Error:' is not recognized as a name of a cmdlet, function, script file, or executable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

@ltrzesniewski
Copy link
Contributor Author

@justunsix thanks for testing and the detailed report! Sorry, I should have mentioned the database issue in the OP, I'll add a warning right now. 😬

justunsix added a commit to justunsix/rust-tests that referenced this pull request Jan 13, 2025
@ltrzesniewski
Copy link
Contributor Author

I found an issue with encoding and pushed a fix: PSConsoleReadLine prompt editing functions are now called in UTF-8 mode in order to handle Atuin search results correctly when those contain codepoints invalid in the current console encoding.

I took the opportunity to add support for an ATUIN_POWERSHELL_PROMPT_OFFSET environment variable (please tell me if you'd prefer a better name) that adds an offset to the Y position of the prompt when returning from atuin search -i, which is useful when the prompt is multi-line.

The compiler was also complaining about an elided lifetime, but not every time for some reason, so I made it explicit.

@justunsix
Copy link

Re-tested after the push and confirming successful tests on:

  • PowerShell 7.4.6 / PSReadline 2.3.5 / Ubuntu 24.04.1 LTS
  • PowerShell 7.4.6 / PSReadLine 2.3.5 / Windows 11 10.0.26100 N/A Build 26100
  • PowerShell 5.1.26100.2161 / PSReadLine 2.3.6 / Windows 11 10.0.26100 N/A Build 26100

Additions from the earlier testing:

  • Confirmed using a multiple line prompt and setting $env:ATUIN_POWERSHELL_PROMPT_OFFSET=-3 as an example with a 4 line prompt prevents extra lines being added in the terminal when using atuin search -i and making a selection. For tests, used a Starship prompt.

  • Tested more atuin commands across different sessions

  • Added testing on Ubuntu

Comments:

  • Regarding the offset, was thinking later the docs will need to be updated and inform the user of the setting.
  • So, I drafted what could be in the installation, Installing the shell plugin section summarizing what the PR can do; can be discussed later.
<TabItem label="powershell">
    Add

    ```shell
    atuin init powershell | Out-String | Invoke-Expression
    ```

    to your $PROFILE file.
    
    If you use a prompt that has more than one line, also add 
    
    ```shell
    $env:ATUIN_POWERSHELL_PROMPT_OFFSET=-X
    ```
    
    to your $PROFILE file. 
    
    Replace X with the number of lines in your prompt, but take away 1. 
    For example, if your prompt has 2 lines, the environment variable would be -1.
</TabItem>

@stark1tty
Copy link

Amazing, cannot wait! Thanks for the hard work.

@ltrzesniewski
Copy link
Contributor Author

⚠️ This stopped working after I updated PowerShell to v7.5.0 😠

Atuin is launched but nothing appears on screen. I don't see anything relevant in the changelog. I guess I'll have to take a look.

@ellie
Copy link
Member

ellie commented Feb 10, 2025

Awh man :( What part breaks?

@ltrzesniewski
Copy link
Contributor Author

I can see that Atuin is launched and even works:

image

Pressing key up N times and key down N+1 times will close it, pressing enter will execute a command, tab will insert it, etc.

The problem is that the UI doesn't show up on screen. You just see the prompt as usual. 😕

@ltrzesniewski
Copy link
Contributor Author

FWIW, it still works on Linux with pwsh 7.5.0

@ltrzesniewski
Copy link
Contributor Author

ltrzesniewski commented Feb 10, 2025

This is caused by PowerShell/PowerShell#20853 - looks like I'd have to manually copy Atuin's stdout to PowerShell's stdout through the script. Ugh. That's cursed.

I'll need to think a bit more about it and maybe find a better solution. In any case, it doesn't look like a simple change, so I'm converting this PR to draft for now. 😞

@ltrzesniewski
Copy link
Contributor Author

ltrzesniewski commented Feb 11, 2025

I worked around the issue by adding a hidden command line option which tells Atuin where to write the result instead of going through stderr. I hope that's ok for you.

We may also see if the issue I reported here gets fixed and drop the second commit if you prefer stderr.

@ltrzesniewski ltrzesniewski marked this pull request as ready for review February 11, 2025 23:19
fzakaria added a commit to fzakaria/atuin that referenced this pull request Feb 26, 2025
Pulled from atuinsh#2543
Fixes interactive mode in fish where the terminal wasn't being displayed properly.

fixes atuinsh#1289

Co-authored-by: Lucas Trzesniewski
@fzakaria fzakaria mentioned this pull request Feb 26, 2025
2 tasks
fzakaria added a commit to fzakaria/atuin that referenced this pull request Feb 27, 2025
Pulled from atuinsh#2543
Fixes interactive mode in fish where the terminal wasn't being displayed properly.

fixes atuinsh#1289

Co-authored-by: Lucas Trzesniewski <[email protected]>
fzakaria added a commit to fzakaria/atuin that referenced this pull request Feb 27, 2025
Pulled from atuinsh#2543
Fixes interactive mode in fish where the terminal wasn't being displayed properly.

fixes atuinsh#1289

Co-authored-by: Lucas Trzesniewski <[email protected]>
@ltrzesniewski
Copy link
Contributor Author

I was a bit bored so I added dotfiles support since it was missing 😅

A few notes:

  • I refactored a bit how the build() functions work in AliasStore and VarStore, since they were unnecessarily querying the same data multiple times, and I didn't want to add another pair of queries.
  • Built-in PowerShell aliases (Set-Alias) are quite useless since they don't let you add implicit arguments (you can alias git, but not git status for instance), so I used functions instead.
    See Support bash style alias's to support tab completion in command string aliases PowerShell/PowerShell#12962
  • Using functions means you can't make syntax errors in your aliases. The output of atuin init powershell won't parse if you do.
  • Environment variables should be fine as long as their names are valid.
  • The "Error: migration 20230531212437 was previously applied but has been modified" message was caused by checking-out files from git in CRLF mode, I submitted a separate PR for this: fix: sql files checksums #2601

@ellie
Copy link
Member

ellie commented Mar 3, 2025

Thank you for all that! Appreciate you keeping this up to date. I'm happy with the hidden command flag + have no strong preference for stderr.

A couple of thoughts

  1. After the breakage from upstream changes earlier, do you think this is likely to happen again? I'd like to avoid merging, announcing support, and then having upstream break things for us. Additionally, do you think this requires more testing from others, or is it good to go? I'm happy to share this PR around a bit if needs be.
  2. If this is merged, how engaged do you intend on remaining with the project? Not asking for any commitment of course, but mostly just concerned that if I merge this and something breaks, I'm not really in a place to support/fix powershell issues at the moment

Thanks again for all of your work here! <3

@BlackHoleFox
Copy link

Drive-by question as a PowerShell user not that familiar with Atuinsh's architecture:

I worked around the issue by adding a hidden command line option which tells Atuin where to write the result instead of going through stderr. I hope that's ok for you.

What gets written to this file/location and would it leak history/commands to disk?

@God-damnit-all
Copy link

@mickey1700 Run Get-Module and post what version PSReadLine is.

@ltrzesniewski
Copy link
Contributor Author

@mickey1700 some delay is expected, unfortunately, as PSReadLine polls for commands every 300ms. It shouldn't be as long as 1 second though, but it's noticeable.

@the-mentor
Copy link

If I understand correctly atuin is starting a new powershell process and maybe if your profile is large it takes longer?

Just a guess I could be totally wrong

@ltrzesniewski
Copy link
Contributor Author

@the-mentor nope, powershell runs atuin, and work continues in the same powershell process when atuin exits.

I'm pretty sure this is due to how PSReadLine polls for commands, but I didn't spend a lot of time on this. I thought maybe an issue could be opened about this in PSReadLine about shortening this delay.

@God-damnit-all
Copy link

God-damnit-all commented Jun 14, 2025

@the-mentor nope, powershell runs atuin, and work continues in the same powershell process when atuin exits.

I'm pretty sure this is due to how PSReadLine polls for commands, but I didn't spend a lot of time on this. I thought maybe an issue could be opened about this in PSReadLine about shortening this delay.

Yeah, I was going to bring that up, but an older version of the module might have other delays.

@the-mentor
Copy link

I have lots of powershell experience so when I get a chance I'll try to see if I can look the code and test it out.

@mickey1700
Copy link

@mickey1700 Run Get-Module and post what version PSReadLine is.

I'm on the latest stable version: 2.3.6. Are you using a beta version and not seeing the issue on your end?

@mickey1700 some delay is expected, unfortunately, as PSReadLine polls for commands every 300ms. It shouldn't be as long as 1 second though, but it's noticeable.

Thanks - I definitely expected some delay, similar to what can be observed with PSFzf. However, in my case, the delay with atuin feels noticeably longer.

If I understand correctly atuin is starting a new powershell process and maybe if your profile is large it takes longer?

Just a guess I could be totally wrong

Good thought! I tested with the -noprofile flag to rule that out, but unfortunately, I saw the same results.

@ltrzesniewski Let me know if there’s anything else I can try to help pinpoint the cause.

@God-damnit-all
Copy link

I'm on the latest stable version: 2.3.6. Are you using a beta version and not seeing the issue on your end?

I have a delay, I just asked because "1 second" seemed a little long to me.

@mickey1700
Copy link

I'm on the latest stable version: 2.3.6. Are you using a beta version and not seeing the issue on your end?

I have a delay, I just asked because "1 second" seemed a little long to me.

Fair enough. I measured the delay during a random invocation — it took around 800 ms to return to the shell. Sometimes it's faster, other times a bit slower.

@ltrzesniewski
Copy link
Contributor Author

I definitely expected some delay, similar to what can be observed with PSFzf. However, in my case, the delay with atuin feels noticeably longer.

This PR runs up to 4 PSReadLine commands after Atuin exits: InvokePrompt, RevertLine, Insert, AcceptLine (along with GetBufferState before running Atuin). I suppose that 300ms delay could be awaited between several of those commands.

@Vacant0mens
Copy link

Vacant0mens commented Jun 16, 2025

This is caused by PowerShell/PowerShell#20853 - looks like I'd have to manually copy Atuin's stdout to PowerShell's stdout through the script. Ugh. That's cursed.

I'll need to think a bit more about it and maybe find a better solution. In any case, it doesn't look like a simple change, so I'm converting this PR to draft for now. 😞

I've tested this on PowerShell 7.5.1 on WSL, and I found a couple of bugs (and a fix for one of them). The two I don't have fixes for are fairly generic, so I'm not sure they would show up for other platforms. I'm also not sure if any of these bugs are the actual issue that was reported to PowerShell, but it seems at least related, so I quoted the previous comment just in case.

Bug 1

I have Escape mapped to the RevertLine function. So when I'm browsing the history with UpArrow or Ctrl+r and choose not to use a line from the history by pressing Escape, it throws the error Cannot call a method on a null-valued expression, coming from this line when the user executes the RevertLine function before choosing a history item:

$suggestion = (Get-Content -Raw $resultFile).Trim()

Fix 1

I know it's not very DRY, but if the .Trim() call gets moved down to both of the Insert calls, like this:

[Microsoft.PowerShell.PSConsoleReadLine]::Insert($suggestion.Substring(17).Trim())

and:

[Microsoft.PowerShell.PSConsoleReadLine]::Insert($suggestion.Trim())

(respectively);

The same error also occurs when it's checking for the __atuin_accept__ string, so adding this line also helps:

if ($null -eq $suggestion) { $suggestion = '' } # <- convert $suggestion to empty string instead of $null
if ( $suggestion.StartsWith('__atuin_accept__:'))

or for PowerShell 7 only, you can change the line to:

if ( ${ $suggestion }?.StartsWith('__atuin_accept__:') ) # <- PS 7 syntax only

I prefer not to say "the error goes away" after these changes, but the scriptblock does handle the $null's a bit better when the user chooses not to use a line from Atuin's history.

Bug 2

Accepting the line from Atuin's history (whether with Enter or Tab), creates 9-10 lines of whitespace before drawing the prompt again, but only when the atuin search -i command causes the console to add lines to the scrolling buffer.

I don't know if this is intended or not, but it's also not a major problem, more like an interesting quirk. I'm not familiar enough with PSReadLine to know how to fix it, or if it can be fixed, and it may only be a bug on WSL, I'm not sure yet, but I can test it on my Debian machine later.

Bug 3

Pressing Enter inserts the line, but it doesn't execute ("Accept") it like I expect it to and/or like it does when using Atuin in Bash or the raw atuin search -i command in PowerShell. (e.g. Tab and Enter effectively do the same thing right now, at least for me in WSL).

Again, this is not a major problem, but it is a slight bother having to hit Enter twice.

Bug 4

PowerShell history in a Linux or Linux-based environment gets added to the same database as the other shells.
e.g. I see PowerShell commands in my Atuin history/search in Bash.

Not sure if this is fixable, but it would be nice to have the two shells handled separately, if that's possible.

@ltrzesniewski
Copy link
Contributor Author

Hello @Vacant0mens, thank you very much for testing and providing a helpful report!

However, the behavior you are experiencing seems to come from using an Atuin PowerShell module which is incompatible with the atuin executable in your path, or from an older version. I had experienced some of the behaviors you describe while developing the PR and had fixed them already, so you shouldn't see those anymore.

Could you please run atuin --version? At the moment I'm writing this message, the PR is at 84379cd and the output should be atuin 18.5.0-beta.3.

If it is something else, you can install the latest version from this page with:

cargo install --git https://github.com/ltrzesniewski/atuin.git --branch powershell-pr
atuin init powershell | Out-String | Invoke-Expression

After installing it with cargo, whereis atuin reports atuin: /home/lucas/.cargo/bin/atuin for me.

All right, let's take a closer look.

Bug 1

This is very surprising to me, as PowerShell shouldn't execute bindings while Atuin is running. Could you show me the command you used to bind Escape to RevertLine? I tried Set-PSReadLineKeyHandler -Chord Escape -Function RevertLine but couldn't reproduce the problem you describe.

I also tested the following: Set-PSReadLineKeyHandler -Chord Escape -ScriptBlock { [System.Console]::Beep() }

It beeps when I press Escape in the shell (the terminal shows a 🔔 icon in the tab at least), but not when I exit Atuin.

image

The line you mention:

$suggestion = (Get-Content -Raw $resultFile).Trim()

comes from a previous version of this PR. The current version has the following:

$suggestion = (Get-Content -Raw $resultFile -Encoding UTF8 | Out-String).Trim()

The Out-String part should fix the $null issue. In any case, this suggests you're not testing the latest version of the PR.

BTW, I can't use the PowerShell 7 syntax, as I want for this feature to work on the PowerShell 5 version which is shipped with Windows.

Bug 2

This is something which was very frustrating, and which I have fixed some time ago. Here's the behavior I get:

image

Bug 3

Pressing enter should execute the selected line. At least it works for me.

I suppose the atuin executable you're using does not recognize the ATUIN_SHELL_POWERSHELL environment variable and does not insert the __atuin_accept__: prefix.

Bug 4

This is default Atuin behavior, but you can use a separate Atuin configuration in your PowerShell session if that's the behavior you'd like to have. You can set a separate ATUIN_CONFIG_DIR environment variable in PowerShell.

@Vacant0mens
Copy link

Ah, darn. My bad. 🤦
I didn't realize I hadn't installed the right version (I had v18.6). I had installed Atuin earlier using the regular scripted-install method. I was also running the scriptblock manually using the code from the PR rather than using atuin init powershell command, which would explain older/already-fixed issues showing up.

That said, for a version of atuin that wasn't built with/for the PowerShell additions and for using an older scriptblock, it worked surprisingly well all things considered! 🎉

I went back and installed the beta version from your branch as you suggested and used the atuin init powershell command as you suggested (and as I should've earlier). Other than Bug 4 that you corrected me on (thank, by the way), I didn't experience any of the issues I mentioned previously. 👌

Good job with this PR! Can't wait for this to get merged and released! 👋

@Vacant0mens
Copy link

Vacant0mens commented Jun 23, 2025

I've also managed to install it just fine on Windows with the same methods.
I don't love that it takes up the whole terminal window when doing the "UpArrow" search, but that seems more like an atuin issue than a powershell issue.

Otherwise it works great!

I haven't had a chance to test it on a Debian machine yet (other than WSL), but I will try to sometime this week.

@ltrzesniewski
Copy link
Contributor Author

I don't love that it takes up the whole terminal window when doing the "UpArrow" search, but that seems more like an atuin issue than a powershell issue.

That may just be a different config, see if it is related to #2249

@lapo-luchini
Copy link

I am syncing my PowerShell and WSL atuin to the same server account and I can find all the lines from any PC… I want this.
What I didn't expect is that the WSL and the PowerShell show up in search as the "same HOST", even though ~/.local/share/atuin/host_id and $env:USERPROFILE/.local/share/atuin/host_id have different values.

@ltrzesniewski
Copy link
Contributor Author

ltrzesniewski commented Jul 1, 2025

@lapo-luchini This should not depend on this PR. I suppose you're either calling the same atuin executable on both environments, or have an XDG_DATA_HOME environment variable defined.

Atuin does the following: if XDG_DATA_HOME is defined, it uses it as your home directory path. Otherwise, it uses USERPROFILE on Windows and HOME on non-Windows builds (meaning it depends on the build target, not on the runtime). It then appends .local/share/atuin/host_id to the home path.

You may check this with commands such as Get-Command atuin on PowerShell, whereis atuin on Linux or where.exe atuin on Windows, or maybe atuin info or atuin doctor on both environments and compare their outputs.

@lapo-luchini
Copy link

@lapo-luchini This should not depend on this PR. I suppose you're either calling the same atuin executable on both environments, or have an XDG_DATA_HOME environment variable defined.

Mhh, no, definitely separated folders:

% atuin info
Config files:
client config: "/home/lapo/.config/atuin/config.toml"
server config: "/home/lapo/.config/atuin/server.toml"
client db path: "/home/lapo/.local/share/atuin/history.db"
key path: "/home/lapo/.local/share/atuin/key"
session path: "/home/lapo/.local/share/atuin/session"

Env Vars:
ATUIN_CONFIG_DIR = "None"

Version info:
version: 18.6.1

vs

PS C:\Users\lapo> atuin info
Config files:
client config: "C:\\Users\\lapo\\.config\\atuin\\config.toml"
server config: "C:\\Users\\lapo\\.config\\atuin\\server.toml"
client db path: "C:\\Users\\lapo\\.local\\share\\atuin\\history.db"
key path: "C:\\Users\\lapo\\.local\\share\\atuin\\key"
session path: "C:\\Users\\lapo\\.local\\share\\atuin\\session"

Env Vars:
ATUIN_CONFIG_DIR = "None"

Version info:
version: 18.5.0-beta.3

@lapo-luchini
Copy link

This might be normal tough, as I see that TABLE history has only a hostname, not a host_id column.

@lapo-luchini
Copy link

Used this a workaround:

% cat /etc/wsl.conf
[network]
hostname = hostname-wsl

@ltrzesniewski ltrzesniewski force-pushed the powershell-pr branch 2 times, most recently from ac08165 to 52beca9 Compare August 3, 2025 15:32
@ltrzesniewski
Copy link
Contributor Author

I rebased the PR on the v18.7.1 release and fixed an issue with quote characters on PS 5.1

/cc @rhinosfly @ajn142

ltrzesniewski and others added 5 commits August 4, 2025 20:20
This adds PowerShell support by invoking the following expression:
    atuin init powershell | Out-String | Invoke-Expression

Co-authored-by: Jason Shirk <[email protected]>
PowerShell handling of native command lines depends on many things
which are difficult to control, such as the OS, the shell version,
or even variables such as $PSNativeCommandArgumentPassing.
See the about_Parsing help page for more details.

Going through environment variables should make everything consistent.
@ltrzesniewski
Copy link
Contributor Author

ltrzesniewski commented Aug 4, 2025

Well, there has been a new Atuin release today, so I rebased the PR on v18.8.0, and added support for command chaining (#2834).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Powershell Support