[Feature] Add Extensible Lifecycle Hooks for Schemas and Project Config#701
[Feature] Add Extensible Lifecycle Hooks for Schemas and Project Config#701lsmonki wants to merge 34 commits intoFission-AI:mainfrom
Conversation
…ission-AI#682) Add hooks system that allows schemas and projects to define LLM instructions at operation lifecycle points (pre/post for new, archive, sync, apply). - Schema YAML and project config support optional `hooks` section - New `openspec hooks` CLI command resolves and returns hooks for a lifecycle point - Without --change, schema is resolved from config.yaml's default schema field - Skill templates updated to call hooks at all lifecycle boundaries - 23 new tests covering parsing, resolution, CLI, and edge cases
…ifecycle points Consolidate the standalone `openspec hooks` command into `openspec instructions --hook <lifecycle-point>`, add pre-verify and post-verify lifecycle points (10 total), update all skill templates to use the new invocation, and document the unified instructions command.
Add 4 new lifecycle points (14 total). The ff skill fires pre-ff/post-ff around the entire operation and pre-continue/post-continue for each artifact iteration within it. The continue skill fires pre-continue/post-continue around each artifact creation.
Complete the lifecycle hooks coverage across all skills (20 total lifecycle points). Add pre/post hooks for explore, bulk-archive, and onboard. Bulk-archive also fires pre-archive/post-archive per individual change within the batch.
- Use trimEnd() instead of trim() to preserve leading whitespace in hook instructions - Reject --schema in hook mode with explicit error instead of silently ignoring it - Clarify pre-new hook wording: schema hooks may apply if config.yaml sets a default schema (not "config-only") - Unify hook ordering note across all skill templates: consistently state "schema hooks first, then config hooks"
…mand The validation is already performed inside resolveHooks() in instruction-loader.ts. The CLI-level check for missing argument (undefined) is kept since it's specific to the command interface.
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds lifecycle hooks support: new hook types and VALID_LIFECYCLE_POINTS, resolveHooks merging schema+config hooks, CLI --hook integration, template changes to invoke hooks pre/post stages, new specs/design/docs/tasks, and comprehensive unit/CLI tests and .gitignore additions. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant CLI as "CLI (instructions)"
participant HooksCmd as "hooksCommand"
participant Resolver as "resolveHooks"
participant Schema as "Schema (.openspec or change metadata)"
participant Config as "ProjectConfig"
participant Skill as "Skill / Agent"
User->>CLI: openspec instructions --hook pre-apply --change myChange --json
CLI->>HooksCmd: hooksCommand(pre-apply, {change: "myChange", json: true})
HooksCmd->>Resolver: resolveHooks(projectRoot, "myChange", "pre-apply")
Resolver->>Schema: Load schema for myChange (metadata or config default)
Schema-->>Resolver: schema hooks (if any)
Resolver->>Config: Load project config hooks
Config-->>Resolver: config hooks (if any)
Resolver->>Resolver: Merge (schema hooks first, then config hooks)
Resolver-->>HooksCmd: [ResolvedHook{source, instruction}...]
HooksCmd-->>CLI: JSON output { lifecyclePoint, changeName, hooks }
CLI-->>User: display JSON
rect rgba(100, 150, 200, 0.5)
Note over Skill,CLI: Skill fetches hooks and executes them in order
Skill->>CLI: openspec instructions --hook pre-apply --change myChange --json
CLI->>HooksCmd: retrieve hooks
HooksCmd-->>Skill: return hooks
Skill->>Skill: Execute hooks sequentially (schema then config)
Skill->>Skill: Proceed with main workflow step
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile OverviewGreptile SummaryThis PR adds a comprehensive lifecycle hook system that enables schemas and projects to inject LLM instructions at 20 operation boundaries (pre/post for all 10 operations). The implementation is well-architected, thoroughly tested, and fully backward-compatible. Key Changes:
Implementation Quality:
Testing Coverage:
Confidence Score: 5/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant Agent as LLM Agent
participant CLI as OpenSpec CLI
participant Hooks as Hooks Command
participant Loader as Instruction Loader
participant Config as Config Parser
participant Schema as Schema Resolver
Note over Agent,CLI: Hook Execution at Lifecycle Point
Agent->>CLI: openspec instructions --hook pre-archive --change "my-change" --json
CLI->>Hooks: hooksCommand("pre-archive", {change: "my-change"})
Hooks->>Loader: resolveHooks(projectRoot, "my-change", "pre-archive")
Note over Loader: Resolution Phase 1: Schema Hooks
Loader->>Schema: resolveSchema("my-change")
Schema-->>Loader: schema.yaml with hooks
Loader->>Loader: Extract schema.hooks["pre-archive"]
Note over Loader: Resolution Phase 2: Config Hooks
Loader->>Config: readProjectConfig(projectRoot)
Config-->>Loader: config.yaml with hooks
Loader->>Loader: Extract config.hooks["pre-archive"]
Loader-->>Hooks: [{source: "schema", instruction: "..."}, {source: "config", instruction: "..."}]
Hooks->>Hooks: Format JSON output
Hooks-->>CLI: JSON with hooks array
CLI-->>Agent: {"lifecyclePoint": "pre-archive", "changeName": "my-change", "hooks": [...]}
Note over Agent: Agent executes each hook instruction in order
Agent->>Agent: Execute schema hook instruction
Agent->>Agent: Execute config hook instruction
Agent->>Agent: Continue with lifecycle operation
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Fix all issues with AI agents
In `@openspec/changes/add-lifecycle-hooks/design.md`:
- Line 239: The design doc's statement "Start with 10 lifecycle points only" is
out of sync with the implementation; update the text in design.md to reflect the
actual count defined in types.ts by changing "10 lifecycle points" to "20
lifecycle points" so it matches the VALID_LIFECYCLE_POINTS array in types.ts
(and keep any explanatory sentence about CLI/LLM cost unchanged).
In `@openspec/changes/add-lifecycle-hooks/specs/lifecycle-hooks/spec.md`:
- Around line 3-4: The Purpose paragraph currently enumerates only "pre/post
archive, sync, new, apply, verify" which is incomplete; update the Purpose
section in specs/lifecycle-hooks/spec.md (the "Purpose" heading and the sentence
referencing operations and the `openspec instructions --hook` flag) to either
list all ten operations (including explore, continue, ff, bulk-archive, onboard)
or replace the explicit list with a general phrase like "pre/post for each
operation" so it matches the full operation list shown later.
In `@openspec/changes/add-lifecycle-hooks/tasks.md`:
- Around line 41-42: The checklist in tasks.md has skipped numbers 6.7 and 8.7
(jumping 6.6→6.8 and 8.6→8.8); update the numbering to remove the gaps so the
sequence is contiguous (e.g., renumber the current 6.8→6.7 and 8.8→8.7), and
ensure the related entries referencing getContinueChangeSkillTemplate,
getOpsxContinueCommandTemplate, getFfChangeSkillTemplate, and
getOpsxFfCommandTemplate remain correct after renumbering.
In `@openspec/config.yaml`:
- Around line 29-69: The config currently contains 20 test scaffolding lifecycle
hooks under the hooks key (e.g., pre-explore, post-explore, pre-new, post-new,
pre-continue, post-continue, pre-ff, post-ff, pre-apply, post-apply, pre-verify,
post-verify, pre-sync, post-sync, pre-archive, post-archive, pre-bulk-archive,
post-bulk-archive, pre-onboard, post-onboard) that instruct the LLM to append to
HOOKSTEST.md; remove these test hooks from the production openspec config and
either (a) delete the entries so no test instructions are shipped, (b) replace
them with real project-specific lifecycle instructions if needed, or (c) move
them into a dedicated test fixture/config used only by acceptance tests; ensure
the hooks root no longer contains the HOOKSTEST.md append instructions and
update any test harness to point at the test fixture if you choose to move them.
In `@src/core/artifact-graph/instruction-loader.ts`:
- Around line 403-405: resolveHooks currently calls readProjectConfig without
error handling, causing filesystem errors to throw; wrap the call to
readProjectConfig in a try/catch (mirroring generateInstructions) so that on
error you set config to undefined (or null) and continue building the hooks
array, optionally logging the error if a logger is available; update the const
config = readProjectConfig(projectRoot) usage in resolveHooks to use the
safely-captured value from the try/catch block.
In `@src/core/templates/skill-templates.ts`:
- Around line 2691-2695: The "post-bulk-archive" post-hook is documented after
the summary which is inconsistent with other post-hooks; move the "Execute
post-bulk-archive hooks" section so it runs before the summary display (i.e.,
swap the current steps 10 and 11) and update the corresponding command template
paragraph that references post-bulk-archive to the same position as other
post-hooks (matching the pattern used by "post-apply", "post-archive", and
"post-sync") so hooks complete before the final summary is shown.
🧹 Nitpick comments (13)
openspec/changes/add-lifecycle-hooks/specs/cli-artifact-workflow/spec.md (1)
24-28: Missing scenario:--schemaflag rejected in hook mode.The PR objectives state that
--schemais rejected when using--hook, but this spec only documents mutual exclusivity with the artifact argument. Consider adding a scenario for--schema+--hookrejection to keep the spec complete.src/core/project-config.ts (1)
165-196: Hooks parsing logic is clean and consistent with existing patterns.One minor observation: the Zod object
z.object({ instruction: z.string().min(1) })at line 179 is re-created on each loop iteration. You could hoist it above the loop for a tiny efficiency gain, similar to howvalidPointsis created once.♻️ Hoist Zod schema outside loop
const parsedHooks: Record<string, { instruction: string }> = {}; let hasValidHooks = false; const validPoints = new Set<string>(VALID_LIFECYCLE_POINTS); + const hookSchema = z.object({ instruction: z.string().min(1) }); for (const [point, hook] of Object.entries(raw.hooks)) { // Warn on unrecognized lifecycle points if (!validPoints.has(point)) { console.warn(`Unknown lifecycle point in hooks: "${point}". Valid points: ${VALID_LIFECYCLE_POINTS.join(', ')}`); continue; } - const hookResult = z.object({ instruction: z.string().min(1) }).safeParse(hook); + const hookResult = hookSchema.safeParse(hook);test/core/artifact-graph/instruction-loader.test.ts (2)
627-652: HelpercreateSchemaWithHooksgenerates valid test schemas — consider a note about quote-safety.The YAML template at lines 641-643 wraps instruction values in double quotes. This works for current test strings but would produce invalid YAML if an instruction contained
"characters. Fine for tests as-is, but worth a brief inline comment if the helper gets reused.
752-774: Test exercises null changeName but only with built-in schema (no hooks).When
changeNameis null, the spec says the system resolves the default schema fromconfig.yaml. This test's config referencesspec-driven(no hooks), so it effectively only tests the "no schema hooks" path. Consider adding a test where the config's default schema does have hooks, to verify that schema hooks from the default schema are also included alongside config hooks.test/core/artifact-graph/schema.test.ts (1)
207-288: Consider adding a test for unrecognized lifecycle point keys in schema hooks.The schema's
HooksSchemausesz.record(z.string(), ...)and accepts any string key without validation, whereas project config parsing explicitly validates againstVALID_LIFECYCLE_POINTSand warns on unknown lifecycle points. A test case like"invalid-point"would verify this behavior and document the design choice that schemas are more permissive than configs.test/core/project-config.test.ts (1)
615-632: Consider asserting onconsole.warnbehavior forhooks: null.The analogous
rules: nulltest (Line 145) explicitly checks that a warning is emitted. This test asserts the correct parsed output but doesn't verify whether a warning is or isn't emitted. Addingexpect(consoleWarnSpy).toHaveBeenCalled()orexpect(consoleWarnSpy).not.toHaveBeenCalled()would make the intent explicit and prevent silent regressions if the warning behavior changes.openspec/changes/add-lifecycle-hooks/specs/specs-sync-skill/spec.md (1)
1-28: Spec is clear and follows the established pattern.Minor note: The archive and verify skill specs include a "Scenario: Hook instruction references change context" section (e.g.,
opsx-archive-skill/spec.mdLine 30–33,opsx-verify-skill/spec.mdLine 30–33). This spec omits it. Consider adding it for consistency if the sync skill also operates within a change context.test/commands/artifact-workflow.test.ts (2)
942-952: Sequential loop over 20 lifecycle points may be slow or flaky in CI.This test spawns 20 separate CLI processes sequentially, each with a 30s timeout, inside a single test with a 60s vitest timeout. If each invocation takes ~3s (process spawn + CLI bootstrap), that's ~60s total — right at the timeout boundary.
Consider either:
- Increasing the vitest timeout for this specific test (e.g.,
120000), or- Spot-checking a smaller representative subset (e.g., first, last, and a middle point) instead of all 20.
943-943: ImportVALID_LIFECYCLE_POINTSinstead of hardcoding the array.The lifecycle points are hardcoded at line 943, but
VALID_LIFECYCLE_POINTSis already exported fromsrc/core/artifact-graph/types.tsand available through the barrel export. If the canonical source is updated, this test won't detect the drift. Import and reuse the constant to keep the test in sync with the source, astest/core/artifact-graph/instruction-loader.test.tsalready does.src/commands/workflow/hooks.ts (1)
92-97: Text output label omits the schema name mentioned in the design doc.The design document (Line 93 of
design.md) shows### From schema (spec-driven)including the schema name, but the implementation outputs just### From schema. If the schema name is useful context for the user/LLM, consider including it. This would require passing the resolved schema name throughResolvedHookorHooksOutput.src/core/artifact-graph/instruction-loader.ts (1)
396-401: Lifecycle point validation creates a newSeton every call.This is a CLI tool so it's not a real performance concern, but for tidiness you could hoist the
Setto module scope as a constant sinceVALID_LIFECYCLE_POINTSis immutable.src/cli/index.ts (1)
35-37: Type alias placed between import statements.
InstructionsActionOptionsis defined on Line 36, sandwiched between twoimportblocks. Consider moving it after all imports for clarity, or colocating it near the instructions command definition.src/core/templates/skill-templates.ts (1)
1595-1784: Consider extracting shared instruction content to reduce duplication, similar togetOnboardInstructions().The explore skill template (Lines 28–322) and explore command template (Lines 1601–1783) are nearly identical, differing only in a few phrasing details. The same applies to new, continue, apply, ff, sync, archive, bulk-archive, and verify. Only onboard already uses a shared
getOnboardInstructions()helper.With 20 hook blocks now injected across both skill and command variants, maintaining consistency between pairs becomes more error-prone (e.g., a hook fix in one could be missed in the other). Extracting shared instruction strings—as done for onboard—would halve the maintenance surface for hook-related content.
openspec/changes/add-lifecycle-hooks/specs/lifecycle-hooks/spec.md
Outdated
Show resolved
Hide resolved
Consistent with other post-hooks (post-archive, post-apply, post-sync) which execute before the final summary is shown.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@openspec/changes/add-lifecycle-hooks/design.md`:
- Around line 90-99: The unlabeled fenced code blocks in the design.md snippets
(e.g., the "## Hooks: post-archive (change: add-dark-mode)" section and other
blocks containing "pre-explore post-explore …", the "openspec instructions
--hook pre-archive --change \"<name>\" --json" command block, and the "ff:
pre-ff …" block) must include language identifiers; update each triple-backtick
fence to use an appropriate tag (e.g., ```text for plain output blocks and
```bash for shell/command examples) so markdownlint MD040 is satisfied and
formatting is consistent across the file.
In `@openspec/changes/add-lifecycle-hooks/specs/lifecycle-hooks/spec.md`:
- Around line 96-130: Update the lifecycle-hooks spec text to explicitly state
(1) the CLI rejects using --schema together with --hook (document the mutual
exclusion and the expected error behavior when both are provided) and (2) JSON
output always returns the full object shape { lifecyclePoint, changeName, hooks
} even when no hooks are found, with hooks: [] and changeName set to null when
no change is provided; ensure these clarifications reference the CLI invocation
form "openspec instructions --hook <lifecycle-point> [--change <name>] [--json]"
and the JSON fields "lifecyclePoint", "changeName", and "hooks" so readers know
to expect an empty array rather than null/omitted fields.
In `@src/core/templates/skill-templates.ts`:
- Around line 2584-2589: Add a fallback note and command for post-archive hooks
in both bulk-archive templates where the docs show running `openspec
instructions --hook post-archive --change "<name>" --json`; after the existing
`mv`/single-archive wording insert a short "Note" that `--change` may not
resolve once the change is moved to archive and instruct to fall back to running
`openspec instructions --hook post-archive --json` if the `--change` invocation
fails, mirroring the single-archive fallback behavior; update both bulk-archive
occurrences (the block around the current post-archive hook example and the
separate occurrence referenced at the later section) so the templates show the
primary command plus the fallback.
|
This feels a little off to me from a design perspective. I'm usually very reluctant to add more branching paths to an agents prompts. We're trying to model a very deterministic flow into a prompt/skill. We are calling this hooks, but what we actually built is prompt injection that depends on the agent choosing to run extra commands. Also, modeling hooks as a flat root hooks map is the wrong abstraction. We need lifecycle behavior attached to the thing being orchestrated: artifacts and commands/operations. I would sort of expect this to be linked to a coding agent's hook system too to be called more deterministically when an openspec skill is invoked. I get what you're trying to do - I was forced to do something similar to make certain things work, but I'm not a fan of cramming more and more instructions into a prompt. The way I've been thinking about OpenSpec hooks is more through the artifacts generated, where we could either 1) integrate with coding agent hooks 2) have some sort of file watcher system that could watch things in the |
Eliminates the type intersection `& { hook?: string }` in cli/index.ts
by adding hook directly to InstructionsOptions.
Hey @TabishB, appreciate the detailed feedback. You raised important points and I want to address them directly. You're right: this is prompt injection No argument there. These hooks are LLM instructions injected into skill prompts. The agent may or may not follow them perfectly. But here's the thing — that's what OpenSpec is. Every layer of the system works this way:
Hooks at operation boundaries are the natural extension of this model to points where no injection mechanism exists today. The compliance guarantees are exactly the same as the rest of the system — no better, no worse. Two layers, not alternativesYou mentioned coding agent hooks and file watchers as alternatives. I think they're actually a different layer that serves a different purpose:
Coding agent hooks would handle things like:
These are mechanical actions — a shell command can do them. No reasoning needed. LLM hooks handle things that only a reasoning agent can do:
No shell script can do these — they require reading, analyzing, and reasoning about project artifacts. On the flat hooks map abstractionYou said: "modeling hooks as a flat root hooks map is the wrong abstraction. We need lifecycle behavior attached to the thing being orchestrated: artifacts and commands/operations." For artifacts, I agree — and that's already covered. Artifacts already have lifecycle behavior attached directly to them through two layers: Schema-level ( # schema.yaml — instruction acts as before + during + after guidance
artifacts:
- id: proposal
generates: proposal.md
template: proposal.md
instruction: |
BEFORE writing:
- Research existing specs in openspec/specs/ to understand current capabilities
- Check if similar changes have been proposed before in openspec/archive/
Create the proposal with sections: Why, What Changes, Capabilities, Impact.
The Capabilities section creates the contract between proposal and specs.
AFTER writing:
- Verify every capability listed has a unique kebab-case name
- Confirm no capability duplicates an existing spec in openspec/specs/
- id: specs
generates: "specs/**/*.md"
template: spec.md
instruction: |
BEFORE writing:
- Read the proposal's Capabilities section — create one spec per capability
- For MODIFIED capabilities, read the existing spec from openspec/specs/
Write specs using SHALL/MUST for normative requirements.
Every requirement MUST have at least one scenario with WHEN/THEN format.
AFTER writing:
- Cross-check: every capability in the proposal has a corresponding spec file
- Verify no orphan specs exist (specs without a matching proposal capability)Project-level ( # config.yaml — rules act as project-specific before/after constraints
schema: spec-driven
context: |
Tech stack: React 19, TypeScript, Tailwind, PostgreSQL.
We deploy to AWS ECS. API follows REST conventions.
All public endpoints require authentication.
rules:
proposal:
- "BEFORE starting: use Linear MCP to find the relevant ticket and include its ID"
- "BEFORE starting: read and follow the instructions in docs/guides/proposal-checklist.md"
- "BEFORE starting: read and follow the instructions in docs/guides/capability-naming.md"
- "AFTER writing: update the Linear ticket description with a link to the proposal"
specs:
- "BEFORE writing: review the team's spec style guide at docs/spec-conventions.md"
- "AFTER writing: verify all scenarios are compatible with our test framework format"
- "AFTER writing: you MUST follow the steps in these files: rules/analyze.md, rules/test.md, rules/conventions.md"
- "Follow the company naming convention: domain-entity-action (e.g., user-auth-login)"
design:
- "BEFORE writing: check ADRs in docs/adr/ for prior decisions on the same area"
- "BEFORE writing: read and follow docs/guides/design-standards.md"
- "AFTER writing: if new ADR-worthy decisions were made, flag them for post-archive"
- "MUST include a rollback strategy for any database migration"
tasks:
- "AFTER writing: verify each task group maps to a single PR boundary"This gives artifacts four layers of behavior already attached to them: What this PR adds is hooks for operations (archive, verify, sync, apply, explore, etc.) — which are exactly the "commands/operations" you mention. These operations don't have an equivalent to The two-source model (schema hooks + config hooks) mirrors the existing artifact pattern:
Path forwardI see these as two complementary iterations:
Both are needed. Neither replaces the other. Happy to discuss further. |
|
It's worth noting that coding agent hooks also support However, coding agent hooks operate at the tool level (Bash, Edit, Write, Skill), not at the OpenSpec operation level (archive, verify, sync). The closest event is |
|
One more thing worth considering: not all coding agents have hooks. Claude Code has a rich hook system (PreToolUse, PostToolUse, Stop, TaskCompleted, prompt/agent types, etc.), but that's specific to Claude Code. OpenSpec is designed to be agent-agnostic — it works with Claude Code, Codex, Cursor, Windsurf, Copilot, Aider, or any agent that can follow markdown instructions. If we rely on coding agent hooks as the primary extensibility mechanism, we either tie OpenSpec to a specific agent — which breaks a core design principle — or we have to implement and maintain hook integrations for every agent, which is a massive surface area that grows with every new agent in the market. LLM instruction hooks ( Coding agent hooks are a great additional layer for teams that use a specific agent and want deterministic guarantees. But they can't be the foundation — the foundation needs to work everywhere. |
Add Extensible Lifecycle Hooks for Schemas and Project Config
Summary
Adds a lifecycle hook system that lets schemas and projects define LLM instructions executed at operation boundaries (pre/post for every skill). Schema-level hooks define workflow-inherent behavior; project-level hooks add project-specific customization. Both are surfaced via
openspec instructions --hook.--hookflag on existinginstructionscommand (no new top-level commands)Motivation
Addresses #682 (Extensible Hook Capability) directly — the original request for pre/post-archive hooks to inject custom consolidation logic (error log management, ADR generation, notifications).
This implementation goes beyond archive to cover all operations, also enabling use cases raised in other issues:
What Changed
Core
src/core/artifact-graph/types.tsVALID_LIFECYCLE_POINTS(20 points),HookSchema,HooksSchemaZod types,ResolvedHookinterfacesrc/core/artifact-graph/instruction-loader.tsresolveHooks()— reads schema + config hooks, returns merged array (schema first)src/core/project-config.tshooksfield with resilient field-by-field parsing and unknown key warningssrc/commands/workflow/hooks.tsCLI
src/cli/index.ts--hook <lifecycle-point>option oninstructionscommand, mutual exclusivity with[artifact],--schemarejection in hook modeSkill Templates
All 10 operations updated in
src/core/templates/skill-templates.ts:Docs & Specs
docs/cli.md— All threeinstructionsmodes documented (artifact, apply, hook)openspec/changes/add-lifecycle-hooks/(proposal, design, specs, tasks)Tests
resolveHooks()ordering and edge casesHook YAML Structure
CLI Usage
JSON Output
{ "lifecyclePoint": "post-archive", "changeName": "add-dark-mode", "hooks": [ { "source": "schema", "instruction": "Generate ADR entries..." }, { "source": "config", "instruction": "Notify Slack channel..." } ] }Backward Compatibility
Fully backward-compatible. The
hooksfield is optional in both schema.yaml and config.yaml. Existing schemas and configs work without modification.Summary by CodeRabbit
New Features
Documentation
Tests
Chores