Merge main into feature/add-qwen-support
Resolved conflicts in release workflow and CLI: - Integrated external script approach for release package creation - Added Qwen Code support to release script and CLI tool checking - Maintained all existing functionality for other AI assistants
This commit is contained in:
191
.github/workflows/manual-release.yml
vendored
191
.github/workflows/manual-release.yml
vendored
@@ -1,191 +0,0 @@
|
|||||||
name: Manual Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
version_bump:
|
|
||||||
description: 'Version bump type'
|
|
||||||
required: true
|
|
||||||
default: 'patch'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- patch
|
|
||||||
- minor
|
|
||||||
- major
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
manual_release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Calculate new version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
# Get the latest tag, or use v0.0.0 if no tags exist
|
|
||||||
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
|
|
||||||
echo "latest_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# Extract version number
|
|
||||||
VERSION=$(echo $LATEST_TAG | sed 's/v//')
|
|
||||||
IFS='.' read -ra VERSION_PARTS <<< "$VERSION"
|
|
||||||
MAJOR=${VERSION_PARTS[0]:-0}
|
|
||||||
MINOR=${VERSION_PARTS[1]:-0}
|
|
||||||
PATCH=${VERSION_PARTS[2]:-0}
|
|
||||||
|
|
||||||
# Increment based on input
|
|
||||||
case "${{ github.event.inputs.version_bump }}" in
|
|
||||||
"major")
|
|
||||||
MAJOR=$((MAJOR + 1))
|
|
||||||
MINOR=0
|
|
||||||
PATCH=0
|
|
||||||
;;
|
|
||||||
"minor")
|
|
||||||
MINOR=$((MINOR + 1))
|
|
||||||
PATCH=0
|
|
||||||
;;
|
|
||||||
"patch")
|
|
||||||
PATCH=$((PATCH + 1))
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
|
||||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
|
||||||
echo "New version will be: $NEW_VERSION (was $LATEST_TAG)"
|
|
||||||
|
|
||||||
- name: Create release package
|
|
||||||
run: |
|
|
||||||
# Create base package directory structure
|
|
||||||
mkdir -p sdd-package-base
|
|
||||||
|
|
||||||
# Copy common folders to base
|
|
||||||
echo "Packaging SDD common components..."
|
|
||||||
|
|
||||||
if [ -d "memory" ]; then
|
|
||||||
cp -r memory sdd-package-base/
|
|
||||||
echo "✓ Copied memory folder ($(find memory -type f | wc -l) files)"
|
|
||||||
else
|
|
||||||
echo "⚠️ memory folder not found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "scripts" ]; then
|
|
||||||
cp -r scripts sdd-package-base/
|
|
||||||
echo "✓ Copied scripts folder ($(find scripts -type f | wc -l) files)"
|
|
||||||
else
|
|
||||||
echo "⚠️ scripts folder not found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create Claude Code package
|
|
||||||
echo "Creating Claude Code package..."
|
|
||||||
mkdir -p sdd-claude-package
|
|
||||||
cp -r sdd-package-base/* sdd-claude-package/
|
|
||||||
if [ -d "agent_templates/claude" ]; then
|
|
||||||
cp -r agent_templates/claude sdd-claude-package/.claude
|
|
||||||
echo "✓ Added Claude Code commands ($(find agent_templates/claude -type f | wc -l) files)"
|
|
||||||
else
|
|
||||||
echo "⚠️ agent_templates/claude folder not found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create Gemini CLI package
|
|
||||||
echo "Creating Gemini CLI package..."
|
|
||||||
mkdir -p sdd-gemini-package
|
|
||||||
cp -r sdd-package-base/* sdd-gemini-package/
|
|
||||||
if [ -d "agent_templates/gemini" ]; then
|
|
||||||
cp -r agent_templates/gemini sdd-gemini-package/.gemini
|
|
||||||
# Move GEMINI.md to root for easier access
|
|
||||||
if [ -f "sdd-gemini-package/.gemini/GEMINI.md" ]; then
|
|
||||||
mv sdd-gemini-package/.gemini/GEMINI.md sdd-gemini-package/GEMINI.md
|
|
||||||
echo "✓ Moved GEMINI.md to root of Gemini package"
|
|
||||||
fi
|
|
||||||
# Remove empty .gemini folder if it only contained GEMINI.md
|
|
||||||
if [ -d "sdd-gemini-package/.gemini" ] && [ -z "$(find sdd-gemini-package/.gemini -type f)" ]; then
|
|
||||||
rm -rf sdd-gemini-package/.gemini
|
|
||||||
echo "✓ Removed empty .gemini folder"
|
|
||||||
fi
|
|
||||||
echo "✓ Added Gemini CLI commands ($(find agent_templates/gemini -type f | wc -l) files)"
|
|
||||||
else
|
|
||||||
echo "⚠️ agent_templates/gemini folder not found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create GitHub Copilot package
|
|
||||||
echo "Creating GitHub Copilot package..."
|
|
||||||
mkdir -p sdd-copilot-package
|
|
||||||
cp -r sdd-package-base/* sdd-copilot-package/
|
|
||||||
if [ -d "agent_templates/copilot" ]; then
|
|
||||||
mkdir -p sdd-copilot-package/.github
|
|
||||||
cp -r agent_templates/copilot/* sdd-copilot-package/.github/
|
|
||||||
echo "✓ Added Copilot instructions to .github ($(find agent_templates/copilot -type f | wc -l) files)"
|
|
||||||
else
|
|
||||||
echo "⚠️ agent_templates/copilot folder not found"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create archive files for each package
|
|
||||||
echo "Creating archive files..."
|
|
||||||
cd sdd-claude-package && zip -r ../spec-kit-template-claude-${{ steps.version.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
cd sdd-gemini-package && zip -r ../spec-kit-template-gemini-${{ steps.version.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${{ steps.version.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "📦 Packages created:"
|
|
||||||
echo "Claude: $(ls -lh spec-kit-template-claude-*.zip | awk '{print $5}')"
|
|
||||||
echo "Gemini: $(ls -lh spec-kit-template-gemini-*.zip | awk '{print $5}')"
|
|
||||||
echo "Copilot: $(ls -lh spec-kit-template-copilot-*.zip | awk '{print $5}')"
|
|
||||||
echo "Copilot: $(ls -lh sdd-template-copilot-*.zip | awk '{print $5}')"
|
|
||||||
|
|
||||||
- name: Generate detailed release notes
|
|
||||||
run: |
|
|
||||||
LAST_TAG=${{ steps.version.outputs.latest_tag }}
|
|
||||||
|
|
||||||
# Get commit range
|
|
||||||
if [ "$LAST_TAG" = "v0.0.0" ]; then
|
|
||||||
COMMIT_RANGE="HEAD~10..HEAD"
|
|
||||||
COMMITS=$(git log --oneline --pretty=format:"- %s" $COMMIT_RANGE 2>/dev/null || echo "- Initial release")
|
|
||||||
else
|
|
||||||
COMMIT_RANGE="$LAST_TAG..HEAD"
|
|
||||||
COMMITS=$(git log --oneline --pretty=format:"- %s" $COMMIT_RANGE 2>/dev/null || echo "- No changes since last release")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Count files in each directory
|
|
||||||
CLAUDE_COUNT=$(find agent_templates/claude -type f 2>/dev/null | wc -l || echo "0")
|
|
||||||
GEMINI_COUNT=$(find agent_templates/gemini -type f 2>/dev/null | wc -l || echo "0")
|
|
||||||
COPILOT_COUNT=$(find agent_templates/copilot -type f 2>/dev/null | wc -l || echo "0")
|
|
||||||
MEMORY_COUNT=$(find memory -type f 2>/dev/null | wc -l || echo "0")
|
|
||||||
SCRIPTS_COUNT=$(find scripts -type f 2>/dev/null | wc -l || echo "0")
|
|
||||||
|
|
||||||
cat > release_notes.md << EOF
|
|
||||||
Template release ${{ steps.version.outputs.new_version }}
|
|
||||||
|
|
||||||
Updated specification-driven development templates for GitHub Copilot, Claude Code, and Gemini CLI.
|
|
||||||
|
|
||||||
Download the template for your preferred AI assistant:
|
|
||||||
- spec-kit-template-copilot-${{ steps.version.outputs.new_version }}.zip
|
|
||||||
- spec-kit-template-claude-${{ steps.version.outputs.new_version }}.zip
|
|
||||||
- spec-kit-template-gemini-${{ steps.version.outputs.new_version }}.zip
|
|
||||||
|
|
||||||
Changes since $LAST_TAG:
|
|
||||||
$COMMITS
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Create GitHub Release
|
|
||||||
run: |
|
|
||||||
# Remove 'v' prefix from version for release title
|
|
||||||
VERSION_NO_V=${{ steps.version.outputs.new_version }}
|
|
||||||
VERSION_NO_V=${VERSION_NO_V#v}
|
|
||||||
|
|
||||||
gh release create ${{ steps.version.outputs.new_version }} \
|
|
||||||
spec-kit-template-copilot-${{ steps.version.outputs.new_version }}.zip \
|
|
||||||
spec-kit-template-claude-${{ steps.version.outputs.new_version }}.zip \
|
|
||||||
spec-kit-template-gemini-${{ steps.version.outputs.new_version }}.zip \
|
|
||||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
|
||||||
--notes-file release_notes.md
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
116
.github/workflows/release.yml
vendored
116
.github/workflows/release.yml
vendored
@@ -7,6 +7,7 @@ on:
|
|||||||
- 'memory/**'
|
- 'memory/**'
|
||||||
- 'scripts/**'
|
- 'scripts/**'
|
||||||
- 'templates/**'
|
- 'templates/**'
|
||||||
|
- '.github/workflows/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -61,119 +62,8 @@ jobs:
|
|||||||
- name: Create release package
|
- name: Create release package
|
||||||
if: steps.check_release.outputs.exists == 'false'
|
if: steps.check_release.outputs.exists == 'false'
|
||||||
run: |
|
run: |
|
||||||
# Create base package directory structure
|
chmod +x .github/workflows/scripts/create-release-packages.sh
|
||||||
mkdir -p sdd-package-base
|
.github/workflows/scripts/create-release-packages.sh ${{ steps.get_tag.outputs.new_version }}
|
||||||
|
|
||||||
# Copy common folders to base
|
|
||||||
if [ -d "memory" ]; then
|
|
||||||
cp -r memory sdd-package-base/
|
|
||||||
echo "Copied memory folder"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "scripts" ]; then
|
|
||||||
cp -r scripts sdd-package-base/
|
|
||||||
echo "Copied scripts folder"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "templates" ]; then
|
|
||||||
mkdir -p sdd-package-base/templates
|
|
||||||
# Copy templates folder but exclude the commands directory
|
|
||||||
find templates -type f -not -path "templates/commands/*" -exec cp --parents {} sdd-package-base/ \;
|
|
||||||
echo "Copied templates folder (excluding commands directory)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate command files for each agent from source templates
|
|
||||||
generate_commands() {
|
|
||||||
local agent=$1
|
|
||||||
local ext=$2
|
|
||||||
local arg_format=$3
|
|
||||||
local output_dir=$4
|
|
||||||
|
|
||||||
mkdir -p "$output_dir"
|
|
||||||
|
|
||||||
for template in templates/commands/*.md; do
|
|
||||||
if [[ -f "$template" ]]; then
|
|
||||||
name=$(basename "$template" .md)
|
|
||||||
description=$(awk '/^description:/ {gsub(/^description: *"?/, ""); gsub(/"$/, ""); print; exit}' "$template" | tr -d '\r')
|
|
||||||
content=$(awk '/^---$/{if(++count==2) start=1; next} start' "$template" | sed "s/{ARGS}/$arg_format/g")
|
|
||||||
|
|
||||||
case $ext in
|
|
||||||
"toml")
|
|
||||||
{
|
|
||||||
echo "description = \"$description\""
|
|
||||||
echo ""
|
|
||||||
echo "prompt = \"\"\""
|
|
||||||
echo "$content"
|
|
||||||
echo "\"\"\""
|
|
||||||
} > "$output_dir/$name.$ext"
|
|
||||||
;;
|
|
||||||
"md")
|
|
||||||
echo "$content" > "$output_dir/$name.$ext"
|
|
||||||
;;
|
|
||||||
"prompt.md")
|
|
||||||
{
|
|
||||||
echo "# $(echo "$description" | sed 's/\. .*//')"
|
|
||||||
echo ""
|
|
||||||
echo "$content"
|
|
||||||
} > "$output_dir/$name.$ext"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create Claude Code package
|
|
||||||
mkdir -p sdd-claude-package
|
|
||||||
cp -r sdd-package-base/* sdd-claude-package/
|
|
||||||
mkdir -p sdd-claude-package/.claude/commands
|
|
||||||
generate_commands "claude" "md" "\$ARGUMENTS" "sdd-claude-package/.claude/commands"
|
|
||||||
echo "Created Claude Code package"
|
|
||||||
|
|
||||||
# Create Gemini CLI package
|
|
||||||
mkdir -p sdd-gemini-package
|
|
||||||
cp -r sdd-package-base/* sdd-gemini-package/
|
|
||||||
mkdir -p sdd-gemini-package/.gemini/commands
|
|
||||||
generate_commands "gemini" "toml" "{{args}}" "sdd-gemini-package/.gemini/commands"
|
|
||||||
if [ -f "agent_templates/gemini/GEMINI.md" ]; then
|
|
||||||
cp agent_templates/gemini/GEMINI.md sdd-gemini-package/GEMINI.md
|
|
||||||
fi
|
|
||||||
echo "Created Gemini CLI package"
|
|
||||||
|
|
||||||
# Create GitHub Copilot package
|
|
||||||
mkdir -p sdd-copilot-package
|
|
||||||
cp -r sdd-package-base/* sdd-copilot-package/
|
|
||||||
mkdir -p sdd-copilot-package/.github/prompts
|
|
||||||
generate_commands "copilot" "prompt.md" "\$ARGUMENTS" "sdd-copilot-package/.github/prompts"
|
|
||||||
echo "Created GitHub Copilot package"
|
|
||||||
|
|
||||||
# Create Qwen CLI package
|
|
||||||
mkdir -p sdd-qwen-package
|
|
||||||
cp -r sdd-package-base/* sdd-qwen-package/
|
|
||||||
mkdir -p sdd-qwen-package/.qwen/commands
|
|
||||||
generate_commands "qwen" "md" "\$ARGUMENTS" "sdd-qwen-package/.qwen/commands"
|
|
||||||
if [ -f "agent_templates/qwen/QWEN.md" ]; then
|
|
||||||
cp agent_templates/qwen/QWEN.md sdd-qwen-package/QWEN.md
|
|
||||||
fi
|
|
||||||
echo "Created Qwen CLI package"
|
|
||||||
|
|
||||||
# Create archive files for each package
|
|
||||||
cd sdd-claude-package && zip -r ../spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
cd sdd-gemini-package && zip -r ../spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
cd sdd-qwen-package && zip -r ../spec-kit-template-qwen-${{ steps.get_tag.outputs.new_version }}.zip . && cd ..
|
|
||||||
|
|
||||||
# List contents for verification
|
|
||||||
echo "Claude package contents:"
|
|
||||||
unzip -l spec-kit-template-claude-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
|
||||||
echo "Gemini package contents:"
|
|
||||||
unzip -l spec-kit-template-gemini-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
|
||||||
echo "Copilot package contents:"
|
|
||||||
unzip -l spec-kit-template-copilot-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
|
||||||
echo "Qwen package contents:"
|
|
||||||
unzip -l spec-kit-template-qwen-${{ steps.get_tag.outputs.new_version }}.zip | head -10
|
|
||||||
|
|
||||||
- name: Generate release notes
|
- name: Generate release notes
|
||||||
if: steps.check_release.outputs.exists == 'false'
|
if: steps.check_release.outputs.exists == 'false'
|
||||||
|
|||||||
104
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file
104
.github/workflows/scripts/create-release-packages.sh
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# create-release-packages.sh (workflow-local)
|
||||||
|
# Build Spec Kit template release archives for each supported AI assistant.
|
||||||
|
# Usage: .github/workflows/scripts/create-release-packages.sh <version>
|
||||||
|
# Version argument should include leading 'v'.
|
||||||
|
|
||||||
|
if [[ $# -ne 1 ]]; then
|
||||||
|
echo "Usage: $0 <version-with-v-prefix>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
NEW_VERSION="$1"
|
||||||
|
if [[ ! $NEW_VERSION =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "Version must look like v0.0.0" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building release packages for $NEW_VERSION"
|
||||||
|
|
||||||
|
rm -rf sdd-package-base sdd-claude-package sdd-gemini-package sdd-copilot-package sdd-qwen-package \
|
||||||
|
spec-kit-template-claude-${NEW_VERSION}.zip \
|
||||||
|
spec-kit-template-gemini-${NEW_VERSION}.zip \
|
||||||
|
spec-kit-template-copilot-${NEW_VERSION}.zip \
|
||||||
|
spec-kit-template-qwen-${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' \
|
||||||
|
-e 's@(/?)scripts/@.specify/scripts/@g' \
|
||||||
|
-e 's@(/?)templates/@.specify/templates/@g'
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_commands() {
|
||||||
|
local agent=$1 ext=$2 arg_format=$3 output_dir=$4
|
||||||
|
mkdir -p "$output_dir"
|
||||||
|
for template in templates/commands/*.md; do
|
||||||
|
[[ -f "$template" ]] || continue
|
||||||
|
local name description body
|
||||||
|
name=$(basename "$template" .md)
|
||||||
|
description=$(awk '/^description:/ {gsub(/^description: *"?/, ""); gsub(/"$/, ""); print; exit}' "$template" | tr -d '\r')
|
||||||
|
body=$(awk '/^---$/{if(++count==2) start=1; next} start' "$template" | sed "s/{ARGS}/$arg_format/g" | rewrite_paths)
|
||||||
|
case $ext in
|
||||||
|
toml)
|
||||||
|
{ echo "description = \"$description\""; echo; echo "prompt = \"\"\""; echo "$body"; echo "\"\"\""; } > "$output_dir/$name.$ext" ;;
|
||||||
|
md)
|
||||||
|
echo "$body" > "$output_dir/$name.$ext" ;;
|
||||||
|
prompt.md)
|
||||||
|
sed "s/{ARGS}/$arg_format/g" "$template" | rewrite_paths > "$output_dir/$name.$ext" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create Claude package
|
||||||
|
echo "Building Claude package..."
|
||||||
|
mkdir -p sdd-claude-package
|
||||||
|
cp -r sdd-package-base/. sdd-claude-package/
|
||||||
|
mkdir -p sdd-claude-package/.claude/commands
|
||||||
|
generate_commands claude md "\$ARGUMENTS" sdd-claude-package/.claude/commands
|
||||||
|
echo "Created Claude package"
|
||||||
|
|
||||||
|
# Create Gemini package
|
||||||
|
echo "Building Gemini package..."
|
||||||
|
mkdir -p sdd-gemini-package
|
||||||
|
cp -r sdd-package-base/. sdd-gemini-package/
|
||||||
|
mkdir -p sdd-gemini-package/.gemini/commands
|
||||||
|
generate_commands gemini toml "{{args}}" sdd-gemini-package/.gemini/commands
|
||||||
|
[[ -f agent_templates/gemini/GEMINI.md ]] && cp agent_templates/gemini/GEMINI.md sdd-gemini-package/GEMINI.md
|
||||||
|
echo "Created Gemini package"
|
||||||
|
|
||||||
|
# Create Copilot package
|
||||||
|
echo "Building Copilot package..."
|
||||||
|
mkdir -p sdd-copilot-package
|
||||||
|
cp -r sdd-package-base/. sdd-copilot-package/
|
||||||
|
mkdir -p sdd-copilot-package/.github/prompts
|
||||||
|
generate_commands copilot prompt.md "\$ARGUMENTS" sdd-copilot-package/.github/prompts
|
||||||
|
echo "Created Copilot package"
|
||||||
|
|
||||||
|
# Create Qwen package
|
||||||
|
echo "Building Qwen package..."
|
||||||
|
mkdir -p sdd-qwen-package
|
||||||
|
cp -r sdd-package-base/. sdd-qwen-package/
|
||||||
|
mkdir -p sdd-qwen-package/.qwen/commands
|
||||||
|
generate_commands qwen md "\$ARGUMENTS" sdd-qwen-package/.qwen/commands
|
||||||
|
[[ -f agent_templates/qwen/QWEN.md ]] && cp agent_templates/qwen/QWEN.md sdd-qwen-package/QWEN.md
|
||||||
|
echo "Created Qwen package"
|
||||||
|
|
||||||
|
( cd sdd-claude-package && zip -r ../spec-kit-template-claude-${NEW_VERSION}.zip . )
|
||||||
|
( cd sdd-gemini-package && zip -r ../spec-kit-template-gemini-${NEW_VERSION}.zip . )
|
||||||
|
( cd sdd-copilot-package && zip -r ../spec-kit-template-copilot-${NEW_VERSION}.zip . )
|
||||||
|
( cd sdd-qwen-package && zip -r ../spec-kit-template-qwen-${NEW_VERSION}.zip . )
|
||||||
|
|
||||||
|
echo "Archives:"
|
||||||
|
ls -1 spec-kit-template-*-${NEW_VERSION}.zip
|
||||||
|
unzip -l spec-kit-template-copilot-${NEW_VERSION}.zip | head -10 || true
|
||||||
|
unzip -l spec-kit-template-qwen-${NEW_VERSION}.zip | head -10 || true
|
||||||
43
README.md
43
README.md
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
|
- [🤔 What is Spec-Driven Development?](#-what-is-spec-driven-development)
|
||||||
- [⚡ Get started](#-get-started)
|
- [⚡ Get started](#-get-started)
|
||||||
|
- [🔧 Specify CLI Reference](#-specify-cli-reference)
|
||||||
- [📚 Core philosophy](#-core-philosophy)
|
- [📚 Core philosophy](#-core-philosophy)
|
||||||
- [🌟 Development phases](#-development-phases)
|
- [🌟 Development phases](#-development-phases)
|
||||||
- [🎯 Experimental goals](#-experimental-goals)
|
- [🎯 Experimental goals](#-experimental-goals)
|
||||||
@@ -64,6 +65,47 @@ Use `/tasks` to create an actionable task list, then ask your agent to implement
|
|||||||
|
|
||||||
For detailed step-by-step instructions, see our [comprehensive guide](./spec-driven.md).
|
For detailed step-by-step instructions, see our [comprehensive guide](./spec-driven.md).
|
||||||
|
|
||||||
|
## 🔧 Specify CLI Reference
|
||||||
|
|
||||||
|
The `specify` command supports the following options:
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|-------------|----------------------------------------------------------------|
|
||||||
|
| `init` | Initialize a new Specify project from the latest template |
|
||||||
|
| `check` | Check for installed tools (`git`, `claude`, `gemini`) |
|
||||||
|
|
||||||
|
### `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` |
|
||||||
|
| `--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) |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Basic project initialization
|
||||||
|
specify init my-project
|
||||||
|
|
||||||
|
# Initialize with specific AI assistant
|
||||||
|
specify init my-project --ai claude
|
||||||
|
|
||||||
|
# Initialize in current directory
|
||||||
|
specify init --here --ai copilot
|
||||||
|
|
||||||
|
# Skip git initialization
|
||||||
|
specify init my-project --ai gemini --no-git
|
||||||
|
|
||||||
|
# Check system requirements
|
||||||
|
specify check
|
||||||
|
```
|
||||||
|
|
||||||
## 📚 Core philosophy
|
## 📚 Core philosophy
|
||||||
|
|
||||||
Spec-Driven Development is a structured process that emphasizes:
|
Spec-Driven Development is a structured process that emphasizes:
|
||||||
@@ -215,7 +257,6 @@ At this stage, your project folder contents should resemble the following:
|
|||||||
│ └── 001-create-taskify
|
│ └── 001-create-taskify
|
||||||
│ └── spec.md
|
│ └── spec.md
|
||||||
└── templates
|
└── templates
|
||||||
├── CLAUDE-template.md
|
|
||||||
├── plan-template.md
|
├── plan-template.md
|
||||||
├── spec-template.md
|
├── spec-template.md
|
||||||
└── tasks-template.md
|
└── tasks-template.md
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Spec-Driven Development **flips the script** on traditional software development
|
|||||||
|
|
||||||
- [Installation Guide](installation.md)
|
- [Installation Guide](installation.md)
|
||||||
- [Quick Start Guide](quickstart.md)
|
- [Quick Start Guide](quickstart.md)
|
||||||
|
- [Local Development](local-development.md)
|
||||||
|
|
||||||
## Core Philosophy
|
## Core Philosophy
|
||||||
|
|
||||||
|
|||||||
165
docs/local-development.md
Normal file
165
docs/local-development.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# Local Development Guide
|
||||||
|
|
||||||
|
This guide shows how to iterate on the `specify` CLI locally without publishing a release or committing to `main` first.
|
||||||
|
|
||||||
|
## 1. Clone and Switch Branches
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/github/spec-kit.git
|
||||||
|
cd spec-kit
|
||||||
|
# Work on a feature branch
|
||||||
|
git checkout -b your-feature-branch
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Run the CLI Directly (Fastest Feedback)
|
||||||
|
|
||||||
|
You can execute the CLI via the module entrypoint without installing anything:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# From repo root
|
||||||
|
python -m src.specify_cli --help
|
||||||
|
python -m src.specify_cli init demo-project --ai claude --ignore-agent-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
If you prefer invoking the script file style (uses shebang):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python src/specify_cli/__init__.py init demo-project
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Use Editable Install (Isolated Environment)
|
||||||
|
|
||||||
|
Create an isolated environment using `uv` so dependencies resolve exactly like end users get them:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create & activate virtual env (uv auto-manages .venv)
|
||||||
|
uv venv
|
||||||
|
source .venv/bin/activate # or on Windows: .venv\\Scripts\\activate
|
||||||
|
|
||||||
|
# Install project in editable mode
|
||||||
|
uv pip install -e .
|
||||||
|
|
||||||
|
# Now 'specify' entrypoint is available
|
||||||
|
specify --help
|
||||||
|
```
|
||||||
|
|
||||||
|
Re-running after code edits requires no reinstall because of editable mode.
|
||||||
|
|
||||||
|
## 4. Invoke with uvx Directly From Git (Current Branch)
|
||||||
|
|
||||||
|
`uvx` can run from a local path (or a Git ref) to simulate user flows:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvx --from . specify init demo-uvx --ai copilot --ignore-agent-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also point uvx at a specific branch without merging:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Push your working branch first
|
||||||
|
git push origin your-feature-branch
|
||||||
|
uvx --from git+https://github.com/github/spec-kit.git@your-feature-branch specify init demo-branch-test
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4a. Absolute Path uvx (Run From Anywhere)
|
||||||
|
|
||||||
|
If you're in another directory, use an absolute path instead of `.`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvx --from /mnt/c/GitHub/spec-kit specify --help
|
||||||
|
uvx --from /mnt/c/GitHub/spec-kit specify init demo-anywhere --ai copilot --ignore-agent-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
Set an environment variable for convenience:
|
||||||
|
```bash
|
||||||
|
export SPEC_KIT_SRC=/mnt/c/GitHub/spec-kit
|
||||||
|
uvx --from "$SPEC_KIT_SRC" specify init demo-env --ai copilot --ignore-agent-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
(Optional) Define a shell function:
|
||||||
|
```bash
|
||||||
|
specify-dev() { uvx --from /mnt/c/GitHub/spec-kit specify "$@"; }
|
||||||
|
# Then
|
||||||
|
specify-dev --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Testing Script Permission Logic
|
||||||
|
|
||||||
|
After running an `init`, check that shell scripts are executable on POSIX systems:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -l scripts | grep .sh
|
||||||
|
# Expect owner execute bit (e.g. -rwxr-xr-x)
|
||||||
|
```
|
||||||
|
On Windows this step is a no-op.
|
||||||
|
|
||||||
|
## 6. Run Lint / Basic Checks (Add Your Own)
|
||||||
|
|
||||||
|
Currently no enforced lint config is bundled, but you can quickly sanity check importability:
|
||||||
|
```bash
|
||||||
|
python -c "import specify_cli; print('Import OK')"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Build a Wheel Locally (Optional)
|
||||||
|
|
||||||
|
Validate packaging before publishing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uv build
|
||||||
|
ls dist/
|
||||||
|
```
|
||||||
|
Install the built artifact into a fresh throwaway environment if needed.
|
||||||
|
|
||||||
|
## 8. Using a Temporary Workspace
|
||||||
|
|
||||||
|
When testing `init --here` in a dirty directory, create a temp workspace:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir /tmp/spec-test && cd /tmp/spec-test
|
||||||
|
python -m src.specify_cli init --here --ai claude --ignore-agent-tools # if repo copied here
|
||||||
|
```
|
||||||
|
Or copy only the modified CLI portion if you want a lighter sandbox.
|
||||||
|
|
||||||
|
## 9. Debug Network / TLS Skips
|
||||||
|
|
||||||
|
If you need to bypass TLS validation while experimenting:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
specify check --skip-tls
|
||||||
|
specify init demo --skip-tls --ai gemini --ignore-agent-tools
|
||||||
|
```
|
||||||
|
(Use only for local experimentation.)
|
||||||
|
|
||||||
|
## 10. Rapid Edit Loop Summary
|
||||||
|
|
||||||
|
| Action | Command |
|
||||||
|
|--------|---------|
|
||||||
|
| Run CLI directly | `python -m src.specify_cli --help` |
|
||||||
|
| Editable install | `uv pip install -e .` then `specify ...` |
|
||||||
|
| Local uvx run (repo root) | `uvx --from . specify ...` |
|
||||||
|
| Local uvx run (abs path) | `uvx --from /mnt/c/GitHub/spec-kit specify ...` |
|
||||||
|
| Git branch uvx | `uvx --from git+URL@branch specify ...` |
|
||||||
|
| Build wheel | `uv build` |
|
||||||
|
|
||||||
|
## 11. Cleaning Up
|
||||||
|
|
||||||
|
Remove build artifacts / virtual env quickly:
|
||||||
|
```bash
|
||||||
|
rm -rf .venv dist build *.egg-info
|
||||||
|
```
|
||||||
|
|
||||||
|
## 12. Common Issues
|
||||||
|
|
||||||
|
| Symptom | Fix |
|
||||||
|
|---------|-----|
|
||||||
|
| `ModuleNotFoundError: typer` | Run `uv pip install -e .` |
|
||||||
|
| Scripts not executable (Linux) | Re-run init (logic adds bits) or `chmod +x scripts/*.sh` |
|
||||||
|
| Git step skipped | You passed `--no-git` or Git not installed |
|
||||||
|
| TLS errors on corporate network | Try `--skip-tls` (not for production) |
|
||||||
|
|
||||||
|
## 13. Next Steps
|
||||||
|
|
||||||
|
- Update docs and run through Quick Start using your modified CLI
|
||||||
|
- Open a PR when satisfied
|
||||||
|
- (Optional) Tag a release once changes land in `main`
|
||||||
|
|
||||||
23
docs/toc.yml
23
docs/toc.yml
@@ -1,10 +1,17 @@
|
|||||||
|
# Home page
|
||||||
- name: Home
|
- name: Home
|
||||||
href: index.md
|
href: index.md
|
||||||
- name: Installation
|
|
||||||
href: installation.md
|
# Getting started section
|
||||||
- name: Quick Start
|
- name: Getting Started
|
||||||
href: quickstart.md
|
items:
|
||||||
- name: Contributing
|
- name: Installation
|
||||||
href: CONTRIBUTING.md
|
href: installation.md
|
||||||
- name: Support
|
- name: Quick Start
|
||||||
href: SUPPORT.md
|
href: quickstart.md
|
||||||
|
|
||||||
|
# Development workflows
|
||||||
|
- name: Development
|
||||||
|
items:
|
||||||
|
- name: Local Development
|
||||||
|
href: local-development.md
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ AI_CHOICES = {
|
|||||||
"qwen": "Qwen Code"
|
"qwen": "Qwen Code"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Claude CLI local installation path after migrate-installer
|
||||||
|
CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude"
|
||||||
|
|
||||||
# ASCII Art Banner
|
# ASCII Art Banner
|
||||||
BANNER = """
|
BANNER = """
|
||||||
███████╗██████╗ ███████╗ ██████╗██╗███████╗██╗ ██╗
|
███████╗██████╗ ███████╗ ██████╗██╗███████╗██╗ ██╗
|
||||||
@@ -336,8 +339,28 @@ def run_command(cmd: list[str], check_return: bool = True, capture: bool = False
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_tool_for_tracker(tool: str, install_hint: str, tracker: StepTracker) -> bool:
|
||||||
|
"""Check if a tool is installed and update tracker."""
|
||||||
|
if shutil.which(tool):
|
||||||
|
tracker.complete(tool, "available")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
tracker.error(tool, f"not found - {install_hint}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def check_tool(tool: str, install_hint: str) -> bool:
|
def check_tool(tool: str, install_hint: str) -> bool:
|
||||||
"""Check if a tool is installed."""
|
"""Check if a tool is installed."""
|
||||||
|
|
||||||
|
# Special handling for Claude CLI after `claude migrate-installer`
|
||||||
|
# See: https://github.com/github/spec-kit/issues/123
|
||||||
|
# The migrate-installer command REMOVES the original executable from PATH
|
||||||
|
# and creates an alias at ~/.claude/local/claude instead
|
||||||
|
# This path should be prioritized over other claude executables in PATH
|
||||||
|
if tool == "claude":
|
||||||
|
if CLAUDE_LOCAL_PATH.exists() and CLAUDE_LOCAL_PATH.is_file():
|
||||||
|
return True
|
||||||
|
|
||||||
if shutil.which(tool):
|
if shutil.which(tool):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
@@ -636,6 +659,67 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, is_curr
|
|||||||
return project_path
|
return project_path
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_executable_scripts(project_path: Path, tracker: StepTracker | None = None) -> None:
|
||||||
|
"""Ensure POSIX .sh scripts in the project .specify/scripts directory have execute bits (no-op on Windows)."""
|
||||||
|
if os.name == "nt":
|
||||||
|
return # Windows: skip silently
|
||||||
|
scripts_dir = project_path / ".specify" / "scripts"
|
||||||
|
if not scripts_dir.is_dir():
|
||||||
|
return
|
||||||
|
failures: list[str] = []
|
||||||
|
updated = 0
|
||||||
|
for script in scripts_dir.glob("*.sh"):
|
||||||
|
try:
|
||||||
|
# Skip symlinks
|
||||||
|
if script.is_symlink():
|
||||||
|
continue
|
||||||
|
# Must be a regular file
|
||||||
|
if not script.is_file():
|
||||||
|
continue
|
||||||
|
# Quick shebang check
|
||||||
|
try:
|
||||||
|
with script.open("rb") as f:
|
||||||
|
first_two = f.read(2)
|
||||||
|
if first_two != b"#!":
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
st = script.stat()
|
||||||
|
mode = st.st_mode
|
||||||
|
# If already any execute bit set, skip
|
||||||
|
if mode & 0o111:
|
||||||
|
continue
|
||||||
|
# Only add execute bits that correspond to existing read bits
|
||||||
|
new_mode = mode
|
||||||
|
if mode & 0o400: # owner read
|
||||||
|
new_mode |= 0o100
|
||||||
|
if mode & 0o040: # group read
|
||||||
|
new_mode |= 0o010
|
||||||
|
if mode & 0o004: # other read
|
||||||
|
new_mode |= 0o001
|
||||||
|
# Fallback: ensure at least owner execute
|
||||||
|
if not (new_mode & 0o100):
|
||||||
|
new_mode |= 0o100
|
||||||
|
os.chmod(script, new_mode)
|
||||||
|
updated += 1
|
||||||
|
except Exception as e:
|
||||||
|
failures.append(f"{script.name}: {e}")
|
||||||
|
if tracker:
|
||||||
|
detail = f"{updated} updated" + (f", {len(failures)} failed" if failures else "")
|
||||||
|
tracker.add("chmod", "Set script permissions")
|
||||||
|
if failures:
|
||||||
|
tracker.error("chmod", detail)
|
||||||
|
else:
|
||||||
|
tracker.complete("chmod", detail)
|
||||||
|
else:
|
||||||
|
if updated:
|
||||||
|
console.print(f"[cyan]Updated execute permissions on {updated} script(s)[/cyan]")
|
||||||
|
if failures:
|
||||||
|
console.print("[yellow]Some scripts could not be updated:[/yellow]")
|
||||||
|
for f in failures:
|
||||||
|
console.print(f" - {f}")
|
||||||
|
|
||||||
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def init(
|
def init(
|
||||||
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here)"),
|
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here)"),
|
||||||
@@ -767,6 +851,7 @@ def init(
|
|||||||
("extract", "Extract template"),
|
("extract", "Extract template"),
|
||||||
("zip-list", "Archive contents"),
|
("zip-list", "Archive contents"),
|
||||||
("extracted-summary", "Extraction summary"),
|
("extracted-summary", "Extraction summary"),
|
||||||
|
("chmod", "Ensure scripts executable"),
|
||||||
("cleanup", "Cleanup"),
|
("cleanup", "Cleanup"),
|
||||||
("git", "Initialize git repository"),
|
("git", "Initialize git repository"),
|
||||||
("final", "Finalize")
|
("final", "Finalize")
|
||||||
@@ -784,6 +869,9 @@ def init(
|
|||||||
|
|
||||||
download_and_extract_template(project_path, selected_ai, here, verbose=False, tracker=tracker, client=local_client)
|
download_and_extract_template(project_path, selected_ai, here, verbose=False, tracker=tracker, client=local_client)
|
||||||
|
|
||||||
|
# Ensure scripts are executable (POSIX)
|
||||||
|
ensure_executable_scripts(project_path, tracker=tracker)
|
||||||
|
|
||||||
# Git step
|
# Git step
|
||||||
if not no_git:
|
if not no_git:
|
||||||
tracker.start("git")
|
tracker.start("git")
|
||||||
@@ -853,38 +941,38 @@ def init(
|
|||||||
# Removed farewell line per user request
|
# Removed farewell line per user request
|
||||||
|
|
||||||
|
|
||||||
# Add skip_tls option to check
|
|
||||||
@app.command()
|
@app.command()
|
||||||
def check(skip_tls: bool = typer.Option(False, "--skip-tls", help="Skip SSL/TLS verification (not recommended)")):
|
def check():
|
||||||
"""Check that all required tools are installed."""
|
"""Check that all required tools are installed."""
|
||||||
show_banner()
|
show_banner()
|
||||||
console.print("[bold]Checking Specify requirements...[/bold]\n")
|
console.print("[bold]Checking for installed tools...[/bold]\n")
|
||||||
|
|
||||||
# Check if we have internet connectivity by trying to reach GitHub API
|
# Create tracker for checking tools
|
||||||
console.print("[cyan]Checking internet connectivity...[/cyan]")
|
tracker = StepTracker("Check Available Tools")
|
||||||
verify = not skip_tls
|
|
||||||
local_ssl_context = ssl_context if verify else False
|
|
||||||
local_client = httpx.Client(verify=local_ssl_context)
|
|
||||||
try:
|
|
||||||
response = local_client.get("https://api.github.com", timeout=5, follow_redirects=True)
|
|
||||||
console.print("[green]✓[/green] Internet connection available")
|
|
||||||
except httpx.RequestError:
|
|
||||||
console.print("[red]✗[/red] No internet connection - required for downloading templates")
|
|
||||||
console.print("[yellow]Please check your internet connection[/yellow]")
|
|
||||||
|
|
||||||
console.print("\n[cyan]Optional tools:[/cyan]")
|
|
||||||
git_ok = check_tool("git", "https://git-scm.com/downloads")
|
|
||||||
|
|
||||||
console.print("\n[cyan]Optional AI tools:[/cyan]")
|
# Add all tools we want to check
|
||||||
claude_ok = check_tool("claude", "Install from: https://docs.anthropic.com/en/docs/claude-code/setup")
|
tracker.add("git", "Git version control")
|
||||||
gemini_ok = check_tool("gemini", "Install from: https://github.com/google-gemini/gemini-cli")
|
tracker.add("claude", "Claude Code CLI")
|
||||||
qwen_ok = check_tool("qwen", "Install from: https://github.com/QwenLM/qwen-code")
|
tracker.add("gemini", "Gemini CLI")
|
||||||
|
tracker.add("qwen", "Qwen Code CLI")
|
||||||
|
|
||||||
console.print("\n[green]✓ Specify CLI is ready to use![/green]")
|
# 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)
|
||||||
|
qwen_ok = check_tool_for_tracker("qwen", "https://github.com/QwenLM/qwen-code", tracker)
|
||||||
|
|
||||||
|
# Render the final tree
|
||||||
|
console.print(tracker.render())
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
console.print("\n[bold green]Specify CLI is ready to use![/bold green]")
|
||||||
|
|
||||||
|
# Recommendations
|
||||||
if not git_ok:
|
if not git_ok:
|
||||||
console.print("[yellow]Consider installing git for repository management[/yellow]")
|
console.print("[dim]Tip: Install git for repository management[/dim]")
|
||||||
if not (claude_ok or gemini_ok or qwen_ok):
|
if not (claude_ok or gemini_ok or qwen_ok):
|
||||||
console.print("[yellow]Consider installing an AI assistant for the best experience[/yellow]")
|
console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
Reference in New Issue
Block a user