Skip to main content
Version: Next

Customize presets

Use an overlay for a small number of customizations. Use an external presets_dir when you need to maintain a complete preset collection.

Choose a starting point:

GoalStart here
Customize a few built-in filesLink an overlay
Maintain your own complete collectionExport presets
Create a new App, Shell, or Sys categoryCreate category metadata
Use an AI assistant to draft a presetCreate with AI

The source choices have different fallback rules:

  • with the built-in base, an overlay replaces matching paths and every unmatched path still comes from the embedded preset;
  • a full external presets_dir is authoritative for app and shell categories. Missing content is not silently borrowed from the binary.

Check Preset Source and Presets Overlay in command output to confirm which files Shine is using.

Create a preset with AI​

Shine ships the portable Agent Skill at skills/shine-preset-author/ in its source and crate package. Register that directory with your AI client's native skill installer or skills-directory mechanism, then ask in ordinary language for an app configuration, shell command, system bootstrap, or customization of a built-in category. Shine does not detect or edit Codex, Claude, Cursor, or other client configuration.

The skill generates a preset, validates it, and previews installation in an isolated directory. It does not activate the preset or run its scripts. Review the result before installing it.

You can use the same flow without an AI client:

mkdir -p my-presets/app/my-editor
cd my-presets/app/my-editor
shine preset new app

Add config files and edit shine.toml.

Validate and package a preset​

Run these checks from the category directory:

shine preset validate . --format json
shine preset lint . --format json
shine preset plan . --platform macos --format json
shine preset test . --format json

preset validate also accepts a repository root, one category directory, or its shine.toml. Root validation scans only direct category directories below app/, shell/, and sys/; an empty root is invalid. It evaluates the macOS, Linux, and Windows declarations on any host, verifies referenced files and locked Bun dependency policy, and reports compatible metadata-free app/shell categories with a legacy_metadata warning. It does not load active source/overlay settings, initialize config, check for updates, write files, access the network, or execute preset code.

The default output is text. --format json is available for tools and CI. Validation errors exit with status 1; warnings do not.

Run shine preset schema --format json when authoring tools need the exact report and fixture formats supported by the installed version. Continue to use preset validate as the acceptance check for App, Shell, and Sys metadata.

Run preset lint after validation to find author-quality and portability issues. Warnings are advisory by default; CI can add --deny-warnings after reviewing them. If lint cannot load the input, run shine preset validate <PATH> to inspect the failure. Lint checks each platform's applicable resources, including PowerShell commands on Windows; App categories or files intentionally unavailable on an OS are skipped for that OS.

Then run preset plan for each target platform. It previews a hypothetical first installation and shows actions, permissions, and blockers without changing the machine. A blocker commonly means the preview lacks a required environment value, trust decision, command, or administrator state; the report is authoring feedback, not approval for a real installation.

Add shine.test.toml when the category needs repeatable cross-platform expectations. Tests describe the starting conditions and expected results without running setup code. Runnable App, Shell, and Sys examples are available under examples/presets. preset test accepts one category, not a repository root. Use relative file bases and placeholders such as ${HOME} in fixture receipts for portable tests.

When you need a distributable artifact, pack the reviewed category outside its source tree:

shine preset pack . --output ../../my-editor.shine-preset.tar.gz --format json

shine.test.toml is for author testing and is not included in the bundle. The output must be outside the category directory, including after resolving symbolic links. Use --force only to replace an existing output; it does not bypass validation or packaging restrictions.

Check App code before execution​

Before distributing App scripts, run:

shine preset lint <PATH>

Shine checks Bun generators, script hooks, artifacts, teardown scripts, and supported local JavaScript/TypeScript dependencies. Fix blocking findings at the reported file and line: declare the access the script needs, or change the script to match its declarations. Parse failures also block execution. Trust, --yes, and opaque_code = "unrestricted" do not bypass these checks.

Common declarations include:

Script behaviorRequired declaration
Fetch from https://example.org/dataNetwork host example.org
Read in with Bun.file('in').text()Filesystem read on in
Copy a file with Node copyFileread on the source and write on the destination
Rename a file with Node renameremove on the source and write on the destination
Read a configured environment valueAn env entry with source, optional target, and sensitivity

