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.
-
specify passes it. If the active integration is already known at install time (from specify init --integration <name> or the stored .specify/init-options.json), specify could 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-teams installs its Claude Code subagent preset or falls back to sequential execution
spex-deep-review selects the right dispatch section for the detected harness
spex core adjusts ship pipeline for available parallelism
The init script shrinks to just specify extension add calls. Each extension is self-contained.
Minimal implementation
- Parse
on_install from extension.yml during specify 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.
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 addcurrently gives them no hook to run it.Proposal
Add an optional
on_installlifecycle hook toextension.yml:When
specify extension addprocesses an extension that declareson_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
specifyalready 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.shcentralizes all of this in a 400-line script. Withon_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 parallelismThe init script shrinks to just
specify extension addcalls. Each extension is self-contained.Minimal implementation
on_installfromextension.ymlduringspecify extension addSPECKIT_INTEGRATION(if known),SPECKIT_PROJECT_ROOTAn
on_uninstallcounterpart would be natural but isn't required for the initial implementation.