Repository: vincpa/z
Branch: master
Commit: 966b1210b987
Files: 11
Total size: 45.9 KB
Directory structure:
gitextract_w1njjv7x/
├── .gitattributes
├── .gitignore
├── FUNDING.yml
├── LICENSE
├── Makefile
├── README
├── README.md
├── z.1
├── z.psd1
├── z.psm1
└── z.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Set default behaviour, in case users don't have core.autocrlf set.
* text=auto
# Explicitly declare text files we want to always be normalized and converted
# to native line endings on checkout.
*.md text
*.gitattributes text
# Declare files that will always have CRLF line endings on checkout.
*.ps1 text eol=crlf
*.psm1 text eol=crlf
*.psd1 text eol=crlf
*.psc1 text eol=crlf
*.ps1xml text eol=crlf
*.clixml text eol=crlf
*.xml text eol=crlf
*.txt text eol=crlf
# Denote all files that are truly binary and should not be mergeable.
*.dll binary
*.exe binary
================================================
FILE: .gitignore
================================================
z
publish.ps1
================================================
FILE: FUNDING.yml
================================================
ko_fi: badmotorfinger
================================================
FILE: LICENSE
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
================================================
FILE: Makefile
================================================
readme:
@groff -man -Tascii z.1 | col -bx
.PHONY: readme
================================================
FILE: README
================================================
qweqwe
Z(1) User Commands Z(1)
NAME
z - jump around
SYNOPSIS
z [-chlrtx] [regex1 regex2 ... regexn]
AVAILABILITY
bash, zsh
DESCRIPTION
Tracks your most used directories, based on 'frecency'.
After a short learning phase, z will take you to the most 'frecent'
directory that matches ALL of the regexes given on the command line.
OPTIONS
-c restrict matches to subdirectories of the current directory
-h show a brief help message
-l list only
-r match by rank only
-t match by recent access only
-x remove the current directory from the datafile
EXAMPLES
z foo cd to most frecent dir matching foo
z foo bar cd to most frecent dir matching foo and bar
z -r foo cd to highest ranked dir matching foo
z -t foo cd to most recently accessed dir matching foo
z -l foo list all dirs matching foo (by frecency)
NOTES
Installation:
Put something like this in your $HOME/.bashrc or $HOME/.zshrc:
. /path/to/z.sh
cd around for a while to build up the db.
PROFIT!!
Optionally:
Set $_Z_CMD to change the command name (default z).
Set $_Z_DATA to change the datafile (default $HOME/.z).
Set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
Set $_Z_NO_PROMPT_COMMAND to handle PROMPT_COMMAND/precmd your-
self.
Set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
(These settings should go in .bashrc/.zshrc before the lines
added above.)
Install the provided man page z.1 somewhere like
/usr/local/man/man1.
Aging:
The rank of directories maintained by z undergoes aging based on a sim-
ple formula. The rank of each entry is incremented every time it is
accessed. When the sum of ranks is greater than 6000, all ranks are
multiplied by 0.99. Entries with a rank lower than 1 are forgotten.
Frecency:
Frecency is a portmanteau of 'recent' and 'frequency'. It is a weighted
rank that depends on how often and how recently something occurred. As
far as I know, Mozilla came up with the term.
To z, a directory that has low ranking but has been accessed recently
will quickly have higher rank than a directory accessed frequently a
long time ago.
Frecency is determined at runtime.
Common:
When multiple directories match all queries, and they all have a common
prefix, z will cd to the shortest matching directory, without regard to
priority. This has been in effect, if undocumented, for quite some
time, but should probably be configurable or reconsidered.
Tab Completion:
z supports tab completion. After any number of arguments, press TAB to
complete on directories that match each argument. Due to limitations of
the completion implementations, only the last argument will be com-
pleted in the shell.
Internally, z decides you've requested a completion if the last argu-
ment passed is an absolute path to an existing directory. This may
cause unexpected behavior if the last argument to z begins with /.
ENVIRONMENT
A function _z() is defined.
The contents of the variable $_Z_CMD is aliased to _z 2>&1. If not set,
$_Z_CMD defaults to z.
The environment variable $_Z_DATA can be used to control the datafile
location. If it is not defined, the location defaults to $HOME/.z.
The environment variable $_Z_NO_RESOLVE_SYMLINKS can be set to prevent
resolving of symlinks. If it is not set, symbolic links will be
resolved when added to the datafile.
In bash, z prepends a command to the PROMPT_COMMAND environment vari-
able to maintain its database. In zsh, z appends a function _z_precmd
to the precmd_functions array.
The environment variable $_Z_NO_PROMPT_COMMAND can be set if you want
to handle PROMPT_COMMAND or precmd yourself.
The environment variable $_Z_EXCLUDE_DIRS can be set to an array of
directories to exclude from tracking. $HOME is always excluded. Direc-
tories must be full paths without trailing slashes.
FILES
Data is stored in $HOME/.z. This can be overridden by setting the
$_Z_DATA environment variable. When initialized, z will raise an error
if this path is a directory, and not function correctly.
A man page (z.1) is provided.
SEE ALSO
regex(7), pushd, popd, autojump, cdargs
Please file bugs at https://github.com/rupa/z/
z January 2013 Z(1)
================================================
FILE: README.md
================================================
---
## Fuel the Codecraft
Time is finite. The system endures, but only in bursts of stolen hours. Feature requests gather like stardust—brilliant, infinite, out of reach.
Invoke /coffee to recharge the core and rekindle the forge. Your signal boosts morale. ☕⚡
[](https://ko-fi.com/C0C8K83RC)
---
## Invoke: `z`
Harness the echoes of your own movement—`z` learns as you roam.
With each `cd`, it maps your path, shaping memory into motion.
A PowerShell-native port of the legendary [*z*](https://github.com/rupa/z) from the Bash realms.
[Install it from the PowerShell Gallery](https://www.powershellgallery.com/packages/z/) and let your history guide your future.
## Origins of the Script
Since the cycle of June 2013, this utility has been forged through hours of iteration—tweaked, refined, and honed to align with PowerShell's rhythm. Born from necessity, it became a tool of mastery, accelerating my traversal through the filesystem—a realm I once wandered daily.
There are no unit tests yet (one day, perhaps), but its true purpose was never perfection. It was time. Time saved. Knowledge gained.
Its mission is simple: bypass the burden of full pathnames and leap to [frecent](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Places/Frecency_algorithm) locations with speed and grace.
Stable. Swift. Battle-tested.
May it serve you as it has served me.
![ExampleUsage]
## Examples
Once installed, begin the ritual—`cd` into a few directories. Let the system observe. Let the memory form.
`cd foo`
`cd HKLM:\software\Microsoft\Office`
`cd 'C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files'`
Based on the examples above, try executing some of these commands.
z foo cd to most frecent folder matching foo
z temp cd to most frecent folder matching `Temporary ASP.NET Files`
z foo -o r cd to highest ranked folder matching foo
z foo -o f cd to highest frecency folder matching foo
z foo -o t cd to the most recently accessed folder matching foo
z -l foo list all dirs matching regex foo
z -l list all entries
z office cd to most frecent folder matching office in drive HKLM (The registry)
z -x remove the current directory from the datafile
z -clean delete inaccessible paths from the datafile
z c:\windows go to c:\windows and log in the datafile (works with any valid path)
z c<TAB> expand entries in the datafile which match 'c'
OK, back to normal writing style.
## Limitations
Below is a list of features which have not yet been ported from the original `z` bash script...yet.
* Specifying two separate regex's and matching on both, i.e. `z foo bar`
* Does not have the ability to restrict searches to sub-directories of the current directory
## Added sugar
* Tab completion support (will not currently work if you have PowerTab installed)
* Works with registry paths such as `HKLM\Software\....` and NetBIOS paths such as `\\server\share`.
* Executing `pushd` will record the current directory for use with `z`.
## Planned Features
[See the issue listing](https://github.com/vincpa/z/issues)
## PowerShell installation
### The easy way using PowerShellGet
For those with Windows 10, you can issue a `Install-Module z -AllowClobber` command.
For those running the latest version of PowerShell `PowerShellGet\Install-Module z -Scope CurrentUser -Force -AllowClobber`
For those with Windows Vista or 7 who are using PowerShell version 3 or 4, you'll need to install [PackageManagement](http://go.microsoft.com/fwlink/?LinkID=746217&clcid=0x409) first before executing `Install-Module z -AllowClobber`.
See the module listing in the [official PowerShell gallary](https://www.powershellgallery.com/packages/z/)
Once complete, run the command `Import-Module z`. For ease of use I recomend placing this command in your [PowerShell startup profile](https://technet.microsoft.com/en-us/library/bb613488(v=vs.85).aspx). Typically `$env:USERPROFILE\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`
### The hard way
Download the `z.psm1` file and save it to your PowerShell module directory. The default location for this is `$env:USERPROFILE\Documents\WindowsPowerShell\Modules` (relative to your Documents folder). You can also extract it to another directory listed in your `$env:PSModulePath`. The full installation path should be `$env:USERPROFILE\Documents\WindowsPowerShell\Modules\z\z.psm1`.
Assuming you want `z` to be avilable in every PowerShell session, open your profile script located at `$env:USERPROFILE\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1` and add the following line.
`Import-Module z`
If the file `Microsoft.PowerShell_profile.ps1` does not exist, you can simply create it and it will be executed the next time a PowerShell session starts.
## Running z
Once the module is installed and has been imported in to your session you can start jumping around. Remember, you need to build up the DB of directories first so be sure to `cd` around your file system. You can also use `z` as an alternative to the `cd` command.
[ExampleUsage]: https://raw.githubusercontent.com/vincpa/z/master/example_usage.gif
================================================
FILE: z.1
================================================
.TH "Z" "1" "January 2013" "z" "User Commands"
.SH
NAME
z \- jump around
.SH
SYNOPSIS
z [\-chlrtx] [regex1 regex2 ... regexn]
.SH
AVAILABILITY
bash, zsh
.SH
DESCRIPTION
Tracks your most used directories, based on 'frecency'.
.P
After a short learning phase, \fBz\fR will take you to the most 'frecent'
directory that matches ALL of the regexes given on the command line.
.SH
OPTIONS
.TP
\fB\-c\fR
restrict matches to subdirectories of the current directory
.TP
\fB\-h\fR
show a brief help message
.TP
\fB\-l\fR
list only
.TP
\fB\-r\fR
match by rank only
.TP
\fB\-t\fR
match by recent access only
.TP
\fB\-x\fR
remove the current directory from the datafile
.SH EXAMPLES
.TP 14
\fBz foo\fR
cd to most frecent dir matching foo
.TP 14
\fBz foo bar\fR
cd to most frecent dir matching foo and bar
.TP 14
\fBz -r foo\fR
cd to highest ranked dir matching foo
.TP 14
\fBz -t foo\fR
cd to most recently accessed dir matching foo
.TP 14
\fBz -l foo\fR
list all dirs matching foo (by frecency)
.SH
NOTES
.SS
Installation:
.P
Put something like this in your \fB$HOME/.bashrc\fR or \fB$HOME/.zshrc\fR:
.RS
.P
\fB. /path/to/z.sh\fR
.RE
.P
\fBcd\fR around for a while to build up the db.
.P
PROFIT!!
.P
Optionally:
.RS
Set \fB$_Z_CMD\fR to change the command name (default \fBz\fR).
.RE
.RS
Set \fB$_Z_DATA\fR to change the datafile (default \fB$HOME/.z\fR).
.RE
.RS
Set \fB$_Z_NO_RESOLVE_SYMLINKS\fR to prevent symlink resolution.
.RE
.RS
Set \fB$_Z_NO_PROMPT_COMMAND\fR to handle \fBPROMPT_COMMAND/precmd\fR yourself.
.RE
.RS
Set \fB$_Z_EXCLUDE_DIRS\fR to an array of directories to exclude.
.RE
.RS
(These settings should go in .bashrc/.zshrc before the lines added above.)
.RE
.RS
Install the provided man page \fBz.1\fR somewhere like \fB/usr/local/man/man1\fR.
.RE
.SS
Aging:
The rank of directories maintained by \fBz\fR undergoes aging based on a simple
formula. The rank of each entry is incremented every time it is accessed. When
the sum of ranks is greater than 6000, all ranks are multiplied by 0.99. Entries
with a rank lower than 1 are forgotten.
.SS
Frecency:
Frecency is a portmanteau of 'recent' and 'frequency'. It is a weighted rank
that depends on how often and how recently something occurred. As far as I
know, Mozilla came up with the term.
.P
To \fBz\fR, a directory that has low ranking but has been accessed recently
will quickly have higher rank than a directory accessed frequently a long time
ago.
.P
Frecency is determined at runtime.
.SS
Common:
When multiple directories match all queries, and they all have a common prefix,
\fBz\fR will cd to the shortest matching directory, without regard to priority.
This has been in effect, if undocumented, for quite some time, but should
probably be configurable or reconsidered.
.SS
Tab Completion:
\fBz\fR supports tab completion. After any number of arguments, press TAB to
complete on directories that match each argument. Due to limitations of the
completion implementations, only the last argument will be completed in the
shell.
.P
Internally, \fBz\fR decides you've requested a completion if the last argument
passed is an absolute path to an existing directory. This may cause unexpected
behavior if the last argument to \fBz\fR begins with \fB/\fR.
.SH
ENVIRONMENT
A function \fB_z()\fR is defined.
.P
The contents of the variable \fB$_Z_CMD\fR is aliased to \fB_z 2>&1\fR. If not
set, \fB$_Z_CMD\fR defaults to \fBz\fR.
.P
The environment variable \fB$_Z_DATA\fR can be used to control the datafile
location. If it is not defined, the location defaults to \fB$HOME/.z\fR.
.P
The environment variable \fB$_Z_NO_RESOLVE_SYMLINKS\fR can be set to prevent
resolving of symlinks. If it is not set, symbolic links will be resolved when
added to the datafile.
.P
In bash, \fBz\fR prepends a command to the \fBPROMPT_COMMAND\fR environment
variable to maintain its database. In zsh, \fBz\fR appends a function
\fB_z_precmd\fR to the \fBprecmd_functions\fR array.
.P
The environment variable \fB$_Z_NO_PROMPT_COMMAND\fR can be set if you want to
handle \fBPROMPT_COMMAND\fR or \fBprecmd\fR yourself.
.P
The environment variable \fB$_Z_EXCLUDE_DIRS\fR can be set to an array of
directories to exclude from tracking. \fB$HOME\fR is always excluded.
Directories must be full paths without trailing slashes.
.SH
FILES
Data is stored in \fB$HOME/.z\fR. This can be overridden by setting the
\fB$_Z_DATA\fR environment variable. When initialized, \fBz\fR will raise an
error if this path is a directory, and not function correctly.
.P
A man page (\fBz.1\fR) is provided.
.SH
SEE ALSO
regex(7), pushd, popd, autojump, cdargs
.P
Please file bugs at https://github.com/rupa/z/
================================================
FILE: z.psd1
================================================
#
# Module manifest for module 'z'
#
# Generated by: Vince Panuccio
#
# Generated on: 24/5/2024
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'z.psm1'
#ModuleToProcess = 'z.psm1'
# Version number of this module.
ModuleVersion = '1.1.15'
# ID used to uniquely identify this module
GUID = 'bc198554-ae1f-4ab6-84ce-5d3a41b74553'
# Author of this module
Author = 'Vince Panuccio'
# Description of the functionality provided by this module
Description = 'z lets you quickly navigate the file system in PowerShell based on your cd command history. It''s a port of the z bash shell script'
# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '3.0'
FileList = 'z.psm1'
# Functions to export from this module
FunctionsToExport = @('z', 'cdX', 'popdX', 'pushdX')
# Aliases to export from this module
AliasesToExport = '*'
# Variables to export from this module
VariablesToExport = 'Z_UsePushLocation'
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
Tags = @('z', 'directory', 'popd', 'pushd', 'jump')
# A URL to the license for this module.
# LicenseUri = ''
# A URL to the main website for this project.
ProjectUri = 'https://github.com/vincpa/z'
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
ReleaseNotes = 'Add support for XDG_CACHE_HOME'
} # End of PSData hashtable
} # End of PrivateData hashtable
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''
}
================================================
FILE: z.psm1
================================================
if (![String]::IsNullOrWhiteSpace($env:XDG_CACHE_HOME)) {
$cdHistoryDir = Join-Path -Path $env:XDG_CACHE_HOME -ChildPath 'powershell-z'
if (!(Test-Path $cdHistoryDir)) {
New-Item -ItemType Directory -Path $cdHistoryDir -Force | Out-Null
}
$cdHistory = Join-Path -Path $cdHistoryDir -ChildPath 'cdHistory'
} elseif (![String]::IsNullOrWhiteSpace($env:HOME)) {
$cdHistory = Join-Path -Path $env:HOME -ChildPath '.cdHistory'
} else {
$cdHistory = Join-Path -Path $env:USERPROFILE -ChildPath '.cdHistory'
}
# The user can set $Z_UsePushLocation to $true to configure z to use Push-location, which allows to use popd/Pop-Location to go back to the previous location.
# Setting or $Z_UsePushLocation to $false will use Set-Location instead.
if ($null -eq $Z_UsePushLocation)
{
# Set default behaviour to use Push-Location
$Z_UsePushLocation = $true;
}
function z {
<#
.SYNOPSIS
Tracks your most used directories, based on 'frecency'. This is done by storing your CD command history and ranking it over time.
.DESCRIPTION
After a short learning phase, z will take you to the most 'frecent'
directory that matches the regex given on the command line.
.PARAMETER JumpPath
A un-escaped regular expression of the directory name to jump to. Character escaping will be done internally.
.PARAMETER Option
Frecency - Match by frecency (default)
Rank - Match by rank only
Time - Match by recent access only
.PARAMETER OnlyCurrentDirectory
Restrict matches to subdirectories of the current directory
.PARAMETER Listfiles
List only, don't navigate to the directory
.PARAMETER $Remove
Remove the current directory from the datafile
.PARAMETER $Clean
Clean up all history entries that cannot be resolved
.NOTES
Current PowerShell implementation is very crude and does not yet support all of the options of the original z bash script.
Although tracking of frequently used directories is obtained through the continued use of the "cd" command, the Windows registry is also scanned for frequently accessed paths.
.LINK
https://github.com/vincpa/z
.EXAMPLE
CD to the most frecent directory matching 'foo'
z foo
.EXAMPLE
CD to the most recently accessed directory matching 'foo'
z foo -o Time
#>
param(
[Parameter(Position=0)]
[string]
${JumpPath},
[ValidateSet("Time", "T", "Frecency", "F", "Rank", "R")]
[Alias('o')]
[string]
$Option = 'Frecency',
[Alias('c')]
[switch]
$OnlyCurrentDirectory = $null,
[Alias('l')]
[switch]
$ListFiles = $null,
[Alias('x')]
[switch]
$Remove = $null,
[Alias('d')]
[switch]
$Clean = $null,
[Alias('p')]
[switch]
$Push = $false
)
if (((-not $Clean) -and (-not $Remove) -and (-not $ListFiles)) -and [string]::IsNullOrWhiteSpace($JumpPath)) { Get-Help z; return; }
# If a valid path is passed in to z, treat it like the normal cd command
if (-not $ListFiles -and -not [string]::IsNullOrWhiteSpace($JumpPath) -and (Test-Path $JumpPath)) {
if ($Push -or $Z_UsePushLocation) {
pushdX $JumpPath
} else {
cdX $JumpPath
}
return;
}
if ((Test-Path $cdHistory)) {
if ($Remove) {
Save-CdCommandHistory $Remove
} elseif ($Clean) {
Cleanup-CdCommandHistory
} else {
# This causes conflicts with the -Remove parameter. Not sure whether to remove registry entry.
#$mruList = Get-MostRecentDirectoryEntries
$providerRegex = $null
If ($OnlyCurrentDirectory) {
$providerRegex = (Get-FormattedLocation).replace('\','\\')
if (-not $providerRegex.EndsWith('\\')) {
$providerRegex += '\\'
}
$providerRegex += '.*?'
} else {
$providerRegex = Get-CurrentSessionProviderDrives ((Get-PSProvider).Drives | select -ExpandProperty Name)
}
$list = @()
$global:history |
? { Get-DirectoryEntryMatchPredicate -path $_.Path -jumpPath $JumpPath -ProviderRegex $providerRegex } | Get-ArgsFilter -Option $Option |
% { if ($ListFiles -or (Test-Path $_.Path.FullName)) {$list += $_} }
if ($ListFiles) {
$newList = $list | % { New-Object PSObject -Property @{Rank = $_.Rank; Path = $_.Path.FullName; LastAccessed = [DateTime]$_.Time } }
Format-Table -InputObject $newList -AutoSize
} else {
if ($list.Length -eq 0) {
# It's not found in the history file, perhaps it's still a valid directory. Let's check.
if ((Test-Path $JumpPath)) {
if ($Push -or $Z_UsePushLocation) {
pushdX $JumpPath
} else {
cdX $JumpPath
}
} else {
Write-Host "$JumpPath Not found"
}
} else {
if ($list.Length -gt 1) {
$entry = $list | Sort-Object -Descending { $_.Score } | select -First 1
} else {
$entry = $list[0]
}
if ($Push -or $Z_UsePushLocation) {
Push-Location $entry.Path.FullName
} else {
Set-Location $entry.Path.FullName
}
Save-CdCommandHistory $Remove
}
}
}
} else {
Save-CdCommandHistory $Remove
}
}
function pushdX
{
[CmdletBinding(DefaultParameterSetName='Path', SupportsTransactions=$true, HelpUri='http://go.microsoft.com/fwlink/?LinkID=113370')]
param(
[Parameter(ParameterSetName='Path', Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string]
${Path},
[Parameter(ParameterSetName='LiteralPath', ValueFromPipelineByPropertyName=$true)]
[Alias('PSPath')]
[string]
${LiteralPath},
[switch]
${PassThru},
[Parameter(ValueFromPipelineByPropertyName=$true)]
[string]
${StackName})
begin
{
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Push-Location', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
Save-CdCommandHistory # Build up the DB.
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
}
function popdX {
[CmdletBinding(SupportsTransactions=$true, HelpUri='http://go.microsoft.com/fwlink/?LinkID=113369')]
param(
[switch]
${PassThru},
[Parameter(ValueFromPipelineByPropertyName=$true)]
[string]
${StackName})
begin
{
try {
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Management\Pop-Location', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
} catch {
throw
}
}
process
{
try {
$steppablePipeline.Process($_)
} catch {
throw
}
}
end
{
try {
$steppablePipeline.End()
} catch {
throw
}
}
<#
.ForwardHelpTargetName Microsoft.PowerShell.Management\Pop-Location
.ForwardHelpCategory Cmdlet
#>
}
# A wrapper function around the existing Set-Location Cmdlet.
function cdX
{
[CmdletBinding(DefaultParameterSetName='Path', SupportsTransactions=$true, HelpUri='http://go.microsoft.com/fwlink/?LinkID=113397')]
param(
[Parameter(ParameterSetName='Path', Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string]
${Path},
[Parameter(ParameterSetName='LiteralPath', Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
[Alias('PSPath')]
[string]
${LiteralPath},
[switch]
${PassThru},
[Parameter(ParameterSetName='Stack', ValueFromPipelineByPropertyName=$true)]
[string]
${StackName})
begin
{
$outBuffer = $null
if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
{
$PSBoundParameters['OutBuffer'] = 1
}
$PSBoundParameters['ErrorAction'] = 'Stop'
$wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Set-Location', [System.Management.Automation.CommandTypes]::Cmdlet)
$scriptCmd = {& $wrappedCmd @PSBoundParameters }
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
process
{
$steppablePipeline.Process($_)
Save-CdCommandHistory # Build up the DB.
}
end
{
$steppablePipeline.End()
}
}
function Get-DirectoryEntryMatchPredicate {
Param(
[Parameter(
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
$Path,
[Parameter(
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[string] $JumpPath,
$ProviderRegex
)
if ($Path -ne $null) {
$null = .{
$providerMatches = [System.Text.RegularExpressions.Regex]::Match($Path.FullName, $ProviderRegex).Success
}
if ($providerMatches) {
# Allows matching of entire names. Remove the first two characters, added by PowerShell when the user presses the TAB key.
if ($JumpPath.StartsWith('.\')) {
$JumpPath = $JumpPath.Substring(2).TrimEnd('\')
}
[System.Text.RegularExpressions.Regex]::Match($Path.Name, [System.Text.RegularExpressions.Regex]::Escape($JumpPath), [System.Text.RegularExpressions.RegexOptions]::IgnoreCase).Success
}
}
}
function Get-CurrentSessionProviderDrives([System.Collections.ArrayList] $ProviderDrives) {
if($IsLinux -Or $IsMacOS) {
# Always only '/' which needs escaped to work in a regex
'\/'
} elseif ($ProviderDrives -ne $null -and $ProviderDrives.Length -gt 0) {
Get-ProviderDrivesRegex $ProviderDrives
} else {
# The FileSystemProvider supports \\ and X:\ paths.
# An ideal solution would be to ask the provider if a path is supported.
# Supports drives such as C:\ and also UNC \\
if ((Get-Location).Provider.ImplementingType.Name -eq 'FileSystemProvider') {
'(?i)^(((' + [String]::Concat( ((Get-Location).Provider.Drives.Name | % { $_ + '|' }) ).TrimEnd('|') + '):\\)|(\\{1,2})).*?'
} else {
Get-ProviderDrivesRegex (Get-Location).Provider.Drives
}
}
}
function Get-ProviderDrivesRegex([System.Collections.ArrayList] $ProviderDrives) {
# UNC paths get special treatment. Allows one to 'z foo -ProviderDrives \\' and specify '\\' as the drive.
if ($ProviderDrives -contains '\\') {
$ProviderDrives.('\\')
}
if ($ProviderDrives.Count -eq 0) {
'(?i)^(\\{1,2}).*?'
} else {
$uncRootPathRegex = '|(\\{1,2})'
'(?i)^((' + [String]::Concat( ($ProviderDrives | % { $_ + '|' }) ).TrimEnd('|') + '):\\)' + $uncRootPathRegex + '.*?'
}
}
function Get-Frecency($rank, $time) {
# Last access date/time
$dx = (Get-Date).Subtract((New-Object System.DateTime -ArgumentList $time)).TotalSeconds
if( $dx -lt 3600 ) { return $rank*4 }
if( $dx -lt 86400 ) { return $rank*2 }
if( $dx -lt 604800 ) { return $rank/2 }
return $rank/4
}
function Cleanup-CdCommandHistory() {
try {
for($i = 0; $i -lt $global:history.Length; $i++) {
$line = $global:history[$i]
if ($line -ne $null) {
$testDir = $line.Path.FullName
if (-not [string]::IsNullOrWhiteSpace($testDir) -and !(Test-Path $testDir)) {
$global:history[$i] = $null
Write-Host "Removed inaccessible path $testDir" -ForegroundColor Yellow
}
}
}
Remove-Old-History
WriteHistoryToDisk
} catch {
Write-Host $_.Exception.ToString() -ForegroundColor Red
}
}
function Remove-Old-History() {
if ($global:history.Length -gt 1000) {
$global:history | ? { $_ -ne $null } | % {$i = 0} {
$lineObj = $_
$lineObj.Rank = $lineObj.Rank * 0.99
# If it's been accessed in the last 14 days it can stay
# or
# If it's rank is greater than 20 and been accessed in the last 30 days it can stay
if ($lineObj.Age -lt 1209600 -or ($lineObj.Rank -ge 5 -and $lineObj.Age -lt 2592000)) {
#$global:history[$i] = ConvertTo-DirectoryEntry (ConvertTo-TextualHistoryEntry $lineObj.Rank $lineObj.Path.FullName $lineObj.Time)
} else {
Write-Host "Removing old item: Rank:" $lineObj.Rank "Age:" ($lineObj.Age/60/60) "Path:" $lineObj.Path.FullName -ForegroundColor Yellow
$global:history[$i] = $null
}
$i++;
}
}
}
function Save-CdCommandHistory($removeCurrentDirectory = $false) {
$currentDirectory = Get-FormattedLocation
try {
$foundDirectory = $false
$runningTotal = 0
for($i = 0; $i -lt $global:history.Length; $i++) {
$line = $global:history[$i]
$canIncreaseRank = $true;
$rank = $line.Rank;
if (-not $foundDirectory) {
$rank = $line.Rank
if ($line.Path.FullName -eq $currentDirectory) {
$foundDirectory = $true
if ($removeCurrentDirectory) {
$canIncreaseRank = $false
$global:history[$i] = $null
Write-Host "Removed entry $currentDirectory" -ForegroundColor Green
} else {
$rank++
Update-HistoryEntryUsageTime $global:history[$i]
}
}
}
if ($canIncreaseRank) {
$runningTotal += $rank
}
}
if (-not $foundDirectory -and $removeCurrentDirectory) {
Write-Host "Current directory not found in CD history data file" -ForegroundColor Red
} else {
if (-not $foundDirectory) {
Save-HistoryEntry 1 $currentDirectory
$runningTotal += 1
}
Remove-Old-History
}
WriteHistoryToDisk
} catch {
Write-Host $_.Exception.ToString() -ForegroundColor Red
}
}
function WriteHistoryToDisk() {
$newList = GetAllHistoryAsText $global:history
Set-Content -Value $newList -Path $cdHistory -Encoding UTF8
}
function GetAllHistoryAsText($history) {
return $history | ? { $_ -ne $null } | % { ConvertTo-TextualHistoryEntry $_.Rank $_.Path.FullName $_.Time }
}
function Get-FormattedLocation() {
if ((Get-Location).Provider.ImplementingType.Name -eq 'FileSystemProvider' -and (Get-Location).Path.Contains('FileSystem::\\')) {
Get-Location | select -ExpandProperty ProviderPath # The registry provider does return a path which z understands. In other words, I'm too lazy.
} else {
Get-Location | select -ExpandProperty Path
}
}
function Format-Rank($rank) {
return $rank.ToString("000#.00", [System.Globalization.CultureInfo]::InvariantCulture);
}
function Save-HistoryEntry($rank, $directory) {
$entry = ConvertTo-TextualHistoryEntry $rank $directory
$global:history += ConvertTo-DirectoryEntry $entry
}
function Update-HistoryEntryUsageTime($historyEntry) {
$historyEntry.Rank++
$historyEntry.Time = (Get-Date).Ticks
}
function ConvertTo-TextualHistoryEntry($rank, $directory, $lastAccessedTicks) {
if ($lastAccessedTicks -eq $null) {
$lastAccessedTicks = (Get-Date).Ticks
}
(Format-Rank $rank) + $lastAccessedTicks + $directory
}
function ConvertTo-DirectoryEntry {
Param(
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String]$line
)
Process {
$null = .{
$pathValue = $line.Substring(25)
try {
$fileName = [System.IO.Path]::GetFileName($pathValue);
# GetFileName() does not work with registry paths
if ($fileName -eq '') {
$lastPathSeparator = $pathValue.LastIndexOf('\');
if ($lastPathSeparator -ge 0) {
$pathValue = $pathValue.TrimEnd('\');
$fileName = $pathValue.Substring( + 1);
}
}
} catch [System.ArgumentException] { }
$time = [long]::Parse($line.Substring(7, 18), [Globalization.CultureInfo]::InvariantCulture)
}
@{
Rank=GetRankFromLine $line;
Time=$time;
Path=@{ Name = $fileName; FullName = $pathValue };
Age=(Get-Date).Subtract((New-Object System.DateTime -ArgumentList $time)).TotalSeconds;
}
}
}
function GetRankFromLine([String]$line) {
$null = .{ $rankStr = $line.Substring(0, 7) }
[double]::Parse($rankStr, [Globalization.CultureInfo]::InvariantCulture)
}
function Get-MostRecentDirectoryEntries {
$mruEntries = (Get-Item -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\TypedPaths | % { $item = $_; $_.GetValueNames() | % { $item.GetValue($_) } })
$mruEntries | % { ConvertTo-TextualHistoryEntry 1 $_ }
}
function Get-ArgsFilter {
Param(
[Parameter(ValueFromPipeline=$true)]
[Hashtable]$historyEntry,
[string]
$Option = 'Frecency'
)
Process {
if ($Option -in ('Frecency', 'F')) {
$_['Score'] = (Get-Frecency $_.Rank $_.Time);
} elseif ($Option -in ('Time', 'T')) {
$_['Score'] = $_.Time;
} elseif ($Option -in ('Rank', 'R')) {
$_['Score'] = $_.Rank;
}
return $_;
}
}
<#
.ForwardHelpTargetName Set-Location
.ForwardHelpCategory Cmdlet
#>
# Get cdHistory and hydrate a in-memory collection
$global:history = @()
if ((Test-Path -Path $cdHistory)) {
$global:history += Get-Content -Path $cdHistory -Encoding UTF8 | ? { (-not [String]::IsNullOrWhiteSpace($_)) } | ConvertTo-DirectoryEntry
}
$orig_cd = (Get-Alias -Name 'cd').Definition
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
set-item alias:cd -value $orig_cd
}
#Override the existing CD command with the wrapper in order to log 'cd' commands.
Set-item alias:cd -Value 'cdX'
Set-Alias -Name pushd -Value pushdX -Force -Option AllScope -Scope Global
Set-Alias -Name popd -Value popdX -Force -Option AllScope -Scope Global
Export-ModuleMember -Function z, cdX, pushdX, popdX -Alias cd, pushd
# Tab Completion
$completion_RunningService = {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
$global:history | Sort-Object { $_.Rank } -Descending | Where-Object { $_.Path.Name -like "*$wordToComplete*" } |
ForEach-Object { New-Object System.Management.Automation.CompletionResult ("'{0}'" -f $_.Path.FullName), $_.Path.FullName, 'ParameterName', ('{0} ({1})' -f $_.Path.Name, $_.Path.FullName) }
}
if (Get-Command -ErrorAction Ignore -Name 'Register-ArgumentCompleter') {
Register-ArgumentCompleter -CommandName 'z' -ParameterName 'JumpPath' -ScriptBlock $completion_RunningService
} else {
if (-not $global:options) { $global:options = @{CustomArgumentCompleters = @{};NativeArgumentCompleters = @{}}}
$global:options['CustomArgumentCompleters']['z:JumpPath'] = $Completion_RunningService
$function:tabexpansion2 = $function:tabexpansion2 -replace 'End(\r\n|\n){','End { if ($null -ne $options) { $options += $global:options} else {$options = $global:options}'
}
================================================
FILE: z.sh
================================================
# Copyright (c) 2009 rupa deadwyler under the WTFPL license
# maintains a jump-list of the directories you actually use
#
# INSTALL:
# * put something like this in your .bashrc/.zshrc:
# . /path/to/z.sh
# * cd around for a while to build up the db
# * PROFIT!!
# * optionally:
# set $_Z_CMD in .bashrc/.zshrc to change the command (default z).
# set $_Z_DATA in .bashrc/.zshrc to change the datafile (default ~/.z).
# set $_Z_NO_RESOLVE_SYMLINKS to prevent symlink resolution.
# set $_Z_NO_PROMPT_COMMAND if you're handling PROMPT_COMMAND yourself.
# set $_Z_EXCLUDE_DIRS to an array of directories to exclude.
#
# USE:
# * z foo # cd to most frecent dir matching foo
# * z foo bar # cd to most frecent dir matching foo and bar
# * z -r foo # cd to highest ranked dir matching foo
# * z -t foo # cd to most recently accessed dir matching foo
# * z -l foo # list matches instead of cd
# * z -c foo # restrict matches to subdirs of $PWD
[ -d "${_Z_DATA:-$HOME/.z}" ] && {
echo "ERROR: z.sh's datafile (${_Z_DATA:-$HOME/.z}) is a directory."
}
_z() {
local datafile="${_Z_DATA:-$HOME/.z}"
# bail out if we don't own ~/.z (we're another user but our ENV is still set)
[ -f "$datafile" -a ! -O "$datafile" ] && return
# add entries
if [ "$1" = "--add" ]; then
shift
# $HOME isn't worth matching
[ "$*" = "$HOME" ] && return
# don't track excluded dirs
local exclude
for exclude in "${_Z_EXCLUDE_DIRS[@]}"; do
[ "$*" = "$exclude" ] && return
done
# maintain the file
local tempfile
tempfile="$datafile.$RANDOM"
while read line; do
[ -d "${line%%\|*}" ] && echo $line
done < "$datafile" | awk -v path="$*" -v now="$(date +%s)" -F"|" '
BEGIN {
rank[path] = 1
time[path] = now
}
$2 >= 1 {
if( $1 == path ) {
rank[$1] = $2 + 1
time[$1] = now
} else {
rank[$1] = $2
time[$1] = $3
}
count += $2
}
END {
if( count > 6000 ) {
for( i in rank ) print i "|" 0.99*rank[i] "|" time[i] # aging
} else for( i in rank ) print i "|" rank[i] "|" time[i]
}
' 2>/dev/null >| "$tempfile"
if [ $? -ne 0 -a -f "$datafile" ]; then
env rm -f "$tempfile"
else
env mv -f "$tempfile" "$datafile" || env rm -f "$tmpfile"
fi
# tab completion
elif [ "$1" = "--complete" ]; then
while read line; do
[ -d "${line%%\|*}" ] && echo $line
done < "$datafile" | awk -v q="$2" -F"|" '
BEGIN {
if( q == tolower(q) ) nocase = 1
split(substr(q,3),fnd," ")
}
{
if( nocase ) {
for( i in fnd ) tolower($1) !~ tolower(fnd[i]) && $1 = ""
} else {
for( i in fnd ) $1 !~ fnd[i] && $1 = ""
}
if( $1 ) print $1
}
' 2>/dev/null
else
# list/go
while [ "$1" ]; do case "$1" in
--) while [ "$1" ]; do shift; local fnd="$fnd $1";done;;
-*) local opt=${1:1}; while [ "$opt" ]; do case ${opt:0:1} in
c) local fnd="^$PWD $fnd";;
h) echo "${_Z_CMD:-z} [-chlrtx] args" >&2; return;;
x) sed -i "\:^${PWD}|.*:d" "$datafile";;
l) local list=1;;
r) local typ="rank";;
t) local typ="recent";;
esac; opt=${opt:1}; done;;
*) local fnd="$fnd $1";;
esac; local last=$1; shift; done
[ "$fnd" -a "$fnd" != "^$PWD " ] || local list=1
# if we hit enter on a completion just go there
case "$last" in
# completions will always start with /
/*) [ -z "$list" -a -d "$last" ] && cd "$last" && return;;
esac
# no file yet
[ -f "$datafile" ] || return
local cd
cd="$(while read line; do
[ -d "${line%%\|*}" ] && echo $line
done < "$datafile" | awk -v t="$(date +%s)" -v list="$list" -v typ="$typ" -v q="$fnd" -F"|" '
function frecent(rank, time) {
dx = t-time
if( dx < 3600 ) return rank*4
if( dx < 86400 ) return rank*2
if( dx < 604800 ) return rank/2
return rank/4
}
function output(files, toopen, override) {
if( list ) {
cmd = "sort -n >&2"
for( i in files ) if( files[i] ) printf "%-10s %s\n", files[i], i | cmd
if( override ) printf "%-10s %s\n", "common:", override > "/dev/stderr"
} else {
if( override ) toopen = override
print toopen
}
}
function common(matches) {
# shortest match
for( i in matches ) {
if( matches[i] && (!short || length(i) < length(short)) ) short = i
}
if( short == "/" ) return
# shortest match must be common to each match. escape special characters in
# a copy when testing, so we can return the original.
clean_short = short
gsub(/[\(\)\[\]\|]/, "\\\\&", clean_short)
for( i in matches ) if( matches[i] && i !~ clean_short ) return
return short
}
BEGIN { split(q, a, " "); oldf = noldf = -9999999999 }
{
if( typ == "rank" ) {
f = $2
} else if( typ == "recent" ) {
f = $3-t
} else f = frecent($2, $3)
wcase[$1] = nocase[$1] = f
for( i in a ) {
if( $1 !~ a[i] ) delete wcase[$1]
if( tolower($1) !~ tolower(a[i]) ) delete nocase[$1]
}
if( wcase[$1] && wcase[$1] > oldf ) {
cx = $1
oldf = wcase[$1]
} else if( nocase[$1] && nocase[$1] > noldf ) {
ncx = $1
noldf = nocase[$1]
}
}
END {
if( cx ) {
output(wcase, cx, common(wcase))
} else if( ncx ) output(nocase, ncx, common(nocase))
}
')"
[ $? -gt 0 ] && return
[ "$cd" ] && cd "$cd"
fi
}
alias ${_Z_CMD:-z}='_z 2>&1'
[ "$_Z_NO_RESOLVE_SYMLINKS" ] || _Z_RESOLVE_SYMLINKS="-P"
if compctl &> /dev/null; then
[ "$_Z_NO_PROMPT_COMMAND" ] || {
# zsh populate directory list, avoid clobbering any other precmds
if [ "$_Z_NO_RESOLVE_SYMLINKS" ]; then
_z_precmd() {
_z --add "${PWD:a}"
}
else
_z_precmd() {
_z --add "${PWD:A}"
}
fi
precmd_functions+=(_z_precmd)
}
# zsh tab completion
_z_zsh_tab_completion() {
local compl
read -l compl
reply=(${(f)"$(_z --complete "$compl")"})
}
compctl -U -K _z_zsh_tab_completion _z
elif complete &> /dev/null; then
# bash tab completion
complete -o filenames -C '_z --complete "$COMP_LINE"' ${_Z_CMD:-z}
[ "$_Z_NO_PROMPT_COMMAND" ] || {
# bash populate directory list. avoid clobbering other PROMPT_COMMANDs.
grep -q "_z --add" <<< "$PROMPT_COMMAND" || {
PROMPT_COMMAND='_z --add "$(pwd '$_Z_RESOLVE_SYMLINKS' 2>/dev/null)" 2>/dev/null;'"$PROMPT_COMMAND"
}
}
fi
gitextract_w1njjv7x/ ├── .gitattributes ├── .gitignore ├── FUNDING.yml ├── LICENSE ├── Makefile ├── README ├── README.md ├── z.1 ├── z.psd1 ├── z.psm1 └── z.sh
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (50K chars).
[
{
"path": ".gitattributes",
"chars": 598,
"preview": "# Set default behaviour, in case users don't have core.autocrlf set.\n* text=auto\n\n# Explicitly declare text files we wan"
},
{
"path": ".gitignore",
"chars": 14,
"preview": "z\npublish.ps1\n"
},
{
"path": "FUNDING.yml",
"chars": 23,
"preview": "ko_fi: badmotorfinger\n\n"
},
{
"path": "LICENSE",
"chars": 1211,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "Makefile",
"chars": 59,
"preview": "readme:\n\t@groff -man -Tascii z.1 | col -bx\n\n.PHONY: readme\n"
},
{
"path": "README",
"chars": 4992,
"preview": "qweqwe\nZ(1) User Commands Z(1)\n\n\n\nNAME\n z - jump around\n\nSY"
},
{
"path": "README.md",
"chars": 5235,
"preview": "---\n\n## Fuel the Codecraft\n\nTime is finite. The system endures, but only in bursts of stolen hours. Feature requests gat"
},
{
"path": "z.1",
"chars": 4629,
"preview": ".TH \"Z\" \"1\" \"January 2013\" \"z\" \"User Commands\"\n.SH\nNAME\nz \\- jump around\n.SH\nSYNOPSIS\nz [\\-chlrtx] [regex1 regex2 ... re"
},
{
"path": "z.psd1",
"chars": 1987,
"preview": "#\r\n# Module manifest for module 'z'\r\n#\r\n# Generated by: Vince Panuccio\r\n#\r\n# Generated on: 24/5/2024\r\n#\r\n\r\n@{\r\n\r\n# Scri"
},
{
"path": "z.psm1",
"chars": 21894,
"preview": "if (![String]::IsNullOrWhiteSpace($env:XDG_CACHE_HOME)) {\r\n $cdHistoryDir = Join-Path -Path $env:XDG_CACHE_HOME -Chi"
},
{
"path": "z.sh",
"chars": 6332,
"preview": "# Copyright (c) 2009 rupa deadwyler under the WTFPL license\n\n# maintains a jump-list of the directories you actually use"
}
]
About this extraction
This page contains the full source code of the vincpa/z GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (45.9 KB), approximately 12.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.