{"id":13586265,"url":"https://github.com/deanishe/alfred-fuzzy","last_synced_at":"2025-08-25T12:23:42.639Z","repository":{"id":50290733,"uuid":"102959965","full_name":"deanishe/alfred-fuzzy","owner":"deanishe","description":"Fuzzy search helper for Alfred 3+ workflows","archived":false,"fork":false,"pushed_at":"2019-12-12T21:01:02.000Z","size":3334,"stargazers_count":95,"open_issues_count":3,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-02T22:33:11.383Z","etag":null,"topics":["alfred","alfred-3","alfred-workflow","alfred3","alfred4","alfred4-workflow","fuzzy","fuzzy-search"],"latest_commit_sha":null,"homepage":"","language":"Python","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/deanishe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-09T14:18:07.000Z","updated_at":"2025-03-31T05:51:37.000Z","dependencies_parsed_at":"2022-08-25T14:31:56.427Z","dependency_job_id":null,"html_url":"https://github.com/deanishe/alfred-fuzzy","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deanishe%2Falfred-fuzzy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deanishe%2Falfred-fuzzy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deanishe%2Falfred-fuzzy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deanishe%2Falfred-fuzzy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deanishe","download_url":"https://codeload.github.com/deanishe/alfred-fuzzy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250539327,"owners_count":21447290,"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":["alfred","alfred-3","alfred-workflow","alfred3","alfred4","alfred4-workflow","fuzzy","fuzzy-search"],"created_at":"2024-08-01T15:05:26.198Z","updated_at":"2025-04-24T00:30:59.671Z","avatar_url":"https://github.com/deanishe.png","language":"Python","funding_links":[],"categories":["Python","Helpers"],"sub_categories":[],"readme":"\nFuzzy search for Alfred\n=======================\n\n`fuzzy.py` is a helper script for Alfred 3+ Script Filters that replaces the \"Alfred filters results\" option with fuzzy search (Alfred uses \"word starts with\").\n\n![](./demo.gif \"\")\n\n\u003c!-- MarkdownTOC autolink=\"true\" bracket=\"round\" depth=\"3\" autoanchor=\"true\" --\u003e\n\n- [How it works](#how-it-works)\n- [Example usage](#example-usage)\n- [Demo](#demo)\n- [Caveats](#caveats)\n    - [Performance](#performance)\n    - [Utility](#utility)\n- [Technical details](#technical-details)\n- [Customisation](#customisation)\n    - [Multiple Script Filters](#multiple-script-filters)\n- [Thanks](#thanks)\n\n\u003c!-- /MarkdownTOC --\u003e\n\n\u003ca name=\"how-it-works\"\u003e\u003c/a\u003e\nHow it works\n------------\n\nInstead of calling your script directly, you call it via `fuzzy.py`, which caches your script's output for the duration of the user session (as long as the user is using your workflow), and filters the items emitted by your script against the user's query using a fuzzy algorithm.\n\nThe query is compared to each item's `match` field if it's present, and against the item's `title` field if not.\n\n\n\u003ca name=\"example-usage\"\u003e\u003c/a\u003e\nExample usage\n-------------\n\n`fuzzy.py` only works in Script Filters, and you should run it as a bash/zsh script (i.e. with `Language = /bin/bash` or `Language = /bin/zsh`).\n\nInstead of running your own script directly, place `./fuzzy.py` in front of it.\n\nFor example, if your Script Filter script looks like this:\n\n```bash\n/usr/bin/python myscript.py\n```\n\nYou would replace it with:\n\n```bash\n# Export user query to `query` environment variable, so `fuzzy.py` can read it\nexport query=\"$1\"\n# Or if you're using \"with input as {query}\"\n# export query=\"{query}\"\n\n# call your original script via `fuzzy.py`\n./fuzzy.py /usr/bin/python myscript.py\n```\n\n**Note**: Don't forget to turn off \"Alfred filters results\"!\n\n\n\u003ca name=\"demo\"\u003e\u003c/a\u003e\nDemo\n----\n\nGrab the [Fuzzy-Demo.alfredworkflow][demo] file from this repo to try out the search and view an example implementation.\n\n\n\u003ca name=\"caveats\"\u003e\u003c/a\u003e\nCaveats\n-------\n\nFuzzy search, and this implementation in particular, are by no means the \"search algorithm to end all algorithms\".\n\n\n\u003ca name=\"performance\"\u003e\u003c/a\u003e\n### Performance ###\n\nBy dint of being written in Python and using a more complex algorithm, `fuzzy.py` can only comfortably handle a small fraction of the number of results that Alfred's native search can. On my 2012 MBA, it becomes noticeably, but not annoyingly, sluggish at about ~2500 items.\n\nIf the script is well-received, I'll reimplement it in a compiled language. My [Go library for Alfred workflows][awgo] uses the same algorithm, and can comfortably handle 20K+ items.\n\n\n\u003ca name=\"utility\"\u003e\u003c/a\u003e\n### Utility ###\n\nFuzzy search is awesome for some datasets, but fairly sucks for others. It can work very, very well when you only want to search one field, such as name/title or filename/filepath, but it tends to provide sub-optimal results when searching across multiple fields, especially keywords/tags.\n\nIn such cases, you'll usually get better results from a word-based search.\n\n\n\u003ca name=\"technical-details\"\u003e\u003c/a\u003e\nTechnical details\n-----------------\n\nThe fuzzy algorithm is taken from [this gist][pyversion] by [@menzenski][menzenski], which is based on Forrest Smith's [reverse engineering of Sublime Text's algorithm][forrest].\n\nThe only addition is smarter handling of non-ASCII. If the user's query contains only ASCII, the search is diacritic-insensitive. If the query contains non-ASCII, the search considers diacritics.\n\n\n\u003ca name=\"customisation\"\u003e\u003c/a\u003e\nCustomisation\n-------------\n\nYou can tweak the algorithm by altering the bonuses and penalties applied, or changing the characters treated as separators.\n\nExport different values for the following environment variables before calling `fuzzy.py` to configure the fuzzy algorithm:\n\n|       Variable      |  Default  |                  Description                  |\n|---------------------|-----------|-----------------------------------------------|\n| `adj_bonus`         | 5         | Bonus for adjacent matches                    |\n| `camel_bonus`       | 10        | Bonus if match is uppercase                   |\n| `sep_bonus`         | 10        | Bonus if after a separator                    |\n| `unmatched_penalty` | -1        | Penalty for each unmatched character          |\n| `lead_penalty`      | -3        | Penalty for each character before first match |\n| `max_lead_penalty`  | -9        | Maximum total `lead_penalty`                  |\n| `separators`        | `_-.([/ ` | Characters to consider separators (for the purposes of assigning `sep_bonus`)                                              |\n\n\n\u003ca name=\"multiple-script-filters\"\u003e\u003c/a\u003e\n### Multiple Script Filters ###\n\nIf you're using multiple Script Filters chained together that use different datasets, you'll need to set the `session_var` environment variable to ensure each one uses a separate cache:\n\n```bash\n# Script Filter 1\nexport query=\"$1\"\n./fuzzy /usr/bin/python myscript.py\n\n# Script Filter 2 (downstream of 1)\nexport query=\"$1\"\nexport session_var=\"fuzzy_filter2\"\n./fuzzy /usr/bin/python myotherscript.py\n```\n\n\u003ca name=\"thanks\"\u003e\u003c/a\u003e\nThanks\n------\n\nThe fuzzy matching code was (mostly) written by [@menzenski][menzenski] and the algorithm was designed by [@forrestthewoods][forrestthewoods].\n\n\n[awgo]: https://github.com/deanishe/awgo\n[demo]: ./Fuzzy-Demo-0.2.alfredworkflow\n[forrest]: https://blog.forrestthewoods.com/reverse-engineering-sublime-text-s-fuzzy-match-4cffeed33fdb\n[forrestthewoods]: https://github.com/forrestthewoods\n[menzenski]: https://github.com/menzenski\n[pyversion]: https://gist.github.com/menzenski/f0f846a254d269bd567e2160485f4b89\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeanishe%2Falfred-fuzzy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeanishe%2Falfred-fuzzy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeanishe%2Falfred-fuzzy/lists"}