{"id":14563823,"url":"https://github.com/bunmagic/bunmagic","last_synced_at":"2025-09-04T06:33:49.284Z","repository":{"id":219412073,"uuid":"748973169","full_name":"bunmagic/bunmagic","owner":"bunmagic","description":"🪄 Magic infused scripting with Bun","archived":false,"fork":false,"pushed_at":"2024-12-01T20:44:29.000Z","size":5097,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-01T21:32:33.575Z","etag":null,"topics":["bun","cli-tool","cli-utilities","typescript"],"latest_commit_sha":null,"homepage":"https://bunmagic.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bunmagic.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-27T07:42:50.000Z","updated_at":"2024-12-01T20:44:33.000Z","dependencies_parsed_at":"2024-09-14T20:38:30.228Z","dependency_job_id":"4660d25f-7354-4d10-a0b1-6780d700a3e0","html_url":"https://github.com/bunmagic/bunmagic","commit_stats":null,"previous_names":["buns-hell/buns","bunshell/bunshell","bunism/bunism","bunmagic/bunmagic"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bunmagic%2Fbunmagic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bunmagic%2Fbunmagic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bunmagic%2Fbunmagic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bunmagic%2Fbunmagic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bunmagic","download_url":"https://codeload.github.com/bunmagic/bunmagic/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231940608,"owners_count":18449183,"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":["bun","cli-tool","cli-utilities","typescript"],"created_at":"2024-09-07T02:05:41.349Z","updated_at":"2025-09-04T06:33:49.267Z","avatar_url":"https://github.com/bunmagic.png","language":"TypeScript","funding_links":[],"categories":["typescript"],"sub_categories":[],"readme":"# 🪄 Bun Magic\n\nCreating shell scripts has never been this easy!\n\nBun Magic simplifies shell scripting by providing a toolkit to create, use and manage scripts.\n\n**Bun Magic in 15 seconds**\nThis is how you create a `demo` command and import `cowsay` from npm, and deliver an important message via the cow.\n\n![demo](https://raw.githubusercontent.com/bunmagic/bunmagic/main/resources/assets/demo.gif)\n\n## 🧙‍♂️ Overview\n\n* **Create** new scripts with `bunmagic create \u003cscript-name\u003e`.\n* **List** all known scripts with `bunmagic list`\n* **Edit** scripts with `bunmagic \u003cscript-name\u003e`\n* **No imports needed!** All Bunmagic utilities are available as globals (e.g., `$`, `cd`, `ack`, `select`, `isDirectory`, `glob`, and more).\n* Use the **built-in utilities** for common CLI tasks without any import statements.\n* Leverage **Bun** to quickly build powerful scripts using all the features that bun provides - run shell commands, import npm packages, and more.\n\n## 🚀 Install\n\n1. Make sure you have [Bun](https://bun.sh/) installed:\n\n```\ncurl -fsSL https://bun.sh/install | bash\n```\n\n2. Install **bunmagic**\n\n```\nbunx bunmagic install\n```\n\n3. Reload terminal and run `bunmagic` for the first time. Done!\n\n### 🤖 Your first script\n\nLet's create a simple script called **lse**.\n\n```sh\nbunmagic lse\n# or\nbunmagic create lse\n```\n\nThis will create a `lse.ts` file in bunmagic source directory and link it up as a bin so that you can run it from anywhere as `lse` command.\n\nNow add in a bit script:\n\n```js\n// No imports needed! All bunmagic utilities are available globally\nconst ext = args[0];\nconst ls = await glob(`*.${ext}`);\nconsole.log(ls.join(\"\\n\"));\n```\n\nNow you can run `lse json` to list all the json files in your current directory.\n\nBunmagic is going to handle creating a binary and making sure it's executable for you.\n\n## 👾 Commands\n\n **Available commands:**\n\n| Command                      | Description                                                                 | Alias       |\n|------------------------------|-----------------------------------------------------------------------------|-------------|\n| `bunmagic create \u003cscript-name\u003e` | Create a new script.                                                        | `new`       |\n| `bunmagic edit [script-name]`   | Edit scripts. Opens all scripts and the `~/.bunmagic` directory if no name is specified. |             |\n| `bunmagic remove \u003cscript-name\u003e` | Remove and unlink a script.                                                 | `rm`        |\n| `bunmagic list`                  | List all known scripts.                                                     | `ls`        |\n| `bunmagic link`                  | Add an additional directory to use as script source.                        |             |\n| `bunmagic unlink`                | Remove a directory from the script source list.                             |             |\n| `bunmagic update`                | Update bunmagic to the latest version.                                      |             |\n| `bunmagic help`                  | Get the full list of available commands.                                    |             |\n| `bunmagic doctor`                | Check if bunmagic is set up correctly.                                      |             |\n| `bunmagic reload [--force]`      | Reload your script files and ensure that they have an executable bin.       |             |\n| `bunmagic version`               | Display the current version of bunmagic.                                    | `-v`        |\n| `bunmagic clean`                 | Remove bin files from the bin directory that don't have a matching script.  |             |\n\n\n## 📦 API\n\n### 🌍 Everything is Global!\n\n**This is a key feature of Bunmagic:** All utilities and helpers are available as global variables in your scripts. You don't need to import anything to use `$`, `args`, `flags`, `cd`, `ack`, `select`, `glob`, and all other Bunmagic utilities. They're just there, ready to use!\n\n```ts\n// No imports needed for Bunmagic utilities!\nawait $`echo \"Shell commands with $\"`;\nconst files = await glob(\"*.json\");\nconst choice = await select(\"Pick one:\", files);\ncd(\"~/projects\");\n```\n\nBun also supports dynamic [package imports](https://bun.sh/docs/runtime/modules#importing-packages) out of the box. So you can use any npm package in your scripts:\n\n```ts\nimport cowsay from 'cowsay';\nconsole.log(cowsay.say({ text: 'Hello, Bunmagic!' }));\n```\n\n\n### `$` Bun Shell\n\nBun [Shell](https://bun.sh/docs/runtime/shell) is available as `$` globally. Use this to run all your shell commands as you would in Bun.\n\n### Arguments\n\nArguments passed to the script are available in 3 global variables:\n\n* `args` - an array of arguments without flags.\n* `flags` - an object with all the flags passed to the script.\n* `argv` - a minimist-like object with all the arguments and flags.\n\nArguments are parsed by [`notMinimist`](./src/globals/not-minimist.ts) - a tiny utility that's inspired by [Minimist](https://www.npmjs.com/package/minimist), but with a smaller footprint.\n\n**Example:**\n\n```shell\n$ example one two --not=false --yes -n 10 --equals is-optional\n```\n\nProduces an object like this:\n\n```ts\nconst args = [ \"one\", \"two\" ];\nconst flags = {\n  not: \"false\",\n  yes: true,\n  n: 10,\n  equals: \"is-optional\",\n}\n\n// Minimist-like `argv` object:\nconst argv = {\n  _: args\n  ...flags\n};\n```\n\nIf you need more control over the arguments, you can use the `Bun.argv` array directly and parse the arguments using any `npm` package you like.\n\nUsing the same shell command above, the output of `Bun.argv` would be:\n\n```ts\nconsole.log(Bun.argv);\n[\n  \"/Users/user/.bun/bin/bun\", \"/Users/user/.bun/bin/bunmagic-exec\", \"/Users/user/.bunmagic/default/example.ts\",\n  \"example\", \"one\", \"two\", \"--not=false\", \"--yes\", \"-n\", \"10\", \"--equals\", \"is-optional\"\n]\n```\n\n### 🧰 Built-in Utilities\n\nAll the utilities below are available as global variables in your scripts - no imports needed!\n\n#### Ansis (an alternative to Chalk)\n\n[ansis](https://www.npmjs.com/package/ansis) - is a small library for styling the terminal output. The interface is almost exactly the same as the one in [chalk](https://www.npmjs.com/package/chalk), but it's much smaller and faster:\n\n```ts\nansis.red(\"This is red text\");\nansis.bold.red.bgGreen(\"This is bold red text on a green background\");\n```\n\n#### $HOME\n\nThe `$HOME` global variable is a shortcut for `os.homedir()`. It holds the absolute path to the current user's home directory. \n\n#### resolveTilde\n\n`resolveTilde(path: string): string`\n\nIf you need to quickly resolve a path that starts with a tilde ( `~` ), you can use the `resolveTilde` function. It replaces the tilde with the absolute path to the current user's home directory.\n\n```ts\nconst resolvedPath = resolveTilde(\"~/my-file.txt\");\nconsole.log(resolvedPath);\n// \u003e /Users/user/my-file.txt\n```\n\n\n#### cd\n\n`cd(path: string): void`\n\n`cd` is a wrapper around `$.cwd`, but it also resolves the tilde ( `~` ) to the home directory.\n\n```ts\n// All these are equivalent\n$.cwd(`${$HOME}/my-folder`)\n$.cwd(resolveTilde(`~/my-folder`))\ncd(`~/my-folder`)\n```\n\n\n#### ack\n\nA prompt to ask a Yes or No question.\n\nInterface: `ack(message: string, default: 'y' | 'n'): Promise\u003cboolean\u003e`\n\nOut of the box, Bun provides [`prompt()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt) and [`confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm) functions Web APIs.\n\n`ack` works exactly like `confirm`, but allows you to change the default value to `y` or `n`.\n\n\n#### select\n\nShow a selection prompt that allows selecting an option from a list. It's an interactive CLI menu that can be navigated using arrow keys or numbers and has simple fuzzy search built-in.\n\nInterface: `select(message: string, options: string[]): Promise\u003cstring\u003e`\n\n```ts\nconst options = [\"one\", \"two\", \"three\"];\nconst selected = await select(\"Select an option:\", options);\n```\n\n\n### 🗄️ Filesystem Utilities\n\nAll filesystem utilities are available as global functions - no imports needed!\n\nIf you need to interact with the filesystem a lot, `fs-extra` is great, but I've only found that often I need only a few key functions.\n\nThat's why I've included only a handful of functions that I've found to be the most useful.\n\n#### isDirectory\n\n`isDirectory(path: string): Promise\u003cboolean\u003e`\n\nDue to Bun's ( v1.0.26 ) current limitations in directly checking if a path is a directory, this function utilizes Node.js's `fs.stat` method to perform the check. If an error occurs during this process (e.g., if the path does not exist), the promise will resolve to `false`. Otherwise, it resolves based on whether the path points to a directory.\n\n#### ensureDirectory\n\nCreate a directory (recursively) if it doesn't exist.\n\nInterface: `ensureDirectory(path: string): Promise\u003cvoid\u003e`\n\nThis function is particularly useful when you need to make sure a directory is present before performing file operations that require the directory's existence.\n\n\n#### glob\n\n`glob(pattern: string = '*', options: GlobScanOptions = {}): Promise\u003cstring[]\u003e` ( [`GlobScanOptions`](https://github.com/oven-sh/bun/blob/49ccad9367b0a30158dbb03ff00bc9a523d43c14/packages/bun-types/bun.d.ts#L4669-L4709) )\n\nThis is a shortcut for [`new Bun.Glob()`](https://bun.sh/docs/api/glob) that allows you to quickly scan a directory and get a list of files that match a pattern. \n\n**Mind The Path**:\nBun.Glob() uses `process.cwd()` by default. If you change paths during the script, `Bun.Glob()` will always return the files relative to the original path. `glob()` will always use the current working directory.\n\n**Mind the Performance**:\n`glob()` is a great quick utility for smaller scripts, but it will scan the entire directory before resolving the promise. If large directories are involved, it's better to use `new Bun.Glob()` directly.\n\n\n## 🎨 Customization\n\n### Custom Globals\n\n\nIf there are any utilities that you'd like to reuse across your Bunmagic scripts, you can add configure them as custom globals.\n\nThis is especially useful if you're migrating over from `zx` and have a lot of scripts that used some utilities that aren't bundled with Bunmagic, like `fs-extra`, `chalk`, etc.\n\nTo define custom globals, create a file: `~/.bunmagic/custom-globals.ts`\n\nIn this file, you can export any JavaScript object, function, or variable that you wish to be available globally and it will be assigned to `globalThis` in all your scripts.\n\nFor example, to make `fs-extra` available in all your scripts by default, add this to your `custom-globals.ts`:\n\n```ts\nexport * as fs from 'fs-extra';\n```\n\n### Environment Variables\n\n#### `BUNMAGIC_STRICT`\n\nBy default, when you run a command that doesn't exist, bunmagic will offer to create it for you. Set `BUNMAGIC_STRICT=1` to disable this behavior and exit with an error instead.\n\n```sh\n# Exit with error for invalid commands instead of prompting to create\nBUNMAGIC_STRICT=1 my-command\n\n# Or export it for the entire session\nexport BUNMAGIC_STRICT=1\n```\n\nThis is particularly useful in CI/CD environments or production scripts where you want commands to fail fast if they don't exist.\n\n### Code Editor\n\n`bunmagic` is going to try to use your `EDITOR` environment variable and fall back to `code` as the editor when opening script files.\n\nIf you're using VSCode must have [VSCode CLI Tools](https://code.visualstudio.com/docs/editor/command-line) installed for files to be opened automatically in VSCode.\n\nIn your shell configuration file, set an `EDITOR` environment variable:\n\n```sh\n# In .zshrc or .bashrc\nexport EDITOR=\"/usr/bin/vim\"\n```\n \n**Note:**\nVSCode supports both of these commands and Bunmagic will use them to open either a single script or a script directory:\n\n```sh\n\u003e code /path/to/scripts/my-script.ts\n\u003e code /path/to/scripts\n```\n\nIf you want your editor to work properly, make sure it can accept both a path to a single script file and a path to a directory. \n\n\n## Credits\n\nThis project is heavily inspired by [zx](https://github.com/google/zx). It paved the way for a new generation of shell scripting, and Bun Shell made it possible to take it to the next level.\n\nBun Magic started out as [zxb](https://github.com/pyronaur/zxb) - a project I built to manage `zx` scripts. But. Bun Magic is a love child of zx, zxb and Bun.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbunmagic%2Fbunmagic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbunmagic%2Fbunmagic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbunmagic%2Fbunmagic/lists"}