Pack manifest reference
pack.yml declares a pack. It requires a hyphen-separated alphanumeric id,
non-empty author and license values, and a semantic version. Empty content
collections remain valid during incremental authoring. name, description,
homepage, tags, and draft are optional metadata. Discovery and search
exclude manifests missing author or license attribution. They also exclude
draft packs unless the consumer passes --allow-draft.
id: example-documentation-standard
name: Documentation standard
version: 1.0.0
license: MIT
description: Adds the engineering documentation standard.
tags:
- documentation
- engineering
managedFiles:
- source: targets/docs/standard.md
target: docs/standard.md
Release layout
Organize a catalog by pack ID and immutable release version. Keep complete
managed files under targets/, mirroring their default project paths, and keep
merge inputs under fragments/<target>/:
packs/
example-documentation-standard/
1.0.0/
pack.yml
targets/
docs/
standard.md
fragments/
package.json/
example-documentation-standard.json
instructions/
setup.md
Omit directories the release does not use. Use scripts/ for packed lifecycle
programs, fixtures/ for isolated lifecycle test workspaces, and examples/
for content that is not installed. Folder names explain purpose; only manifest
selectors install content.
| Field | Rules |
|---|---|
id | Required stable identifier: alphanumeric segments joined by single hyphens. |
version | Required Semantic Version. |
name | Optional non-empty human-readable name. |
author | Required non-empty author or maintainer attribution. |
draft | Optional visibility marker. Defaults to false; drafts require --allow-draft in discovery and search but remain available to direct commands. |
homepage | Optional absolute HTTP or HTTPS URI. |
license | Required non-empty license identifier or expression. |
managedFiles | Each entry has one source, directory, or glob selector and a non-empty project-relative target that cannot contain . or .. segments. |
packs | Each composite reference has a hyphen-separated alphanumeric ID and an exact version. Optional condition selects whether the reference participates in lifecycle planning. |
parameters | Ordered identifier-named string, bool, or enum declarations. Enums require unique values and may set multiple: true; multi-select defaults are unique arrays of allowed values. |
Reference parameters | String, Boolean, or unique string-array bindings for a referenced pack. Runtime validation checks arrays against the target multi-select enum. |
condition | Boolean, comparison, membership, and isDefault(identifier) expressions joined with logical operators. |
strategy | copy with overwrite, fail-if-exists, skip-if-exists, or backup-and-overwrite; or merge with lines, section, or json. |
tags | Optional list of up to 15 unique, non-empty tags. Search matches tags; discover lists them, and inspect previews the first five. |
template | Enables Scriban parsing. Defaults to false; set true only when this source uses parameters or Scriban functions. |
hooks | Ordered script or instruction declarations grouped by lifecycle event. |
Parameters
Parameter declaration order is prompt order. Luna prompts each root pack's parameters before walking its references, then follows active references in manifest order. When a reference condition is false, parameters belonging only to that inactive branch are not prompted. A shared dependency remains promptable when another active reference reaches it.
Use required: true for an unconditional input or requiredWhen for a
conditional input. These properties are mutually exclusive. requiredWhen
uses the same Boolean, comparison, membership, isDefault(identifier), &&,
||, !, and parenthesized expressions as condition. Every parameter named
by requiredWhen must appear earlier in the same parameters mapping.
parameters:
features:
type: enum
multiple: true
values: [api, web]
default: [api]
useDefaults:
type: bool
default: true
apiName:
type: string
requiredWhen: isDefault(useDefaults) && "api" in features
apiName is required only when both predicates are true. Do not alphabetize
parameters when their prompt dependency requires another declaration first.
Lifecycle hooks
Optional hooks arrays run or display ordered work around install, update, and
uninstall.
Each declaration is a script or instruction. Script hooks use either a
direct executable command or a packed file with an explicit runner. Arguments
are Scriban templates rendered from resolved pack parameters. Each rendered
item remains one literal argv value, not part of a shell command string.
hooks:
preInstall:
- type: instruction
file: instructions/setup.md
templating: true
condition: isDefault(projectType)
- type: script
file: scripts/setup.ps1
runner: pwsh
arguments:
- -ProjectType
- '{{ projectType }}'
description: Configure project tooling.
postUpdate:
- type: script
command: dotnet
arguments:
- tool
- restore
postUninstall:
- type: instruction
file: instructions/cleanup.md
Supported hook names are preInstall, postInstall, preUpdate, postUpdate,
preUninstall, and postUninstall. Script items require type: script. Packed-file scripts require
both file and runner; direct scripts require command. Instruction items
require type: instruction and a pack-relative Markdown file; optional
templating: true enables Scriban before display. Hook order within each event
is significant. Optional condition uses the same parameter expressions as a
managed file and omits the hook when false. isDefault(parameterName) compares
the resolved value with an explicitly declared default. A composite reference
can set disabledHooks to suppress every typed hook in selected events for that
transient pack.
id: example-application-foundation
version: 1.0.0
license: MIT
packs:
- id: example-documentation-standard
version: 1.0.0
disabledHooks:
- preInstall
- postUpdate
Consumers select luna install, luna update, and luna uninstall behavior with
--scripts prompt|run|skip. Prompt mode requires a matching source or
source-plus-pack trust entry, or interactive approval for each untrusted hook.
Run mode permits all non-suppressed scripts for one invocation; skip mode runs
no scripts. --skip-instructions independently prevents instruction loading
and display. Scripts execute with the invoking user's authority, so authors
should keep them minimal and describe externally visible effects. Instructions
never execute and do not use script trust. Argument and instruction rendering
occurs before dry-run formatting; script arguments render before trust
authorization. command, runner, and file remain literal. See
Use Scriban templates.
Uninstall resolves hooks from the exact releases recorded in the lock file. If their source is unavailable, LunaPack warns and proceeds without uninstall hooks.
Manifests using the former top-level scripts map must migrate to hooks, turn
each event value into an array, and add type: script to every declaration.
LunaPack rejects the old shape so mixed ordering cannot be ambiguous.
CLI authoring migration is direct:
- Replace
luna pack add script ...withluna pack add hook script .... - Replace
luna pack rm script <event>withluna pack rm hook <event> <position>. - Replace
luna pack scriptswithluna pack hooks.
The version directory containing the manifest is the pack root. Source selectors read from that root; directory and glob matches retain their relative paths below the target.
Managed-file target values are portable pack defaults. Consumers can remap
them in project-level lunapack.yml configuration or through luna install
options; pack authors do not declare consumer remapping in pack.yml. See
Remap managed targets for
mapping syntax, precedence, lifecycle retention, and explicit relocation.
LunaPack rejects rooted and escaping targets. Before mutation it also rejects existing symbolic-link, junction, or reparse-point ancestors inside the workspace. Replacing a hard-linked target creates a new file identity so other names retain their prior content. A same-user process can still race path inspection and use; do not run LunaPack concurrently with an untrusted local process that can modify the workspace.
Selected source files copy as-is unless their selector sets template: true.
Template-enabled files are UTF-8 Scriban templates. Resolved parameter names are
available as template globals, and supported date-time functions are available.
Templates cannot access the filesystem, host services, or custom functions.
See Add managed content for selector and copy behavior, and Merge content into shared files for exact line, section, and JSON merge semantics.