The script reads the target name from an environment mapping. Only documented fixed variables are exempt from declaration; a SHINE_ prefix alone is not an exemption. Never put environment values or URL credentials in capability declarations.

lint checks all App code entries, including generators whose inputs are absent. preset plan and preset test check only entries selected by their hypothetical inputs. These commands do not run scripts, download dependencies, or read real environment values.

Coverage gaps—for example, dynamic paths, package dependencies, subprocess effects, or unsupported languages—are advisory. A declaration that the checked source does not confirm is also advisory. A passing check does not prove that code is safe or fully analyzed; review the code and retain the normal trust and approval steps.

Use --format json for complete diagnostics and --deny-warnings when warnings should fail CI. In info or update, --verbose shows more detail without executing generators; explicit evaluation still requires --run-generators and trust.

Tool compatibility​

If your editor or CI consumes JSON reports, run shine preset schema --format json after upgrading Shine and check that the consumer supports the installed version. Use the generated schema for report fields and shine.test.toml fixtures instead of copying version numbers from older examples.

Treat complete = false or a nonempty unfinished_cases list as an incomplete check. A fixture can expect a blocked installation, but cancellation or exhausted analysis limits still mean the test could not finish. Resolve the reported issue before accepting the result.

Migrate a 1.x source​

Preview the active external source and overlay, or name a repository, category, or manifest:

OperationCommand
Preview active external sourcesshine preset migrate --dry-run
Preview this source's migrationshine preset migrate ./my-presets --dry-run
Apply after reviewing the previewshine preset migrate ./my-presets

Review the metadata diff before confirming; the default answer is No. Migration changes only shine.toml and backs it up before writing. It does not change scripts, data files, environment values, installed resources, or trust. Resolve entries reported as needing manual migration; Sys v1 dispatchers require the v2 migration guide.

Use --yes only after review. For automated inspection, use --format json --dry-run; applying with JSON output also requires --yes. JSON reports do not include file contents or diffs.

A Git-managed overlay is read-only. Migrate and validate its upstream checkout, commit and push the changes, then run shine preset pull on each consuming device.

If migration reports mixed LF/CRLF line endings, choose one style for that file and retry.

Describe capabilities and required inputs​

Permission schema v1/v2 tables are optional author capability statements. App statements belong to the category root, each Shell [[files]] command can have [files.permissions], and each Sys [[items]] target can have [items.permissions]. Shine validates any table you provide, but a missing or empty table does not hide or confine arbitrary code. Supported App code still needs accurate declarations for statically identified behavior. Unsupported versions, unknown fields, invalid identities, and duplicates are errors.

[permissions]
schema_version = 1
administrator = true
filesystem = [
{ access = ["read", "write"], base = "home", path = ".config/example" },
{ access = ["execute"], base = "preset", path = "build.ts" },
]
network = [{ scope = "host", host = "api.example.com" }]
commands = ["bun"]
environment = [{ name = "API_TOKEN", sensitivity = "secret" }]
system = [{ capability = "split-dns", resource = "private-domain" }]

Permission schema v2 supports the unrestricted author statement:

[permissions]
schema_version = 2
opaque_code = "unrestricted"

For Shell and Sys categories, use permission_defaults to apply one declaration to entries that do not provide their own target-local override:

[permission_defaults]
schema_version = 2
opaque_code = "unrestricted"

[[files]]
source = "foo.sh"
target = "foo"

[[files]]
source = "bar.sh"
target = "bar"

Defaults are resolved separately for each command or Sys item; a targeted operation does not inherit capabilities from unrelated entries. An entry-level permission table replaces the default for that entry.

Filesystem bases are home, shine, data-dir, preset, or absolute; non-absolute paths are normalized relative paths, with . meaning the selected base root. Commands contain one program identity without arguments. Environment entries contain names and plain/secret sensitivity, never values or ciphertext. Do not repeat commands, script paths, ordinary destinations, or fixed package providers already declared in the preset configuration. Declare statically identified App script effects; additional statements can help reviewers understand intended effects.

opaque_code = "unrestricted" describes code for review; it does not grant trust or administrator access. All scripts run with the access available to their process and are not sandboxed, whether this field is present or absent. Declare environment inputs and administrator requirements explicitly. Capability statements are not proof that all script effects have been disclosed.

