Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .github/workflows/release-preset-trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Release Preset Trigger

# Manually dispatch with a preset id + version; it validates and pushes a
# `<preset-id>-vX.Y.Z` tag, which fires release-preset.yml.

on:
workflow_dispatch:
inputs:
preset_id:
description: 'Preset directory name under spec-kit-presets/ (e.g., copilot-sub-agents)'
required: true
type: string
version:
description: 'Version to release (e.g., 1.0.0)'
required: true
type: string

jobs:
tag-and-release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Validate inputs
env:
PRESET_ID: ${{ github.event.inputs.preset_id }}
VERSION: ${{ github.event.inputs.version }}
run: |
# Strip optional v prefix
VERSION="${VERSION#v}"

# Validate version format
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Invalid version format '$VERSION'. Must be X.Y.Z" >&2
exit 1
fi

# Validate preset directory exists
if [[ ! -d "spec-kit-presets/$PRESET_ID" ]]; then
echo "Error: Preset directory 'spec-kit-presets/$PRESET_ID' not found" >&2
exit 1
fi

# Validate preset.yml exists
if [[ ! -f "spec-kit-presets/$PRESET_ID/preset.yml" ]]; then
echo "Error: spec-kit-presets/$PRESET_ID/preset.yml not found" >&2
exit 1
fi

TAG="${PRESET_ID}-v${VERSION}"

# Check if tag already exists
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Error: Tag '$TAG' already exists" >&2
exit 1
fi

echo "tag=$TAG" >> "$GITHUB_ENV"
echo "Will create tag: $TAG"

- name: Configure Git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Create and push tag
run: |
git tag "$tag"
git push origin "$tag"
echo "Pushed tag $tag — release workflow will handle the rest"
93 changes: 93 additions & 0 deletions .github/workflows/release-preset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Release Preset

# Pushing a `<preset-id>-vX.Y.Z` tag builds that preset's zip inline and publishes it
# as a release asset — matching the `download_url` entries in
# spec-kit-presets/catalog.json.

on:
push:
tags:
- '*-v[0-9]+.[0-9]+.[0-9]+'

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Parse tag
id: parse
run: |
TAG="${GITHUB_REF#refs/tags/}"
# Extract preset id (everything before the last -vX.Y.Z)
PRESET_ID="${TAG%-v*}"
VERSION="${TAG#*-v}"

# Validate version format
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Error: Invalid version format '$VERSION'" >&2
exit 1
fi

# Validate preset directory exists
if [[ ! -d "spec-kit-presets/$PRESET_ID" ]]; then
echo "Error: Preset directory 'spec-kit-presets/$PRESET_ID' not found" >&2
exit 1
fi

# Validate preset.yml exists
if [[ ! -f "spec-kit-presets/$PRESET_ID/preset.yml" ]]; then
echo "Error: spec-kit-presets/$PRESET_ID/preset.yml not found" >&2
exit 1
fi

echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "preset_id=$PRESET_ID" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "Releasing $PRESET_ID v$VERSION"

- name: Create preset zip
run: |
cd "spec-kit-presets/${{ steps.parse.outputs.preset_id }}"
zip -r "$GITHUB_WORKSPACE/${{ steps.parse.outputs.preset_id }}.zip" . \
-x '.*' '__pycache__/*'

- name: Generate release notes
id: notes
run: |
PRESET_ID="${{ steps.parse.outputs.preset_id }}"
VERSION="${{ steps.parse.outputs.version }}"
CHANGELOG="spec-kit-presets/$PRESET_ID/CHANGELOG.md"

{
echo 'body<<EOF'
if [[ -f "$CHANGELOG" ]]; then
# Extract the section for this version from CHANGELOG.md
awk -v ver="$VERSION" '
/^## \[/ {
if (found) exit
if (index($0, ver)) found=1
}
found { print }
' "$CHANGELOG"
else
echo "Release $PRESET_ID v$VERSION"
fi
echo 'EOF'
} >> "$GITHUB_OUTPUT"

- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PRESET_ID="${{ steps.parse.outputs.preset_id }}"
TAG="${{ steps.parse.outputs.tag }}"
VERSION="${{ steps.parse.outputs.version }}"

gh release create "$TAG" \
"$PRESET_ID.zip" \
--title "$PRESET_ID v$VERSION" \
--notes "${{ steps.notes.outputs.body }}"
36 changes: 36 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,42 @@ runs the CLI.
and automatically on the next session start. This is distinct from this plugin's own
skills, which are refreshed with `copilot plugin install` / `/plugin`.

## Spec Kit presets (`spec-kit-presets/`) — keep the plumbing boundary

`spec-kit-presets/` holds **Copilot-specific Spec Kit presets** — this repo is their
canonical, sole home. Guard the boundary so contributors never conflate the two
toolchains:

- **Two different consumers.** Copilot plumbing (`plugin.json`, `skills/`, `plugins/`,
`.github/plugin/marketplace.json`) is consumed by the **`copilot plugin`** CLI/App.
Presets are consumed by the **`specify` CLI** (`specify preset add`). They are *not*
Copilot plugins, skills, canvases, or marketplace entries.
- **Isolate, don't scatter.** All preset content — including its `catalog.json` — lives
**inside** `spec-kit-presets/`. Do **not** put a preset `catalog.json` at the repo
root, and do not mix it up with the Copilot marketplace manifest at
`.github/plugin/marketplace.json`. Keep the boundary note in
`spec-kit-presets/README.md`.
- **Naming convention: `copilot-<scope>[-<behavior>]`.** Preset ids (directory,
`preset.yml` `id`, `catalog.json` key) carry a short **`copilot-`** prefix marking
them Copilot-specific (e.g. `copilot-sub-agents`, `copilot-assess-ask-questions`),
and display names lead with **"Copilot"** (e.g. "Copilot Sub-Agent Delegation").
Do **not** use the full `spec-kit-copilot-*` plugin prefix for preset ids — that
namespace is Copilot plugins (`copilot plugin`), and reusing it here would re-blur
the plumbing boundary and bloat `specify preset add`.
- **Promotion criterion: Copilot-specific only.** A preset belongs here only if it
depends on Copilot's own agent mechanisms (e.g. `copilot-sub-agents` uses the VS Code
`runSubagent` tool / Copilot CLI sub-agents / `.github/agents/`; `copilot-assess-ask-questions`
requires Copilot's interactive `ask_user` tool with no plain-text fallback).
Agent-agnostic presets (generic themes, or workflows tied to an extension rather
than to Copilot's tools) do **not** belong here. Do not import them.
- **Independent versioning & release.** Each preset carries its own `version` in
`preset.yml` and a matching `catalog.json` entry, separate from plugin versions.
Releases are cut by CI (`.github/workflows/release-preset.yml`), which zips the
preset **inline** (no build script) on a pushed `<preset>-v<version>` tag; use the
**Release Preset Trigger** workflow to create that tag from a preset id + version.
When revving a preset, bump `preset.yml` + the `catalog.json` entry together
**before** tagging.

## When revving the core skills plugin

1. Re-enumerate the `specify` CLI surface for the **latest** release
Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,23 @@ The plugins are independently installable and versioned. Install the core skills
the assessment canvas, the bug fix canvas, the spec-driven development canvas, or
any combination.

## Spec Kit presets

This repo also hosts **Copilot-specific Spec Kit presets** under
[`spec-kit-presets/`](spec-kit-presets). These are *not* Copilot plugins — they are
consumed by the **`specify` CLI** (`specify preset add`), and are kept in their own
isolated subtree (with their own `catalog.json`) so Spec Kit plumbing is never
confused with Copilot plugin/marketplace plumbing.

| Preset | Requires | Why it is Copilot-specific |
| --- | --- | --- |
| [`copilot-sub-agents`](spec-kit-presets/copilot-sub-agents) | Spec Kit `>= 0.2.0` | Uses Copilot delegation — VS Code `runSubagent`, Copilot CLI sub-agents, `.github/agents/` |
| [`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) |

See [`spec-kit-presets/README.md`](spec-kit-presets/README.md) for the plumbing
boundary, install commands, and versioning. Only Copilot-specific presets are hosted
here; agent-agnostic presets do not belong in this Copilot integration hub.

## Core skills plugin

`spec-kit-copilot` gives Copilot focused skills—one per `specify` command group—so
Expand Down Expand Up @@ -170,10 +187,10 @@ See this plugin driving Spec-Driven Development end to end with this community-c

```javascript
spec-kit-copilot/
├── plugin.json # Core skills plugin manifest
├── plugin.json # Core skills plugin manifest (Copilot plumbing)
├── README.md
├── .github/plugin/
│ └── marketplace.json # Marketplace manifest (for distribution)
│ └── marketplace.json # Copilot marketplace manifest (NOT the preset catalog)
├── plugins/
│ ├── spec-kit-copilot-assess/
│ │ ├── plugin.json # Assessment canvas plugin manifest
Expand All @@ -187,6 +204,11 @@ spec-kit-copilot/
│ ├── plugin.json # Spec-driven development canvas plugin manifest
│ └── extensions/
│ └── sdd-canvas/
├── spec-kit-presets/ # Spec Kit plumbing — consumed by `specify preset add`
│ ├── README.md # plumbing boundary note
│ ├── catalog.json # preset catalog (NOT the Copilot marketplace)
│ ├── copilot-sub-agents/
│ └── copilot-assess-ask-questions/
└── skills/
├── speckit-cli-setup/SKILL.md
├── speckit-init/SKILL.md
Expand Down
8 changes: 7 additions & 1 deletion skills/speckit-preset/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ specify preset remove <id>

# Catalogs (sources presets are resolved from)
specify preset catalog list
specify preset catalog add <url>
specify preset catalog add <url> --name <name> [--install-allowed]
specify preset catalog remove <name>
```

## Notes

- Resolution priority: **lower number = higher precedence** (default `10`).
- **Catalogs are added discovery-only by default.** `specify preset catalog add`
requires `--name` and defaults to `--no-install-allowed`; presets from a
discovery-only catalog can be browsed but not installed (install errors with a
"discovery-only" message). Pass `--install-allowed` to permit installs — only for
catalogs you trust. The `install_allowed` policy lives in the consumer's
`.specify/preset-catalogs.yml`, not in the catalog's own `catalog.json`.
- A preset can also be installed at project creation:
`specify init <name> --integration copilot --integration-options="--skills" --script sh --preset <id>`
(use `--script ps` on Windows; see the speckit-init skill for the full OS-aware form and
Expand Down
100 changes: 100 additions & 0 deletions spec-kit-presets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Spec Kit presets (Copilot-specific)

> [!IMPORTANT]
> **This directory is Spec Kit plumbing, not Copilot plumbing.** Everything here is
> consumed by the **`specify` CLI** via `specify preset add` — it is *not* a Copilot
> plugin, skill, extension, or marketplace entry. Do not confuse the `catalog.json`
> in this directory with the Copilot marketplace manifest at
> [`.github/plugin/marketplace.json`](../.github/plugin/marketplace.json), and do not
> confuse a preset here with a Copilot plugin under [`plugins/`](../plugins) or a
> skill under [`skills/`](../skills).

| Plumbing | Consumed by | Lives in |
| --- | --- | --- |
| **Copilot** plugins / skills / canvases | `copilot plugin …` (Copilot CLI & App) | root `plugin.json`, `skills/`, `plugins/`, `.github/plugin/marketplace.json` |
| **Spec Kit** presets (this folder) | `specify preset …` (Spec Kit `specify` CLI) | `spec-kit-presets/` |

## What lives here

These are the **Copilot-specific** Spec Kit presets — presets that depend on
**Copilot's own agent mechanisms** rather than being agent-agnostic. This directory is
their canonical home.

| Preset | Requires | Why it is Copilot-specific |
| --- | --- | --- |
| [`copilot-sub-agents`](copilot-sub-agents) | Spec Kit `>= 0.2.0` | Built around Copilot delegation mechanisms — VS Code's `runSubagent` tool, Copilot CLI sub-agent processes, and custom agents in `.github/agents/` / `~/.copilot/agents/`. |
| [`copilot-assess-ask-questions`](copilot-assess-ask-questions) | Spec Kit `>= 0.9.0`, the `assess` extension | Drives the assess pipeline through Copilot's interactive `ask_user` tool (App, CLI, VS Code). No plain-text fallback — not meant for agents without an interactive question tool. |

Only Copilot-specific presets belong here. Agent-agnostic presets (generic themes,
extension-specific workflows that don't rely on Copilot's tools) do **not** belong in
this Copilot integration hub.

## Installing a preset

**Recommended — register the catalog once, then install by id.** Catalogs are
discovery-only by default, so `--install-allowed` is required to install from them
(and `--name` is required):

```bash
specify preset catalog add https://raw.githubusercontent.com/github/spec-kit-copilot/main/spec-kit-presets/catalog.json \
--name spec-kit-copilot --install-allowed

# then add by id — the normal way:
specify preset add copilot-sub-agents
specify preset add copilot-assess-ask-questions # also: specify extension add assess
```

The two methods below are escape hatches, not the primary path:

- **One-off, without registering a catalog** — install straight from a release zip
(`--from` requires an HTTPS URL):

```bash
specify preset add --from https://github.com/github/spec-kit-copilot/releases/download/copilot-sub-agents-v1.0.0/copilot-sub-agents.zip
specify preset add --from https://github.com/github/spec-kit-copilot/releases/download/copilot-assess-ask-questions-v1.0.0/copilot-assess-ask-questions.zip
```

- **Local development only** — install from a working clone of this repo:

```bash
specify preset add --dev ./spec-kit-presets/copilot-sub-agents
specify preset add --dev ./spec-kit-presets/copilot-assess-ask-questions
```

## Layout

```text
spec-kit-presets/
├── README.md # this file (the plumbing boundary note)
├── catalog.json # Spec Kit preset catalog (NOT the Copilot marketplace)
├── copilot-sub-agents/
│ ├── preset.yml
│ └── commands/
└── copilot-assess-ask-questions/
├── preset.yml
└── commands/
```

## Versioning & distribution

Presets are versioned and released **independently** of the Copilot plugins in this
repo. Each preset carries its own `version` in `preset.yml` and its own
`catalog.json` entry. `specify preset add <name>` (catalog install) resolves a
release-asset zip via each entry's `download_url`, tagged
`<preset>-v<version>` (e.g. `copilot-sub-agents-v1.0.0`).

Releases are cut by CI — there is no local build script. The zip is built **inside**
the release workflow (`.github/workflows/release-preset.yml`) from the preset
directory, so `preset.yml` and `commands/` sit at the archive root. To publish:

- **Preferred:** run the **Release Preset Trigger** workflow
(`.github/workflows/release-preset-trigger.yml`) via *Actions → Run workflow* with
the preset id and version; it validates, then creates and pushes the
`<preset>-v<version>` tag.
- **Or** push the tag yourself (`git tag copilot-sub-agents-v1.0.0 && git push origin
copilot-sub-agents-v1.0.0`).

Either path fires `release-preset.yml`, which builds the zip and creates the GitHub
release with that asset. When revving a preset, bump its `preset.yml` version and the
matching `catalog.json` entry together **before** tagging.

Loading