ChangePlan from your initializer or migration. It holds ordered operations; constructing it does not write files.
ChangePlan() when a hook has no work to propose.
File operations
Use ProjectContext.files to read staged files or propose writes, copies, templates, and deletion. The file operation reference explains the result of each operation. Paths must be canonical project-relative paths. Absolute paths, traversal, source symlinks, and reserved state paths such as.git/, .harnest/, and harnest-packs.lock are rejected. File/directory conversions require manual migration.
YAML operations
Use ProjectContext.yaml to read configuration and propose field edits while retaining unrelated values. Use tuples for key paths.("deployment", "region") identifies a nested field; ("deployment.region",) identifies a literal key containing a dot. A rename cannot move a field into itself or its descendants. A scalar parent is a conflict, not an object to replace implicitly.
YAML documents must be mappings with unique keys and a single document. Edits preserve unrelated values but normalise formatting and do not preserve comments.
Write policies
Use the enum values fromharnest.authoring, rather than strings:
IF_MISSING is the default for file writes, verbatim copies, templates, and YAML sets. MANAGED is appropriate when a later pack release updates a generated workflow or an unchanged default.
Add team documentation to the project
Teams can copy Markdown guides, API references, runbooks, PDFs, images, and other files into generated projects. Usefrom_file to preserve the original bytes without decoding text or substituting dollar expressions.
Set templates on your ProjectPack to the directory containing the packaged assets:
source is relative to the pack’s templates directory; path is relative to the generated agent. Sources must be regular files inside the template directory and included in the distributed package.
Each operation copies one file for teammates; these documents are excluded from compilation. Use agent templates for model-facing instructions and skills.
Use policy=WritePolicy.MANAGED in a migration to update an unchanged pack-owned document. Local edits block replacement.
Templates
Settemplates=Path(__file__).parent / "templates" when defining your pack. Without values, template content is copied literally, including dollar signs.
Supplying values enables Python string.Template substitutions:
team-notes.txt
$$ for a literal dollar sign when substitution is enabled. Missing templates or substitutions fail the hook and block application.