Interactive lifecycle operations can authorize the listed external code when you confirm the Plan. This authorization applies only to that operation and captured snapshot; it saves no persistent grant. The confirmation includes code delivered for later execution, such as installed Shell commands. Automation and --yes require an existing matching grant; --yes cannot supply human code consent. Explicit info/update --run-generators evaluation also requires a grant; use interactive shine app refresh when you want to approve a generator for one operation.

For persistent authorization, inspect the target first, then choose snapshot or development trust. Development trust also authorizes future code changes; use it only when you intend that scope.

OperationCommand
Inspect without granting trustshine trust inspect <TARGET>
Grant snapshot trustshine trust grant <TARGET>
Trust future changesshine trust grant <TARGET> --development

Snapshot trust covers the reviewed target and effective category content. Changes to files, metadata, source layers, or executable permissions require renewed review. Dependencies such as node_modules, interpreters, external tools, and downloads are not protected by this grant.

Development trust is long-term authorization of future code from the displayed local source for the specified target and capability. Source directory/layer changes and new targets or capabilities need new authorization. It does not mean continuing code review. Shell live deployment requires this mode. When live deployment is blocked, review the target with shine trust inspect <TARGET> before choosing shine trust grant <TARGET> --development. If you do not want to authorize future code changes, set external_shell_mode = "snapshot" and review again; ordinary snapshot trust cannot authorize live deployment. opaque_code = "unrestricted" does not enable development trust. Trust is granted locally; a Preset cannot distribute or enable its own authorization. Use trust list to check grants and trust list --verbose for details and review guidance.

Every external Shell command uses the canonical shell/<CATEGORY>/<COMMAND> trust target. Use preset to inspect or trust every executable target in the current active Preset. Revoking preset clears every stored Preset trust grant:

Inspect the target first, then choose only the required grant or revoke operation. Development trust also authorizes future code changes; use it only when you intend that scope.

OperationCommand
Inspect without granting trustshine trust inspect preset
Grant snapshot trustshine trust grant preset
Trust future changesshine trust grant preset --development
Revoke trustshine trust revoke preset

Older grants marked as needing review cannot authorize new operations. Review and grant them again.

AI tools may generate and statically validate Presets, but cannot approve their own code. Review the actual generated changes through a trusted human interface. A changed snapshot invalidates the approval. Development trust for an AI-writable directory permits future AI edits for enrolled targets. An AI with unrestricted terminal access can grant trust or execute scripts directly; its tool host must enforce that authority boundary.

From source folders to installed capabilities​

Any tool or process that places a preset folder on a machine can be the synchronization layer. Shine does not require Git or provide general-purpose folder synchronization. It turns selected source files into installed capabilities: it creates managed command entries, resolves local values, keeps an installed snapshot by default, reports pending changes, and removes only what it owns.

The built-in shell/image-tools/ category is a complete example. It exposes three image commands through this metadata:

description = "Personal image workflow commands."

[[files]]
source = "compress.ts"
target = "img-compress"
runtime = "bun"
platforms = ["unix", "windows"]
env = [{ source = "IMAGE_QUALITY", sensitivity = "plain" }]

[[files]]
source = "resize.ts"
target = "img-resize"
runtime = "bun"
platforms = ["unix", "windows"]
env = [{ source = "IMAGE_QUALITY", sensitivity = "plain" }, { source = "IMAGE_MAX_WIDTH", sensitivity = "plain" }, { source = "IMAGE_MAX_HEIGHT", sensitivity = "plain" }]

[[files]]
source = "convert.ts"
target = "img-convert"
runtime = "bun"
platforms = ["unix", "windows"]
env = [{ source = "IMAGE_QUALITY", sensitivity = "plain" }]

These commands compress, resize, and convert JPEG, PNG, and WebP. Every machine that runs them needs Bun 1.3.14 or newer in PATH; Shine does not bundle Bun. No separate image library is needed.

The workflow below uses all three commands, so install the whole category:

shine info shell/image-tools
shine install shell/image-tools

After installation, open a new terminal or reload your shell profile as described in Shell presets, then run the commands:

img-compress photo.jpg screenshots/
img-resize --width 1280 --output-dir ./resized photos/
img-convert --format webp --quality 75 --output-dir ./webp hero.png gallery/
shine info shell/image-tools --diff
shine upgrade shell/image-tools
shine shell uninstall image-tools/img-compress --dry-run

