Repository navigation
feat: extension lifecycle hook for post-install setup #3359
Description
This builds on the discussion in #3303. The conclusion there was that agent-specific command specialization belongs in userland (presets, extensions) rather than in a core capability matrix, and I agree with the materialization invariant argument. What I'd like to propose here is the minimal plumbing that would make that userland approach practical.
The problem
Extensions that target multiple agent harnesses need to adapt their commands at install time. Today, the consuming project's init script has to centralize all that logic: detect the harness, then run per-extension, per-harness setup (select presets, adjust configuration, copy adapter scripts). This doesn't scale. Every new extension means more branches in the project's init script, maintained by someone who didn't write the extension.
The extension author is the right person to own that adaptation, but specify extension add currently gives them no hook to run it.
Proposal
Add an optional on_install lifecycle hook to extension.yml:
extension:
id: spex-teams
# ...existing fields...
hooks:
on_install:
script: scripts/on-install.sh
description: "Select agent-appropriate preset for subagent dispatch"When specify extension add processes an extension that declares on_install, it invokes the script after the extension is installed into .specify/extensions/<id>/. The script runs with the extension's directory as context and can do whatever it needs: select a preset, skip unsupported commands, copy adapter files, adjust configuration.
Harness context
The script needs to know which harness it's adapting for. Two options, not mutually exclusive:
-
Extension detects it. The script runs its own detection logic (environment variables, directory presence, init-options). This works today, no core changes needed beyond the hook itself.
-
specifypasses it. If the active integration is already known at install time (fromspecify init --integration <name>or the stored.specify/init-options.json),specifycould pass it as an environment variable (SPECKIT_INTEGRATION=claude) or a script argument. This would be a convenience, not a requirement. The extension could still override or extend the detection.
Option 2 is cleaner since specify already knows the integration. But the hook is useful even with just option 1.
What the hook would NOT do
This deliberately avoids the capability matrix from #3303. The hook doesn't introduce conditional template blocks, capability keys, or any shared vocabulary between core and extensions. The result is still fully materialized, auditable, self-contained project files. The hook is pure plumbing: "run this script after install."
Use case: cc-spex
cc-spex has 7 extensions with 30 commands. 24 are portable as-is. 6 need agent-specific adaptation (subagent dispatch patterns, interactive prompt tools). Today, spex-init.sh centralizes all of this in a 400-line script. With on_install, each extension would own its own adaptation:
spex-teamsinstalls its Claude Code subagent preset or falls back to sequential executionspex-deep-reviewselects the right dispatch section for the detected harnessspexcore adjustsshippipeline for available parallelism
The init script shrinks to just specify extension add calls. Each extension is self-contained.
Minimal implementation
- Parse
on_installfromextension.ymlduringspecify extension add - After installation, invoke the script with:
- Working directory: the extension's installed path
- Environment:
SPECKIT_INTEGRATION(if known),SPECKIT_PROJECT_ROOT
- Non-zero exit from the script fails the installation (with the script's stderr as the error message)
An on_uninstall counterpart would be natural but isn't required for the initial implementation.
Activity
mnriem commentedon Jul 6, 2026
Thanks for the detailed write-up, and for separating this cleanly from the capability-matrix discussion in #3303.
I want to push back on the need for a new
on_installhook, because I think everything here is already achievable with existing primitives — and the workflow engine is a natural fit for this.This can already be accomplished with a workflow. The pain you describe — a 400-line
spex-init.shthat centralizes per-extension, per-harness setup — is something a workflow can express directly. The engine already shipsshell,command,switch/if, andinitstep types, resumable state, gates, and an expression engine withintegration: "auto"resolution. Adaptation logic that today lives in an imperative init script becomes declarative, reviewable steps:This hits your core goal directly: the author owns the adaptation, and the consuming project's init script shrinks to nothing.
It ships with the extensions. cc-spex's real distribution unit is a bundle, and bundles already declare
provides.workflowsalongsideprovides.extensions(seeexamples/bundles/developer/bundle.yml). So the setup workflow travels with the extensions — no separate maintenance, no central branching.And the one-command install you're reaching for already exists.
specify workflow runnatively accepts an HTTPS URL — it downloads the YAML, enforces HTTPS-only (HTTP only for loopback), resolves GitHub release assets, and re-validates the scheme across redirects. So the whole thing is:That single command installs every extension, branches on the detected integration, runs the per-harness adapters, and is resumable — with zero new
extension.ymlschema.Mapping it against the proposal:
shell+switchprovides.workflowsspecify workflow run <https-url>specifypasses the integrationinputs.integration/autoThe only thing
on_installadds on top of this is implicit execution duringspecify extension add— and that implicitness is exactly the cost. Today the CLI executes zero extension scripts;addis a materialization step, not a code-execution step. Anon_installhook would havespecify extension addsilentlysubprocessarbitrary author shell on every install, which is a meaningfully larger trust surface than the current model (where author code is either surfaced to the agent the user is watching, or run explicitly via a workflow the user invoked). It also drags in anon_uninstallcounterpart, cross-platform bash+powershell pairs, and containment rules for what the script may touch.With the workflow approach, the same shell invocations live in fetched-and-validated, reviewable YAML that the user runs knowingly, rather than a hidden side effect of
add.So I'd lean toward not adding the hook, and instead documenting
specify workflow run <url>(bundled workflow) as the supported pattern for post-install, agent-specific setup. Happy to help sketch a referencespex-setup.ymlif that'd be useful.rhuss commentedon Jul 11, 2026
Thanks for the greate feedback (and also the pushback). After some meandering, I think spec-kit workflows are the right solution to get cc-spex agent-agnostic. Let's close this issue.
Really appreciate the insights and also the pushback to prevent spec-kit to become bloated with unneeded features. It's too easy to throw over something without having the full context of the project. I'm happy that there is still a human in the loop :-) 🙏