Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5828e58f84 | ||
|
|
dd57e9d444 | ||
|
|
558e682865 | ||
|
|
63bc6b495d | ||
|
|
70b3db27db | ||
|
|
6e94588615 | ||
|
|
ad9c93c13b | ||
|
|
f979b64338 | ||
|
|
b1591282f6 | ||
|
|
60b015a094 | ||
|
|
0c2b367ba0 | ||
|
|
6b8b1a8b93 | ||
|
|
0e6f513c14 | ||
|
|
6f81f7d6a0 | ||
|
|
c875bd0f30 | ||
|
|
736e282562 | ||
|
|
542751fcd1 | ||
|
|
6c83e9ff66 | ||
|
|
a55448057b | ||
|
|
88cded5c4d | ||
|
|
0ad2f169d2 | ||
|
|
fa3171ca6e | ||
|
|
117ec67e47 | ||
|
|
5bd7027526 | ||
|
|
ec7d87f121 | ||
|
|
85e5eedef8 | ||
|
|
0a5b1ac538 |
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -81,7 +81,7 @@ jobs:
|
||||
cat > release_notes.md << EOF
|
||||
Template release ${{ steps.get_tag.outputs.new_version }}
|
||||
|
||||
Updated specification-driven development templates for GitHub Copilot, Claude Code, and Gemini CLI.
|
||||
Updated specification-driven development templates for GitHub Copilot, Claude Code, Gemini CLI, and Cursor.
|
||||
|
||||
Now includes per-script variants for POSIX shell (sh) and PowerShell (ps).
|
||||
|
||||
@@ -92,6 +92,8 @@ jobs:
|
||||
- spec-kit-template-claude-ps-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-gemini-sh-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-gemini-ps-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-cursor-sh-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
- spec-kit-template-cursor-ps-${{ steps.get_tag.outputs.new_version }}.zip
|
||||
EOF
|
||||
|
||||
echo "Generated release notes:"
|
||||
@@ -110,6 +112,8 @@ jobs:
|
||||
spec-kit-template-claude-ps-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-gemini-sh-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-gemini-ps-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-cursor-sh-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
spec-kit-template-cursor-ps-${{ steps.get_tag.outputs.new_version }}.zip \
|
||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||
--notes-file release_notes.md
|
||||
env:
|
||||
|
||||
149
.github/workflows/scripts/create-release-packages.sh
vendored
149
.github/workflows/scripts/create-release-packages.sh
vendored
@@ -4,7 +4,14 @@ set -euo pipefail
|
||||
# create-release-packages.sh (workflow-local)
|
||||
# Build Spec Kit template release archives for each supported AI assistant and script type.
|
||||
# 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
|
||||
echo "Usage: $0 <version-with-v-prefix>" >&2
|
||||
@@ -20,14 +27,6 @@ echo "Building release packages for $NEW_VERSION"
|
||||
|
||||
rm -rf sdd-package-base* sdd-*-package-* spec-kit-template-*-${NEW_VERSION}.zip || true
|
||||
|
||||
mkdir -p sdd-package-base
|
||||
SPEC_DIR="sdd-package-base/.specify"
|
||||
mkdir -p "$SPEC_DIR"
|
||||
|
||||
[[ -d memory ]] && { cp -r memory "$SPEC_DIR/"; echo "Copied memory -> .specify"; }
|
||||
[[ -d scripts ]] && { cp -r scripts "$SPEC_DIR/"; echo "Copied scripts -> .specify/scripts"; }
|
||||
[[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -exec cp --parents {} "$SPEC_DIR"/ \; ; echo "Copied templates -> .specify/templates"; }
|
||||
|
||||
rewrite_paths() {
|
||||
sed -E \
|
||||
-e 's@(/?)memory/@.specify/memory/@g' \
|
||||
@@ -40,22 +39,36 @@ generate_commands() {
|
||||
mkdir -p "$output_dir"
|
||||
for template in templates/commands/*.md; do
|
||||
[[ -f "$template" ]] || continue
|
||||
local name description raw_body variant_line injected body
|
||||
local name description script_command body
|
||||
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")
|
||||
# Find single-line variant comment matching the variant: <!-- VARIANT:sh ... --> or <!-- VARIANT:ps ... -->
|
||||
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
|
||||
echo "Warning: no variant line found for $script_variant in $template" >&2
|
||||
variant_line="(Missing variant command for $script_variant)"
|
||||
|
||||
# Normalize line endings
|
||||
file_content=$(tr -d '\r' < "$template")
|
||||
|
||||
# Extract description and script command from YAML frontmatter
|
||||
description=$(printf '%s\n' "$file_content" | awk '/^description:/ {sub(/^description:[[:space:]]*/, ""); print; exit}')
|
||||
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
|
||||
# Replace the token VARIANT-INJECT with the selected variant line
|
||||
injected=$(printf '%s\n' "$raw_body" | sed "s/VARIANT-INJECT/${variant_line//\//\/}/")
|
||||
# Remove all single-line variant comments
|
||||
injected=$(printf '%s\n' "$injected" | sed '/<!--[[:space:]]*VARIANT:sh/d' | sed '/<!--[[:space:]]*VARIANT:ps/d')
|
||||
# Apply arg substitution and path rewrite
|
||||
body=$(printf '%s\n' "$injected" | sed "s/{ARGS}/$arg_format/g" | sed "s/__AGENT__/$agent/g" | rewrite_paths)
|
||||
|
||||
# Replace {SCRIPT} placeholder with the script command
|
||||
body=$(printf '%s\n' "$file_content" | sed "s|{SCRIPT}|${script_command}|g")
|
||||
|
||||
# Remove the scripts: section from frontmatter while preserving YAML structure
|
||||
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
|
||||
toml)
|
||||
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;;
|
||||
@@ -72,16 +85,48 @@ build_variant() {
|
||||
local base_dir="sdd-${agent}-package-${script}"
|
||||
echo "Building $agent ($script) package..."
|
||||
mkdir -p "$base_dir"
|
||||
cp -r sdd-package-base/. "$base_dir"/
|
||||
|
||||
# Copy base structure but filter scripts by variant
|
||||
SPEC_DIR="$base_dir/.specify"
|
||||
mkdir -p "$SPEC_DIR"
|
||||
|
||||
[[ -d memory ]] && { cp -r memory "$SPEC_DIR/"; echo "Copied memory -> .specify"; }
|
||||
|
||||
# Only copy the relevant script variant directory
|
||||
if [[ -d scripts ]]; then
|
||||
mkdir -p "$SPEC_DIR/scripts"
|
||||
case $script in
|
||||
sh)
|
||||
[[ -d scripts/bash ]] && { cp -r scripts/bash "$SPEC_DIR/scripts/"; echo "Copied scripts/bash -> .specify/scripts"; }
|
||||
# Copy any script files that aren't in variant-specific directories
|
||||
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
|
||||
;;
|
||||
ps)
|
||||
[[ -d scripts/powershell ]] && { cp -r scripts/powershell "$SPEC_DIR/scripts/"; echo "Copied scripts/powershell -> .specify/scripts"; }
|
||||
# Copy any script files that aren't in variant-specific directories
|
||||
find scripts -maxdepth 1 -type f -exec cp {} "$SPEC_DIR/scripts/" \; 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
[[ -d templates ]] && { mkdir -p "$SPEC_DIR/templates"; find templates -type f -not -path "templates/commands/*" -exec cp --parents {} "$SPEC_DIR"/ \; ; echo "Copied templates -> .specify/templates"; }
|
||||
# Inject variant into plan-template.md within .specify/templates if present
|
||||
local plan_tpl="$base_dir/.specify/templates/plan-template.md"
|
||||
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")
|
||||
if [[ -n $variant_line ]]; then
|
||||
plan_norm=$(tr -d '\r' < "$plan_tpl")
|
||||
# Extract script command from YAML frontmatter
|
||||
script_command=$(printf '%s\n' "$plan_norm" | awk -v sv="$script" '/^[[:space:]]*'"$script"':[[:space:]]*/ {sub(/^[[:space:]]*'"$script"':[[:space:]]*/, ""); print; exit}')
|
||||
if [[ -n $script_command ]]; then
|
||||
# Always prefix with .specify/ for plan usage
|
||||
script_command=".specify/$script_command"
|
||||
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"
|
||||
# Replace {SCRIPT} placeholder with the script command and __AGENT__ with agent name
|
||||
substituted=$(sed "s|{SCRIPT}|${script_command}|g" "$plan_tpl" | tr -d '\r' | sed "s|__AGENT__|${agent}|g")
|
||||
# Strip YAML frontmatter from plan template output (keep body only)
|
||||
stripped=$(printf '%s\n' "$substituted" | awk 'BEGIN{fm=0;dash=0} /^---$/ {dash++; if(dash==1){fm=1; next} else if(dash==2){fm=0; next}} {if(!fm) print}')
|
||||
printf '%s\n' "$stripped" > "$plan_tpl"
|
||||
else
|
||||
echo "Warning: no plan-template variant for $script" >&2
|
||||
echo "Warning: no plan-template script command found for $script in YAML frontmatter" >&2
|
||||
fi
|
||||
fi
|
||||
case $agent in
|
||||
@@ -95,14 +140,56 @@ build_variant() {
|
||||
copilot)
|
||||
mkdir -p "$base_dir/.github/prompts"
|
||||
generate_commands copilot prompt.md "\$ARGUMENTS" "$base_dir/.github/prompts" "$script" ;;
|
||||
cursor)
|
||||
mkdir -p "$base_dir/.cursor/commands"
|
||||
generate_commands cursor md "\$ARGUMENTS" "$base_dir/.cursor/commands" "$script" ;;
|
||||
esac
|
||||
( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . )
|
||||
echo "Created spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip"
|
||||
}
|
||||
|
||||
# Build for each agent+script variant
|
||||
for agent in claude gemini copilot; do
|
||||
for script in sh ps; do
|
||||
# Determine agent list
|
||||
ALL_AGENTS=(claude gemini copilot cursor)
|
||||
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"
|
||||
done
|
||||
done
|
||||
|
||||
22
CHANGELOG.md
Normal file
22
CHANGELOG.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to the Specify CLI will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.0.4] - 2025-09-14
|
||||
|
||||
### Added
|
||||
|
||||
- SOCKS proxy support for corporate environments via `httpx[socks]` dependency
|
||||
|
||||
### Fixed
|
||||
|
||||
N/A
|
||||
|
||||
### Changed
|
||||
|
||||
N/A
|
||||
30
README.md
30
README.md
@@ -16,6 +16,7 @@
|
||||
|
||||
- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
|
||||
- [⚡ Get started](#-get-started)
|
||||
- [📽️ Video Overview](#️-video-overview)
|
||||
- [🔧 Specify CLI Reference](#-specify-cli-reference)
|
||||
- [📚 Core philosophy](#-core-philosophy)
|
||||
- [🌟 Development phases](#-development-phases)
|
||||
@@ -45,7 +46,7 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME
|
||||
|
||||
### 2. Create the spec
|
||||
|
||||
Use the `/specify` command to describe what you want to build. Focus on the **what** and **why**, not the tech stack.
|
||||
Use the **`/specify`** command to describe what you want to build. Focus on the **what** and **why**, not the tech stack.
|
||||
|
||||
```bash
|
||||
/specify Build an application that can help me organize my photos in separate photo albums. Albums are grouped by date and can be re-organized by dragging and dropping on the main page. Albums are never in other nested albums. Within each album, photos are previewed in a tile-like interface.
|
||||
@@ -53,7 +54,7 @@ Use the `/specify` command to describe what you want to build. Focus on the **wh
|
||||
|
||||
### 3. Create a technical implementation plan
|
||||
|
||||
Use the `/plan` command to provide your tech stack and architecture choices.
|
||||
Use the **`/plan`** command to provide your tech stack and architecture choices.
|
||||
|
||||
```bash
|
||||
/plan The application uses Vite with minimal number of libraries. Use vanilla HTML, CSS, and JavaScript as much as possible. Images are not uploaded anywhere and metadata is stored in a local SQLite database.
|
||||
@@ -61,10 +62,16 @@ Use the `/plan` command to provide your tech stack and architecture choices.
|
||||
|
||||
### 4. Break down and implement
|
||||
|
||||
Use `/tasks` to create an actionable task list, then ask your agent to implement the feature.
|
||||
Use **`/tasks`** to create an actionable task list, then ask your agent to implement the feature.
|
||||
|
||||
For detailed step-by-step instructions, see our [comprehensive guide](./spec-driven.md).
|
||||
|
||||
## 📽️ Video Overview
|
||||
|
||||
Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)!
|
||||
|
||||
[](https://www.youtube.com/watch?v=a9eR1xsfvHg&pp=0gcJCckJAYcqIYzv)
|
||||
|
||||
## 🔧 Specify CLI Reference
|
||||
|
||||
The `specify` command supports the following options:
|
||||
@@ -74,18 +81,20 @@ The `specify` command supports the following options:
|
||||
| Command | Description |
|
||||
|-------------|----------------------------------------------------------------|
|
||||
| `init` | Initialize a new Specify project from the latest template |
|
||||
| `check` | Check for installed tools (`git`, `claude`, `gemini`) |
|
||||
| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`) |
|
||||
|
||||
### `specify init` Arguments & Options
|
||||
|
||||
| Argument/Option | Type | Description |
|
||||
|------------------------|----------|------------------------------------------------------------------------------|
|
||||
| `<project-name>` | Argument | Name for your new project directory (optional if using `--here`) |
|
||||
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, or `copilot` |
|
||||
| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, or `cursor` |
|
||||
| `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) |
|
||||
| `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code |
|
||||
| `--no-git` | Flag | Skip git repository initialization |
|
||||
| `--here` | Flag | Initialize project in the current directory instead of creating a new one |
|
||||
| `--skip-tls` | Flag | Skip SSL/TLS verification (not recommended) |
|
||||
| `--debug` | Flag | Enable detailed debug output for troubleshooting |
|
||||
|
||||
### Examples
|
||||
|
||||
@@ -96,12 +105,21 @@ specify init my-project
|
||||
# Initialize with specific AI assistant
|
||||
specify init my-project --ai claude
|
||||
|
||||
# Initialize with Cursor support
|
||||
specify init my-project --ai cursor
|
||||
|
||||
# Initialize with PowerShell scripts (Windows/cross-platform)
|
||||
specify init my-project --ai copilot --script ps
|
||||
|
||||
# Initialize in current directory
|
||||
specify init --here --ai copilot
|
||||
|
||||
# Skip git initialization
|
||||
specify init my-project --ai gemini --no-git
|
||||
|
||||
# Enable debug output for troubleshooting
|
||||
specify init my-project --ai claude --debug
|
||||
|
||||
# Check system requirements
|
||||
specify check
|
||||
```
|
||||
@@ -152,7 +170,7 @@ Our research and experimentation focus on:
|
||||
## 🔧 Prerequisites
|
||||
|
||||
- **Linux/macOS** (or WSL2 on Windows)
|
||||
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), or [Gemini CLI](https://github.com/google-gemini/gemini-cli)
|
||||
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Gemini CLI](https://github.com/google-gemini/gemini-cli), or [Cursor](https://cursor.sh/)
|
||||
- [uv](https://docs.astral.sh/uv/) for package management
|
||||
- [Python 3.11+](https://www.python.org/downloads/)
|
||||
- [Git](https://git-scm.com/downloads)
|
||||
|
||||
@@ -12,7 +12,7 @@ Spec-Driven Development **flips the script** on traditional software development
|
||||
|
||||
- [Installation Guide](installation.md)
|
||||
- [Quick Start Guide](quickstart.md)
|
||||
- [Local Development](local-development.md)
|
||||
- [Local Development](local-development.md)
|
||||
|
||||
## Core Philosophy
|
||||
|
||||
|
||||
BIN
media/spec-kit-video-header.jpg
Normal file
BIN
media/spec-kit-video-header.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
@@ -1,12 +1,12 @@
|
||||
[project]
|
||||
name = "specify-cli"
|
||||
version = "0.0.3"
|
||||
version = "0.0.4"
|
||||
description = "Setup tool for Specify spec-driven development projects"
|
||||
requires-python = ">=3.11"
|
||||
dependencies = [
|
||||
"typer",
|
||||
"rich",
|
||||
"httpx",
|
||||
"httpx[socks]",
|
||||
"platformdirs",
|
||||
"readchar",
|
||||
"truststore>=0.10.4",
|
||||
|
||||
@@ -4,7 +4,7 @@ REPO_ROOT=$(git rev-parse --show-toplevel)
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
FEATURE_DIR="$REPO_ROOT/specs/$CURRENT_BRANCH"
|
||||
NEW_PLAN="$FEATURE_DIR/plan.md"
|
||||
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"; GEMINI_FILE="$REPO_ROOT/GEMINI.md"; COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
|
||||
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"; GEMINI_FILE="$REPO_ROOT/GEMINI.md"; COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"; CURSOR_FILE="$REPO_ROOT/.cursor/rules/specify-rules.mdc"
|
||||
AGENT_TYPE="$1"
|
||||
[ -f "$NEW_PLAN" ] || { echo "ERROR: No plan.md found at $NEW_PLAN"; exit 1; }
|
||||
echo "=== Updating agent context files for feature $CURRENT_BRANCH ==="
|
||||
@@ -13,7 +13,7 @@ NEW_FRAMEWORK=$(grep "^**Primary Dependencies**: " "$NEW_PLAN" 2>/dev/null | hea
|
||||
NEW_DB=$(grep "^**Storage**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Storage**: //' | grep -v "N/A" | grep -v "NEEDS CLARIFICATION" || echo "")
|
||||
NEW_PROJECT_TYPE=$(grep "^**Project Type**: " "$NEW_PLAN" 2>/dev/null | head -1 | sed 's/^**Project Type**: //' || echo "")
|
||||
update_agent_file() { local target_file="$1" agent_name="$2"; echo "Updating $agent_name context file: $target_file"; local temp_file=$(mktemp); if [ ! -f "$target_file" ]; then
|
||||
echo "Creating new $agent_name context file..."; if [ -f "$REPO_ROOT/templates/agent-file-template.md" ]; then cp "$REPO_ROOT/templates/agent-file-template.md" "$temp_file"; else echo "ERROR: Template not found"; return 1; fi;
|
||||
echo "Creating new $agent_name context file..."; if [ -f "$REPO_ROOT/.specify/templates/agent-file-template.md" ]; then cp "$REPO_ROOT/templates/agent-file-template.md" "$temp_file"; else echo "ERROR: Template not found"; return 1; fi;
|
||||
sed -i.bak "s/\[PROJECT NAME\]/$(basename $REPO_ROOT)/" "$temp_file"; sed -i.bak "s/\[DATE\]/$(date +%Y-%m-%d)/" "$temp_file"; sed -i.bak "s/\[EXTRACTED FROM ALL PLAN.MD FILES\]/- $NEW_LANG + $NEW_FRAMEWORK ($CURRENT_BRANCH)/" "$temp_file";
|
||||
if [[ "$NEW_PROJECT_TYPE" == *"web"* ]]; then sed -i.bak "s|\[ACTUAL STRUCTURE FROM PLANS\]|backend/\nfrontend/\ntests/|" "$temp_file"; else sed -i.bak "s|\[ACTUAL STRUCTURE FROM PLANS\]|src/\ntests/|" "$temp_file"; fi;
|
||||
if [[ "$NEW_LANG" == *"Python"* ]]; then COMMANDS="cd src && pytest && ruff check ."; elif [[ "$NEW_LANG" == *"Rust"* ]]; then COMMANDS="cargo test && cargo clippy"; elif [[ "$NEW_LANG" == *"JavaScript"* ]] || [[ "$NEW_LANG" == *"TypeScript"* ]]; then COMMANDS="npm test && npm run lint"; else COMMANDS="# Add commands for $NEW_LANG"; fi; sed -i.bak "s|\[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES\]|$COMMANDS|" "$temp_file";
|
||||
@@ -51,7 +51,12 @@ case "$AGENT_TYPE" in
|
||||
claude) update_agent_file "$CLAUDE_FILE" "Claude Code" ;;
|
||||
gemini) update_agent_file "$GEMINI_FILE" "Gemini CLI" ;;
|
||||
copilot) update_agent_file "$COPILOT_FILE" "GitHub Copilot" ;;
|
||||
"") [ -f "$CLAUDE_FILE" ] && update_agent_file "$CLAUDE_FILE" "Claude Code"; [ -f "$GEMINI_FILE" ] && update_agent_file "$GEMINI_FILE" "Gemini CLI"; [ -f "$COPILOT_FILE" ] && update_agent_file "$COPILOT_FILE" "GitHub Copilot"; if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ]; then update_agent_file "$CLAUDE_FILE" "Claude Code"; fi ;;
|
||||
*) echo "ERROR: Unknown agent type '$AGENT_TYPE'"; exit 1 ;;
|
||||
cursor) update_agent_file "$CURSOR_FILE" "Cursor IDE" ;;
|
||||
"") [ -f "$CLAUDE_FILE" ] && update_agent_file "$CLAUDE_FILE" "Claude Code"; \
|
||||
[ -f "$GEMINI_FILE" ] && update_agent_file "$GEMINI_FILE" "Gemini CLI"; \
|
||||
[ -f "$COPILOT_FILE" ] && update_agent_file "$COPILOT_FILE" "GitHub Copilot"; \
|
||||
[ -f "$CURSOR_FILE" ] && update_agent_file "$CURSOR_FILE" "Cursor IDE"; \
|
||||
if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ] && [ ! -f "$CURSOR_FILE" ]; then update_agent_file "$CLAUDE_FILE" "Claude Code"; fi ;;
|
||||
*) echo "ERROR: Unknown agent type '$AGENT_TYPE' (expected claude|gemini|copilot|cursor)"; exit 1 ;;
|
||||
esac
|
||||
echo; echo "Summary of changes:"; [ -n "$NEW_LANG" ] && echo "- Added language: $NEW_LANG"; [ -n "$NEW_FRAMEWORK" ] && echo "- Added framework: $NEW_FRAMEWORK"; [ -n "$NEW_DB" ] && [ "$NEW_DB" != "N/A" ] && echo "- Added database: $NEW_DB"; echo; echo "Usage: $0 [claude|gemini|copilot]"
|
||||
echo; echo "Summary of changes:"; [ -n "$NEW_LANG" ] && echo "- Added language: $NEW_LANG"; [ -n "$NEW_FRAMEWORK" ] && echo "- Added framework: $NEW_FRAMEWORK"; [ -n "$NEW_DB" ] && [ "$NEW_DB" != "N/A" ] && echo "- Added database: $NEW_DB"; echo; echo "Usage: $0 [claude|gemini|copilot|cursor]"
|
||||
|
||||
@@ -12,6 +12,7 @@ if (-not (Test-Path $newPlan)) { Write-Error "ERROR: No plan.md found at $newPla
|
||||
$claudeFile = Join-Path $repoRoot 'CLAUDE.md'
|
||||
$geminiFile = Join-Path $repoRoot 'GEMINI.md'
|
||||
$copilotFile = Join-Path $repoRoot '.github/copilot-instructions.md'
|
||||
$cursorFile = Join-Path $repoRoot '.cursor/rules/specify-rules.mdc'
|
||||
|
||||
Write-Output "=== Updating agent context files for feature $currentBranch ==="
|
||||
|
||||
@@ -30,7 +31,7 @@ $newProjectType = Get-PlanValue 'Project Type'
|
||||
|
||||
function Initialize-AgentFile($targetFile, $agentName) {
|
||||
if (Test-Path $targetFile) { return }
|
||||
$template = Join-Path $repoRoot 'templates/agent-file-template.md'
|
||||
$template = Join-Path $repoRoot '.specify/templates/agent-file-template.md'
|
||||
if (-not (Test-Path $template)) { Write-Error "Template not found: $template"; return }
|
||||
$content = Get-Content $template -Raw
|
||||
$content = $content.Replace('[PROJECT NAME]', (Split-Path $repoRoot -Leaf))
|
||||
@@ -43,8 +44,8 @@ function Initialize-AgentFile($targetFile, $agentName) {
|
||||
elseif ($newLang -match 'JavaScript|TypeScript') { $commands = 'npm test && npm run lint' }
|
||||
else { $commands = "# Add commands for $newLang" }
|
||||
$content = $content.Replace('[ONLY COMMANDS FOR ACTIVE TECHNOLOGIES]', $commands)
|
||||
$content = $content.Replace('[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]', "$newLang: Follow standard conventions")
|
||||
$content = $content.Replace('[LAST 3 FEATURES AND WHAT THEY ADDED]', "- $currentBranch: Added $newLang + $newFramework")
|
||||
$content = $content.Replace('[LANGUAGE-SPECIFIC, ONLY FOR LANGUAGES IN USE]', "${newLang}: Follow standard conventions")
|
||||
$content = $content.Replace('[LAST 3 FEATURES AND WHAT THEY ADDED]', "- ${currentBranch}: Added ${newLang} + ${newFramework}")
|
||||
$content | Set-Content $targetFile -Encoding UTF8
|
||||
}
|
||||
|
||||
@@ -55,7 +56,7 @@ function Update-AgentFile($targetFile, $agentName) {
|
||||
if ($newDb -and $newDb -ne 'N/A' -and ($content -notmatch [regex]::Escape($newDb))) { $content = $content -replace '(## Active Technologies\n)', "`$1- $newDb ($currentBranch)`n" }
|
||||
if ($content -match '## Recent Changes\n([\s\S]*?)(\n\n|$)') {
|
||||
$changesBlock = $matches[1].Trim().Split("`n")
|
||||
$changesBlock = ,"- $currentBranch: Added $newLang + $newFramework" + $changesBlock
|
||||
$changesBlock = ,"- ${currentBranch}: Added ${newLang} + ${newFramework}" + $changesBlock
|
||||
$changesBlock = $changesBlock | Where-Object { $_ } | Select-Object -First 3
|
||||
$joined = ($changesBlock -join "`n")
|
||||
$content = [regex]::Replace($content, '## Recent Changes\n([\s\S]*?)(\n\n|$)', "## Recent Changes`n$joined`n`n")
|
||||
@@ -69,16 +70,22 @@ switch ($AgentType) {
|
||||
'claude' { Update-AgentFile $claudeFile 'Claude Code' }
|
||||
'gemini' { Update-AgentFile $geminiFile 'Gemini CLI' }
|
||||
'copilot' { Update-AgentFile $copilotFile 'GitHub Copilot' }
|
||||
'cursor' { Update-AgentFile $cursorFile 'Cursor IDE' }
|
||||
'' {
|
||||
foreach ($pair in @(@{file=$claudeFile; name='Claude Code'}, @{file=$geminiFile; name='Gemini CLI'}, @{file=$copilotFile; name='GitHub Copilot'})) {
|
||||
foreach ($pair in @(
|
||||
@{file=$claudeFile; name='Claude Code'},
|
||||
@{file=$geminiFile; name='Gemini CLI'},
|
||||
@{file=$copilotFile; name='GitHub Copilot'},
|
||||
@{file=$cursorFile; name='Cursor IDE'}
|
||||
)) {
|
||||
if (Test-Path $pair.file) { Update-AgentFile $pair.file $pair.name }
|
||||
}
|
||||
if (-not (Test-Path $claudeFile) -and -not (Test-Path $geminiFile) -and -not (Test-Path $copilotFile)) {
|
||||
if (-not (Test-Path $claudeFile) -and -not (Test-Path $geminiFile) -and -not (Test-Path $copilotFile) -and -not (Test-Path $cursorFile)) {
|
||||
Write-Output 'No agent context files found. Creating Claude Code context file by default.'
|
||||
Update-AgentFile $claudeFile 'Claude Code'
|
||||
}
|
||||
}
|
||||
Default { Write-Error "ERROR: Unknown agent type '$AgentType'. Use: claude, gemini, copilot, or leave empty for all."; exit 1 }
|
||||
Default { Write-Error "ERROR: Unknown agent type '$AgentType'. Use: claude, gemini, copilot, cursor or leave empty for all."; exit 1 }
|
||||
}
|
||||
|
||||
Write-Output ''
|
||||
@@ -88,4 +95,4 @@ if ($newFramework) { Write-Output "- Added framework: $newFramework" }
|
||||
if ($newDb -and $newDb -ne 'N/A') { Write-Output "- Added database: $newDb" }
|
||||
|
||||
Write-Output ''
|
||||
Write-Output 'Usage: ./update-agent-context.ps1 [claude|gemini|copilot]'
|
||||
Write-Output 'Usage: ./update-agent-context.ps1 [claude|gemini|copilot|cursor]'
|
||||
|
||||
@@ -56,7 +56,8 @@ client = httpx.Client(verify=ssl_context)
|
||||
AI_CHOICES = {
|
||||
"copilot": "GitHub Copilot",
|
||||
"claude": "Claude Code",
|
||||
"gemini": "Gemini CLI"
|
||||
"gemini": "Gemini CLI",
|
||||
"cursor": "Cursor"
|
||||
}
|
||||
# Add script type choices
|
||||
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
|
||||
@@ -721,7 +722,7 @@ def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None =
|
||||
@app.command()
|
||||
def init(
|
||||
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here)"),
|
||||
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, or copilot"),
|
||||
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, or cursor"),
|
||||
script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"),
|
||||
ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI agent tools like Claude Code"),
|
||||
no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),
|
||||
@@ -734,7 +735,7 @@ def init(
|
||||
|
||||
This command will:
|
||||
1. Check that required tools are installed (git is optional)
|
||||
2. Let you choose your AI assistant (Claude Code, Gemini CLI, or GitHub Copilot)
|
||||
2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, or Cursor)
|
||||
3. Download the appropriate template from GitHub
|
||||
4. Extract the template to a new project directory or current directory
|
||||
5. Initialize a fresh git repository (if not --no-git and no existing repo)
|
||||
@@ -745,6 +746,7 @@ def init(
|
||||
specify init my-project --ai claude
|
||||
specify init my-project --ai gemini
|
||||
specify init my-project --ai copilot --no-git
|
||||
specify init my-project --ai cursor
|
||||
specify init --ignore-agent-tools my-project
|
||||
specify init --here --ai claude
|
||||
specify init --here
|
||||
@@ -823,8 +825,7 @@ def init(
|
||||
if not check_tool("gemini", "Install from: https://github.com/google-gemini/gemini-cli"):
|
||||
console.print("[red]Error:[/red] Gemini CLI is required for Gemini projects")
|
||||
agent_tool_missing = True
|
||||
# GitHub Copilot check is not needed as it's typically available in supported IDEs
|
||||
|
||||
|
||||
if agent_tool_missing:
|
||||
console.print("\n[red]Required AI tool is missing![/red]")
|
||||
console.print("[yellow]Tip:[/yellow] Use --ignore-agent-tools to skip this check")
|
||||
@@ -974,11 +975,18 @@ def check():
|
||||
tracker.add("git", "Git version control")
|
||||
tracker.add("claude", "Claude Code CLI")
|
||||
tracker.add("gemini", "Gemini CLI")
|
||||
tracker.add("code", "VS Code (for GitHub Copilot)")
|
||||
tracker.add("cursor-agent", "Cursor IDE agent (optional)")
|
||||
|
||||
# Check each tool
|
||||
git_ok = check_tool_for_tracker("git", "https://git-scm.com/downloads", tracker)
|
||||
claude_ok = check_tool_for_tracker("claude", "https://docs.anthropic.com/en/docs/claude-code/setup", tracker)
|
||||
gemini_ok = check_tool_for_tracker("gemini", "https://github.com/google-gemini/gemini-cli", tracker)
|
||||
# Check for VS Code (code or code-insiders)
|
||||
code_ok = check_tool_for_tracker("code", "https://code.visualstudio.com/", tracker)
|
||||
if not code_ok:
|
||||
code_ok = check_tool_for_tracker("code-insiders", "https://code.visualstudio.com/insiders/", tracker)
|
||||
cursor_ok = check_tool_for_tracker("cursor-agent", "https://cursor.sh/", tracker)
|
||||
|
||||
# Render the final tree
|
||||
console.print(tracker.render())
|
||||
|
||||
@@ -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:
|
||||
|
||||
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:
|
||||
- The feature requirements and user stories
|
||||
- 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:
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
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:
|
||||
- Always read plan.md for tech stack and libraries
|
||||
- IF EXISTS: Read data-model.md for entities
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# Implementation Plan: [FEATURE]
|
||||
---
|
||||
description: "Implementation plan template for feature development"
|
||||
scripts:
|
||||
sh: scripts/bash/update-agent-context.sh __AGENT__
|
||||
ps: scripts/powershell/update-agent-context.ps1 -AgentType __AGENT__
|
||||
---
|
||||
|
||||
<!-- VARIANT:sh - Run `/scripts/bash/update-agent-context.sh __AGENT__` for your AI assistant -->
|
||||
<!-- VARIANT:ps - Run `/scripts/powershell/update-agent-context.ps1 -AgentType __AGENT__` for your AI assistant -->
|
||||
# Implementation Plan: [FEATURE]
|
||||
|
||||
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
||||
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
||||
@@ -174,7 +178,7 @@ ios/ or android/
|
||||
- Quickstart test = story validation steps
|
||||
|
||||
5. **Update agent file incrementally** (O(1) operation):
|
||||
VARIANT-INJECT
|
||||
- Run `{SCRIPT}` for your AI assistant
|
||||
- If exists: Add only NEW tech from current plan
|
||||
- Preserve manual additions between markers
|
||||
- Update recent changes (keep last 3)
|
||||
@@ -187,7 +191,7 @@ ios/ or android/
|
||||
*This section describes what the /tasks command will do - DO NOT execute during /plan*
|
||||
|
||||
**Task Generation Strategy**:
|
||||
- Load `/templates/tasks-template.md` as base
|
||||
- Load `.specify/templates/tasks-template.md` as base
|
||||
- Generate tasks from Phase 1 design docs (contracts, data model, quickstart)
|
||||
- Each contract → contract test task [P]
|
||||
- Each entity → model creation task [P]
|
||||
|
||||
Reference in New Issue
Block a user