The last command previews removal of img-compress only. If you only need that command in the first place, install shell/image-tools/img-compress instead of the whole category.

Each command accepts multiple file or directory inputs. A directory scan processes direct JPEG, PNG, and WebP children only; it never recurses. Without --output-dir, output stays beside its source as photo.compressed.jpg, photo.resized.jpg, or the selected conversion extension. An output directory flattens the selected inputs, so duplicate target names fail explicitly. Existing files also fail unless --force is present, and source images are never modified in place. --force does not permit replacing an input image.

Batch processing continues after an individual failure and returns a nonzero status if any item failed. The first 20 failures appear in the terminal. If there are more, the complete list is also written to a uniquely named image-tools-errors-*.log under --output-dir, or the current directory when no output directory was supplied.

IMAGE_QUALITY, IMAGE_MAX_WIDTH, and IMAGE_MAX_HEIGHT default to 80, 1920, and 1080. Command options override them for one invocation; changing the Shine values keeps the preference on that machine. In the default snapshot mode, changing a copied or external source still requires shine upgrade before the installed command changes.

Override selected files with an overlay​

An overlay replaces base-preset files at the same relative path and can add new categories:

shine preset overlay link ~/dotfiles/shine-overlay --create
shine preset overlay info

When you want to stop using this overlay:

shine preset overlay unlink

Unlink removes overlay settings from the active configuration; it does not delete source files. Removing a project override can restore an inherited global overlay. Run shine preset overlay info to inspect the effective overlay afterward.

For example, app/starship/starship.toml overrides the file at the same path in the base source; other presets continue to use the base source.

Mirror an overlay from Git​

Shine can manage a local mirror of a read-only overlay repository used by multiple devices:

Git-managed overlays are global settings. Run the following commands outside any Shine project: neither the current directory nor its ancestors may contain a shine.config.toml. Inside a Shine project, overlay link --git is rejected before changing the configuration or mirror. For a project-specific overlay, clone the repository yourself and use shine preset overlay link <PATH> from the project instead.

shine preset overlay link --git https://example.com/team/shine-overlay.git --branch main
shine preset pull
shine preset overlay info

The managed copy lives under ~/.shine/overlay/. Each pull replaces it with the remote branch's latest state and discards local edits. Make changes and push them in an upstream checkout, then synchronize each device with shine preset pull, shine update --pull, or shine upgrade --pull. Relinking with a different Git URL switches the existing mirror to that repository. Without --branch, each sync follows the configured remote's default branch. A failed fetch preserves the previous checkout.

The managed overlay must be a standalone Git checkout at that directory. Shine refuses ordinary directories, linked checkouts, and linked Git metadata without changing their contents. Move an unexpected directory aside before retrying. After a successful fetch, synchronization discards tracked edits, untracked and ignored files, and nested repositories in the managed mirror, even when the remote commit is unchanged. Keep local customizations in a manually linked overlay instead.

To customize one built-in category, copy it at the overlay root instead of exporting everything. For example, copy Surge before editing its local proxy, group, and rule files:

cd ~/dotfiles/shine-overlay
shine preset copy app/surge

The command accepts complete app/<name>, shell/<name>, or sys/<name> categories. Existing files are overwritten only with --force. The overlay replaces only paths you keep, so delete copied files you do not customize and let them continue to come from the built-in version. See Manage application configuration for the remaining Surge steps.

Export the complete preset collection​

shine preset link ~/dotfiles/shine-presets --create
shine preset export

Export includes built-in presets plus overrides and additions from the active overlay. It does not copy files that exist only in the external base directory. preset copy always extracts the original built-in category.

Replace <PATH> in activation commands with your actual directory, quoted as one shell argument.

Both preset copy and preset export reject linked or special output files and linked subdirectories, including with --force. Choose an ordinary directory tree for the output. Without --force, existing regular files are preserved.

After linking an external directory, install, list, and update read from it. Command output identifies the active preset source.

You can also select the source for one command through an environment variable:

SHINE_PRESETS=~/dotfiles/shine-presets shine preset export

Choose how external shell presets are deployed​

External shell presets use snapshot mode by default. During installation, Shine copies the effective category under ~/.shine/installed/shell/ and commands execute the managed copy. After editing the source, run shine update to inspect changes and shine upgrade to apply them. This gives shell scripts the same review-before-update flow as application configuration. update reports legacy direct-link installations, and upgrade migrates them.

