{"owner":"jetify-com","repo":"devbox","hasSkills":true,"totalSkillsCount":9,"totalTokensCount":6188,"categories":["anthropic-skill","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/release-devbox/SKILL.md",".agents/skills/use-devbox/SKILL.md","examples/plugins/local/README.md","examples/plugins/local/my-plugin/plugin.json","examples/plugins/v2-local/plugin1/plugin.json","examples/plugins/v2-local/plugin1/plugin1a/plugin.json","examples/plugins/v2-local/plugin2/plugin.json","examples/plugins/v2-local/plugin3/plugin.json","plugins/README.md"],"skills":{".agents/skills/release-devbox/SKILL.md":"---\nname: release-devbox\ndescription: Cut a Devbox CLI release — recommend the version bump, write the release notes, and drive the draft or publish flow. Use when asked to release devbox, cut a version, ship 0.x.y, publish a release, or when a release is stuck partway through and needs diagnosing or resuming.\n---\n\n# Release the Devbox CLI\n\n`scripts/release.ts` owns every mechanical step and the order they run in. Your\njob is the judgment: read what's shipping, recommend a version, write the title\nand notes, and help the user pick how to release.\n\nTwo entry points, both of which walk the whole pipeline:\n\n| Command | What it does |\n| --- | --- |\n| `devbox run draft-release` | Builds the release, leaves it as a draft for review |\n| `devbox run publish-release` | Builds and takes it live, **or** finishes an existing draft |\n\nRead-only helpers: `devbox run release-changes` and `devbox run release-status`.\n\n## 1. Look at what would ship\n\n```bash\ndevbox run release-changes\n```\n\nThis prints the commits since the last release grouped by kind (breaking /\nfeatures / fixes / other), the recommended version, and whether `flake.nix`\nneeds bumping. Read it before saying anything about the release.\n\nThe recommendation follows devbox's own history: it's pre-1.0, so **breaking\nchanges take a minor bump** (0.17.5 → 0.18.0) and **everything else takes a\npatch bump** — 0.17.4 shipped new features as a patch. Say what the script\nrecommends and why, but sanity-check it against the actual diff. A release that\nremoves whole subsystems deserves a minor bump even if nobody wrote `feat!:`.\n\n## 2. Ask how they want to release\n\nPresent the three options and let the user choose — don't assume:\n\n1. **Draft first** (`devbox run draft-release`) — builds everything and stops at\n   a draft. Right when the notes need review, or when the release is being\n   coordinated with an announcement. This is the safe default.\n2. **Publish directly** (`devbox run publish-release`) — same pipeline, then\n   takes it live. Right for routine patch releases.\n3. **Publish an existing draft** (`devbox run publish-release`) — if a draft is\n   already sitting there, the command lists it and offers to finish it. Check\n   with `devbox run release-status` first.\n\n## 3. Write the title and notes\n\nThe script prompts for these in `$EDITOR` when a human drives it. When you're\ndriving, write them yourself and pass them as flags — that's the point of doing\nthis through an agent.\n\nGet the raw material from GitHub's generated changelog:\n\n```bash\ngh api repos/jetify-com/devbox/releases/generate-notes \\\n  -f tag_name=<version> -f previous_tag_name=<prev> -f target_commitish=main --jq '.body'\n```\n\nThat's a dump of PR titles. Rewrite it into user-facing notes in the house style\nthat 0.17.4 established and 0.18.0 is the cleanest example of:\n\n````markdown\n## What's Changed\n\n### 💥 Breaking Changes\n\n* **What was removed** — what users must do instead, by @author ([#1234](https://github.com/jetify-com/devbox/pull/1234))\n\n### ✨ New Features\n\n* **Short bold lead-in** — what changed and why a user cares, by @author ([#1234](...))\n\n### 🐛 Bug Fixes\n\n* Plain one-liners for small fixes, by @author ([#1234](...)).\n* **Group related fixes** — combine several PRs into one bullet when they share\n  a root cause ([#1234](...)) and ([#1235](...)), by @author.\n\n### 🧹 Maintenance\n\n* Dependency bumps, CI work, docs. Group aggressively; nobody reads this section\n  line by line.\n\n## New Contributors\n* @newperson made their first contribution in https://github.com/jetify-com/devbox/pull/1234\n\n**Full Changelog**: https://github.com/jetify-com/devbox/compare/<prev>...<version>\n````\n\nRules that matter:\n\n- **Lead with impact, not the commit subject.** \"Shells in paths with spaces now\n  work\" beats \"quote the shellrc source guard\".\n- **Breaking changes go first and say what to do instead.** For a release that\n  removes features, this is the whole story — say plainly what's gone and what\n  replaces it, or that nothing does.\n- **Only include sections with content.** Drop the ones that are empty.\n- **Keep attribution.** Every bullet ends with `by @author` and its PR link.\n- **Use plain backticks for code.** Do *not* escape them as `` \\` ``. Nothing\n  re-interprets the body — goreleaser's Discord announcer is `enabled: false` in\n  `.goreleaser.yaml` — and Markdown renders `` \\` `` as a literal backtick\n  rather than opening a code span. That's why 0.17.4's published notes are full\n  of stray backslashes. The script unescapes any that slip through, but don't\n  write them.\n\nFor the title, default to the bare version (`0.18.0`). A short theme suffix is\nfine when the release has one: `0.18.0 — Devbox goes fully local`.\n\n**Show the user the title and full notes and get explicit sign-off before\nrunning anything.** That's the last checkpoint before it goes live.\n\n## 4. Run it\n\nSave the notes to `.context/release-notes-<version>.md`, then:\n\n```bash\nnode scripts/release.ts --draft \\\n  --version 0.18.0 \\\n  --title \"0.18.0\" \\\n  --notes-file .context/release-notes-0.18.0.md \\\n  --yes\n```\n\nSwap `--draft` for `--publish` to go live. Notes on driving it as an agent:\n\n- **`--yes` is required when you run it.** Its confirmation prompts need a TTY,\n  and your shell doesn't have one — without `--yes` it stops with \"this step\n  needs an answer but stdin is not a terminal\". Only pass it after the user has\n  signed off; it's skipping *their* checkpoint, not adding one.\n- **It blocks for ~15 minutes** waiting on `cli-release`. Run it in the\n  background and check back, rather than letting the call time out.\n- **It's resumable.** Every step is idempotent, so if it fails partway, fix the\n  cause and re-run the same command — it picks up where it left off rather than\n  duplicating work.\n- **`--skip-cli-tests` exists but is a loaded gun.** It only skips the *check*\n  that the latest `cli-tests` run on `main` isn't red; `cli-release` still runs\n  the whole suite and fails the build if it isn't green. A run that's merely\n  queued or in progress (the normal state right after the flake bump merges)\n  doesn't block — only a finished, failed run does. Use the flag when that\n  check is wrong (a flake you've confirmed), not to push past a genuinely red\n  `main`.\n\nIf `flake.nix` needs bumping, the script offers to do the whole thing: it\nrewrites `lastTag`, refreshes `vendor-hash` and `flake.lock`, commits to\n`bump-flake-<version>`, pushes, opens the PR, and puts you back on `main` with a\nclean tree. Then it stops (exit 0 — this is a planned pause, not a failure)\nand prints the exact command to continue with, e.g.\n`devbox run publish-release --version 0.18.2`. That bump has to be reviewed and\nmerged before the tag is pushed, otherwise the tagged commit ships the wrong\nversion string. Get it merged and run the printed command; `--version` skips\nthe version prompt and preflight pulls the new `main` and carries on. A re-run\nwhile the PR is still open stops immediately with its URL rather than opening\na second one.\n\nPreflight is picky about the checkout on purpose, since the tag lands on\nwhatever `HEAD` is. A `main` that's merely behind `origin/main` and clean\nfast-forwards itself; anything else (wrong branch, dirty tree, local-only\ncommits) stops with the command that fixes it.\n\n## Why the order is what it is\n\nDon't work around these; they're why the script exists.\n\n- **Draft before tag.** goreleaser runs with `release.disable` and only builds\n  `dist/`; `cli-release` uploads that to the release for the tag, looking it up\n  by tag with `gh`. With no draft it creates one from GitHub's generated notes —\n  usable, but not the notes you wrote. (goreleaser used to do the upload itself,\n  but it matched drafts by *title*, so any release with a real title got a\n  silent duplicate draft with bare-commit-SHA notes. That's what shipped as the\n  0.17.5 release notes, and what stalled 0.18.0.)\n- **Publish after the build.** `docker-image-release` fires on the release event\n  and immediately downloads the release tarballs to bake into the image.\n  Publishing before `cli-release` uploads them fails the Docker build. That's\n  exactly what happened on 0.17.3 and 0.17.5, both published from the GitHub UI,\n  which creates the tag and publishes in one action.\n- **Flake bump before the `cli-tests` check.** The bump has to merge into `main`,\n  which re-runs `cli-tests` there — so a result read before the bump is about a\n  commit that won't be released. Settling the bump first also means a red `main`\n  doesn't hide the fact that a bump PR is needed.\n- **Publish from local credentials, not CI.** GitHub doesn't trigger workflows\n  from events raised by `GITHUB_TOKEN`. That's why CI-created edge releases never\n  trigger `docker-image-release`, and why publishing can't just be a CI step.\n\n## If something is stuck\n\n`devbox run release-status` reports the tag, draft, asset count, `cli-release`\nresult and the current `flake.nix` version in one shot.\n\n| Symptom | Cause | Fix |\n| --- | --- | --- |\n| `cli-release` never started | Tag push didn't land | Re-run the same command |\n| `cli-release` failed in `tests` | Red `main` (see below) | Fix the test, re-run |\n| Draft has 0 assets | The upload step didn't run or failed | Check `cli-release`'s \"Attach artifacts\" step, re-run |\n| Published but installer serves the old version | `cli-post-release` failed or is still running | `gh run list --workflow=cli-post-release.yml` |\n| Docker build failed | Published before assets uploaded | Re-run `docker-image-release` via `workflow_dispatch` with the tag |\n\n## Known breakage\n\nCheck these are still true before blaming the release itself:\n\n- **`main` was red from 2026-07-02 until #2951.** The macOS `zig-hello-world`\n  example test failed because `build.zig` used the pre-Zig-0.12 API while\n  `devbox.lock` pinned zig 0.11.0; the upgrade to zig 0.16 fixed it. A red\n  `main` blocks *all* releases — `cli-release` gates on the test suite — so\n  check the current state rather than assuming either way.\n- **`flake.nix` drifts.** It sat at `0.17.3` through both the 0.17.4 and 0.17.5\n  releases. The script catches this now and opens the bump PR for you.\n",".agents/skills/use-devbox/SKILL.md":"---\nname: use-devbox\ndescription: Run commands inside a devbox environment when a directory provides one. If a `devbox.json` exists in the current directory (or an ancestor/subdir root), devbox can supply tools and scripts the bare shell lacks — e.g. psql, python, gcloud, node. If a binary is provided by devbox, prefer it over the system binary.\n---\n\n# Use devbox\n\n`devbox` applies to directories that have a `devbox.json` (at the directory root or a subdir root). Outside such directories, devbox does nothing useful.\n\n[devbox](https://github.com/jetify-com/devbox) creates isolated, reproducible development environments from a `devbox.json` file. The packages it lists come from the Nix package registry and are made available *inside* the devbox environment — not the bare shell.\n\n## When to reach for it\n\n- The directory has a `devbox.json`.\n- Before concluding a tool is unavailable, check for a `devbox.json` — the project may install that tool through devbox.\n- The user explicitly mentions devbox.\n\n## Check it's installed\n\ndevbox is not always installed. Check first:\n\n```bash\ncommand -v devbox\n```\n\nIf that prints nothing, install it (non-interactive):\n\n```bash\ncurl -fsSL https://get.jetify.com/devbox | bash\n```\n\ndevbox installs to `~/.local/bin` (or `/usr/local/bin`). If `devbox` still isn't found after installing, ensure that directory is on `PATH`. devbox itself requires the Nix package manager; the install script sets it up if missing.\n\n## How to detect\n\nLook for a `devbox.json` in the working directory or an ancestor:\n\n```bash\nls devbox.json 2>/dev/null || find . -maxdepth 2 -name devbox.json 2>/dev/null\n```\n\nIf one exists, the packages it lists are available *inside* the devbox environment (not the bare shell).\n\n## How to run\n\nRun any command inside the devbox environment with:\n\n```bash\ndevbox run [cmd]\n```\n\nExamples:\n\n```bash\ndevbox run psql \"$MASTER_DB_URL\" -c 'SHOW wal_level;'\ndevbox run python script.py\ndevbox run gcloud sql instances describe <instance>\n```\n\n`devbox run` is the right choice for agents and scripts: it loads the environment, runs one command non-interactively, and exits. The first run in a fresh environment may be slow while packages download/build; later runs are cached and fast.\n\nIf a directory other than the cwd holds the `devbox.json`, run from that directory (e.g. `devbox run --config path/to/devbox.json [cmd]` or `cd` into it first).\n\n## Project scripts\n\n`devbox.json` can define named scripts under a `\"shell\": { \"scripts\": { ... } }` block. Run them by name — `devbox run <script-name>` — instead of retyping the underlying command. For example, this repo defines `test`, `lint`, `fmt`, `build`, and `tidy`, so `devbox run test` runs the project's test suite inside the environment.\n\nTo see what's defined, run `devbox run` with no script name (it lists available scripts) or inspect `devbox.json`.\n\n## Choosing where a binary comes from\n\nWhen you need a binary, decide its source in this order:\n\n1. **Provided by devbox for the current project** — if the project's `devbox.json` supplies it, prefer the devbox version over any system install. Run it via `devbox run [cmd]`.\n2. **Provided by the system but not devbox** — use the system binary.\n3. **Not provided by devbox or the system** — check whether devbox can supply it:\n\n   ```bash\n   devbox search [pkg]\n   ```\n\n   If it's available, **ask the user** whether they want to install it into the current project before doing so. Only on their confirmation:\n\n   ```bash\n   devbox add [pkg]\n   ```\n\n   Don't add packages to a project's `devbox.json` without the user's go-ahead. (`devbox rm [pkg]` removes one.)\n\n## Services\n\nSome projects define long-running services (databases, queues, etc.) in their devbox config:\n\n```bash\ndevbox services ls          # list defined services\ndevbox services up           # start services (add -b to run in background)\ndevbox services stop         # stop them\n```\n\n## Global packages\n\n`devbox global` manages a machine-wide environment that's independent of any project (`devbox global add/rm/list`). Prefer per-project packages unless the user explicitly wants something available everywhere.\n\n## Discovering more\n\n- `devbox --help` — full command list (shell, run, add, services, etc.).\n- `devbox run` without args, or inspect `devbox.json`, to see project-defined scripts.\n- `devbox shell` opens an interactive subshell with the environment loaded (prefer `devbox run` for one-off, non-interactive commands).\n- To see what binaries devbox is adding, run `devbox install` and then inspect the `.devbox/nix/profile/default/bin/` directory.\n\n## Key facts\n\n- devbox is not always installed — check with `command -v devbox` and install via `curl -fsSL https://get.jetify.com/devbox | bash` if missing.\n- Only directories with a `devbox.json` use it.\n- `devbox.json` may live in the repo root **or** a subdir root — check both.\n- Packages/scripts defined in `devbox.json` are only on PATH inside `devbox run`/`devbox shell`, not the plain shell.\n- Prefer `devbox run` over `devbox shell` for non-interactive, one-off commands.\n- The first run after adding packages or in a fresh checkout can be slow (downloading/building); subsequent runs are cached.\n","examples/plugins/local/README.md":"# Custom plugin example\n\nShows how to write custom local plugin. Plugins can:\n\n* Install packages\n* Create templatized files (including flakes)\n* Declare services (using process-compose)\n","examples/plugins/local/my-plugin/plugin.json":"{\n  \"name\": \"my-plugin\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Example custom plugin\",\n  \"env\": {\n    \"MY_FOO_VAR\": \"BAR\"\n  },\n  \"create_files\": {\n    /*\nthis is a comment inside the create files\n    */\n    \"{{ .Virtenv }}/empty-dir\": \"\",\n    \"{{ .Virtenv }}/some-file\": \"some-file.txt\",\n    \"{{ .DevboxDir }}/some-file.txt\": \"some-file.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"shell\": {\n      // this is a comment before init-hooks\n      \"init_hook\": [\n          \"echo \\\"ran local plugin init hook\\\"\",\n          \"export MY_INIT_HOOK_VAR=BAR\"\n      ]\n  },\n  // Aliases defined by the plugin. \"greet\" is overridden by the parent\n  // devbox.json, while \"plugin_only\" is provided solely by this plugin.\n  \"aliases\": {\n      \"greet\": \"echo greeting-from-plugin\",\n      \"plugin_only\": \"echo from-plugin\"\n  }\n}\n","examples/plugins/v2-local/plugin1/plugin.json":"{\n  \"name\": \"plugin1\",\n  \"packages\": [\"hello@latest\"],\n  \"env\": {\n    \"PLUGIN1_ENV\": \"success\",\n    \"PLUGIN1_ENV2\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"create_files\": {\n    \"{{ .DevboxDir }}/foo.txt\": \"foo.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"include\": [\n    \"./plugin1a\"\n  ]\n}\n","examples/plugins/v2-local/plugin1/plugin1a/plugin.json":"{\n  \"name\": \"plugin1a\",\n  \"packages\": [\"cowsay@latest\"],\n  \"env\": {\n    \"PLUGIN1A_ENV\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1A_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1A_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"include\": [\n    \"../../plugin2\"\n  ]\n}\n","examples/plugins/v2-local/plugin2/plugin.json":"{\n  \"name\": \"plugin2\",\n  \"create_files\": {\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  }\n}\n","examples/plugins/v2-local/plugin3/plugin.json":"{\n  \"name\": \"plugin3\"\n}\n","plugins/README.md":"# Contributing a Plugin\n\nPlugins make it easier to get started with packages that require additional setup when installed with Nix, and they offer a familiar interface for configuring packages. They also help keep all of your project's configuration within your project directory, which helps maintain portability and isolation.\n\n## Getting Started\n\nBefore writing a plugin, we recommend reading the [User Documentation](https://www.jetify.com/docs/devbox/guides/plugins/) on plugins, as well as inspecting and testing a few of the plugins in this directory. Note that the plugins in this directory are compiled into the Devbox binary, but your plugin can be sourced from a local directory or from within your project.\n\nIf you're looking for plugin ideas, check out our [Issues page](https://github.com/jetify-com/devbox/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+request%22) for any user requests.\n\nBefore contributing, please consult our [Contributing Guide](../CONTRIBUTING.md) and [Code of Conduct](../CODE_OF_CONDUCT.md) for details on how to contribute to Devbox.\n\n### Testing your Plugin\n\n1. Create a new `devbox.json` in an empty directory using `devbox init`.\n2. Add your plugin to the `include` section of the `devbox.json` file. Add any expected packages using `devbox add <pkg>`.\n3. Check that your plugin creates the correct files and environment variables when running `devbox shell`\n4. If you are looking for sample projects to test your plugin with, check out our [examples](https://github.com/jetify-com/devbox/tree/main/examples).\n\n## Plugin Design\n\nPlugins are defined as Go JSON Template files, using the following schema:\n\n```json\n{\n  \"name\": \"\",\n  \"version\": \"\",\n  \"description\": \"\",\n  \"env\": {\n    \"<key>\": \"<value>\"\n  },\n  \"create_files\": {\n    \"<destination>\": \"<source>\"\n  },\n  \"init_hook\": [\n    \"<bash commands>\"\n  ]\n}\n```\n\nA plugin can define services by adding a `process-compose.yaml` file in its `create_files` stanza.\n\n### Plugin Lifecycle\n\nPlugins are activated whenever a developer runs `devbox shell`, runs a script with `devbox run`, or starts a service using `devbox services start|restart`. The lifecycle of a devbox shell with plugins works as follows:\n\n```mermaid\n---\ntitle: Devbox Shell Lifecycle\n---\nflowchart TD\n   A[Plugin env] --> B\n   B[User env] --> C\n   C[Plugin init_hook] --> D[User Init Hook]\n   D -->  E{Start Shell}\n   E --> F & G & H\n   F[Interactive Shell]\n   G[Run Scripts]\n   H[Start Services]\n```\n\n### Template Placeholders\n\nDevbox's Plugin System provides a few special placeholders that should be used when specifying paths for env variables and helper files:\n\n* `{{ .DevboxProjectDir }}` – points to the root folder of the user's project, where their `devbox.json` is stored.\n* `{{ .DevboxDirRoot }}` – points to `<projectDir>/devbox.d`, the shared parent directory of every plugin's `DevboxDir`. (Despite its name, this is *not* the project root — use `{{ .DevboxProjectDir }}` for that.)\n* `{{ .DevboxDir }}` – points to `<projectDir>/devbox.d/<plugin.name>`. This directory is public and added to source control by default. This directory is not modified or recreated by Devbox after the initial package installation. You should use this location for files that a user will want to modify and check-in to source control alongside their project (e.g., `.conf` files or other configs).\n* `{{ .Virtenv }}` – points to `<projectDir>/.devbox/virtenv/<plugin_name>` whenever the plugin activates. This directory is hidden and added to `.gitignore` by default You should use this location for files or variables that a user should not check-in or edit directly. Files in this directory should be considered managed by Devbox, and may be recreated or modified after the initial installation.\n\n### Fields\n\n#### `name` *string*\n\nThe name of your plugin. This is used to identify your plugin when a user runs `devbox info`. If `match` is not set, the plugin will automatically activate when a package is added to a devbox.json project that matches `name`.\n\n#### `version` *string*\n\nThe version of your plugin. You should start your version at 0.0.1 and bump it whenever you merge an update to the plugin.\n\n#### `match` *string*\n\nA regex expression that is used to identify when the plugin will be activated. Devbox will activate your plugin when a package installed with `devbox add` matches this regular expression.\n\nThe regex you provide should match a package name. You can look up packages at `nixhub.io`\n\n#### `readme` *string*\n\nSpecial usage instructions or notes to display when your plugin activates or when a user runs `devbox info`. You do not need to document variables, helper files, or services, since these are automatically printed when a user runs `devbox info`.\n\n#### `env` *object*\n\nA map of `\"key\" : \"value\"` pairs used to set environment variables in `devbox shell` when the plugin is activated. These variables will be printed when a user runs `devbox info`, and can be overridden by a user's `devbox.json`.\n\n#### `create_files` *object*\n\nA map of `\"destination\":\"source\"` pairs that can be used to create or copy files into the user's devbox directory when the plugin is activated. For example:\n\n```json\n\"create_files\": {\n    \"{{ .DevboxDir }}/Caddyfile\": \"caddy/Caddyfile\"\n}\n```\n\nWill copy the Caddyfile in the `plugins/caddy` folder to `devbox.d/caddy/Caddyfile` in the user's project directory.\n\nYou should use this to copy starter config files or templates needed to run the plugin's package.\n\n#### `init_hook` *string | string[]*\n\nA single `bash` command or list of `bash` commands that should run before the user's shell is initialized. This will run every time a shell is started, so you should avoid any resource heavy or long running processes in this step.\n\n### Adding Services\n\nDevbox uses [Process Compose](https://github.com/F1bonacc1/process-compose) to run services and background processes.\n\nPlugins can add services to a user's project by adding a `process-compose.yaml` file to the `create_files` stanza. This file will be automatically detected by Devbox, and started when a user runs `devbox services up` or `devbox services start`.\n\nSee the process compose [docs](https://github.com/F1bonacc1/process-compose) for details on how to write define services in `process-compose.yaml`. You can also check the plugins in this directory for examples on how to write services.\n\n## Tips for Writing Plugins\n\n* Only add plugins for packages that require configuration to work with Devbox.\n* Plugins should try to use the same configuration conventions (environment variables, configuration files) as their packages. This lets developers configure their packages in a way that they are familiar with, using existing documentation.\n* If you think a user may want to override or change a parameter, define it as an environment variable in `env`. This makes it possible for a developer to override the parameter in their `devbox.json` file\n* If you're adding a helper file that you think a developer would want check into source control, create it in `{{ .DevboxDir }}`. If you're creating a file that would not be checked into source control, create it in `{{ .Virtenv }}`.\n* Unless there is a very good reason, we do not recommend creating files outside of `{{ .DevboxDir }}` or `{{ .Virtenv }}`. This helps keep user projects clean and well organized.\n"},"files":{".agents/skills/release-devbox/SKILL.md":"---\nname: release-devbox\ndescription: Cut a Devbox CLI release — recommend the version bump, write the release notes, and drive the draft or publish flow. Use when asked to release devbox, cut a version, ship 0.x.y, publish a release, or when a release is stuck partway through and needs diagnosing or resuming.\n---\n\n# Release the Devbox CLI\n\n`scripts/release.ts` owns every mechanical step and the order they run in. Your\njob is the judgment: read what's shipping, recommend a version, write the title\nand notes, and help the user pick how to release.\n\nTwo entry points, both of which walk the whole pipeline:\n\n| Command | What it does |\n| --- | --- |\n| `devbox run draft-release` | Builds the release, leaves it as a draft for review |\n| `devbox run publish-release` | Builds and takes it live, **or** finishes an existing draft |\n\nRead-only helpers: `devbox run release-changes` and `devbox run release-status`.\n\n## 1. Look at what would ship\n\n```bash\ndevbox run release-changes\n```\n\nThis prints the commits since the last release grouped by kind (breaking /\nfeatures / fixes / other), the recommended version, and whether `flake.nix`\nneeds bumping. Read it before saying anything about the release.\n\nThe recommendation follows devbox's own history: it's pre-1.0, so **breaking\nchanges take a minor bump** (0.17.5 → 0.18.0) and **everything else takes a\npatch bump** — 0.17.4 shipped new features as a patch. Say what the script\nrecommends and why, but sanity-check it against the actual diff. A release that\nremoves whole subsystems deserves a minor bump even if nobody wrote `feat!:`.\n\n## 2. Ask how they want to release\n\nPresent the three options and let the user choose — don't assume:\n\n1. **Draft first** (`devbox run draft-release`) — builds everything and stops at\n   a draft. Right when the notes need review, or when the release is being\n   coordinated with an announcement. This is the safe default.\n2. **Publish directly** (`devbox run publish-release`) — same pipeline, then\n   takes it live. Right for routine patch releases.\n3. **Publish an existing draft** (`devbox run publish-release`) — if a draft is\n   already sitting there, the command lists it and offers to finish it. Check\n   with `devbox run release-status` first.\n\n## 3. Write the title and notes\n\nThe script prompts for these in `$EDITOR` when a human drives it. When you're\ndriving, write them yourself and pass them as flags — that's the point of doing\nthis through an agent.\n\nGet the raw material from GitHub's generated changelog:\n\n```bash\ngh api repos/jetify-com/devbox/releases/generate-notes \\\n  -f tag_name=<version> -f previous_tag_name=<prev> -f target_commitish=main --jq '.body'\n```\n\nThat's a dump of PR titles. Rewrite it into user-facing notes in the house style\nthat 0.17.4 established and 0.18.0 is the cleanest example of:\n\n````markdown\n## What's Changed\n\n### 💥 Breaking Changes\n\n* **What was removed** — what users must do instead, by @author ([#1234](https://github.com/jetify-com/devbox/pull/1234))\n\n### ✨ New Features\n\n* **Short bold lead-in** — what changed and why a user cares, by @author ([#1234](...))\n\n### 🐛 Bug Fixes\n\n* Plain one-liners for small fixes, by @author ([#1234](...)).\n* **Group related fixes** — combine several PRs into one bullet when they share\n  a root cause ([#1234](...)) and ([#1235](...)), by @author.\n\n### 🧹 Maintenance\n\n* Dependency bumps, CI work, docs. Group aggressively; nobody reads this section\n  line by line.\n\n## New Contributors\n* @newperson made their first contribution in https://github.com/jetify-com/devbox/pull/1234\n\n**Full Changelog**: https://github.com/jetify-com/devbox/compare/<prev>...<version>\n````\n\nRules that matter:\n\n- **Lead with impact, not the commit subject.** \"Shells in paths with spaces now\n  work\" beats \"quote the shellrc source guard\".\n- **Breaking changes go first and say what to do instead.** For a release that\n  removes features, this is the whole story — say plainly what's gone and what\n  replaces it, or that nothing does.\n- **Only include sections with content.** Drop the ones that are empty.\n- **Keep attribution.** Every bullet ends with `by @author` and its PR link.\n- **Use plain backticks for code.** Do *not* escape them as `` \\` ``. Nothing\n  re-interprets the body — goreleaser's Discord announcer is `enabled: false` in\n  `.goreleaser.yaml` — and Markdown renders `` \\` `` as a literal backtick\n  rather than opening a code span. That's why 0.17.4's published notes are full\n  of stray backslashes. The script unescapes any that slip through, but don't\n  write them.\n\nFor the title, default to the bare version (`0.18.0`). A short theme suffix is\nfine when the release has one: `0.18.0 — Devbox goes fully local`.\n\n**Show the user the title and full notes and get explicit sign-off before\nrunning anything.** That's the last checkpoint before it goes live.\n\n## 4. Run it\n\nSave the notes to `.context/release-notes-<version>.md`, then:\n\n```bash\nnode scripts/release.ts --draft \\\n  --version 0.18.0 \\\n  --title \"0.18.0\" \\\n  --notes-file .context/release-notes-0.18.0.md \\\n  --yes\n```\n\nSwap `--draft` for `--publish` to go live. Notes on driving it as an agent:\n\n- **`--yes` is required when you run it.** Its confirmation prompts need a TTY,\n  and your shell doesn't have one — without `--yes` it stops with \"this step\n  needs an answer but stdin is not a terminal\". Only pass it after the user has\n  signed off; it's skipping *their* checkpoint, not adding one.\n- **It blocks for ~15 minutes** waiting on `cli-release`. Run it in the\n  background and check back, rather than letting the call time out.\n- **It's resumable.** Every step is idempotent, so if it fails partway, fix the\n  cause and re-run the same command — it picks up where it left off rather than\n  duplicating work.\n- **`--skip-cli-tests` exists but is a loaded gun.** It only skips the *check*\n  that the latest `cli-tests` run on `main` isn't red; `cli-release` still runs\n  the whole suite and fails the build if it isn't green. A run that's merely\n  queued or in progress (the normal state right after the flake bump merges)\n  doesn't block — only a finished, failed run does. Use the flag when that\n  check is wrong (a flake you've confirmed), not to push past a genuinely red\n  `main`.\n\nIf `flake.nix` needs bumping, the script offers to do the whole thing: it\nrewrites `lastTag`, refreshes `vendor-hash` and `flake.lock`, commits to\n`bump-flake-<version>`, pushes, opens the PR, and puts you back on `main` with a\nclean tree. Then it stops (exit 0 — this is a planned pause, not a failure)\nand prints the exact command to continue with, e.g.\n`devbox run publish-release --version 0.18.2`. That bump has to be reviewed and\nmerged before the tag is pushed, otherwise the tagged commit ships the wrong\nversion string. Get it merged and run the printed command; `--version` skips\nthe version prompt and preflight pulls the new `main` and carries on. A re-run\nwhile the PR is still open stops immediately with its URL rather than opening\na second one.\n\nPreflight is picky about the checkout on purpose, since the tag lands on\nwhatever `HEAD` is. A `main` that's merely behind `origin/main` and clean\nfast-forwards itself; anything else (wrong branch, dirty tree, local-only\ncommits) stops with the command that fixes it.\n\n## Why the order is what it is\n\nDon't work around these; they're why the script exists.\n\n- **Draft before tag.** goreleaser runs with `release.disable` and only builds\n  `dist/`; `cli-release` uploads that to the release for the tag, looking it up\n  by tag with `gh`. With no draft it creates one from GitHub's generated notes —\n  usable, but not the notes you wrote. (goreleaser used to do the upload itself,\n  but it matched drafts by *title*, so any release with a real title got a\n  silent duplicate draft with bare-commit-SHA notes. That's what shipped as the\n  0.17.5 release notes, and what stalled 0.18.0.)\n- **Publish after the build.** `docker-image-release` fires on the release event\n  and immediately downloads the release tarballs to bake into the image.\n  Publishing before `cli-release` uploads them fails the Docker build. That's\n  exactly what happened on 0.17.3 and 0.17.5, both published from the GitHub UI,\n  which creates the tag and publishes in one action.\n- **Flake bump before the `cli-tests` check.** The bump has to merge into `main`,\n  which re-runs `cli-tests` there — so a result read before the bump is about a\n  commit that won't be released. Settling the bump first also means a red `main`\n  doesn't hide the fact that a bump PR is needed.\n- **Publish from local credentials, not CI.** GitHub doesn't trigger workflows\n  from events raised by `GITHUB_TOKEN`. That's why CI-created edge releases never\n  trigger `docker-image-release`, and why publishing can't just be a CI step.\n\n## If something is stuck\n\n`devbox run release-status` reports the tag, draft, asset count, `cli-release`\nresult and the current `flake.nix` version in one shot.\n\n| Symptom | Cause | Fix |\n| --- | --- | --- |\n| `cli-release` never started | Tag push didn't land | Re-run the same command |\n| `cli-release` failed in `tests` | Red `main` (see below) | Fix the test, re-run |\n| Draft has 0 assets | The upload step didn't run or failed | Check `cli-release`'s \"Attach artifacts\" step, re-run |\n| Published but installer serves the old version | `cli-post-release` failed or is still running | `gh run list --workflow=cli-post-release.yml` |\n| Docker build failed | Published before assets uploaded | Re-run `docker-image-release` via `workflow_dispatch` with the tag |\n\n## Known breakage\n\nCheck these are still true before blaming the release itself:\n\n- **`main` was red from 2026-07-02 until #2951.** The macOS `zig-hello-world`\n  example test failed because `build.zig` used the pre-Zig-0.12 API while\n  `devbox.lock` pinned zig 0.11.0; the upgrade to zig 0.16 fixed it. A red\n  `main` blocks *all* releases — `cli-release` gates on the test suite — so\n  check the current state rather than assuming either way.\n- **`flake.nix` drifts.** It sat at `0.17.3` through both the 0.17.4 and 0.17.5\n  releases. The script catches this now and opens the bump PR for you.\n",".agents/skills/use-devbox/SKILL.md":"---\nname: use-devbox\ndescription: Run commands inside a devbox environment when a directory provides one. If a `devbox.json` exists in the current directory (or an ancestor/subdir root), devbox can supply tools and scripts the bare shell lacks — e.g. psql, python, gcloud, node. If a binary is provided by devbox, prefer it over the system binary.\n---\n\n# Use devbox\n\n`devbox` applies to directories that have a `devbox.json` (at the directory root or a subdir root). Outside such directories, devbox does nothing useful.\n\n[devbox](https://github.com/jetify-com/devbox) creates isolated, reproducible development environments from a `devbox.json` file. The packages it lists come from the Nix package registry and are made available *inside* the devbox environment — not the bare shell.\n\n## When to reach for it\n\n- The directory has a `devbox.json`.\n- Before concluding a tool is unavailable, check for a `devbox.json` — the project may install that tool through devbox.\n- The user explicitly mentions devbox.\n\n## Check it's installed\n\ndevbox is not always installed. Check first:\n\n```bash\ncommand -v devbox\n```\n\nIf that prints nothing, install it (non-interactive):\n\n```bash\ncurl -fsSL https://get.jetify.com/devbox | bash\n```\n\ndevbox installs to `~/.local/bin` (or `/usr/local/bin`). If `devbox` still isn't found after installing, ensure that directory is on `PATH`. devbox itself requires the Nix package manager; the install script sets it up if missing.\n\n## How to detect\n\nLook for a `devbox.json` in the working directory or an ancestor:\n\n```bash\nls devbox.json 2>/dev/null || find . -maxdepth 2 -name devbox.json 2>/dev/null\n```\n\nIf one exists, the packages it lists are available *inside* the devbox environment (not the bare shell).\n\n## How to run\n\nRun any command inside the devbox environment with:\n\n```bash\ndevbox run [cmd]\n```\n\nExamples:\n\n```bash\ndevbox run psql \"$MASTER_DB_URL\" -c 'SHOW wal_level;'\ndevbox run python script.py\ndevbox run gcloud sql instances describe <instance>\n```\n\n`devbox run` is the right choice for agents and scripts: it loads the environment, runs one command non-interactively, and exits. The first run in a fresh environment may be slow while packages download/build; later runs are cached and fast.\n\nIf a directory other than the cwd holds the `devbox.json`, run from that directory (e.g. `devbox run --config path/to/devbox.json [cmd]` or `cd` into it first).\n\n## Project scripts\n\n`devbox.json` can define named scripts under a `\"shell\": { \"scripts\": { ... } }` block. Run them by name — `devbox run <script-name>` — instead of retyping the underlying command. For example, this repo defines `test`, `lint`, `fmt`, `build`, and `tidy`, so `devbox run test` runs the project's test suite inside the environment.\n\nTo see what's defined, run `devbox run` with no script name (it lists available scripts) or inspect `devbox.json`.\n\n## Choosing where a binary comes from\n\nWhen you need a binary, decide its source in this order:\n\n1. **Provided by devbox for the current project** — if the project's `devbox.json` supplies it, prefer the devbox version over any system install. Run it via `devbox run [cmd]`.\n2. **Provided by the system but not devbox** — use the system binary.\n3. **Not provided by devbox or the system** — check whether devbox can supply it:\n\n   ```bash\n   devbox search [pkg]\n   ```\n\n   If it's available, **ask the user** whether they want to install it into the current project before doing so. Only on their confirmation:\n\n   ```bash\n   devbox add [pkg]\n   ```\n\n   Don't add packages to a project's `devbox.json` without the user's go-ahead. (`devbox rm [pkg]` removes one.)\n\n## Services\n\nSome projects define long-running services (databases, queues, etc.) in their devbox config:\n\n```bash\ndevbox services ls          # list defined services\ndevbox services up           # start services (add -b to run in background)\ndevbox services stop         # stop them\n```\n\n## Global packages\n\n`devbox global` manages a machine-wide environment that's independent of any project (`devbox global add/rm/list`). Prefer per-project packages unless the user explicitly wants something available everywhere.\n\n## Discovering more\n\n- `devbox --help` — full command list (shell, run, add, services, etc.).\n- `devbox run` without args, or inspect `devbox.json`, to see project-defined scripts.\n- `devbox shell` opens an interactive subshell with the environment loaded (prefer `devbox run` for one-off, non-interactive commands).\n- To see what binaries devbox is adding, run `devbox install` and then inspect the `.devbox/nix/profile/default/bin/` directory.\n\n## Key facts\n\n- devbox is not always installed — check with `command -v devbox` and install via `curl -fsSL https://get.jetify.com/devbox | bash` if missing.\n- Only directories with a `devbox.json` use it.\n- `devbox.json` may live in the repo root **or** a subdir root — check both.\n- Packages/scripts defined in `devbox.json` are only on PATH inside `devbox run`/`devbox shell`, not the plain shell.\n- Prefer `devbox run` over `devbox shell` for non-interactive, one-off commands.\n- The first run after adding packages or in a fresh checkout can be slow (downloading/building); subsequent runs are cached.\n","examples/plugins/local/README.md":"# Custom plugin example\n\nShows how to write custom local plugin. Plugins can:\n\n* Install packages\n* Create templatized files (including flakes)\n* Declare services (using process-compose)\n","examples/plugins/local/my-plugin/plugin.json":"{\n  \"name\": \"my-plugin\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Example custom plugin\",\n  \"env\": {\n    \"MY_FOO_VAR\": \"BAR\"\n  },\n  \"create_files\": {\n    /*\nthis is a comment inside the create files\n    */\n    \"{{ .Virtenv }}/empty-dir\": \"\",\n    \"{{ .Virtenv }}/some-file\": \"some-file.txt\",\n    \"{{ .DevboxDir }}/some-file.txt\": \"some-file.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"shell\": {\n      // this is a comment before init-hooks\n      \"init_hook\": [\n          \"echo \\\"ran local plugin init hook\\\"\",\n          \"export MY_INIT_HOOK_VAR=BAR\"\n      ]\n  },\n  // Aliases defined by the plugin. \"greet\" is overridden by the parent\n  // devbox.json, while \"plugin_only\" is provided solely by this plugin.\n  \"aliases\": {\n      \"greet\": \"echo greeting-from-plugin\",\n      \"plugin_only\": \"echo from-plugin\"\n  }\n}\n","examples/plugins/v2-local/plugin1/plugin.json":"{\n  \"name\": \"plugin1\",\n  \"packages\": [\"hello@latest\"],\n  \"env\": {\n    \"PLUGIN1_ENV\": \"success\",\n    \"PLUGIN1_ENV2\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"create_files\": {\n    \"{{ .DevboxDir }}/foo.txt\": \"foo.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"include\": [\n    \"./plugin1a\"\n  ]\n}\n","examples/plugins/v2-local/plugin1/plugin1a/plugin.json":"{\n  \"name\": \"plugin1a\",\n  \"packages\": [\"cowsay@latest\"],\n  \"env\": {\n    \"PLUGIN1A_ENV\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1A_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1A_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"include\": [\n    \"../../plugin2\"\n  ]\n}\n","examples/plugins/v2-local/plugin2/plugin.json":"{\n  \"name\": \"plugin2\",\n  \"create_files\": {\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  }\n}\n","examples/plugins/v2-local/plugin3/plugin.json":"{\n  \"name\": \"plugin3\"\n}\n","plugins/README.md":"# Contributing a Plugin\n\nPlugins make it easier to get started with packages that require additional setup when installed with Nix, and they offer a familiar interface for configuring packages. They also help keep all of your project's configuration within your project directory, which helps maintain portability and isolation.\n\n## Getting Started\n\nBefore writing a plugin, we recommend reading the [User Documentation](https://www.jetify.com/docs/devbox/guides/plugins/) on plugins, as well as inspecting and testing a few of the plugins in this directory. Note that the plugins in this directory are compiled into the Devbox binary, but your plugin can be sourced from a local directory or from within your project.\n\nIf you're looking for plugin ideas, check out our [Issues page](https://github.com/jetify-com/devbox/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+request%22) for any user requests.\n\nBefore contributing, please consult our [Contributing Guide](../CONTRIBUTING.md) and [Code of Conduct](../CODE_OF_CONDUCT.md) for details on how to contribute to Devbox.\n\n### Testing your Plugin\n\n1. Create a new `devbox.json` in an empty directory using `devbox init`.\n2. Add your plugin to the `include` section of the `devbox.json` file. Add any expected packages using `devbox add <pkg>`.\n3. Check that your plugin creates the correct files and environment variables when running `devbox shell`\n4. If you are looking for sample projects to test your plugin with, check out our [examples](https://github.com/jetify-com/devbox/tree/main/examples).\n\n## Plugin Design\n\nPlugins are defined as Go JSON Template files, using the following schema:\n\n```json\n{\n  \"name\": \"\",\n  \"version\": \"\",\n  \"description\": \"\",\n  \"env\": {\n    \"<key>\": \"<value>\"\n  },\n  \"create_files\": {\n    \"<destination>\": \"<source>\"\n  },\n  \"init_hook\": [\n    \"<bash commands>\"\n  ]\n}\n```\n\nA plugin can define services by adding a `process-compose.yaml` file in its `create_files` stanza.\n\n### Plugin Lifecycle\n\nPlugins are activated whenever a developer runs `devbox shell`, runs a script with `devbox run`, or starts a service using `devbox services start|restart`. The lifecycle of a devbox shell with plugins works as follows:\n\n```mermaid\n---\ntitle: Devbox Shell Lifecycle\n---\nflowchart TD\n   A[Plugin env] --> B\n   B[User env] --> C\n   C[Plugin init_hook] --> D[User Init Hook]\n   D -->  E{Start Shell}\n   E --> F & G & H\n   F[Interactive Shell]\n   G[Run Scripts]\n   H[Start Services]\n```\n\n### Template Placeholders\n\nDevbox's Plugin System provides a few special placeholders that should be used when specifying paths for env variables and helper files:\n\n* `{{ .DevboxProjectDir }}` – points to the root folder of the user's project, where their `devbox.json` is stored.\n* `{{ .DevboxDirRoot }}` – points to `<projectDir>/devbox.d`, the shared parent directory of every plugin's `DevboxDir`. (Despite its name, this is *not* the project root — use `{{ .DevboxProjectDir }}` for that.)\n* `{{ .DevboxDir }}` – points to `<projectDir>/devbox.d/<plugin.name>`. This directory is public and added to source control by default. This directory is not modified or recreated by Devbox after the initial package installation. You should use this location for files that a user will want to modify and check-in to source control alongside their project (e.g., `.conf` files or other configs).\n* `{{ .Virtenv }}` – points to `<projectDir>/.devbox/virtenv/<plugin_name>` whenever the plugin activates. This directory is hidden and added to `.gitignore` by default You should use this location for files or variables that a user should not check-in or edit directly. Files in this directory should be considered managed by Devbox, and may be recreated or modified after the initial installation.\n\n### Fields\n\n#### `name` *string*\n\nThe name of your plugin. This is used to identify your plugin when a user runs `devbox info`. If `match` is not set, the plugin will automatically activate when a package is added to a devbox.json project that matches `name`.\n\n#### `version` *string*\n\nThe version of your plugin. You should start your version at 0.0.1 and bump it whenever you merge an update to the plugin.\n\n#### `match` *string*\n\nA regex expression that is used to identify when the plugin will be activated. Devbox will activate your plugin when a package installed with `devbox add` matches this regular expression.\n\nThe regex you provide should match a package name. You can look up packages at `nixhub.io`\n\n#### `readme` *string*\n\nSpecial usage instructions or notes to display when your plugin activates or when a user runs `devbox info`. You do not need to document variables, helper files, or services, since these are automatically printed when a user runs `devbox info`.\n\n#### `env` *object*\n\nA map of `\"key\" : \"value\"` pairs used to set environment variables in `devbox shell` when the plugin is activated. These variables will be printed when a user runs `devbox info`, and can be overridden by a user's `devbox.json`.\n\n#### `create_files` *object*\n\nA map of `\"destination\":\"source\"` pairs that can be used to create or copy files into the user's devbox directory when the plugin is activated. For example:\n\n```json\n\"create_files\": {\n    \"{{ .DevboxDir }}/Caddyfile\": \"caddy/Caddyfile\"\n}\n```\n\nWill copy the Caddyfile in the `plugins/caddy` folder to `devbox.d/caddy/Caddyfile` in the user's project directory.\n\nYou should use this to copy starter config files or templates needed to run the plugin's package.\n\n#### `init_hook` *string | string[]*\n\nA single `bash` command or list of `bash` commands that should run before the user's shell is initialized. This will run every time a shell is started, so you should avoid any resource heavy or long running processes in this step.\n\n### Adding Services\n\nDevbox uses [Process Compose](https://github.com/F1bonacc1/process-compose) to run services and background processes.\n\nPlugins can add services to a user's project by adding a `process-compose.yaml` file to the `create_files` stanza. This file will be automatically detected by Devbox, and started when a user runs `devbox services up` or `devbox services start`.\n\nSee the process compose [docs](https://github.com/F1bonacc1/process-compose) for details on how to write define services in `process-compose.yaml`. You can also check the plugins in this directory for examples on how to write services.\n\n## Tips for Writing Plugins\n\n* Only add plugins for packages that require configuration to work with Devbox.\n* Plugins should try to use the same configuration conventions (environment variables, configuration files) as their packages. This lets developers configure their packages in a way that they are familiar with, using existing documentation.\n* If you think a user may want to override or change a parameter, define it as an environment variable in `env`. This makes it possible for a developer to override the parameter in their `devbox.json` file\n* If you're adding a helper file that you think a developer would want check into source control, create it in `{{ .DevboxDir }}`. If you're creating a file that would not be checked into source control, create it in `{{ .Virtenv }}`.\n* Unless there is a very good reason, we do not recommend creating files outside of `{{ .DevboxDir }}` or `{{ .Virtenv }}`. This helps keep user projects clean and well organized.\n"},"items":[{"name":"SKILL.md","path":".agents/skills/release-devbox/SKILL.md","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/.agents/skills/release-devbox/SKILL.md","title":"release-devbox","category":"anthropic-skill","format":"markdown","content":"---\nname: release-devbox\ndescription: Cut a Devbox CLI release — recommend the version bump, write the release notes, and drive the draft or publish flow. Use when asked to release devbox, cut a version, ship 0.x.y, publish a release, or when a release is stuck partway through and needs diagnosing or resuming.\n---\n\n# Release the Devbox CLI\n\n`scripts/release.ts` owns every mechanical step and the order they run in. Your\njob is the judgment: read what's shipping, recommend a version, write the title\nand notes, and help the user pick how to release.\n\nTwo entry points, both of which walk the whole pipeline:\n\n| Command | What it does |\n| --- | --- |\n| `devbox run draft-release` | Builds the release, leaves it as a draft for review |\n| `devbox run publish-release` | Builds and takes it live, **or** finishes an existing draft |\n\nRead-only helpers: `devbox run release-changes` and `devbox run release-status`.\n\n## 1. Look at what would ship\n\n```bash\ndevbox run release-changes\n```\n\nThis prints the commits since the last release grouped by kind (breaking /\nfeatures / fixes / other), the recommended version, and whether `flake.nix`\nneeds bumping. Read it before saying anything about the release.\n\nThe recommendation follows devbox's own history: it's pre-1.0, so **breaking\nchanges take a minor bump** (0.17.5 → 0.18.0) and **everything else takes a\npatch bump** — 0.17.4 shipped new features as a patch. Say what the script\nrecommends and why, but sanity-check it against the actual diff. A release that\nremoves whole subsystems deserves a minor bump even if nobody wrote `feat!:`.\n\n## 2. Ask how they want to release\n\nPresent the three options and let the user choose — don't assume:\n\n1. **Draft first** (`devbox run draft-release`) — builds everything and stops at\n   a draft. Right when the notes need review, or when the release is being\n   coordinated with an announcement. This is the safe default.\n2. **Publish directly** (`devbox run publish-release`) — same pipeline, then\n   takes it live. Right for routine patch releases.\n3. **Publish an existing draft** (`devbox run publish-release`) — if a draft is\n   already sitting there, the command lists it and offers to finish it. Check\n   with `devbox run release-status` first.\n\n## 3. Write the title and notes\n\nThe script prompts for these in `$EDITOR` when a human drives it. When you're\ndriving, write them yourself and pass them as flags — that's the point of doing\nthis through an agent.\n\nGet the raw material from GitHub's generated changelog:\n\n```bash\ngh api repos/jetify-com/devbox/releases/generate-notes \\\n  -f tag_name=<version> -f previous_tag_name=<prev> -f target_commitish=main --jq '.body'\n```\n\nThat's a dump of PR titles. Rewrite it into user-facing notes in the house style\nthat 0.17.4 established and 0.18.0 is the cleanest example of:\n\n````markdown\n## What's Changed\n\n### 💥 Breaking Changes\n\n* **What was removed** — what users must do instead, by @author ([#1234](https://github.com/jetify-com/devbox/pull/1234))\n\n### ✨ New Features\n\n* **Short bold lead-in** — what changed and why a user cares, by @author ([#1234](...))\n\n### 🐛 Bug Fixes\n\n* Plain one-liners for small fixes, by @author ([#1234](...)).\n* **Group related fixes** — combine several PRs into one bullet when they share\n  a root cause ([#1234](...)) and ([#1235](...)), by @author.\n\n### 🧹 Maintenance\n\n* Dependency bumps, CI work, docs. Group aggressively; nobody reads this section\n  line by line.\n\n## New Contributors\n* @newperson made their first contribution in https://github.com/jetify-com/devbox/pull/1234\n\n**Full Changelog**: https://github.com/jetify-com/devbox/compare/<prev>...<version>\n````\n\nRules that matter:\n\n- **Lead with impact, not the commit subject.** \"Shells in paths with spaces now\n  work\" beats \"quote the shellrc source guard\".\n- **Breaking changes go first and say what to do instead.** For a release that\n  removes features, this is the whole story — say plainly what's gone and what\n  replaces it, or that nothing does.\n- **Only include sections with content.** Drop the ones that are empty.\n- **Keep attribution.** Every bullet ends with `by @author` and its PR link.\n- **Use plain backticks for code.** Do *not* escape them as `` \\` ``. Nothing\n  re-interprets the body — goreleaser's Discord announcer is `enabled: false` in\n  `.goreleaser.yaml` — and Markdown renders `` \\` `` as a literal backtick\n  rather than opening a code span. That's why 0.17.4's published notes are full\n  of stray backslashes. The script unescapes any that slip through, but don't\n  write them.\n\nFor the title, default to the bare version (`0.18.0`). A short theme suffix is\nfine when the release has one: `0.18.0 — Devbox goes fully local`.\n\n**Show the user the title and full notes and get explicit sign-off before\nrunning anything.** That's the last checkpoint before it goes live.\n\n## 4. Run it\n\nSave the notes to `.context/release-notes-<version>.md`, then:\n\n```bash\nnode scripts/release.ts --draft \\\n  --version 0.18.0 \\\n  --title \"0.18.0\" \\\n  --notes-file .context/release-notes-0.18.0.md \\\n  --yes\n```\n\nSwap `--draft` for `--publish` to go live. Notes on driving it as an agent:\n\n- **`--yes` is required when you run it.** Its confirmation prompts need a TTY,\n  and your shell doesn't have one — without `--yes` it stops with \"this step\n  needs an answer but stdin is not a terminal\". Only pass it after the user has\n  signed off; it's skipping *their* checkpoint, not adding one.\n- **It blocks for ~15 minutes** waiting on `cli-release`. Run it in the\n  background and check back, rather than letting the call time out.\n- **It's resumable.** Every step is idempotent, so if it fails partway, fix the\n  cause and re-run the same command — it picks up where it left off rather than\n  duplicating work.\n- **`--skip-cli-tests` exists but is a loaded gun.** It only skips the *check*\n  that the latest `cli-tests` run on `main` isn't red; `cli-release` still runs\n  the whole suite and fails the build if it isn't green. A run that's merely\n  queued or in progress (the normal state right after the flake bump merges)\n  doesn't block — only a finished, failed run does. Use the flag when that\n  check is wrong (a flake you've confirmed), not to push past a genuinely red\n  `main`.\n\nIf `flake.nix` needs bumping, the script offers to do the whole thing: it\nrewrites `lastTag`, refreshes `vendor-hash` and `flake.lock`, commits to\n`bump-flake-<version>`, pushes, opens the PR, and puts you back on `main` with a\nclean tree. Then it stops (exit 0 — this is a planned pause, not a failure)\nand prints the exact command to continue with, e.g.\n`devbox run publish-release --version 0.18.2`. That bump has to be reviewed and\nmerged before the tag is pushed, otherwise the tagged commit ships the wrong\nversion string. Get it merged and run the printed command; `--version` skips\nthe version prompt and preflight pulls the new `main` and carries on. A re-run\nwhile the PR is still open stops immediately with its URL rather than opening\na second one.\n\nPreflight is picky about the checkout on purpose, since the tag lands on\nwhatever `HEAD` is. A `main` that's merely behind `origin/main` and clean\nfast-forwards itself; anything else (wrong branch, dirty tree, local-only\ncommits) stops with the command that fixes it.\n\n## Why the order is what it is\n\nDon't work around these; they're why the script exists.\n\n- **Draft before tag.** goreleaser runs with `release.disable` and only builds\n  `dist/`; `cli-release` uploads that to the release for the tag, looking it up\n  by tag with `gh`. With no draft it creates one from GitHub's generated notes —\n  usable, but not the notes you wrote. (goreleaser used to do the upload itself,\n  but it matched drafts by *title*, so any release with a real title got a\n  silent duplicate draft with bare-commit-SHA notes. That's what shipped as the\n  0.17.5 release notes, and what stalled 0.18.0.)\n- **Publish after the build.** `docker-image-release` fires on the release event\n  and immediately downloads the release tarballs to bake into the image.\n  Publishing before `cli-release` uploads them fails the Docker build. That's\n  exactly what happened on 0.17.3 and 0.17.5, both published from the GitHub UI,\n  which creates the tag and publishes in one action.\n- **Flake bump before the `cli-tests` check.** The bump has to merge into `main`,\n  which re-runs `cli-tests` there — so a result read before the bump is about a\n  commit that won't be released. Settling the bump first also means a red `main`\n  doesn't hide the fact that a bump PR is needed.\n- **Publish from local credentials, not CI.** GitHub doesn't trigger workflows\n  from events raised by `GITHUB_TOKEN`. That's why CI-created edge releases never\n  trigger `docker-image-release`, and why publishing can't just be a CI step.\n\n## If something is stuck\n\n`devbox run release-status` reports the tag, draft, asset count, `cli-release`\nresult and the current `flake.nix` version in one shot.\n\n| Symptom | Cause | Fix |\n| --- | --- | --- |\n| `cli-release` never started | Tag push didn't land | Re-run the same command |\n| `cli-release` failed in `tests` | Red `main` (see below) | Fix the test, re-run |\n| Draft has 0 assets | The upload step didn't run or failed | Check `cli-release`'s \"Attach artifacts\" step, re-run |\n| Published but installer serves the old version | `cli-post-release` failed or is still running | `gh run list --workflow=cli-post-release.yml` |\n| Docker build failed | Published before assets uploaded | Re-run `docker-image-release` via `workflow_dispatch` with the tag |\n\n## Known breakage\n\nCheck these are still true before blaming the release itself:\n\n- **`main` was red from 2026-07-02 until #2951.** The macOS `zig-hello-world`\n  example test failed because `build.zig` used the pre-Zig-0.12 API while\n  `devbox.lock` pinned zig 0.11.0; the upgrade to zig 0.16 fixed it. A red\n  `main` blocks *all* releases — `cli-release` gates on the test suite — so\n  check the current state rather than assuming either way.\n- **`flake.nix` drifts.** It sat at `0.17.3` through both the 0.17.4 and 0.17.5\n  releases. The script catches this now and opens the bump PR for you.\n","frontmatter":{"name":"release-devbox","description":"Cut a Devbox CLI release — recommend the version bump, write the release notes, and drive the draft or publish flow. Use when asked to release devbox, cut a version, ship 0.x.y, publish a release, or when a release is stuck partway through and needs diagnosing or resuming."},"isInternal":false,"tokens":2644,"sizeBytes":10207},{"name":"SKILL.md","path":".agents/skills/use-devbox/SKILL.md","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/.agents/skills/use-devbox/SKILL.md","title":"use-devbox","category":"anthropic-skill","format":"markdown","content":"---\nname: use-devbox\ndescription: Run commands inside a devbox environment when a directory provides one. If a `devbox.json` exists in the current directory (or an ancestor/subdir root), devbox can supply tools and scripts the bare shell lacks — e.g. psql, python, gcloud, node. If a binary is provided by devbox, prefer it over the system binary.\n---\n\n# Use devbox\n\n`devbox` applies to directories that have a `devbox.json` (at the directory root or a subdir root). Outside such directories, devbox does nothing useful.\n\n[devbox](https://github.com/jetify-com/devbox) creates isolated, reproducible development environments from a `devbox.json` file. The packages it lists come from the Nix package registry and are made available *inside* the devbox environment — not the bare shell.\n\n## When to reach for it\n\n- The directory has a `devbox.json`.\n- Before concluding a tool is unavailable, check for a `devbox.json` — the project may install that tool through devbox.\n- The user explicitly mentions devbox.\n\n## Check it's installed\n\ndevbox is not always installed. Check first:\n\n```bash\ncommand -v devbox\n```\n\nIf that prints nothing, install it (non-interactive):\n\n```bash\ncurl -fsSL https://get.jetify.com/devbox | bash\n```\n\ndevbox installs to `~/.local/bin` (or `/usr/local/bin`). If `devbox` still isn't found after installing, ensure that directory is on `PATH`. devbox itself requires the Nix package manager; the install script sets it up if missing.\n\n## How to detect\n\nLook for a `devbox.json` in the working directory or an ancestor:\n\n```bash\nls devbox.json 2>/dev/null || find . -maxdepth 2 -name devbox.json 2>/dev/null\n```\n\nIf one exists, the packages it lists are available *inside* the devbox environment (not the bare shell).\n\n## How to run\n\nRun any command inside the devbox environment with:\n\n```bash\ndevbox run [cmd]\n```\n\nExamples:\n\n```bash\ndevbox run psql \"$MASTER_DB_URL\" -c 'SHOW wal_level;'\ndevbox run python script.py\ndevbox run gcloud sql instances describe <instance>\n```\n\n`devbox run` is the right choice for agents and scripts: it loads the environment, runs one command non-interactively, and exits. The first run in a fresh environment may be slow while packages download/build; later runs are cached and fast.\n\nIf a directory other than the cwd holds the `devbox.json`, run from that directory (e.g. `devbox run --config path/to/devbox.json [cmd]` or `cd` into it first).\n\n## Project scripts\n\n`devbox.json` can define named scripts under a `\"shell\": { \"scripts\": { ... } }` block. Run them by name — `devbox run <script-name>` — instead of retyping the underlying command. For example, this repo defines `test`, `lint`, `fmt`, `build`, and `tidy`, so `devbox run test` runs the project's test suite inside the environment.\n\nTo see what's defined, run `devbox run` with no script name (it lists available scripts) or inspect `devbox.json`.\n\n## Choosing where a binary comes from\n\nWhen you need a binary, decide its source in this order:\n\n1. **Provided by devbox for the current project** — if the project's `devbox.json` supplies it, prefer the devbox version over any system install. Run it via `devbox run [cmd]`.\n2. **Provided by the system but not devbox** — use the system binary.\n3. **Not provided by devbox or the system** — check whether devbox can supply it:\n\n   ```bash\n   devbox search [pkg]\n   ```\n\n   If it's available, **ask the user** whether they want to install it into the current project before doing so. Only on their confirmation:\n\n   ```bash\n   devbox add [pkg]\n   ```\n\n   Don't add packages to a project's `devbox.json` without the user's go-ahead. (`devbox rm [pkg]` removes one.)\n\n## Services\n\nSome projects define long-running services (databases, queues, etc.) in their devbox config:\n\n```bash\ndevbox services ls          # list defined services\ndevbox services up           # start services (add -b to run in background)\ndevbox services stop         # stop them\n```\n\n## Global packages\n\n`devbox global` manages a machine-wide environment that's independent of any project (`devbox global add/rm/list`). Prefer per-project packages unless the user explicitly wants something available everywhere.\n\n## Discovering more\n\n- `devbox --help` — full command list (shell, run, add, services, etc.).\n- `devbox run` without args, or inspect `devbox.json`, to see project-defined scripts.\n- `devbox shell` opens an interactive subshell with the environment loaded (prefer `devbox run` for one-off, non-interactive commands).\n- To see what binaries devbox is adding, run `devbox install` and then inspect the `.devbox/nix/profile/default/bin/` directory.\n\n## Key facts\n\n- devbox is not always installed — check with `command -v devbox` and install via `curl -fsSL https://get.jetify.com/devbox | bash` if missing.\n- Only directories with a `devbox.json` use it.\n- `devbox.json` may live in the repo root **or** a subdir root — check both.\n- Packages/scripts defined in `devbox.json` are only on PATH inside `devbox run`/`devbox shell`, not the plain shell.\n- Prefer `devbox run` over `devbox shell` for non-interactive, one-off commands.\n- The first run after adding packages or in a fresh checkout can be slow (downloading/building); subsequent runs are cached.\n","frontmatter":{"name":"use-devbox","description":"Run commands inside a devbox environment when a directory provides one. If a `devbox.json` exists in the current directory (or an ancestor/subdir root), devbox can supply tools and scripts the bare shell lacks — e.g. psql, python, gcloud, node. If a binary is provided by devbox, prefer it over the system binary."},"isInternal":false,"tokens":1286,"sizeBytes":5231},{"name":"plugin.json","path":"examples/plugins/local/my-plugin/plugin.json","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/examples/plugins/local/my-plugin/plugin.json","title":"my-plugin Plugin Manifest","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"my-plugin\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Example custom plugin\",\n  \"env\": {\n    \"MY_FOO_VAR\": \"BAR\"\n  },\n  \"create_files\": {\n    /*\nthis is a comment inside the create files\n    */\n    \"{{ .Virtenv }}/empty-dir\": \"\",\n    \"{{ .Virtenv }}/some-file\": \"some-file.txt\",\n    \"{{ .DevboxDir }}/some-file.txt\": \"some-file.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"shell\": {\n      // this is a comment before init-hooks\n      \"init_hook\": [\n          \"echo \\\"ran local plugin init hook\\\"\",\n          \"export MY_INIT_HOOK_VAR=BAR\"\n      ]\n  },\n  // Aliases defined by the plugin. \"greet\" is overridden by the parent\n  // devbox.json, while \"plugin_only\" is provided solely by this plugin.\n  \"aliases\": {\n      \"greet\": \"echo greeting-from-plugin\",\n      \"plugin_only\": \"echo from-plugin\"\n  }\n}\n","isInternal":false,"tokens":236,"sizeBytes":847},{"name":"README.md","path":"examples/plugins/local/README.md","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/examples/plugins/local/README.md","title":"local Documentation","category":"plugin-manifest","format":"markdown","content":"# Custom plugin example\n\nShows how to write custom local plugin. Plugins can:\n\n* Install packages\n* Create templatized files (including flakes)\n* Declare services (using process-compose)\n","isInternal":false,"tokens":38,"sizeBytes":187},{"name":"plugin.json","path":"examples/plugins/v2-local/plugin1/plugin.json","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/examples/plugins/v2-local/plugin1/plugin.json","title":"plugin1","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"plugin1\",\n  \"packages\": [\"hello@latest\"],\n  \"env\": {\n    \"PLUGIN1_ENV\": \"success\",\n    \"PLUGIN1_ENV2\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"create_files\": {\n    \"{{ .DevboxDir }}/foo.txt\": \"foo.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"include\": [\n    \"./plugin1a\"\n  ]\n}\n","frontmatter":{"name":"plugin1","packages":["hello@latest"],"env":{"PLUGIN1_ENV":"success","PLUGIN1_ENV2":"success"},"shell":{"init_hook":["export PLUGIN1_INIT_HOOK=success"],"scripts":{"plugin_1_script":["echo success"]}},"create_files":{"{{ .DevboxDir }}/foo.txt":"foo.txt","{{ .Virtenv }}/process-compose.yaml":"process-compose.yaml"},"include":["./plugin1a"]},"isInternal":false,"tokens":143,"sizeBytes":469},{"name":"plugin.json","path":"examples/plugins/v2-local/plugin1/plugin1a/plugin.json","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/examples/plugins/v2-local/plugin1/plugin1a/plugin.json","title":"plugin1a","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"plugin1a\",\n  \"packages\": [\"cowsay@latest\"],\n  \"env\": {\n    \"PLUGIN1A_ENV\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1A_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1A_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"include\": [\n    \"../../plugin2\"\n  ]\n}\n","frontmatter":{"name":"plugin1a","packages":["cowsay@latest"],"env":{"PLUGIN1A_ENV":"success"},"shell":{"init_hook":["export PLUGIN1A_INIT_HOOK=success"],"scripts":{"plugin_1A_script":["echo success"]}},"include":["../../plugin2"]},"isInternal":false,"tokens":100,"sizeBytes":312},{"name":"plugin.json","path":"examples/plugins/v2-local/plugin2/plugin.json","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/examples/plugins/v2-local/plugin2/plugin.json","title":"plugin2","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"plugin2\",\n  \"create_files\": {\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  }\n}\n","frontmatter":{"name":"plugin2","create_files":{"{{ .Virtenv }}/process-compose.yaml":"process-compose.yaml"}},"isInternal":false,"tokens":34,"sizeBytes":115},{"name":"plugin.json","path":"examples/plugins/v2-local/plugin3/plugin.json","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/examples/plugins/v2-local/plugin3/plugin.json","title":"plugin3","category":"plugin-manifest","format":"json","content":"{\n  \"name\": \"plugin3\"\n}\n","frontmatter":{"name":"plugin3"},"isInternal":false,"tokens":10,"sizeBytes":24},{"name":"README.md","path":"plugins/README.md","rawUrl":"https://raw.githubusercontent.com/jetify-com/devbox/HEAD/plugins/README.md","title":"plugins Documentation","category":"plugin-manifest","format":"markdown","content":"# Contributing a Plugin\n\nPlugins make it easier to get started with packages that require additional setup when installed with Nix, and they offer a familiar interface for configuring packages. They also help keep all of your project's configuration within your project directory, which helps maintain portability and isolation.\n\n## Getting Started\n\nBefore writing a plugin, we recommend reading the [User Documentation](https://www.jetify.com/docs/devbox/guides/plugins/) on plugins, as well as inspecting and testing a few of the plugins in this directory. Note that the plugins in this directory are compiled into the Devbox binary, but your plugin can be sourced from a local directory or from within your project.\n\nIf you're looking for plugin ideas, check out our [Issues page](https://github.com/jetify-com/devbox/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+request%22) for any user requests.\n\nBefore contributing, please consult our [Contributing Guide](../CONTRIBUTING.md) and [Code of Conduct](../CODE_OF_CONDUCT.md) for details on how to contribute to Devbox.\n\n### Testing your Plugin\n\n1. Create a new `devbox.json` in an empty directory using `devbox init`.\n2. Add your plugin to the `include` section of the `devbox.json` file. Add any expected packages using `devbox add <pkg>`.\n3. Check that your plugin creates the correct files and environment variables when running `devbox shell`\n4. If you are looking for sample projects to test your plugin with, check out our [examples](https://github.com/jetify-com/devbox/tree/main/examples).\n\n## Plugin Design\n\nPlugins are defined as Go JSON Template files, using the following schema:\n\n```json\n{\n  \"name\": \"\",\n  \"version\": \"\",\n  \"description\": \"\",\n  \"env\": {\n    \"<key>\": \"<value>\"\n  },\n  \"create_files\": {\n    \"<destination>\": \"<source>\"\n  },\n  \"init_hook\": [\n    \"<bash commands>\"\n  ]\n}\n```\n\nA plugin can define services by adding a `process-compose.yaml` file in its `create_files` stanza.\n\n### Plugin Lifecycle\n\nPlugins are activated whenever a developer runs `devbox shell`, runs a script with `devbox run`, or starts a service using `devbox services start|restart`. The lifecycle of a devbox shell with plugins works as follows:\n\n```mermaid\n---\ntitle: Devbox Shell Lifecycle\n---\nflowchart TD\n   A[Plugin env] --> B\n   B[User env] --> C\n   C[Plugin init_hook] --> D[User Init Hook]\n   D -->  E{Start Shell}\n   E --> F & G & H\n   F[Interactive Shell]\n   G[Run Scripts]\n   H[Start Services]\n```\n\n### Template Placeholders\n\nDevbox's Plugin System provides a few special placeholders that should be used when specifying paths for env variables and helper files:\n\n* `{{ .DevboxProjectDir }}` – points to the root folder of the user's project, where their `devbox.json` is stored.\n* `{{ .DevboxDirRoot }}` – points to `<projectDir>/devbox.d`, the shared parent directory of every plugin's `DevboxDir`. (Despite its name, this is *not* the project root — use `{{ .DevboxProjectDir }}` for that.)\n* `{{ .DevboxDir }}` – points to `<projectDir>/devbox.d/<plugin.name>`. This directory is public and added to source control by default. This directory is not modified or recreated by Devbox after the initial package installation. You should use this location for files that a user will want to modify and check-in to source control alongside their project (e.g., `.conf` files or other configs).\n* `{{ .Virtenv }}` – points to `<projectDir>/.devbox/virtenv/<plugin_name>` whenever the plugin activates. This directory is hidden and added to `.gitignore` by default You should use this location for files or variables that a user should not check-in or edit directly. Files in this directory should be considered managed by Devbox, and may be recreated or modified after the initial installation.\n\n### Fields\n\n#### `name` *string*\n\nThe name of your plugin. This is used to identify your plugin when a user runs `devbox info`. If `match` is not set, the plugin will automatically activate when a package is added to a devbox.json project that matches `name`.\n\n#### `version` *string*\n\nThe version of your plugin. You should start your version at 0.0.1 and bump it whenever you merge an update to the plugin.\n\n#### `match` *string*\n\nA regex expression that is used to identify when the plugin will be activated. Devbox will activate your plugin when a package installed with `devbox add` matches this regular expression.\n\nThe regex you provide should match a package name. You can look up packages at `nixhub.io`\n\n#### `readme` *string*\n\nSpecial usage instructions or notes to display when your plugin activates or when a user runs `devbox info`. You do not need to document variables, helper files, or services, since these are automatically printed when a user runs `devbox info`.\n\n#### `env` *object*\n\nA map of `\"key\" : \"value\"` pairs used to set environment variables in `devbox shell` when the plugin is activated. These variables will be printed when a user runs `devbox info`, and can be overridden by a user's `devbox.json`.\n\n#### `create_files` *object*\n\nA map of `\"destination\":\"source\"` pairs that can be used to create or copy files into the user's devbox directory when the plugin is activated. For example:\n\n```json\n\"create_files\": {\n    \"{{ .DevboxDir }}/Caddyfile\": \"caddy/Caddyfile\"\n}\n```\n\nWill copy the Caddyfile in the `plugins/caddy` folder to `devbox.d/caddy/Caddyfile` in the user's project directory.\n\nYou should use this to copy starter config files or templates needed to run the plugin's package.\n\n#### `init_hook` *string | string[]*\n\nA single `bash` command or list of `bash` commands that should run before the user's shell is initialized. This will run every time a shell is started, so you should avoid any resource heavy or long running processes in this step.\n\n### Adding Services\n\nDevbox uses [Process Compose](https://github.com/F1bonacc1/process-compose) to run services and background processes.\n\nPlugins can add services to a user's project by adding a `process-compose.yaml` file to the `create_files` stanza. This file will be automatically detected by Devbox, and started when a user runs `devbox services up` or `devbox services start`.\n\nSee the process compose [docs](https://github.com/F1bonacc1/process-compose) for details on how to write define services in `process-compose.yaml`. You can also check the plugins in this directory for examples on how to write services.\n\n## Tips for Writing Plugins\n\n* Only add plugins for packages that require configuration to work with Devbox.\n* Plugins should try to use the same configuration conventions (environment variables, configuration files) as their packages. This lets developers configure their packages in a way that they are familiar with, using existing documentation.\n* If you think a user may want to override or change a parameter, define it as an environment variable in `env`. This makes it possible for a developer to override the parameter in their `devbox.json` file\n* If you're adding a helper file that you think a developer would want check into source control, create it in `{{ .DevboxDir }}`. If you're creating a file that would not be checked into source control, create it in `{{ .Virtenv }}`.\n* Unless there is a very good reason, we do not recommend creating files outside of `{{ .DevboxDir }}` or `{{ .Virtenv }}`. This helps keep user projects clean and well organized.\n","isInternal":false,"tokens":1697,"sizeBytes":7353}],"systemPromptSnippet":"<yakaai_skills repo=\"jetify-com/devbox\">\n<!-- File: .agents/skills/release-devbox/SKILL.md (Tokens: ~2644 | Category: anthropic-skill) -->\n---\nname: release-devbox\ndescription: Cut a Devbox CLI release — recommend the version bump, write the release notes, and drive the draft or publish flow. Use when asked to release devbox, cut a version, ship 0.x.y, publish a release, or when a release is stuck partway through and needs diagnosing or resuming.\n---\n\n# Release the Devbox CLI\n\n`scripts/release.ts` owns every mechanical step and the order they run in. Your\njob is the judgment: read what's shipping, recommend a version, write the title\nand notes, and help the user pick how to release.\n\nTwo entry points, both of which walk the whole pipeline:\n\n| Command | What it does |\n| --- | --- |\n| `devbox run draft-release` | Builds the release, leaves it as a draft for review |\n| `devbox run publish-release` | Builds and takes it live, **or** finishes an existing draft |\n\nRead-only helpers: `devbox run release-changes` and `devbox run release-status`.\n\n## 1. Look at what would ship\n\n```bash\ndevbox run release-changes\n```\n\nThis prints the commits since the last release grouped by kind (breaking /\nfeatures / fixes / other), the recommended version, and whether `flake.nix`\nneeds bumping. Read it before saying anything about the release.\n\nThe recommendation follows devbox's own history: it's pre-1.0, so **breaking\nchanges take a minor bump** (0.17.5 → 0.18.0) and **everything else takes a\npatch bump** — 0.17.4 shipped new features as a patch. Say what the script\nrecommends and why, but sanity-check it against the actual diff. A release that\nremoves whole subsystems deserves a minor bump even if nobody wrote `feat!:`.\n\n## 2. Ask how they want to release\n\nPresent the three options and let the user choose — don't assume:\n\n1. **Draft first** (`devbox run draft-release`) — builds everything and stops at\n   a draft. Right when the notes need review, or when the release is being\n   coordinated with an announcement. This is the safe default.\n2. **Publish directly** (`devbox run publish-release`) — same pipeline, then\n   takes it live. Right for routine patch releases.\n3. **Publish an existing draft** (`devbox run publish-release`) — if a draft is\n   already sitting there, the command lists it and offers to finish it. Check\n   with `devbox run release-status` first.\n\n## 3. Write the title and notes\n\nThe script prompts for these in `$EDITOR` when a human drives it. When you're\ndriving, write them yourself and pass them as flags — that's the point of doing\nthis through an agent.\n\nGet the raw material from GitHub's generated changelog:\n\n```bash\ngh api repos/jetify-com/devbox/releases/generate-notes \\\n  -f tag_name=<version> -f previous_tag_name=<prev> -f target_commitish=main --jq '.body'\n```\n\nThat's a dump of PR titles. Rewrite it into user-facing notes in the house style\nthat 0.17.4 established and 0.18.0 is the cleanest example of:\n\n````markdown\n## What's Changed\n\n### 💥 Breaking Changes\n\n* **What was removed** — what users must do instead, by @author ([#1234](https://github.com/jetify-com/devbox/pull/1234))\n\n### ✨ New Features\n\n* **Short bold lead-in** — what changed and why a user cares, by @author ([#1234](...))\n\n### 🐛 Bug Fixes\n\n* Plain one-liners for small fixes, by @author ([#1234](...)).\n* **Group related fixes** — combine several PRs into one bullet when they share\n  a root cause ([#1234](...)) and ([#1235](...)), by @author.\n\n### 🧹 Maintenance\n\n* Dependency bumps, CI work, docs. Group aggressively; nobody reads this section\n  line by line.\n\n## New Contributors\n* @newperson made their first contribution in https://github.com/jetify-com/devbox/pull/1234\n\n**Full Changelog**: https://github.com/jetify-com/devbox/compare/<prev>...<version>\n````\n\nRules that matter:\n\n- **Lead with impact, not the commit subject.** \"Shells in paths with spaces now\n  work\" beats \"quote the shellrc source guard\".\n- **Breaking changes go first and say what to do instead.** For a release that\n  removes features, this is the whole story — say plainly what's gone and what\n  replaces it, or that nothing does.\n- **Only include sections with content.** Drop the ones that are empty.\n- **Keep attribution.** Every bullet ends with `by @author` and its PR link.\n- **Use plain backticks for code.** Do *not* escape them as `` \\` ``. Nothing\n  re-interprets the body — goreleaser's Discord announcer is `enabled: false` in\n  `.goreleaser.yaml` — and Markdown renders `` \\` `` as a literal backtick\n  rather than opening a code span. That's why 0.17.4's published notes are full\n  of stray backslashes. The script unescapes any that slip through, but don't\n  write them.\n\nFor the title, default to the bare version (`0.18.0`). A short theme suffix is\nfine when the release has one: `0.18.0 — Devbox goes fully local`.\n\n**Show the user the title and full notes and get explicit sign-off before\nrunning anything.** That's the last checkpoint before it goes live.\n\n## 4. Run it\n\nSave the notes to `.context/release-notes-<version>.md`, then:\n\n```bash\nnode scripts/release.ts --draft \\\n  --version 0.18.0 \\\n  --title \"0.18.0\" \\\n  --notes-file .context/release-notes-0.18.0.md \\\n  --yes\n```\n\nSwap `--draft` for `--publish` to go live. Notes on driving it as an agent:\n\n- **`--yes` is required when you run it.** Its confirmation prompts need a TTY,\n  and your shell doesn't have one — without `--yes` it stops with \"this step\n  needs an answer but stdin is not a terminal\". Only pass it after the user has\n  signed off; it's skipping *their* checkpoint, not adding one.\n- **It blocks for ~15 minutes** waiting on `cli-release`. Run it in the\n  background and check back, rather than letting the call time out.\n- **It's resumable.** Every step is idempotent, so if it fails partway, fix the\n  cause and re-run the same command — it picks up where it left off rather than\n  duplicating work.\n- **`--skip-cli-tests` exists but is a loaded gun.** It only skips the *check*\n  that the latest `cli-tests` run on `main` isn't red; `cli-release` still runs\n  the whole suite and fails the build if it isn't green. A run that's merely\n  queued or in progress (the normal state right after the flake bump merges)\n  doesn't block — only a finished, failed run does. Use the flag when that\n  check is wrong (a flake you've confirmed), not to push past a genuinely red\n  `main`.\n\nIf `flake.nix` needs bumping, the script offers to do the whole thing: it\nrewrites `lastTag`, refreshes `vendor-hash` and `flake.lock`, commits to\n`bump-flake-<version>`, pushes, opens the PR, and puts you back on `main` with a\nclean tree. Then it stops (exit 0 — this is a planned pause, not a failure)\nand prints the exact command to continue with, e.g.\n`devbox run publish-release --version 0.18.2`. That bump has to be reviewed and\nmerged before the tag is pushed, otherwise the tagged commit ships the wrong\nversion string. Get it merged and run the printed command; `--version` skips\nthe version prompt and preflight pulls the new `main` and carries on. A re-run\nwhile the PR is still open stops immediately with its URL rather than opening\na second one.\n\nPreflight is picky about the checkout on purpose, since the tag lands on\nwhatever `HEAD` is. A `main` that's merely behind `origin/main` and clean\nfast-forwards itself; anything else (wrong branch, dirty tree, local-only\ncommits) stops with the command that fixes it.\n\n## Why the order is what it is\n\nDon't work around these; they're why the script exists.\n\n- **Draft before tag.** goreleaser runs with `release.disable` and only builds\n  `dist/`; `cli-release` uploads that to the release for the tag, looking it up\n  by tag with `gh`. With no draft it creates one from GitHub's generated notes —\n  usable, but not the notes you wrote. (goreleaser used to do the upload itself,\n  but it matched drafts by *title*, so any release with a real title got a\n  silent duplicate draft with bare-commit-SHA notes. That's what shipped as the\n  0.17.5 release notes, and what stalled 0.18.0.)\n- **Publish after the build.** `docker-image-release` fires on the release event\n  and immediately downloads the release tarballs to bake into the image.\n  Publishing before `cli-release` uploads them fails the Docker build. That's\n  exactly what happened on 0.17.3 and 0.17.5, both published from the GitHub UI,\n  which creates the tag and publishes in one action.\n- **Flake bump before the `cli-tests` check.** The bump has to merge into `main`,\n  which re-runs `cli-tests` there — so a result read before the bump is about a\n  commit that won't be released. Settling the bump first also means a red `main`\n  doesn't hide the fact that a bump PR is needed.\n- **Publish from local credentials, not CI.** GitHub doesn't trigger workflows\n  from events raised by `GITHUB_TOKEN`. That's why CI-created edge releases never\n  trigger `docker-image-release`, and why publishing can't just be a CI step.\n\n## If something is stuck\n\n`devbox run release-status` reports the tag, draft, asset count, `cli-release`\nresult and the current `flake.nix` version in one shot.\n\n| Symptom | Cause | Fix |\n| --- | --- | --- |\n| `cli-release` never started | Tag push didn't land | Re-run the same command |\n| `cli-release` failed in `tests` | Red `main` (see below) | Fix the test, re-run |\n| Draft has 0 assets | The upload step didn't run or failed | Check `cli-release`'s \"Attach artifacts\" step, re-run |\n| Published but installer serves the old version | `cli-post-release` failed or is still running | `gh run list --workflow=cli-post-release.yml` |\n| Docker build failed | Published before assets uploaded | Re-run `docker-image-release` via `workflow_dispatch` with the tag |\n\n## Known breakage\n\nCheck these are still true before blaming the release itself:\n\n- **`main` was red from 2026-07-02 until #2951.** The macOS `zig-hello-world`\n  example test failed because `build.zig` used the pre-Zig-0.12 API while\n  `devbox.lock` pinned zig 0.11.0; the upgrade to zig 0.16 fixed it. A red\n  `main` blocks *all* releases — `cli-release` gates on the test suite — so\n  check the current state rather than assuming either way.\n- **`flake.nix` drifts.** It sat at `0.17.3` through both the 0.17.4 and 0.17.5\n  releases. The script catches this now and opens the bump PR for you.\n\n<!-- File: .agents/skills/use-devbox/SKILL.md (Tokens: ~1286 | Category: anthropic-skill) -->\n---\nname: use-devbox\ndescription: Run commands inside a devbox environment when a directory provides one. If a `devbox.json` exists in the current directory (or an ancestor/subdir root), devbox can supply tools and scripts the bare shell lacks — e.g. psql, python, gcloud, node. If a binary is provided by devbox, prefer it over the system binary.\n---\n\n# Use devbox\n\n`devbox` applies to directories that have a `devbox.json` (at the directory root or a subdir root). Outside such directories, devbox does nothing useful.\n\n[devbox](https://github.com/jetify-com/devbox) creates isolated, reproducible development environments from a `devbox.json` file. The packages it lists come from the Nix package registry and are made available *inside* the devbox environment — not the bare shell.\n\n## When to reach for it\n\n- The directory has a `devbox.json`.\n- Before concluding a tool is unavailable, check for a `devbox.json` — the project may install that tool through devbox.\n- The user explicitly mentions devbox.\n\n## Check it's installed\n\ndevbox is not always installed. Check first:\n\n```bash\ncommand -v devbox\n```\n\nIf that prints nothing, install it (non-interactive):\n\n```bash\ncurl -fsSL https://get.jetify.com/devbox | bash\n```\n\ndevbox installs to `~/.local/bin` (or `/usr/local/bin`). If `devbox` still isn't found after installing, ensure that directory is on `PATH`. devbox itself requires the Nix package manager; the install script sets it up if missing.\n\n## How to detect\n\nLook for a `devbox.json` in the working directory or an ancestor:\n\n```bash\nls devbox.json 2>/dev/null || find . -maxdepth 2 -name devbox.json 2>/dev/null\n```\n\nIf one exists, the packages it lists are available *inside* the devbox environment (not the bare shell).\n\n## How to run\n\nRun any command inside the devbox environment with:\n\n```bash\ndevbox run [cmd]\n```\n\nExamples:\n\n```bash\ndevbox run psql \"$MASTER_DB_URL\" -c 'SHOW wal_level;'\ndevbox run python script.py\ndevbox run gcloud sql instances describe <instance>\n```\n\n`devbox run` is the right choice for agents and scripts: it loads the environment, runs one command non-interactively, and exits. The first run in a fresh environment may be slow while packages download/build; later runs are cached and fast.\n\nIf a directory other than the cwd holds the `devbox.json`, run from that directory (e.g. `devbox run --config path/to/devbox.json [cmd]` or `cd` into it first).\n\n## Project scripts\n\n`devbox.json` can define named scripts under a `\"shell\": { \"scripts\": { ... } }` block. Run them by name — `devbox run <script-name>` — instead of retyping the underlying command. For example, this repo defines `test`, `lint`, `fmt`, `build`, and `tidy`, so `devbox run test` runs the project's test suite inside the environment.\n\nTo see what's defined, run `devbox run` with no script name (it lists available scripts) or inspect `devbox.json`.\n\n## Choosing where a binary comes from\n\nWhen you need a binary, decide its source in this order:\n\n1. **Provided by devbox for the current project** — if the project's `devbox.json` supplies it, prefer the devbox version over any system install. Run it via `devbox run [cmd]`.\n2. **Provided by the system but not devbox** — use the system binary.\n3. **Not provided by devbox or the system** — check whether devbox can supply it:\n\n   ```bash\n   devbox search [pkg]\n   ```\n\n   If it's available, **ask the user** whether they want to install it into the current project before doing so. Only on their confirmation:\n\n   ```bash\n   devbox add [pkg]\n   ```\n\n   Don't add packages to a project's `devbox.json` without the user's go-ahead. (`devbox rm [pkg]` removes one.)\n\n## Services\n\nSome projects define long-running services (databases, queues, etc.) in their devbox config:\n\n```bash\ndevbox services ls          # list defined services\ndevbox services up           # start services (add -b to run in background)\ndevbox services stop         # stop them\n```\n\n## Global packages\n\n`devbox global` manages a machine-wide environment that's independent of any project (`devbox global add/rm/list`). Prefer per-project packages unless the user explicitly wants something available everywhere.\n\n## Discovering more\n\n- `devbox --help` — full command list (shell, run, add, services, etc.).\n- `devbox run` without args, or inspect `devbox.json`, to see project-defined scripts.\n- `devbox shell` opens an interactive subshell with the environment loaded (prefer `devbox run` for one-off, non-interactive commands).\n- To see what binaries devbox is adding, run `devbox install` and then inspect the `.devbox/nix/profile/default/bin/` directory.\n\n## Key facts\n\n- devbox is not always installed — check with `command -v devbox` and install via `curl -fsSL https://get.jetify.com/devbox | bash` if missing.\n- Only directories with a `devbox.json` use it.\n- `devbox.json` may live in the repo root **or** a subdir root — check both.\n- Packages/scripts defined in `devbox.json` are only on PATH inside `devbox run`/`devbox shell`, not the plain shell.\n- Prefer `devbox run` over `devbox shell` for non-interactive, one-off commands.\n- The first run after adding packages or in a fresh checkout can be slow (downloading/building); subsequent runs are cached.\n\n<!-- File: examples/plugins/local/my-plugin/plugin.json (Tokens: ~236 | Category: plugin-manifest) -->\n{\n  \"name\": \"my-plugin\",\n  \"version\": \"0.0.1\",\n  \"description\": \"Example custom plugin\",\n  \"env\": {\n    \"MY_FOO_VAR\": \"BAR\"\n  },\n  \"create_files\": {\n    /*\nthis is a comment inside the create files\n    */\n    \"{{ .Virtenv }}/empty-dir\": \"\",\n    \"{{ .Virtenv }}/some-file\": \"some-file.txt\",\n    \"{{ .DevboxDir }}/some-file.txt\": \"some-file.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"shell\": {\n      // this is a comment before init-hooks\n      \"init_hook\": [\n          \"echo \\\"ran local plugin init hook\\\"\",\n          \"export MY_INIT_HOOK_VAR=BAR\"\n      ]\n  },\n  // Aliases defined by the plugin. \"greet\" is overridden by the parent\n  // devbox.json, while \"plugin_only\" is provided solely by this plugin.\n  \"aliases\": {\n      \"greet\": \"echo greeting-from-plugin\",\n      \"plugin_only\": \"echo from-plugin\"\n  }\n}\n\n<!-- File: examples/plugins/local/README.md (Tokens: ~38 | Category: plugin-manifest) -->\n# Custom plugin example\n\nShows how to write custom local plugin. Plugins can:\n\n* Install packages\n* Create templatized files (including flakes)\n* Declare services (using process-compose)\n\n<!-- File: examples/plugins/v2-local/plugin1/plugin.json (Tokens: ~143 | Category: plugin-manifest) -->\n{\n  \"name\": \"plugin1\",\n  \"packages\": [\"hello@latest\"],\n  \"env\": {\n    \"PLUGIN1_ENV\": \"success\",\n    \"PLUGIN1_ENV2\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"create_files\": {\n    \"{{ .DevboxDir }}/foo.txt\": \"foo.txt\",\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  },\n  \"include\": [\n    \"./plugin1a\"\n  ]\n}\n\n<!-- File: examples/plugins/v2-local/plugin1/plugin1a/plugin.json (Tokens: ~100 | Category: plugin-manifest) -->\n{\n  \"name\": \"plugin1a\",\n  \"packages\": [\"cowsay@latest\"],\n  \"env\": {\n    \"PLUGIN1A_ENV\": \"success\"\n  },\n  \"shell\": {\n    \"init_hook\": [\n      \"export PLUGIN1A_INIT_HOOK=success\"\n    ],\n    \"scripts\": {\n      \"plugin_1A_script\": [\n        \"echo success\"\n      ]\n    }\n  },\n  \"include\": [\n    \"../../plugin2\"\n  ]\n}\n\n<!-- File: examples/plugins/v2-local/plugin2/plugin.json (Tokens: ~34 | Category: plugin-manifest) -->\n{\n  \"name\": \"plugin2\",\n  \"create_files\": {\n    \"{{ .Virtenv }}/process-compose.yaml\": \"process-compose.yaml\"\n  }\n}\n\n<!-- File: examples/plugins/v2-local/plugin3/plugin.json (Tokens: ~10 | Category: plugin-manifest) -->\n{\n  \"name\": \"plugin3\"\n}\n\n<!-- File: plugins/README.md (Tokens: ~1697 | Category: plugin-manifest) -->\n# Contributing a Plugin\n\nPlugins make it easier to get started with packages that require additional setup when installed with Nix, and they offer a familiar interface for configuring packages. They also help keep all of your project's configuration within your project directory, which helps maintain portability and isolation.\n\n## Getting Started\n\nBefore writing a plugin, we recommend reading the [User Documentation](https://www.jetify.com/docs/devbox/guides/plugins/) on plugins, as well as inspecting and testing a few of the plugins in this directory. Note that the plugins in this directory are compiled into the Devbox binary, but your plugin can be sourced from a local directory or from within your project.\n\nIf you're looking for plugin ideas, check out our [Issues page](https://github.com/jetify-com/devbox/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+request%22) for any user requests.\n\nBefore contributing, please consult our [Contributing Guide](../CONTRIBUTING.md) and [Code of Conduct](../CODE_OF_CONDUCT.md) for details on how to contribute to Devbox.\n\n### Testing your Plugin\n\n1. Create a new `devbox.json` in an empty directory using `devbox init`.\n2. Add your plugin to the `include` section of the `devbox.json` file. Add any expected packages using `devbox add <pkg>`.\n3. Check that your plugin creates the correct files and environment variables when running `devbox shell`\n4. If you are looking for sample projects to test your plugin with, check out our [examples](https://github.com/jetify-com/devbox/tree/main/examples).\n\n## Plugin Design\n\nPlugins are defined as Go JSON Template files, using the following schema:\n\n```json\n{\n  \"name\": \"\",\n  \"version\": \"\",\n  \"description\": \"\",\n  \"env\": {\n    \"<key>\": \"<value>\"\n  },\n  \"create_files\": {\n    \"<destination>\": \"<source>\"\n  },\n  \"init_hook\": [\n    \"<bash commands>\"\n  ]\n}\n```\n\nA plugin can define services by adding a `process-compose.yaml` file in its `create_files` stanza.\n\n### Plugin Lifecycle\n\nPlugins are activated whenever a developer runs `devbox shell`, runs a script with `devbox run`, or starts a service using `devbox services start|restart`. The lifecycle of a devbox shell with plugins works as follows:\n\n```mermaid\n---\ntitle: Devbox Shell Lifecycle\n---\nflowchart TD\n   A[Plugin env] --> B\n   B[User env] --> C\n   C[Plugin init_hook] --> D[User Init Hook]\n   D -->  E{Start Shell}\n   E --> F & G & H\n   F[Interactive Shell]\n   G[Run Scripts]\n   H[Start Services]\n```\n\n### Template Placeholders\n\nDevbox's Plugin System provides a few special placeholders that should be used when specifying paths for env variables and helper files:\n\n* `{{ .DevboxProjectDir }}` – points to the root folder of the user's project, where their `devbox.json` is stored.\n* `{{ .DevboxDirRoot }}` – points to `<projectDir>/devbox.d`, the shared parent directory of every plugin's `DevboxDir`. (Despite its name, this is *not* the project root — use `{{ .DevboxProjectDir }}` for that.)\n* `{{ .DevboxDir }}` – points to `<projectDir>/devbox.d/<plugin.name>`. This directory is public and added to source control by default. This directory is not modified or recreated by Devbox after the initial package installation. You should use this location for files that a user will want to modify and check-in to source control alongside their project (e.g., `.conf` files or other configs).\n* `{{ .Virtenv }}` – points to `<projectDir>/.devbox/virtenv/<plugin_name>` whenever the plugin activates. This directory is hidden and added to `.gitignore` by default You should use this location for files or variables that a user should not check-in or edit directly. Files in this directory should be considered managed by Devbox, and may be recreated or modified after the initial installation.\n\n### Fields\n\n#### `name` *string*\n\nThe name of your plugin. This is used to identify your plugin when a user runs `devbox info`. If `match` is not set, the plugin will automatically activate when a package is added to a devbox.json project that matches `name`.\n\n#### `version` *string*\n\nThe version of your plugin. You should start your version at 0.0.1 and bump it whenever you merge an update to the plugin.\n\n#### `match` *string*\n\nA regex expression that is used to identify when the plugin will be activated. Devbox will activate your plugin when a package installed with `devbox add` matches this regular expression.\n\nThe regex you provide should match a package name. You can look up packages at `nixhub.io`\n\n#### `readme` *string*\n\nSpecial usage instructions or notes to display when your plugin activates or when a user runs `devbox info`. You do not need to document variables, helper files, or services, since these are automatically printed when a user runs `devbox info`.\n\n#### `env` *object*\n\nA map of `\"key\" : \"value\"` pairs used to set environment variables in `devbox shell` when the plugin is activated. These variables will be printed when a user runs `devbox info`, and can be overridden by a user's `devbox.json`.\n\n#### `create_files` *object*\n\nA map of `\"destination\":\"source\"` pairs that can be used to create or copy files into the user's devbox directory when the plugin is activated. For example:\n\n```json\n\"create_files\": {\n    \"{{ .DevboxDir }}/Caddyfile\": \"caddy/Caddyfile\"\n}\n```\n\nWill copy the Caddyfile in the `plugins/caddy` folder to `devbox.d/caddy/Caddyfile` in the user's project directory.\n\nYou should use this to copy starter config files or templates needed to run the plugin's package.\n\n#### `init_hook` *string | string[]*\n\nA single `bash` command or list of `bash` commands that should run before the user's shell is initialized. This will run every time a shell is started, so you should avoid any resource heavy or long running processes in this step.\n\n### Adding Services\n\nDevbox uses [Process Compose](https://github.com/F1bonacc1/process-compose) to run services and background processes.\n\nPlugins can add services to a user's project by adding a `process-compose.yaml` file to the `create_files` stanza. This file will be automatically detected by Devbox, and started when a user runs `devbox services up` or `devbox services start`.\n\nSee the process compose [docs](https://github.com/F1bonacc1/process-compose) for details on how to write define services in `process-compose.yaml`. You can also check the plugins in this directory for examples on how to write services.\n\n## Tips for Writing Plugins\n\n* Only add plugins for packages that require configuration to work with Devbox.\n* Plugins should try to use the same configuration conventions (environment variables, configuration files) as their packages. This lets developers configure their packages in a way that they are familiar with, using existing documentation.\n* If you think a user may want to override or change a parameter, define it as an environment variable in `env`. This makes it possible for a developer to override the parameter in their `devbox.json` file\n* If you're adding a helper file that you think a developer would want check into source control, create it in `{{ .DevboxDir }}`. If you're creating a file that would not be checked into source control, create it in `{{ .Virtenv }}`.\n* Unless there is a very good reason, we do not recommend creating files outside of `{{ .DevboxDir }}` or `{{ .Virtenv }}`. This helps keep user projects clean and well organized.\n</yakaai_skills>"}