Skip to content

Commit f77d25d

Browse files
mnriemCopilot
andauthored
Add opt-in Copilot extension lifecycle hooks preset (#57)
* Add opt-in Copilot extension lifecycle hooks preset Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Test installed Git extension hooks with both SDD baselines Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Strengthen lifecycle hook behavioral test oracles Record rejected hook attempts before exiting and isolate hook permission denial from baseline execution. Add deterministic regressions for forbidden, duplicate, retried, and out-of-order attempts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Cover Lean convergence completion and strengthen hook evidence Handle completed Lean assessments with existing open work without claiming convergence. Verify wrapper absence after resolution changes, final assistant failure reporting, and constrained successful Git hook invocations with negative regression cases. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Accept failure-qualified workflow completion reports Exclude completed unsuccessfully and completed with errors from success detection. Cover both wordings across failure and denial cases while retaining rejection of contradictory success claims. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify pending hook preset installation and portable guide links Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Resolve hook suggestions to installed Copilot skill invocations Keep dotted command IDs for identification and execution markers while presenting host-supported skill invocations for optional hooks. Cover suggestions in fixture and installed Git extension transcripts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Validate supplied preset archives and reject wrapped hook retries Compare externally built release ZIP file names, counts, and bytes with the package via HOOKS_PRESET_ARCHIVE. Add corrupted archive regressions and reject shell-wrapped retry attempts instead of ignoring them. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Normalize archive line endings and allow safe hook inspections Compare release files after CRLF-to-LF normalization while preserving all other bytes. Recognize constrained head, ls and sed print inspections without accepting executable sed expressions, wrapped execution or failed retries. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * Clarify baseline entry checkpoint and derive version expectations Run pre-hooks before the baseline workflow regardless of its starting heading. Validate semantic version format and derive release URL and README assertions from the manifest version. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent e4ff145 commit f77d25d

8 files changed

Lines changed: 1500 additions & 1 deletion

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ confused with Copilot plugin/marketplace plumbing.
6363
| [`copilot-full-sdd`](spec-kit-presets/copilot-full-sdd) | Spec Kit `== 1.2.3`, Copilot skills mode | Alternative full SDD baseline retaining traditional artifacts and quality gates; nine command replacements without extension lifecycle-hook dispatch. Initial release pending; use the documented local-development install. |
6464
| [`copilot-sub-agents`](spec-kit-presets/copilot-sub-agents) | Spec Kit `>= 0.8.0` | Uses Copilot delegation — VS Code `runSubagent`, Copilot CLI sub-agents, `.github/agents/` |
6565
| [`copilot-assess-ask-questions`](spec-kit-presets/copilot-assess-ask-questions) | Spec Kit `>= 0.9.0`, `assess` extension | Drives the assess pipeline through Copilot's interactive `ask_user` tool (no plain-text fallback) |
66+
| [`copilot-extension-hooks`](spec-kit-presets/copilot-extension-hooks) | Specify `==1.2.3`, one Copilot Lean/Full SDD baseline | Opt-in Specify extension lifecycle dispatch through Copilot skills; not native Copilot hooks |
6667

6768
See [`spec-kit-presets/README.md`](spec-kit-presets/README.md) for the plumbing
6869
boundary, install commands, and versioning. Maintainers judge whether a package

‎spec-kit-presets/README.md‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ for maintainer guidance on which packages belong here.
2323

2424
| Preset | Requires | Copilot purpose |
2525
| --- | --- | --- |
26+
| [`copilot-extension-hooks`](copilot-extension-hooks) | Specify `==1.2.3`, one Copilot Lean/Full SDD baseline | Opt-in lifecycle dispatch through installed Copilot command skills; preserves the selected baseline and does not control native hooks. See its explicit activation/regeneration flow. |
2627
| [`copilot-full-sdd`](copilot-full-sdd) | Spec Kit `== 1.2.3`, Copilot skills mode | Full core workflow registered as Copilot skills; nine commands identical to pinned core except for extension lifecycle-hook deletions. Initial release pending. |
2728
| [`copilot-lean-sdd`](copilot-lean-sdd) | Spec Kit `>= 1.0.8`, Copilot skills mode | Upstream Lean's minimal artifacts with Copilot skill invocation and `ask_user`; replaces all nine core SDD commands without extension lifecycle-hook dispatch. Initial release pending. |
2829
| [`copilot-sub-agents`](copilot-sub-agents) | Spec Kit `>= 0.8.0` | Built around Copilot delegation mechanisms — VS Code's `runSubagent` tool, Copilot CLI sub-agent processes, and custom agents in `.github/agents/` / `~/.copilot/agents/`. |
@@ -41,6 +42,9 @@ specify preset catalog add https://raw.githubusercontent.com/github/spec-kit-cop
4142
# then add by id — the normal way:
4243
specify preset add copilot-sub-agents
4344
specify preset add copilot-assess-ask-questions # also: specify extension add assess
45+
# After publication only; until then, use the local-development instructions below.
46+
# Opt-in, after selecting one compatible Copilot SDD baseline:
47+
specify preset add copilot-extension-hooks --priority 5
4448
```
4549

4650
`copilot-lean-sdd` **1.0.0 is not released yet**. Its catalog entry reserves the
@@ -53,6 +57,10 @@ for baseline selection, prerequisites, and compatibility limits.
5357
[local-development install](copilot-full-sdd#installation-and-prerequisites)
5458
until publication. Full and Lean are alternative baselines, not cumulative layers.
5559

60+
`copilot-extension-hooks` **1.0.0 is also pending release**; use its
61+
[local-development install](copilot-extension-hooks#install-and-configure).
62+
It is an opt-in layer over one selected baseline, not another baseline.
63+
5664
The two methods below are escape hatches, not the primary path:
5765

5866
- **One-off, without registering a catalog** — install straight from a release zip
@@ -70,6 +78,7 @@ The two methods below are escape hatches, not the primary path:
7078
specify preset add --dev ./spec-kit-presets/copilot-assess-ask-questions
7179
specify preset add --dev ./spec-kit-presets/copilot-lean-sdd
7280
specify preset add --dev ./spec-kit-presets/copilot-full-sdd # alternative to Lean
81+
specify preset add --dev ./spec-kit-presets/copilot-extension-hooks --priority 5 # opt-in layer
7382
```
7483

7584
## Layout
@@ -78,6 +87,10 @@ The two methods below are escape hatches, not the primary path:
7887
spec-kit-presets/
7988
├── README.md # this file (the plumbing boundary note)
8089
├── catalog.json # Spec Kit preset catalog (NOT the Copilot marketplace)
90+
├── copilot-extension-hooks/
91+
│ ├── preset.yml
92+
│ ├── LICENSE
93+
│ └── commands/
8194
├── copilot-full-sdd/
8295
│ ├── preset.yml
8396
│ ├── LICENSE

‎spec-kit-presets/catalog.json‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,28 @@
11
{
22
"schema_version": "1.0",
3-
"updated_at": "2026-10-22T00:00:00Z",
3+
"updated_at": "2026-10-23T00:00:00Z",
44
"catalog_url": "https://raw.githubusercontent.com/github/spec-kit-copilot/main/spec-kit-presets/catalog.json",
55
"presets": {
6+
"copilot-extension-hooks": {
7+
"name": "Copilot Extension Hooks",
8+
"id": "copilot-extension-hooks",
9+
"version": "1.0.0",
10+
"description": "Opt-in Specify extension lifecycle-hook dispatch around either Copilot SDD baseline in Copilot skills mode.",
11+
"author": "mnriem",
12+
"repository": "https://github.com/github/spec-kit-copilot",
13+
"download_url": "https://github.com/github/spec-kit-copilot/releases/download/copilot-extension-hooks-v1.0.0/copilot-extension-hooks.zip",
14+
"homepage": "https://github.com/github/spec-kit-copilot",
15+
"documentation": "https://github.com/github/spec-kit-copilot/blob/main/spec-kit-presets/copilot-extension-hooks/README.md",
16+
"license": "MIT",
17+
"requires": {
18+
"speckit_version": "==1.2.3"
19+
},
20+
"provides": {
21+
"templates": 0,
22+
"commands": 9
23+
},
24+
"tags": ["copilot", "extension-hooks", "sdd"]
25+
},
626
"copilot-full-sdd": {
727
"name": "Copilot Full SDD",
828
"id": "copilot-full-sdd",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright GitHub, Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Copilot Extension Hooks
2+
3+
Copilot Extension Hooks **1.0.0** is an independently configurable, opt-in
4+
Specify preset for Copilot **skills mode**. It wraps one selected
5+
`copilot-lean-sdd` or `copilot-full-sdd` baseline without replacing or editing
6+
its body, metadata, artifact requirements, approval gates, or safeguards.
7+
It is not a Copilot plugin and does not control Copilot native hooks/events.
8+
9+
Requires released Specify CLI **1.2.3 exactly**, a project initialized by that
10+
release in Copilot skills mode, and exactly one of those hook-free baselines.
11+
The version constraint describes the validated composition contract, not the
12+
version of this preset. Full also requires 1.2.3; Lean's broader requirement is
13+
compatible with it. Install trusted extensions separately; this preset neither
14+
installs extensions nor registers hooks. Their installed command skills must be
15+
available to the Copilot host.
16+
17+
## Install and configure
18+
19+
**Initial release pending.** The catalog reserves a release URL; source addition
20+
does not publish an asset. For local development in a disposable project:
21+
22+
```bash
23+
specify --version # must report 1.2.3
24+
specify init /path/to/project --integration copilot --integration-options="--skills" --non-interactive --script sh
25+
cd /path/to/project
26+
specify preset add --dev /path/to/spec-kit-copilot/spec-kit-presets/copilot-lean-sdd --priority 10
27+
# OR install copilot-full-sdd at priority 10, not both baselines.
28+
specify preset add --dev /path/to/spec-kit-copilot/spec-kit-presets/copilot-extension-hooks --priority 5
29+
specify preset resolve speckit.plan
30+
```
31+
32+
Use `--script ps` or `--script py` as appropriate. After publication,
33+
[register the preset catalog](https://github.com/github/spec-kit-copilot/blob/main/spec-kit-presets/README.md#installing-a-preset), then install
34+
the selected baseline and `specify preset add copilot-extension-hooks --priority 5`.
35+
See the shared [release guide](https://github.com/github/spec-kit-copilot/blob/main/spec-kit-presets/README.md#versioning--distribution).
36+
37+
**Lower preset numbers have higher precedence.** The hooks layer must precede
38+
the replacement baseline (for example, hooks `5`, baseline `10`). A higher
39+
precedence replacement or project-local command override hides this wrapper.
40+
Equal priorities depend on id ordering; use distinct priorities instead.
41+
Do not compose with hook-bearing core fallbacks or another hook dispatcher.
42+
The CLI does not enforce the baseline prerequisite or exclusivity for this
43+
preset. Confirm the generated `.github/skills/speckit-*/SKILL.md` contains one
44+
`Copilot extension lifecycle checkpoints` section and the intended baseline.
45+
All nine commands are covered: constitution, specify, clarify, plan, tasks,
46+
analyze, checklist, implement, converge. `taskstoissues` is not covered.
47+
48+
Configure individual hooks in the extension-managed `.specify/extensions.yml`:
49+
50+
```yaml
51+
hooks:
52+
before_plan:
53+
- extension: example
54+
command: speckit.example.prepare
55+
optional: false
56+
after_plan:
57+
- extension: example
58+
command: speckit.example.review
59+
enabled: true
60+
optional: true
61+
prompt: "Review the completed plan?"
62+
```
63+
64+
Replace example ids with real installed extension commands. Hooks follow the
65+
core command-template contract: YAML list order (not hook-priority sorting),
66+
missing `enabled` means enabled, explicit `enabled: false` is skipped,
67+
optional hooks are presented rather than automatically invoked, and mandatory
68+
hooks are actually invoked with results awaited sequentially. Missing `optional`
69+
defaults to optional, matching extension registration. Nonempty `condition`
70+
expressions are skipped, not interpreted by the agent; this is not
71+
HookExecutor's condition-evaluating path. The reserved
72+
`settings.auto_execute_hooks` is not an activation switch.
73+
74+
Hook IDs and invocations are distinct: `speckit.git.commit` identifies the hook,
75+
while its installed `speckit-git-commit` skill is invoked in Copilot CLI as
76+
`/speckit-git-commit`. Optional-hook suggestions use the host-supported skill
77+
invocation; `EXECUTE_COMMAND` retains the configured dotted ID. Unavailable
78+
optional skills are reported as unavailable, not offered as executable commands.
79+
80+
Invalid YAML is reported with its parser error and the warning that mandatory
81+
hooks could not be checked, then the workflow continues, as in core.
82+
An unavailable, denied, or failed mandatory invocation instead stops the
83+
invocation and reports failure. Completion post-hooks do not run after a
84+
baseline prerequisite/gate/validation stop. The wrapper preserves each command's
85+
checkpoint, including a completed Lean convergence assessment that leaves
86+
existing open tasks unchanged: its reported remaining work is not convergence,
87+
but it still reaches `after_converge`. Blocked assessments do not.
88+
It keeps an invocation-local dispatch record to avoid
89+
duplicate invocation, including report/checklist re-entry. It is prompt guidance,
90+
not a runtime executor, persistent lock, sandbox, or deterministic host enforcement.
91+
92+
## Changing activation or priority
93+
94+
Specify 1.2.3's `preset enable`, `disable`, and `set-priority` change resolution
95+
state but **do not regenerate existing skills**. `/skills reload` reloads files;
96+
it does not reconcile them. Do not claim hooks are off just because resolution
97+
or registry state changed.
98+
99+
The validated explicit flow, with a surviving enabled baseline, is:
100+
101+
```bash
102+
# Turn hooks off and restore generated baseline skills:
103+
specify preset remove copilot-extension-hooks
104+
105+
# Turn hooks back on, or apply a different wrapper priority:
106+
specify preset add --dev /path/to/spec-kit-copilot/spec-kit-presets/copilot-extension-hooks --priority 5
107+
# After publication, use: specify preset add copilot-extension-hooks --priority 5
108+
```
109+
110+
Inspect generated skills after each change, then run `/skills reload` in the
111+
current Copilot session (or start a new session). Removal/reinstallation is an
112+
explicit configuration operation, not an automatic migration; authored workflow
113+
artifacts are not changed. Use this flow rather than merely toggling an installed
114+
preset. Reliable installed-but-disabled defaults and automatic reconciliation
115+
remain shared Specify prerequisites. A future bundle may install this component
116+
disabled by default; bundle assembly/defaults are not implemented here.
117+
118+
## Source, license, and validation
119+
120+
Hook-handling rules and output blocks are adapted from
121+
[Spec Kit core commands at `27d14e20c187c1f2916540ea201ecbdc9f8ed588`](https://github.com/github/spec-kit/tree/27d14e20c187c1f2916540ea201ecbdc9f8ed588/templates/commands),
122+
the source retained by Full before hook removal, and checked against released
123+
1.2.3. Shared handling is factored into one wrapper; command-specific checkpoint
124+
placement is retained. Copilot skill invocation, single-dispatch tracking, and
125+
explicit mandatory-failure reporting are added. Copyright GitHub, Inc.;
126+
[MIT licensed](LICENSE).
127+
128+
Run `python -m unittest discover -s spec-kit-presets/tests -p 'test_extension_hooks.py' -v`
129+
with PyYAML installed. Set `HOOKS_SPECIFY_BIN` to Specify 1.2.3 and
130+
`HOOKS_BASELINES_ROOT` to a source tree's `spec-kit-presets` containing both
131+
canonical baselines to include real init/install/composition/removal checks.
132+
Set `HOOKS_PRESET_ARCHIVE` to an externally built release ZIP to compare its
133+
file names, count, and file bytes with the package, normalizing CRLF to LF on
134+
both sides while preserving every other byte. This archive check is
135+
skipped when no ZIP is supplied; creating a ZIP from the expected inventory is
136+
not release-workflow validation.
137+
These checks validate files and generation, not model compliance. Bounded
138+
Copilot CLI marker-hook exercises are opt-in and consume model usage:
139+
140+
```bash
141+
PYTHONPATH=spec-kit-presets/tests HOOKS_COPILOT_BIN=/absolute/path/to/copilot \
142+
python -m unittest test_extension_hooks.CopilotBehaviorTests -v
143+
```
144+
145+
Keep the same `HOOKS_SPECIFY_BIN` and `HOOKS_BASELINES_ROOT` environment variables.
146+
These disposable local exercises cover mandatory invocation/waiting, list order,
147+
disabled/conditional/optional skipping, duplicate suppression, missing baseline
148+
inputs, denied execution, mandatory pre/post failure, absent-layer behavior, and
149+
malformed YAML. Each host process has a five-minute timeout. To retain diagnostic
150+
transcripts, set `HOOKS_EVIDENCE_DIR` to a private directory outside the repository.
151+
Rejected forbidden, duplicate, retried, or out-of-order marker calls are recorded
152+
before failing, so continuing after a tool error cannot hide the attempt.
153+
The denied-hook case blocks only the hook entry point, not the baseline's Python
154+
command, and also checks that baseline execution was not attempted.
155+
Reporting checks inspect final assistant responses for mandatory-hook failures
156+
and assistant-authored warnings for malformed YAML, not matching tool output.
157+
Negative transcript cases reject tool-only evidence and contradictory success.
158+
Separate Lean convergence cases cover existing open work and blocked assessment.
159+
The marker-hook cases seed registrations and command skills directly; they do
160+
not exercise extension installation.
161+
162+
For the separate end-to-end installed-extension test, also set
163+
`HOOKS_SPECIFY_SOURCE` to the extracted Spec Kit **1.2.3** source tree. That test
164+
installs the unmodified bundled `git` extension with `specify extension add --dev`,
165+
checks enabled inventory and automatically registered hooks/generated skills,
166+
then executes `specify` with each baseline. It verifies that the mandatory
167+
`before_specify` hook actually invokes the extension's branch-creation script
168+
once using a constrained single-command format with a successful tool result
169+
and script JSON output, then verifies the resulting local branch. Reading the
170+
script does not count as an invocation; shell-wrapped attempts and combined
171+
calls are rejected rather than ignored, including failed retries. Simple `head`,
172+
`ls`, and numeric-range `sed -n` print inspections are allowed without counting
173+
as execution; executable `sed` expressions are not. Optional commit
174+
hooks remain unrequested. All Git operations are confined to throwaway
175+
repositories without remotes; no existing project or global Git configuration
176+
is changed.
177+
178+
They are behavioral samples, not proof of enforcement in every host; Copilot App
179+
behavior is not established by CLI tests.

0 commit comments

Comments
 (0)