For preset development, explicitly enable live mode when linking the source:

shine preset link ~/dotfiles/shine-presets --live

In live mode, ordinary shell and Bun source changes take effect on the next invocation. Files with transforms are rendered before each call; a rendering failure aborts that call rather than executing stale output. Changes to entry metadata such as target, runtime, transforms, or env still require shine upgrade to rebuild the managed entry. Restore snapshot mode by running shine preset link <PATH> without --live, or use shine preset unlink.

If a linked overlay or live preset directory is moved, link the new path and run shine update. Snapshot deployments stay current when their effective relative files and bytes are unchanged. Live deployments report the old and new source paths because shine upgrade must repoint their managed command entries; this relocation is shown separately from content changes.

Use locked packages in external Bun presets​

Built-in Bun presets remain self-contained. External presets and overlays may use ordinary registry packages by committing both package.json and bun.lock in the same physical category directory as the effective script:

shell/my-tools/
├── shine.toml
├── package.json
├── bun.lock
├── command.ts
└── shared.ts

This convention also applies to Bun app artifact, teardown, and generator scripts under app/<category>/. Both files are required. Shine rejects a lone manifest or lock and, in this first version, any trustedDependencies declaration. An overlay declaration applies only when the overlay supplies the effective script; adding package files beside an inherited built-in script does not enable dependencies for it.

Scripts without the required manifest and lock file cannot download packages automatically. A locked external script may download missing packages on its first execution; list and info do not fetch them. Shine does not copy node_modules or clear Bun's shared caches when you uninstall a preset or Shine itself.

For snapshot Shell presets, package or lock changes appear in shine update and take effect after shine upgrade. In live mode they are read on the next command invocation, while status still reports that the installed command should be refreshed. Fully offline machines need the relevant Bun cache already populated, or a bundled/vendored script. Native extensions, workspaces, file:, link:, and dependencies requiring lifecycle scripts are not guaranteed in this version.

To migrate an external script that currently relies on Bun's implicit installation, create its category-local package.json, generate bun.lock with the repository's Bun version, commit both, and test from an empty Bun cache. Without the pair, bare package imports now fail instead of being downloaded automatically.

Create a commit-ready preset repository​

cd ~/dotfiles/shine-presets
shine init

This creates shine.config.toml and sets presets_dir to the current directory. Shine searches from the working directory upward for the nearest project configuration, so commands work in its subdirectories. Use shine init --yes in non-interactive scripts.

Pull Git-managed sources​

When the external preset directory or a manually linked overlay is a Git worktree, pull sources alone or pull before inspecting or applying configuration:

Choose one pull/update operation:

OperationCommand
Pull preset sources onlyshine preset pull
Pull, then check for updatesshine update --pull
Pull, then apply configuration updatesshine upgrade --pull

Shine locates the repositories containing the base presets and active overlay. If both sources are in one repository it pulls only once; non-Git sources are skipped. update --pull and upgrade --pull reload configuration after pulling, so an updated shine.config.toml affects later steps.

Every repository processed must meet these conditions before any pull begins:

  • the worktree has no tracked or untracked changes;
  • HEAD is on a branch, not detached;
  • the current branch has an upstream.

These sources use git pull --ff-only. Shine never stashes, rebases, resets, or resolves conflicts. Validation stops before modifying any repository. These restrictions do not apply to an overlay managed with --git, which is intentionally disposable. Git must be available in PATH.

Create category metadata​

Generate a shine.toml template in an application, shell, or sys category directory:

Choose the category type; run only its command:

OperationCommand
Create an App templateshine preset new app
Create a Shell templateshine preset new shell
Create a Sys templateshine preset new sys

To start from a built-in category, run shine preset copy <kind>/<name> from your repository or overlay root. It creates the kind/category path.

Templates omit an empty permission table. Add --unrestricted if you want the unrestricted author statement; it does not grant trust, skip approval, or sandbox the script. See capability declarations.

Existing files require --force; review them before replacing the template. After editing metadata, first run shine preset validate . --format json, then use the relevant isolated installation --dry-run command. Shell install dry-run previews command entries without installing or modifying files.

Give an application file its own destination​

