Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fa3171ca6e | ||
|
|
117ec67e47 | ||
|
|
5bd7027526 | ||
|
|
ec7d87f121 | ||
|
|
85e5eedef8 | ||
|
|
0a5b1ac538 |
103
.github/workflows/scripts/create-release-packages.sh
vendored
103
.github/workflows/scripts/create-release-packages.sh
vendored
@@ -4,7 +4,14 @@ set -euo pipefail
|
|||||||
# create-release-packages.sh (workflow-local)
|
# create-release-packages.sh (workflow-local)
|
||||||
# Build Spec Kit template release archives for each supported AI assistant and script type.
|
# Build Spec Kit template release archives for each supported AI assistant and script type.
|
||||||
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
|
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
|
||||||
# Version argument should include leading 'v'.
|
# Version argument should include leading 'v'.
|
||||||
|
# Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built.
|
||||||
|
# AGENTS : space or comma separated subset of: claude gemini copilot (default: all)
|
||||||
|
# SCRIPTS : space or comma separated subset of: sh ps (default: both)
|
||||||
|
# Examples:
|
||||||
|
# AGENTS=claude SCRIPTS=sh $0 v0.2.0
|
||||||
|
# AGENTS="copilot,gemini" $0 v0.2.0
|
||||||
|
# SCRIPTS=ps $0 v0.2.0
|
||||||
|
|
||||||
if [[ $# -ne 1 ]]; then
|
if [[ $# -ne 1 ]]; then
|
||||||
echo "Usage: $0 <version-with-v-prefix>" >&2
|
echo "Usage: $0 <version-with-v-prefix>" >&2
|
||||||
@@ -40,22 +47,36 @@ generate_commands() {
|
|||||||
mkdir -p "$output_dir"
|
mkdir -p "$output_dir"
|
||||||
for template in templates/commands/*.md; do
|
for template in templates/commands/*.md; do
|
||||||
[[ -f "$template" ]] || continue
|
[[ -f "$template" ]] || continue
|
||||||
local name description raw_body variant_line injected body
|
local name description script_command body
|
||||||
name=$(basename "$template" .md)
|
name=$(basename "$template" .md)
|
||||||
description=$(awk '/^description:/ {gsub(/^description: *"?/, ""); gsub(/"$/, ""); print; exit}' "$template" | tr -d '\r')
|
|
||||||
raw_body=$(awk '/^---$/{if(++count==2) start=1; next} start' "$template")
|
# Normalize line endings
|
||||||
# Find single-line variant comment matching the variant: <!-- VARIANT:sh ... --> or <!-- VARIANT:ps ... -->
|
file_content=$(tr -d '\r' < "$template")
|
||||||
variant_line=$(printf '%s\n' "$raw_body" | awk -v sv="$script_variant" '/<!--[[:space:]]+VARIANT:'sv'/ {match($0, /VARIANT:'"sv"'[[:space:]]+(.*)-->/, m); if (m[1]!="") {print m[1]; exit}}')
|
|
||||||
if [[ -z $variant_line ]]; then
|
# Extract description and script command from YAML frontmatter
|
||||||
echo "Warning: no variant line found for $script_variant in $template" >&2
|
description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
|
||||||
variant_line="(Missing variant command for $script_variant)"
|
script_command=$(printf '%s\n' "$file_content" | awk -v sv="$script_variant" '/^[[:space:]]*'"$script_variant"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script_variant"':[[:space:]]*/, ""); print; exit}')
|
||||||
|
|
||||||
|
if [[ -z $script_command ]]; then
|
||||||
|
echo "Warning: no script command found for $script_variant in $template" >&2
|
||||||
|
script_command="(Missing script command for $script_variant)"
|
||||||
fi
|
fi
|
||||||
# Replace the token VARIANT-INJECT with the selected variant line
|
|
||||||
injected=$(printf '%s\n' "$raw_body" | sed "s/VARIANT-INJECT/${variant_line//\//\/}/")
|
# Replace {SCRIPT} placeholder with the script command
|
||||||
# Remove all single-line variant comments
|
body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")
|
||||||
injected=$(printf '%s\n' "$injected" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d')
|
|
||||||
# Apply arg substitution and path rewrite
|
# Remove the scripts: section from frontmatter while preserving YAML structure
|
||||||
body=$(printf '%s\n' "$injected" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)
|
body=$(printf '%s\n' "$body" | awk '
|
||||||
|
/^---$/ { print; if (++dash_count == 1) in_frontmatter=1; else in_frontmatter=0; next }
|
||||||
|
in_frontmatter && /^scripts:$/ { skip_scripts=1; next }
|
||||||
|
in_frontmatter && /^[a-zA-Z].*:/ && skip_scripts { skip_scripts=0 }
|
||||||
|
in_frontmatter && skip_scripts && /^[[:space:]]/ { next }
|
||||||
|
{ print }
|
||||||
|
')
|
||||||
|
|
||||||
|
# Apply other substitutions
|
||||||
|
body=$(printf '%s\n' "$body" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)
|
||||||
|
|
||||||
case $ext in
|
case $ext in
|
||||||
toml)
|
toml)
|
||||||
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;;
|
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;;
|
||||||
@@ -76,12 +97,13 @@ build_variant() {
|
|||||||
# Inject variant into plan-template.md within .specify/templates if present
|
# Inject variant into plan-template.md within .specify/templates if present
|
||||||
local plan_tpl="$base_dir/.specify/templates/plan-template.md"
|
local plan_tpl="$base_dir/.specify/templates/plan-template.md"
|
||||||
if [[ -f "$plan_tpl" ]]; then
|
if [[ -f "$plan_tpl" ]]; then
|
||||||
variant_line=$(awk -v sv="$script" '/<!--[[:space:]]*VARIANT:'"$script"'/ {match($0, /VARIANT:'"$script"'[[:space:]]+(.*)-->/, m); if(m[1]!=""){print m[1]; exit}}' "$plan_tpl")
|
plan_norm=$(tr -d '\r' < "$plan_tpl")
|
||||||
|
variant_line=$(printf '%s\n' "$plan_norm" | grep -E "<!--[[:space:]]*VARIANT:$script" | head -1 | sed -E "s/.*VARIANT:$script[[:space:]]+//; s/-->.*//; s/^[[:space:]]+//; s/[[:space:]]+$//")
|
||||||
if [[ -n $variant_line ]]; then
|
if [[ -n $variant_line ]]; then
|
||||||
tmp_file=$(mktemp)
|
tmp_file=$(mktemp)
|
||||||
sed "s/VARIANT-INJECT/${variant_line//\//\/}/" "$plan_tpl" | sed "/__AGENT__/s//${agent}/g" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d' > "$tmp_file" && mv "$tmp_file" "$plan_tpl"
|
sed "s|VARIANT-INJECT|${variant_line}|" "$plan_tpl" | tr -d '\r' | sed "s|__AGENT__|${agent}|g" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d' > "$tmp_file" && mv "$tmp_file" "$plan_tpl"
|
||||||
else
|
else
|
||||||
echo "Warning: no plan-template variant for $script" >&2
|
echo "Warning: no plan-template variant for $script (pattern not matched)" >&2
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
case $agent in
|
case $agent in
|
||||||
@@ -100,9 +122,48 @@ build_variant() {
|
|||||||
echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
|
echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build for each agent+script variant
|
# Determine agent list
|
||||||
for agent in claude gemini copilot; do
|
ALL_AGENTS=(claude gemini copilot)
|
||||||
for script in sh ps; do
|
ALL_SCRIPTS=(sh ps)
|
||||||
|
|
||||||
|
norm_list() {
|
||||||
|
# convert comma+space separated -> space separated unique while preserving order of first occurrence
|
||||||
|
tr ',\n' ' ' | awk '{for(i=1;i<=NF;i++){if(!seen[$i]++){printf((out?" ":"") $i)}}}END{printf("\n")}'
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_subset() {
|
||||||
|
local type=$1; shift; local -n allowed=$1; shift; local items=($@)
|
||||||
|
local ok=1
|
||||||
|
for it in "${items[@]}"; do
|
||||||
|
local found=0
|
||||||
|
for a in "${allowed[@]}"; do [[ $it == $a ]] && { found=1; break; }; done
|
||||||
|
if [[ $found -eq 0 ]]; then
|
||||||
|
echo "Error: unknown $type '$it' (allowed: ${allowed[*]})" >&2
|
||||||
|
ok=0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return $ok
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -n ${AGENTS:-} ]]; then
|
||||||
|
AGENT_LIST=($(printf '%s' "$AGENTS" | norm_list))
|
||||||
|
validate_subset agent ALL_AGENTS "${AGENT_LIST[@]}" || exit 1
|
||||||
|
else
|
||||||
|
AGENT_LIST=(${ALL_AGENTS[@]})
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n ${SCRIPTS:-} ]]; then
|
||||||
|
SCRIPT_LIST=($(printf '%s' "$SCRIPTS" | norm_list))
|
||||||
|
validate_subset script ALL_SCRIPTS "${SCRIPT_LIST[@]}" || exit 1
|
||||||
|
else
|
||||||
|
SCRIPT_LIST=(${ALL_SCRIPTS[@]})
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Agents: ${AGENT_LIST[*]}"
|
||||||
|
echo "Scripts: ${SCRIPT_LIST[*]}"
|
||||||
|
|
||||||
|
for agent in "${AGENT_LIST[@]}"; do
|
||||||
|
for script in "${SCRIPT_LIST[@]}"; do
|
||||||
build_variant "$agent" "$script"
|
build_variant "$agent" "$script"
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<!-- VARIANT:sh 1. Run `scripts/bash/setup-plan.sh --json` from the repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. All future file paths must be absolute. -->
|
---
|
||||||
<!-- VARIANT:ps 1. Run `scripts/powershell/setup-plan.ps1 -Json` from the repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. All future file paths must be absolute. -->
|
description: Execute the implementation planning workflow using the plan template to generate design artifacts.
|
||||||
|
scripts:
|
||||||
|
sh: scripts/bash/setup-plan.sh --json
|
||||||
|
ps: scripts/powershell/setup-plan.ps1 -Json
|
||||||
|
---
|
||||||
|
|
||||||
Given the implementation details provided as an argument, do this:
|
Given the implementation details provided as an argument, do this:
|
||||||
|
|
||||||
1. VARIANT-INJECT
|
1. Run `{SCRIPT}` from the repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. All future file paths must be absolute.
|
||||||
2. Read and analyze the feature specification to understand:
|
2. Read and analyze the feature specification to understand:
|
||||||
- The feature requirements and user stories
|
- The feature requirements and user stories
|
||||||
- Functional and non-functional requirements
|
- Functional and non-functional requirements
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<!-- VARIANT:sh 1. Run the script `scripts/bash/create-new-feature.sh --json "{ARGS}"` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute. -->
|
---
|
||||||
<!-- VARIANT:ps 1. Run the script `scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute. -->
|
description: Create or update the feature specification from a natural language feature description.
|
||||||
|
scripts:
|
||||||
|
sh: scripts/bash/create-new-feature.sh --json "{ARGS}"
|
||||||
|
ps: scripts/powershell/create-new-feature.ps1 -Json "{ARGS}"
|
||||||
|
---
|
||||||
|
|
||||||
Given the feature description provided as an argument, do this:
|
Given the feature description provided as an argument, do this:
|
||||||
|
|
||||||
1. VARIANT-INJECT
|
1. Run the script `{SCRIPT}` from repo root and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
|
||||||
2. Load `templates/spec-template.md` to understand required sections.
|
2. Load `templates/spec-template.md` to understand required sections.
|
||||||
3. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
|
3. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings.
|
||||||
4. Report completion with branch name, spec file path, and readiness for the next phase.
|
4. Report completion with branch name, spec file path, and readiness for the next phase.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
<!-- VARIANT:sh 1. Run `scripts/bash/check-task-prerequisites.sh --json` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. -->
|
---
|
||||||
<!-- VARIANT:ps 1. Run `scripts/powershell/check-task-prerequisites.ps1 -Json` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. -->
|
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
|
||||||
|
scripts:
|
||||||
|
sh: scripts/bash/check-task-prerequisites.sh --json
|
||||||
|
ps: scripts/powershell/check-task-prerequisites.ps1 -Json
|
||||||
|
---
|
||||||
|
|
||||||
Given the context provided as an argument, do this:
|
Given the context provided as an argument, do this:
|
||||||
|
|
||||||
1. VARIANT-INJECT
|
1. Run `{SCRIPT}` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute.
|
||||||
2. Load and analyze available design documents:
|
2. Load and analyze available design documents:
|
||||||
- Always read plan.md for tech stack and libraries
|
- Always read plan.md for tech stack and libraries
|
||||||
- IF EXISTS: Read data-model.md for entities
|
- IF EXISTS: Read data-model.md for entities
|
||||||
|
|||||||
Reference in New Issue
Block a user