{"id":16841108,"url":"https://github.com/camdencheek/fre","last_synced_at":"2025-03-17T04:33:42.127Z","repository":{"id":37864694,"uuid":"146933450","full_name":"camdencheek/fre","owner":"camdencheek","description":"Command line frecency tracking","archived":false,"fork":false,"pushed_at":"2023-12-29T00:24:03.000Z","size":177,"stargazers_count":134,"open_issues_count":2,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-16T08:23:27.991Z","etag":null,"topics":["autojump","command-line","command-line-tool","frecency","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/camdencheek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-31T19:15:20.000Z","updated_at":"2025-03-07T11:40:38.000Z","dependencies_parsed_at":"2023-12-29T00:25:29.271Z","dependency_job_id":"8bc0e01b-0863-42b1-9b34-8b13f7effe6f","html_url":"https://github.com/camdencheek/fre","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camdencheek%2Ffre","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camdencheek%2Ffre/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camdencheek%2Ffre/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/camdencheek%2Ffre/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/camdencheek","download_url":"https://codeload.github.com/camdencheek/fre/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243975225,"owners_count":20377542,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["autojump","command-line","command-line-tool","frecency","rust"],"created_at":"2024-10-13T12:40:17.240Z","updated_at":"2025-03-17T04:33:41.838Z","avatar_url":"https://github.com/camdencheek.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# FREcency tracking (`fre`)\n\n`fre` is a CLI tool for tracking your most-used directories and files. \nThough inspired by tools like `autojump` or the `z` plugin for `zsh`, it takes a slightly \ndifferent approach to tracking and providing usage data. \nThe primary difference is `fre` does not support jumping. Instead, \nit just keeps track of and provides sorting methods for directories, \nwhich can then be filtered by another application like `fzf`, \nwhich does a much better job of filtering than something I can write.\nAdditionally, it uses an algorithm in which the weights of each directory\ndecay exponentially, so more recently used directories are ranked more highly\nin a smooth manner.\n\n\n## Usage\n\n`fre` is primarily designed to interface with `fzf`. For general usage, \na user will create a shell hook that adds a directory every time the current \ndirectory is changed. This will start to build your profile of most-used directories. \nThen, `fre` can be used as a source for `fzf`. I personally use the `fzf`-provided \ncontrol-T bindings, modified to use `fre` as input. Some examples are below.\n\nBasic usage\n```sh\n# Print directories, sorted by frecency, then pipe to fzf\nfre --sorted | fzf --no-sort\n\n# Print directories and their associated frecency, sorted by frecency\nfre --stat\n\n# Log a visit to a directory\nfre --add /home/user/new_dir\n\n# Decrease weight of a directory by 10 visits\nfre --decrease 10 /home/user/too_high_dir\n\n# Print directories and the time since they were last visited in hours\nfre --stat --sort_method recent\n\n# Print directories and the number of times they've been visited\nfre --stat --sort_method frequent\n\n# Purge directories that no longer exist\nfre --sorted | while read dir ; do if [ ! -d \"$dir\" ] ; then fre --delete \"$dir\";  fi ; done\n```\n\n## Installation\n\nFrom source: `git clone https://github.com/camdencheek/fre.git \u0026\u0026 cargo install --path ./fre`\n\nFrom crate: `cargo install fre`\n\nArch linux: `yay -S fre`\n\nmacOS: `brew install camdencheek/brew/fre`\n\nFor integration with `fzf` CTRL-T, define the following environment variables \n```zsh\nexport FZF_CTRL_T_COMMAND='command fre --sorted'\nexport FZF_CTRL_T_OPTS='--tiebreak=index'\n```\n\nTo preferentially use results from `fre`, but fall back to other results, we can use \n`cat` to combine results before sending them to `fzf`. My favorite alternate source \nis `fd` ([link](https://github.com/sharkdp/fd)), but the more common `find` can also be \nused. The following options first use `fre` results, then use all the subdirectories \nof the current directory, then use every subdirectory in your home directory. \nThis is what I personally use.\n\n```zsh\nexport FZF_CTRL_T_COMMAND='command cat \u003c(fre --sorted) \u003c(fd -t d) \u003c(fd -t d . ~)'\nexport FZF_CTRL_T_OPTS='--tiebreak=index'\n```\n\n### Shell integration\n\nDon't see your shell here? feel free to open a PR to add it!\n\n#### zsh\n(credit to `autojump`)\n\n```zsh\nfre_chpwd() {\n  fre --add \"$(pwd)\"\n}\ntypeset -gaU chpwd_functions\nchpwd_functions+=fre_chpwd\n```\n\n#### bash\n(credit to `autojump`)\n\nIn your `~/.profile`:\n\n```zsh\nPROMPT_COMMAND=\"${PROMPT_COMMAND:+$(echo \"${PROMPT_COMMAND}\" | awk '{gsub(/; *$/,\"\")}2') ; }\"'fre --add \"$(pwd)\"'\n```\n\n## Comparison to existing solutions\n\nThe three projects I'm familiar with that are closest in function to this are `autojump`, the `z` shell plugin, and the `d` portion (and maybe the `f` in the future) of `fasd`. \n\nThe primary difference from the rest of these is its reliance on a tool like `fzf` to provide any solid directory jumping functionality. This was an intentional choice, sticking to the Unix philosophy of \"do one thing, and do it well\". \n\nThe other major change from these pieces of software is the algorithm used to rank directories.  `autojump` uses the following formula:\n\n```python\n\ndef add_path(data, path, weight=10):\n    # ...\n    data[path] = sqrt((data.get(path, 0) ** 2) + (weight ** 2))\n    # ...\n```\n\nLooking at it closely, it seems to just be calculating the hypotenuse of a triangle where one side is the length of the previous weight and the other is the length of the weight being added. This does not take into account time passed since access at all, which is not ideal since I would rather not have directories from years ago ranked highly.\n\n`fasd` and `z` both use the same frecency function that looks something like this:\n\n```zsh\nfunction frecent(rank, time) {\n    dx = t-time\n    if( dx \u003c 3600 ) return rank*4\n    if( dx \u003c 86400 ) return rank*2\n    if( dx \u003c 604800 ) return rank/2\n    return rank/4\n}\n```\n\nThis works fine until you re-visit an old directory. Then, suddenly, `dx` is small again and all the old visits are re-weighted to `rank*4`, causing it to jump up in the sorted output. This is not really ideal. I want to be able to re-visit an old directory once without messing up my directory ranking. \n\n`fre` uses a frecency algorithm where the weight of a directory visit decays over time. Given a list of visit times (bold x), the frecency of the directory would look something like this (using lambda as the half life and \"now\" as the current time at calculation):\n\n\u003ca href=\"https://user-images.githubusercontent.com/12631702/48453749-a1bbbc00-e782-11e8-9c4e-4c367db02794.png\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/12631702/48453749-a1bbbc00-e782-11e8-9c4e-4c367db02794.png\" align=\"center\" height=\"100\" width=\"450\" \u003e\u003c/a\u003e\n\nWith a little bit of mathemagics, we don't actually have to store the vector of access times. We can compress everything down into one number as long as we're okay not being able to dynamically change the half life. \n\nThis algorithm provides a much more intuitive implementation of frecency that tends to come up with results that more closely match those we would naturally expect.\n\n## Support\n\nI use this regularly on MacOS and Linux. I wrote it to be usable on Windows as well, \nbut I don't run any tests for it. Caveat emptor.\n\n\n## Stability\n\nI've been using this for over a year with no changes now, and it does everything I need it to do. I'm happy to add features or accept changes if this is not the case for you.\n\n## About the algorithm\n\nThe algorithm used combines the concepts of frequency and recency into a single, sortable statistic called \"frecency\".\nTo my knowledge, this term was first coined by Mozilla to describe their URL suggestions algorithm. \nIn fact, Mozilla already came up with nearly this exact algorithm and \n[considered using it to replace Firefox's frecency algorithm](https://wiki.mozilla.org/User:Jesse/NewFrecency?title=User:Jesse/NewFrecency).\nThe algorithm is also very similar to the cache replacement problem, and a more formal treatment of the\nmath behind it can be found in this [IEEE article](https://ieeexplore.ieee.org/document/970573) (sorry for the paywall).\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcamdencheek%2Ffre","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcamdencheek%2Ffre","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcamdencheek%2Ffre/lists"}