An application category has a default dest, but an explicit [[files]] entry may override that root. target stays relative to the selected root:

dest = "~/.config/my-app"

[[files]]
source = "config.toml"
target = "config.toml"

[[files]]
source = "shared/rules.list"
target = "rules/provider.list"
dest = { base = "data-dir", path = "com.example.my-app" }

The override accepts the same absolute string or platform mapping as category destinations. Mappings accept exact macos, linux, and windows keys plus unix as a macOS/Linux fallback; an exact key wins when both are present. A missing branch omits that category or file on the corresponding OS. platforms arrays on explicit App and Shell files use the same four selectors, combine them with OR semantics, and must not be empty. The structured data-dir form is file-only and resolves the platform's user application-data root: %APPDATA% on Windows, Application Support on macOS, and XDG_DATA_HOME (or ~/.local/share) on Linux. path and target must be relative and cannot contain ...

Legacy App presets without shine.toml still support a # shine-dest: full destination path on the first line, or immediately after a shebang. The path must be absolute after home expansion and cannot contain ... Replace parent components with an explicit destination before retrying.

Shine rejects two entries that resolve to the same destination before writing anything. If a later metadata revision moves an already managed source, shine upgrade moves it only when the old copy is unmodified and the new destination is free. Otherwise both locations are left untouched for the user to resolve.

Declare runtime environment inputs​

Shell Bun commands and App generators, hooks, and artifacts use structured env entries:

env = [
{ source = "API_URL", sensitivity = "plain" },
{ source = "SERVICE_TOKEN", target = "API_TOKEN", sensitivity = "secret" },
]

target defaults to source. Declare names and sensitivity only, never values or ciphertext. Each entry receives only its own declared Shine inputs; a category's other inputs are not added. Sensitivity must agree wherever the same source is declared in the effective permission scope. It describes the permission contract and does not itself encrypt or decrypt a value. App inputs keep their existing configured-value behavior; Shell inputs retain encrypted-first lookup. Generator when_env still names a source from its env list and remains a separate condition.

Legacy string arrays such as env = ["SOURCE=TARGET"] are no longer accepted in preset metadata. Relevant commands report a migration hint; unrelated commands do not automatically migrate files. Review and apply the conversion explicitly:

OperationCommand
Preview this source's migrationshine preset migrate ./my-presets --dry-run
Apply after reviewing the previewshine preset migrate ./my-presets

Migration reads the old explicit sensitivity declarations, preserves aliases and conditions, and backs up metadata before applying changes. Missing or conflicting sensitivity requires manual correction. Git-managed overlays must be migrated in their upstream checkout. Migration does not run scripts, decrypt values, grant trust, or update installed launchers and recovery records. Changed external snapshots require renewed trust review. Use normal install/upgrade after migration. The command-line shine env run --with SOURCE=TARGET syntax is unchanged.

Shell entries with an optional runtime​

runtime selects the runtime for a shell preset command entry; it does not add an interactive shell. Without it, entries use native .sh or .ps1 files. The only configurable alternative today is bun:

[[files]]
source = "my-tool.ts"
target = "my-tool"
runtime = "bun"
platforms = ["unix", "windows"]
env = [{ source = "API_URL", sensitivity = "plain" }, { source = "SERVICE_TOKEN", target = "API_TOKEN", sensitivity = "secret" }]

Supported extensions are .ts, .js, .mts, and .mjs. Shine creates a managed entry without an extension, which users invoke as my-tool. Existing native .sh and .ps1 entries remain compatible.

Every device that runs the command needs Bun in PATH. Shine does not install Bun or manage node_modules; external categories may opt into Bun-managed locked packages as described above. runtime = "bun" cannot be combined with needs_source = true.

The optional env list applies only to Bun entries and follows the environment input format. For each declared source, Shine prefers SOURCE_SECRET, then plain SOURCE. It does not add the workspace's default secret bindings. When env is declared, the machine must also have shine in PATH. Declare only names and sensitivity in metadata, never values or ciphertext.

Python, Node, and Deno are not currently valid runtime values.

Author a system bootstrap item​

A sys category is one OS directory such as sys/ubuntu/. Every executable sys preset declares version = 2, then describes ordinary ensure-present software with detection and a fixed provider:

version = 2

[[items]]
id = "mise"
label = "mise"
description = "Install mise without managing its versions."

[items.detect]
kind = "command"
command = "mise"
version_args = ["--version"]

[items.install]
kind = "package"
provider = "homebrew" # homebrew-cask, apt, or winget are also supported
package = "mise"

[[items.shell]]
shells = ["bash", "zsh"]
phase = "post"
when_command = "mise"
eval = ["mise", "activate", "{shell}"]

[profiles.recommended]
items = ["mise"]

Detection supports command, path, and any command/path probes. Package installation ensures the software is present; it does not upgrade an existing installation. A complex item may use [items.install] kind = "script", path = "install/<item>.sh"; the script handles only that item, returns a normal exit code. Every init item must declare both detect and install; there is no platform-wide dispatcher fallback. Version 1 manifests are rejected before detection or profile writes; see the v2 migration guide.

Shell integrations accept exactly one of path, env, eval, source, aliases, or fragment. Use profile/base.pre.sh and profile/base.post.sh only for OS-wide content; put complex item logic in profile/<item>.sh. Phase, optional priority, manifest order, and declaration order determine stable composition. Named [profiles.*] tables select bootstrap items; they do not define shell content or disable integrations outside the selection.

External sys install scripts and executable profile content (eval, source, fragments, and base files) require either human confirmation of the current lifecycle Plan or an existing matching grant; the project config and Preset cannot authorize themselves. A snapshot grant is invalidated by changed category content or source layer; an explicit development grant accepts code edits only from its enrolled source. If trust is missing during bootstrap preflight, no installer has run yet. Static detection, package metadata, PATH, env, and aliases remain available without a grant. Validate with shine sys list, shine sys info <ITEM>, and shine sys bootstrap <ITEM> --dry-run.

Application artifact runtimes​

An application's [artifact] can also use Bun for cross-platform apply and teardown scripts:

[artifact]
script = "build.ts"
teardown = "unbuild.ts"
runtime = "bun"
env = [{ source = "PROFILE_PATH", sensitivity = "plain" }, { source = "API_TOKEN", sensitivity = "secret" }]

The default is native, which executes the script directly. bun accepts only .ts, .js, .mts, or .mjs and requires Bun on the machine. env is the only Shine [env] allowlist passed to the artifact. Each entry declares its source, optional target alias, and sensitivity together; no duplicate [permissions].environment entry is needed. Listed sources are forwarded only when configured; missing optional values are omitted. Fixed application path variables are listed below. External presets require human confirmation of the operation plan or a matching trust grant.

Run a script after an App configuration changes​

Declare post_install or post_upgrade at the top level of shine.toml, before any table headers. Each accepts one hook or a list. For example:

post_upgrade = [{ script = "refresh.ts", runtime = "bun", env = [{ source = "API_URL", sensitivity = "plain" }, { source = "API_TOKEN", sensitivity = "secret" }] }]

[permissions]
schema_version = 1
network = [{ scope = "host", host = "api.example.com" }]

Use exactly one of command or script per hook. A command hook runs a program with optional args; a script hook runs a category-relative script and may specify runtime = "bun". runtime is valid only with script. Use the same Bun file extensions and dependency rules as artifacts. Declare the actual access needed by your script; the example assumes requests to api.example.com.

Command hooks require their declared inputs to be configured. Script hooks omit absent optional inputs, so the script must handle them. Required missing inputs block approval. External hooks need the same review or trust as other App code. Do not call artifact lifecycle commands from a hook; use an explicit artifact operation instead. See hook behavior for when hooks run and how failures are reported.

Paths available to App scripts​

Generators, script hooks, artifacts, and teardown scripts receive these variables without an env declaration. Command hooks do not receive this App-specific set.

VariableUse
SHINE_APP_IDThe application category name
SHINE_APP_DIR, SHINE_APP_SOURCE_DIRThe files provided to the current script; also its working directory
SHINE_APP_OVERLAY_DIROverlay files for the current run; empty when there is no overlay
SHINE_CONFIG_DIRThe active Shine configuration and state root
SHINE_STATE_DIRPersistent output for this application category
SHINE_CACHE_DIRCache output for this application category

The script source directories are temporary. Do not save their paths or write persistent results there; use SHINE_STATE_DIR or SHINE_CACHE_DIR, creating the output directory when needed.