mirror of
https://github.com/github/spec-kit.git
synced 2026-01-31 13:03:36 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7050a3151c | ||
|
|
9e84f46e56 | ||
|
|
5e32de1f3f | ||
|
|
5558b24475 | ||
|
|
f892b9e1cb | ||
|
|
a66af9b7f5 | ||
|
|
a5fdd53a3e | ||
|
|
ed0fa8fffe | ||
|
|
098380a46f | ||
|
|
74f7e508a4 | ||
|
|
8130d98bcc | ||
|
|
315269d9a8 | ||
|
|
03c7021270 | ||
|
|
3b000fce4d | ||
|
|
a945077b8d | ||
|
|
4522fb4c44 | ||
|
|
36ff7e6505 | ||
|
|
defb1870da | ||
|
|
b61f04c898 | ||
|
|
64745162df | ||
|
|
97df98b9a0 | ||
|
|
36383b411f |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -1,2 +1,3 @@
|
|||||||
# Global code owner
|
# Global code owner
|
||||||
* @localden
|
* @localden
|
||||||
|
|
||||||
|
|||||||
1
.github/workflows/docs.yml
vendored
1
.github/workflows/docs.yml
vendored
@@ -65,3 +65,4 @@ jobs:
|
|||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
id: deployment
|
id: deployment
|
||||||
uses: actions/deploy-pages@v4
|
uses: actions/deploy-pages@v4
|
||||||
|
|
||||||
|
|||||||
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@@ -57,3 +57,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
chmod +x .github/workflows/scripts/update-version.sh
|
chmod +x .github/workflows/scripts/update-version.sh
|
||||||
.github/workflows/scripts/update-version.sh ${{ steps.get_tag.outputs.new_version }}
|
.github/workflows/scripts/update-version.sh ${{ steps.get_tag.outputs.new_version }}
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ if gh release view "$VERSION" >/dev/null 2>&1; then
|
|||||||
else
|
else
|
||||||
echo "exists=false" >> $GITHUB_OUTPUT
|
echo "exists=false" >> $GITHUB_OUTPUT
|
||||||
echo "Release $VERSION does not exist, proceeding..."
|
echo "Release $VERSION does not exist, proceeding..."
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -43,4 +43,4 @@ gh release create "$VERSION" \
|
|||||||
.genreleases/spec-kit-template-q-sh-"$VERSION".zip \
|
.genreleases/spec-kit-template-q-sh-"$VERSION".zip \
|
||||||
.genreleases/spec-kit-template-q-ps-"$VERSION".zip \
|
.genreleases/spec-kit-template-q-ps-"$VERSION".zip \
|
||||||
--title "Spec Kit Templates - $VERSION_NO_V" \
|
--title "Spec Kit Templates - $VERSION_NO_V" \
|
||||||
--notes-file release_notes.md
|
--notes-file release_notes.md
|
||||||
|
|||||||
@@ -237,3 +237,4 @@ done
|
|||||||
|
|
||||||
echo "Archives in $GENRELEASES_DIR:"
|
echo "Archives in $GENRELEASES_DIR:"
|
||||||
ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip
|
ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip
|
||||||
|
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ This is the latest set of releases that you can use with your agent of choice. W
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
echo "Generated release notes:"
|
echo "Generated release notes:"
|
||||||
cat release_notes.md
|
cat release_notes.md
|
||||||
|
|||||||
@@ -21,4 +21,4 @@ PATCH=$((PATCH + 1))
|
|||||||
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
NEW_VERSION="v$MAJOR.$MINOR.$PATCH"
|
||||||
|
|
||||||
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "New version will be: $NEW_VERSION"
|
echo "New version will be: $NEW_VERSION"
|
||||||
|
|||||||
2
.github/workflows/scripts/update-version.sh
vendored
2
.github/workflows/scripts/update-version.sh
vendored
@@ -20,4 +20,4 @@ if [ -f "pyproject.toml" ]; then
|
|||||||
echo "Updated pyproject.toml version to $PYTHON_VERSION (for release artifacts only)"
|
echo "Updated pyproject.toml version to $PYTHON_VERSION (for release artifacts only)"
|
||||||
else
|
else
|
||||||
echo "Warning: pyproject.toml not found, skipping version update"
|
echo "Warning: pyproject.toml not found, skipping version update"
|
||||||
fi
|
fi
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -42,4 +42,4 @@ env/
|
|||||||
# Spec Kit-specific files
|
# Spec Kit-specific files
|
||||||
.genreleases/
|
.genreleases/
|
||||||
*.zip
|
*.zip
|
||||||
sdd-*/
|
sdd-*/
|
||||||
|
|||||||
@@ -326,3 +326,4 @@ When adding new agents:
|
|||||||
---
|
---
|
||||||
|
|
||||||
*This documentation should be updated whenever new agents are added to maintain accuracy and completeness.*
|
*This documentation should be updated whenever new agents are added to maintain accuracy and completeness.*
|
||||||
|
|
||||||
|
|||||||
24
CHANGELOG.md
24
CHANGELOG.md
@@ -7,6 +7,29 @@ All notable changes to the Specify CLI and templates are documented here.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [0.0.20] - 2025-10-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Intelligent Branch Naming**: `create-new-feature` scripts now support `--short-name` parameter for custom branch names
|
||||||
|
- When `--short-name` provided: Uses the custom name directly (cleaned and formatted)
|
||||||
|
- When omitted: Automatically generates meaningful names using stop word filtering and length-based filtering
|
||||||
|
- Filters out common stop words (I, want, to, the, for, etc.)
|
||||||
|
- Removes words shorter than 3 characters (unless they're uppercase acronyms)
|
||||||
|
- Takes 3-4 most meaningful words from the description
|
||||||
|
- **Enforces GitHub's 244-byte branch name limit** with automatic truncation and warnings
|
||||||
|
- Examples:
|
||||||
|
- "I want to create user authentication" → `001-create-user-authentication`
|
||||||
|
- "Implement OAuth2 integration for API" → `001-implement-oauth2-integration-api`
|
||||||
|
- "Fix payment processing bug" → `001-fix-payment-processing`
|
||||||
|
- Very long descriptions are automatically truncated at word boundaries to stay within limits
|
||||||
|
- Designed for AI agents to provide semantic short names while maintaining standalone usability
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Enhanced help documentation for `create-new-feature.sh` and `create-new-feature.ps1` scripts with examples
|
||||||
|
- Branch names now validated against GitHub's 244-byte limit with automatic truncation if needed
|
||||||
|
|
||||||
## [0.0.19] - 2025-10-10
|
## [0.0.19] - 2025-10-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -143,3 +166,4 @@ N/A
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
N/A
|
N/A
|
||||||
|
|
||||||
|
|||||||
@@ -71,4 +71,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi
|
|||||||
available at [http://contributor-covenant.org/version/1/4][version]
|
available at [http://contributor-covenant.org/version/1/4][version]
|
||||||
|
|
||||||
[homepage]: http://contributor-covenant.org
|
[homepage]: http://contributor-covenant.org
|
||||||
[version]: http://contributor-covenant.org/version/1/4/
|
[version]: http://contributor-covenant.org/version/1/4/
|
||||||
|
|||||||
@@ -108,3 +108,4 @@ Please be respectful to maintainers and disclose AI assistance.
|
|||||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||||
- [GitHub Help](https://help.github.com)
|
- [GitHub Help](https://help.github.com)
|
||||||
|
|
||||||
|
|||||||
1
LICENSE
1
LICENSE
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<strong>An effort to allow organizations to focus on product scenarios rather than writing undifferentiated code with the help of Spec-Driven Development.</strong>
|
<strong>An open source toolkit that allows you to focus on product scenarios and predictable outcomes instead of vibe coding every piece from scratch.</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -42,7 +42,7 @@ Spec-Driven Development **flips the script** on traditional software development
|
|||||||
|
|
||||||
## ⚡ Get Started
|
## ⚡ Get Started
|
||||||
|
|
||||||
### 1. Install Specify
|
### 1. Install Specify CLI
|
||||||
|
|
||||||
Choose your preferred installation method:
|
Choose your preferred installation method:
|
||||||
|
|
||||||
@@ -84,6 +84,8 @@ uvx --from git+https://github.com/github/spec-kit.git specify init <PROJECT_NAME
|
|||||||
|
|
||||||
### 2. Establish project principles
|
### 2. Establish project principles
|
||||||
|
|
||||||
|
Launch your AI assistant in the project directory. The `/speckit.*` commands are available in the assistant.
|
||||||
|
|
||||||
Use the **`/speckit.constitution`** command to create your project's governing principles and development guidelines that will guide all subsequent development.
|
Use the **`/speckit.constitution`** command to create your project's governing principles and development guidelines that will guide all subsequent development.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -623,3 +625,4 @@ This project is heavily influenced by and based on the work and research of [Joh
|
|||||||
## 📄 License
|
## 📄 License
|
||||||
|
|
||||||
This project is licensed under the terms of the MIT open source license. Please refer to the [LICENSE](./LICENSE) file for the full terms.
|
This project is licensed under the terms of the MIT open source license. Please refer to the [LICENSE](./LICENSE) file for the full terms.
|
||||||
|
|
||||||
|
|||||||
@@ -28,4 +28,4 @@ This information will help us triage your report more quickly.
|
|||||||
|
|
||||||
## Policy
|
## Policy
|
||||||
|
|
||||||
See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms)
|
See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms)
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ For help or questions about using this project, please:
|
|||||||
## GitHub Support Policy
|
## GitHub Support Policy
|
||||||
|
|
||||||
Support for this project is limited to the resources listed above.
|
Support for this project is limited to the resources listed above.
|
||||||
|
|
||||||
|
|||||||
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@@ -6,3 +6,4 @@ obj/
|
|||||||
# Temporary files
|
# Temporary files
|
||||||
*.tmp
|
*.tmp
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
|||||||
@@ -31,3 +31,4 @@ To build the documentation locally:
|
|||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
Documentation is automatically built and deployed to GitHub Pages when changes are pushed to the `main` branch. The workflow is defined in `.github/workflows/docs.yml`.
|
Documentation is automatically built and deployed to GitHub Pages when changes are pushed to the `main` branch. The workflow is defined in `.github/workflows/docs.yml`.
|
||||||
|
|
||||||
|
|||||||
@@ -68,3 +68,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,3 +60,4 @@ Please see our [Contributing Guide](https://github.com/github/spec-kit/blob/main
|
|||||||
## Support
|
## Support
|
||||||
|
|
||||||
For support, please check our [Support Guide](https://github.com/github/spec-kit/blob/main/SUPPORT.md) or open an issue on GitHub.
|
For support, please check our [Support Guide](https://github.com/github/spec-kit/blob/main/SUPPORT.md) or open an issue on GitHub.
|
||||||
|
|
||||||
|
|||||||
@@ -87,3 +87,4 @@ git config --global credential.helper manager
|
|||||||
echo "Cleaning up..."
|
echo "Cleaning up..."
|
||||||
rm gcm-linux_amd64.2.6.1.deb
|
rm gcm-linux_amd64.2.6.1.deb
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -166,3 +166,4 @@ rm -rf .venv dist build *.egg-info
|
|||||||
- Open a PR when satisfied
|
- Open a PR when satisfied
|
||||||
- (Optional) Tag a release once changes land in `main`
|
- (Optional) Tag a release once changes land in `main`
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -120,3 +120,4 @@ implement specs/002-create-taskify/plan.md
|
|||||||
- Read the complete methodology for in-depth guidance
|
- Read the complete methodology for in-depth guidance
|
||||||
- Check out more examples in the repository
|
- Check out more examples in the repository
|
||||||
- Explore the source code on GitHub
|
- Explore the source code on GitHub
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,4 @@
|
|||||||
items:
|
items:
|
||||||
- name: Local Development
|
- name: Local Development
|
||||||
href: local-development.md
|
href: local-development.md
|
||||||
|
|
||||||
|
|||||||
@@ -47,4 +47,4 @@
|
|||||||
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
|
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
|
||||||
|
|
||||||
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
||||||
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
|
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "specify-cli"
|
name = "specify-cli"
|
||||||
version = "0.0.19"
|
version = "0.0.20"
|
||||||
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)."
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -21,3 +21,4 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[tool.hatch.build.targets.wheel]
|
[tool.hatch.build.targets.wheel]
|
||||||
packages = ["src/specify_cli"]
|
packages = ["src/specify_cli"]
|
||||||
|
|
||||||
|
|||||||
@@ -163,4 +163,4 @@ else
|
|||||||
if $INCLUDE_TASKS; then
|
if $INCLUDE_TASKS; then
|
||||||
check_file "$TASKS" "tasks.md"
|
check_file "$TASKS" "tasks.md"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -153,3 +153,4 @@ EOF
|
|||||||
|
|
||||||
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||||
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,46 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
JSON_MODE=false
|
JSON_MODE=false
|
||||||
|
SHORT_NAME=""
|
||||||
ARGS=()
|
ARGS=()
|
||||||
for arg in "$@"; do
|
i=0
|
||||||
|
while [ $i -le $# ]; do
|
||||||
|
arg="${!i}"
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--json) JSON_MODE=true ;;
|
--json)
|
||||||
--help|-h) echo "Usage: $0 [--json] <feature_description>"; exit 0 ;;
|
JSON_MODE=true
|
||||||
*) ARGS+=("$arg") ;;
|
;;
|
||||||
|
--short-name)
|
||||||
|
if [ $((i + 1)) -ge $# ]; then
|
||||||
|
echo 'Error: --short-name requires a value' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
i=$((i + 1))
|
||||||
|
SHORT_NAME="${!i}"
|
||||||
|
;;
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: $0 [--json] [--short-name <name>] <feature_description>"
|
||||||
|
echo ""
|
||||||
|
echo "Options:"
|
||||||
|
echo " --json Output in JSON format"
|
||||||
|
echo " --short-name <name> Provide a custom short name (2-4 words) for the branch"
|
||||||
|
echo " --help, -h Show this help message"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " $0 'Add user authentication system' --short-name 'user-auth'"
|
||||||
|
echo " $0 'Implement OAuth2 integration for API'"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
ARGS+=("$arg")
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
i=$((i + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
FEATURE_DESCRIPTION="${ARGS[*]}"
|
FEATURE_DESCRIPTION="${ARGS[*]}"
|
||||||
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
||||||
echo "Usage: $0 [--json] <feature_description>" >&2
|
echo "Usage: $0 [--json] [--short-name <name>] <feature_description>" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -67,9 +95,84 @@ fi
|
|||||||
NEXT=$((HIGHEST + 1))
|
NEXT=$((HIGHEST + 1))
|
||||||
FEATURE_NUM=$(printf "%03d" "$NEXT")
|
FEATURE_NUM=$(printf "%03d" "$NEXT")
|
||||||
|
|
||||||
BRANCH_NAME=$(echo "$FEATURE_DESCRIPTION" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
|
# Function to generate branch name with stop word filtering and length filtering
|
||||||
WORDS=$(echo "$BRANCH_NAME" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//')
|
generate_branch_name() {
|
||||||
BRANCH_NAME="${FEATURE_NUM}-${WORDS}"
|
local description="$1"
|
||||||
|
|
||||||
|
# Common stop words to filter out
|
||||||
|
local stop_words="^(i|a|an|the|to|for|of|in|on|at|by|with|from|is|are|was|were|be|been|being|have|has|had|do|does|did|will|would|should|could|can|may|might|must|shall|this|that|these|those|my|your|our|their|want|need|add|get|set)$"
|
||||||
|
|
||||||
|
# Convert to lowercase and split into words
|
||||||
|
local clean_name=$(echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/ /g')
|
||||||
|
|
||||||
|
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
|
||||||
|
local meaningful_words=()
|
||||||
|
for word in $clean_name; do
|
||||||
|
# Skip empty words
|
||||||
|
[ -z "$word" ] && continue
|
||||||
|
|
||||||
|
# Keep words that are NOT stop words AND (length >= 3 OR are potential acronyms)
|
||||||
|
if ! echo "$word" | grep -qiE "$stop_words"; then
|
||||||
|
if [ ${#word} -ge 3 ]; then
|
||||||
|
meaningful_words+=("$word")
|
||||||
|
elif echo "$description" | grep -q "\b${word^^}\b"; then
|
||||||
|
# Keep short words if they appear as uppercase in original (likely acronyms)
|
||||||
|
meaningful_words+=("$word")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# If we have meaningful words, use first 3-4 of them
|
||||||
|
if [ ${#meaningful_words[@]} -gt 0 ]; then
|
||||||
|
local max_words=3
|
||||||
|
if [ ${#meaningful_words[@]} -eq 4 ]; then max_words=4; fi
|
||||||
|
|
||||||
|
local result=""
|
||||||
|
local count=0
|
||||||
|
for word in "${meaningful_words[@]}"; do
|
||||||
|
if [ $count -ge $max_words ]; then break; fi
|
||||||
|
if [ -n "$result" ]; then result="$result-"; fi
|
||||||
|
result="$result$word"
|
||||||
|
count=$((count + 1))
|
||||||
|
done
|
||||||
|
echo "$result"
|
||||||
|
else
|
||||||
|
# Fallback to original logic if no meaningful words found
|
||||||
|
echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//' | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate branch name
|
||||||
|
if [ -n "$SHORT_NAME" ]; then
|
||||||
|
# Use provided short name, just clean it up
|
||||||
|
BRANCH_SUFFIX=$(echo "$SHORT_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
|
||||||
|
else
|
||||||
|
# Generate from description with smart filtering
|
||||||
|
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
|
||||||
|
fi
|
||||||
|
|
||||||
|
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
||||||
|
|
||||||
|
# GitHub enforces a 244-byte limit on branch names
|
||||||
|
# Validate and truncate if necessary
|
||||||
|
MAX_BRANCH_LENGTH=244
|
||||||
|
if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
|
||||||
|
# Calculate how much we need to trim from suffix
|
||||||
|
# Account for: feature number (3) + hyphen (1) = 4 chars
|
||||||
|
MAX_SUFFIX_LENGTH=$((MAX_BRANCH_LENGTH - 4))
|
||||||
|
|
||||||
|
# Truncate suffix at word boundary if possible
|
||||||
|
TRUNCATED_SUFFIX=$(echo "$BRANCH_SUFFIX" | cut -c1-$MAX_SUFFIX_LENGTH)
|
||||||
|
# Remove trailing hyphen if truncation created one
|
||||||
|
TRUNCATED_SUFFIX=$(echo "$TRUNCATED_SUFFIX" | sed 's/-$//')
|
||||||
|
|
||||||
|
ORIGINAL_BRANCH_NAME="$BRANCH_NAME"
|
||||||
|
BRANCH_NAME="${FEATURE_NUM}-${TRUNCATED_SUFFIX}"
|
||||||
|
|
||||||
|
>&2 echo "[specify] Warning: Branch name exceeded GitHub's 244-byte limit"
|
||||||
|
>&2 echo "[specify] Original: $ORIGINAL_BRANCH_NAME (${#ORIGINAL_BRANCH_NAME} bytes)"
|
||||||
|
>&2 echo "[specify] Truncated to: $BRANCH_NAME (${#BRANCH_NAME} bytes)"
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$HAS_GIT" = true ]; then
|
if [ "$HAS_GIT" = true ]; then
|
||||||
git checkout -b "$BRANCH_NAME"
|
git checkout -b "$BRANCH_NAME"
|
||||||
|
|||||||
@@ -58,3 +58,4 @@ else
|
|||||||
echo "BRANCH: $CURRENT_BRANCH"
|
echo "BRANCH: $CURRENT_BRANCH"
|
||||||
echo "HAS_GIT: $HAS_GIT"
|
echo "HAS_GIT: $HAS_GIT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -250,7 +250,7 @@ get_commands_for_language() {
|
|||||||
echo "cargo test && cargo clippy"
|
echo "cargo test && cargo clippy"
|
||||||
;;
|
;;
|
||||||
*"JavaScript"*|*"TypeScript"*)
|
*"JavaScript"*|*"TypeScript"*)
|
||||||
echo "npm test \&\& npm run lint"
|
echo "npm test \\&\\& npm run lint"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "# Add commands for $lang"
|
echo "# Add commands for $lang"
|
||||||
@@ -736,3 +736,4 @@ main() {
|
|||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
main "$@"
|
main "$@"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -145,4 +145,4 @@ if ($Json) {
|
|||||||
if ($IncludeTasks) {
|
if ($IncludeTasks) {
|
||||||
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Out-Null
|
Test-FileExists -Path $paths.TASKS -Description 'tasks.md' | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,3 +134,4 @@ function Test-DirHasFiles {
|
|||||||
return $false
|
return $false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,20 +3,39 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[switch]$Json,
|
[switch]$Json,
|
||||||
|
[string]$ShortName,
|
||||||
|
[switch]$Help,
|
||||||
[Parameter(ValueFromRemainingArguments = $true)]
|
[Parameter(ValueFromRemainingArguments = $true)]
|
||||||
[string[]]$FeatureDescription
|
[string[]]$FeatureDescription
|
||||||
)
|
)
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
# Show help if requested
|
||||||
|
if ($Help) {
|
||||||
|
Write-Host "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] <feature description>"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Options:"
|
||||||
|
Write-Host " -Json Output in JSON format"
|
||||||
|
Write-Host " -ShortName <name> Provide a custom short name (2-4 words) for the branch"
|
||||||
|
Write-Host " -Help Show this help message"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Examples:"
|
||||||
|
Write-Host " ./create-new-feature.ps1 'Add user authentication system' -ShortName 'user-auth'"
|
||||||
|
Write-Host " ./create-new-feature.ps1 'Implement OAuth2 integration for API'"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if feature description provided
|
||||||
if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
|
if (-not $FeatureDescription -or $FeatureDescription.Count -eq 0) {
|
||||||
Write-Error "Usage: ./create-new-feature.ps1 [-Json] <feature description>"
|
Write-Error "Usage: ./create-new-feature.ps1 [-Json] [-ShortName <name>] <feature description>"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
$featureDesc = ($FeatureDescription -join ' ').Trim()
|
$featureDesc = ($FeatureDescription -join ' ').Trim()
|
||||||
|
|
||||||
# Resolve repository root. Prefer git information when available, but fall back
|
# Resolve repository root. Prefer git information when available, but fall back
|
||||||
# to searching for repository markers so the workflow still functions in repositories that
|
# to searching for repository markers so the workflow still functions in repositories that
|
||||||
# were initialised with --no-git.
|
# were initialized with --no-git.
|
||||||
function Find-RepositoryRoot {
|
function Find-RepositoryRoot {
|
||||||
param(
|
param(
|
||||||
[string]$StartDir,
|
[string]$StartDir,
|
||||||
@@ -72,9 +91,82 @@ if (Test-Path $specsDir) {
|
|||||||
$next = $highest + 1
|
$next = $highest + 1
|
||||||
$featureNum = ('{0:000}' -f $next)
|
$featureNum = ('{0:000}' -f $next)
|
||||||
|
|
||||||
$branchName = $featureDesc.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
# Function to generate branch name with stop word filtering and length filtering
|
||||||
$words = ($branchName -split '-') | Where-Object { $_ } | Select-Object -First 3
|
function Get-BranchName {
|
||||||
$branchName = "$featureNum-$([string]::Join('-', $words))"
|
param([string]$Description)
|
||||||
|
|
||||||
|
# Common stop words to filter out
|
||||||
|
$stopWords = @(
|
||||||
|
'i', 'a', 'an', 'the', 'to', 'for', 'of', 'in', 'on', 'at', 'by', 'with', 'from',
|
||||||
|
'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
|
||||||
|
'do', 'does', 'did', 'will', 'would', 'should', 'could', 'can', 'may', 'might', 'must', 'shall',
|
||||||
|
'this', 'that', 'these', 'those', 'my', 'your', 'our', 'their',
|
||||||
|
'want', 'need', 'add', 'get', 'set'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Convert to lowercase and extract words (alphanumeric only)
|
||||||
|
$cleanName = $Description.ToLower() -replace '[^a-z0-9\s]', ' '
|
||||||
|
$words = $cleanName -split '\s+' | Where-Object { $_ }
|
||||||
|
|
||||||
|
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
|
||||||
|
$meaningfulWords = @()
|
||||||
|
foreach ($word in $words) {
|
||||||
|
# Skip stop words
|
||||||
|
if ($stopWords -contains $word) { continue }
|
||||||
|
|
||||||
|
# Keep words that are length >= 3 OR appear as uppercase in original (likely acronyms)
|
||||||
|
if ($word.Length -ge 3) {
|
||||||
|
$meaningfulWords += $word
|
||||||
|
} elseif ($Description -match "\b$($word.ToUpper())\b") {
|
||||||
|
# Keep short words if they appear as uppercase in original (likely acronyms)
|
||||||
|
$meaningfulWords += $word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# If we have meaningful words, use first 3-4 of them
|
||||||
|
if ($meaningfulWords.Count -gt 0) {
|
||||||
|
$maxWords = if ($meaningfulWords.Count -eq 4) { 4 } else { 3 }
|
||||||
|
$result = ($meaningfulWords | Select-Object -First $maxWords) -join '-'
|
||||||
|
return $result
|
||||||
|
} else {
|
||||||
|
# Fallback to original logic if no meaningful words found
|
||||||
|
$result = $Description.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
||||||
|
$fallbackWords = ($result -split '-') | Where-Object { $_ } | Select-Object -First 3
|
||||||
|
return [string]::Join('-', $fallbackWords)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Generate branch name
|
||||||
|
if ($ShortName) {
|
||||||
|
# Use provided short name, just clean it up
|
||||||
|
$branchSuffix = $ShortName.ToLower() -replace '[^a-z0-9]', '-' -replace '-{2,}', '-' -replace '^-', '' -replace '-$', ''
|
||||||
|
} else {
|
||||||
|
# Generate from description with smart filtering
|
||||||
|
$branchSuffix = Get-BranchName -Description $featureDesc
|
||||||
|
}
|
||||||
|
|
||||||
|
$branchName = "$featureNum-$branchSuffix"
|
||||||
|
|
||||||
|
# GitHub enforces a 244-byte limit on branch names
|
||||||
|
# Validate and truncate if necessary
|
||||||
|
$maxBranchLength = 244
|
||||||
|
if ($branchName.Length -gt $maxBranchLength) {
|
||||||
|
# Calculate how much we need to trim from suffix
|
||||||
|
# Account for: feature number (3) + hyphen (1) = 4 chars
|
||||||
|
$maxSuffixLength = $maxBranchLength - 4
|
||||||
|
|
||||||
|
# Truncate suffix
|
||||||
|
$truncatedSuffix = $branchSuffix.Substring(0, [Math]::Min($branchSuffix.Length, $maxSuffixLength))
|
||||||
|
# Remove trailing hyphen if truncation created one
|
||||||
|
$truncatedSuffix = $truncatedSuffix -replace '-$', ''
|
||||||
|
|
||||||
|
$originalBranchName = $branchName
|
||||||
|
$branchName = "$featureNum-$truncatedSuffix"
|
||||||
|
|
||||||
|
Write-Warning "[specify] Branch name exceeded GitHub's 244-byte limit"
|
||||||
|
Write-Warning "[specify] Original: $originalBranchName ($($originalBranchName.Length) bytes)"
|
||||||
|
Write-Warning "[specify] Truncated to: $branchName ($($branchName.Length) bytes)"
|
||||||
|
}
|
||||||
|
|
||||||
if ($hasGit) {
|
if ($hasGit) {
|
||||||
try {
|
try {
|
||||||
@@ -115,3 +207,4 @@ if ($Json) {
|
|||||||
Write-Output "HAS_GIT: $hasGit"
|
Write-Output "HAS_GIT: $hasGit"
|
||||||
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
|
Write-Output "SPECIFY_FEATURE environment variable set to: $branchName"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,3 +59,4 @@ if ($Json) {
|
|||||||
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
|
Write-Output "BRANCH: $($paths.CURRENT_BRANCH)"
|
||||||
Write-Output "HAS_GIT: $($paths.HAS_GIT)"
|
Write-Output "HAS_GIT: $($paths.HAS_GIT)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -434,3 +434,4 @@ function Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Main
|
Main
|
||||||
|
|
||||||
|
|||||||
@@ -401,3 +401,4 @@ By embedding these principles into the specification and planning process, SDD e
|
|||||||
This isn't about replacing developers or automating creativity. It's about amplifying human capability by automating mechanical translation. It's about creating a tight feedback loop where specifications, research, and code evolve together, each iteration bringing deeper understanding and better alignment between intent and implementation.
|
This isn't about replacing developers or automating creativity. It's about amplifying human capability by automating mechanical translation. It's about creating a tight feedback loop where specifications, research, and code evolve together, each iteration bringing deeper understanding and better alignment between intent and implementation.
|
||||||
|
|
||||||
Software development needs better tools for maintaining alignment between intent and implementation. SDD provides the methodology for achieving this alignment through executable specifications that generate code rather than merely guiding it.
|
Software development needs better tools for maintaining alignment between intent and implementation. SDD provides the methodology for achieving this alignment through executable specifications that generate code rather than merely guiding it.
|
||||||
|
|
||||||
|
|||||||
@@ -485,6 +485,73 @@ def init_git_repo(project_path: Path, quiet: bool = False) -> Tuple[bool, Option
|
|||||||
finally:
|
finally:
|
||||||
os.chdir(original_cwd)
|
os.chdir(original_cwd)
|
||||||
|
|
||||||
|
def handle_vscode_settings(sub_item, dest_file, rel_path, verbose=False, tracker=None) -> None:
|
||||||
|
"""Handle merging or copying of .vscode/settings.json files."""
|
||||||
|
def log(message, color="green"):
|
||||||
|
if verbose and not tracker:
|
||||||
|
console.print(f"[{color}]{message}[/] {rel_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(sub_item, 'r', encoding='utf-8') as f:
|
||||||
|
new_settings = json.load(f)
|
||||||
|
|
||||||
|
if dest_file.exists():
|
||||||
|
merged = merge_json_files(dest_file, new_settings, verbose=verbose and not tracker)
|
||||||
|
with open(dest_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(merged, f, indent=4)
|
||||||
|
f.write('\n')
|
||||||
|
log("Merged:", "green")
|
||||||
|
else:
|
||||||
|
shutil.copy2(sub_item, dest_file)
|
||||||
|
log("Copied (no existing settings.json):", "blue")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log(f"Warning: Could not merge, copying instead: {e}", "yellow")
|
||||||
|
shutil.copy2(sub_item, dest_file)
|
||||||
|
|
||||||
|
def merge_json_files(existing_path: Path, new_content: dict, verbose: bool = False) -> dict:
|
||||||
|
"""Merge new JSON content into existing JSON file.
|
||||||
|
|
||||||
|
Performs a deep merge where:
|
||||||
|
- New keys are added
|
||||||
|
- Existing keys are preserved unless overwritten by new content
|
||||||
|
- Nested dictionaries are merged recursively
|
||||||
|
- Lists and other values are replaced (not merged)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
existing_path: Path to existing JSON file
|
||||||
|
new_content: New JSON content to merge in
|
||||||
|
verbose: Whether to print merge details
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Merged JSON content as dict
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(existing_path, 'r', encoding='utf-8') as f:
|
||||||
|
existing_content = json.load(f)
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError):
|
||||||
|
# If file doesn't exist or is invalid, just use new content
|
||||||
|
return new_content
|
||||||
|
|
||||||
|
def deep_merge(base: dict, update: dict) -> dict:
|
||||||
|
"""Recursively merge update dict into base dict."""
|
||||||
|
result = base.copy()
|
||||||
|
for key, value in update.items():
|
||||||
|
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
||||||
|
# Recursively merge nested dictionaries
|
||||||
|
result[key] = deep_merge(result[key], value)
|
||||||
|
else:
|
||||||
|
# Add new key or replace existing value
|
||||||
|
result[key] = value
|
||||||
|
return result
|
||||||
|
|
||||||
|
merged = deep_merge(existing_content, new_content)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
console.print(f"[cyan]Merged JSON file:[/cyan] {existing_path.name}")
|
||||||
|
|
||||||
|
return merged
|
||||||
|
|
||||||
def download_template_from_github(ai_assistant: str, download_dir: Path, *, script_type: str = "sh", verbose: bool = True, show_progress: bool = True, client: httpx.Client = None, debug: bool = False, github_token: str = None) -> Tuple[Path, dict]:
|
def download_template_from_github(ai_assistant: str, download_dir: Path, *, script_type: str = "sh", verbose: bool = True, show_progress: bool = True, client: httpx.Client = None, debug: bool = False, github_token: str = None) -> Tuple[Path, dict]:
|
||||||
repo_owner = "github"
|
repo_owner = "github"
|
||||||
repo_name = "spec-kit"
|
repo_name = "spec-kit"
|
||||||
@@ -676,7 +743,11 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, script_
|
|||||||
rel_path = sub_item.relative_to(item)
|
rel_path = sub_item.relative_to(item)
|
||||||
dest_file = dest_path / rel_path
|
dest_file = dest_path / rel_path
|
||||||
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
||||||
shutil.copy2(sub_item, dest_file)
|
# Special handling for .vscode/settings.json - merge instead of overwrite
|
||||||
|
if dest_file.name == "settings.json" and dest_file.parent.name == ".vscode":
|
||||||
|
handle_vscode_settings(sub_item, dest_file, rel_path, verbose, tracker)
|
||||||
|
else:
|
||||||
|
shutil.copy2(sub_item, dest_file)
|
||||||
else:
|
else:
|
||||||
shutil.copytree(item, dest_path)
|
shutil.copytree(item, dest_path)
|
||||||
else:
|
else:
|
||||||
@@ -1093,18 +1164,25 @@ def check():
|
|||||||
|
|
||||||
tracker.add("git", "Git version control")
|
tracker.add("git", "Git version control")
|
||||||
git_ok = check_tool("git", tracker=tracker)
|
git_ok = check_tool("git", tracker=tracker)
|
||||||
|
|
||||||
agent_results = {}
|
agent_results = {}
|
||||||
for agent_key, agent_config in AGENT_CONFIG.items():
|
for agent_key, agent_config in AGENT_CONFIG.items():
|
||||||
agent_name = agent_config["name"]
|
agent_name = agent_config["name"]
|
||||||
|
requires_cli = agent_config["requires_cli"]
|
||||||
|
|
||||||
tracker.add(agent_key, agent_name)
|
tracker.add(agent_key, agent_name)
|
||||||
agent_results[agent_key] = check_tool(agent_key, tracker=tracker)
|
|
||||||
|
if requires_cli:
|
||||||
|
agent_results[agent_key] = check_tool(agent_key, tracker=tracker)
|
||||||
|
else:
|
||||||
|
# IDE-based agent - skip CLI check and mark as optional
|
||||||
|
tracker.skip(agent_key, "IDE-based, no CLI check")
|
||||||
|
agent_results[agent_key] = False # Don't count IDE agents as "found"
|
||||||
|
|
||||||
# Check VS Code variants (not in agent config)
|
# Check VS Code variants (not in agent config)
|
||||||
tracker.add("code", "Visual Studio Code")
|
tracker.add("code", "Visual Studio Code")
|
||||||
code_ok = check_tool("code", tracker=tracker)
|
code_ok = check_tool("code", tracker=tracker)
|
||||||
|
|
||||||
tracker.add("code-insiders", "Visual Studio Code Insiders")
|
tracker.add("code-insiders", "Visual Studio Code Insiders")
|
||||||
code_insiders_ok = check_tool("code-insiders", tracker=tracker)
|
code_insiders_ok = check_tool("code-insiders", tracker=tracker)
|
||||||
|
|
||||||
@@ -1123,3 +1201,4 @@ def main():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ Auto-generated from all feature plans. Last updated: [DATE]
|
|||||||
[LAST 3 FEATURES AND WHAT THEY ADDED]
|
[LAST 3 FEATURES AND WHAT THEY ADDED]
|
||||||
|
|
||||||
<!-- MANUAL ADDITIONS START -->
|
<!-- MANUAL ADDITIONS START -->
|
||||||
<!-- MANUAL ADDITIONS END -->
|
<!-- MANUAL ADDITIONS END -->
|
||||||
|
|||||||
@@ -38,3 +38,4 @@
|
|||||||
- Add comments or findings inline
|
- Add comments or findings inline
|
||||||
- Link to relevant resources or documentation
|
- Link to relevant resources or documentation
|
||||||
- Items are numbered sequentially for easy reference
|
- Items are numbered sequentially for easy reference
|
||||||
|
|
||||||
|
|||||||
@@ -185,3 +185,4 @@ Ask the user: "Would you like me to suggest concrete remediation edits for the t
|
|||||||
## Context
|
## Context
|
||||||
|
|
||||||
{ARGS}
|
{ARGS}
|
||||||
|
|
||||||
|
|||||||
@@ -288,3 +288,4 @@ Sample items:
|
|||||||
- Correct: Validation of requirement quality
|
- Correct: Validation of requirement quality
|
||||||
- Wrong: "Does it do X?"
|
- Wrong: "Does it do X?"
|
||||||
- Correct: "Is X clearly specified?"
|
- Correct: "Is X clearly specified?"
|
||||||
|
|
||||||
|
|||||||
@@ -177,3 +177,4 @@ Behavior rules:
|
|||||||
- If quota reached with unresolved high-impact categories remaining, explicitly flag them under Deferred with rationale.
|
- If quota reached with unresolved high-impact categories remaining, explicitly flag them under Deferred with rationale.
|
||||||
|
|
||||||
Context for prioritization: {ARGS}
|
Context for prioritization: {ARGS}
|
||||||
|
|
||||||
|
|||||||
@@ -75,3 +75,4 @@ If the user supplies partial updates (e.g., only one principle revision), still
|
|||||||
If critical info missing (e.g., ratification date truly unknown), insert `TODO(<FIELD_NAME>): explanation` and include in the Sync Impact Report under deferred items.
|
If critical info missing (e.g., ratification date truly unknown), insert `TODO(<FIELD_NAME>): explanation` and include in the Sync Impact Report under deferred items.
|
||||||
|
|
||||||
Do not create a new template; always operate on the existing `/memory/constitution.md` file.
|
Do not create a new template; always operate on the existing `/memory/constitution.md` file.
|
||||||
|
|
||||||
|
|||||||
@@ -129,3 +129,4 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
- Report final status with summary of completed work
|
- Report final status with summary of completed work
|
||||||
|
|
||||||
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/tasks` first to regenerate the task list.
|
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/tasks` first to regenerate the task list.
|
||||||
|
|
||||||
|
|||||||
@@ -84,3 +84,4 @@ You **MUST** consider the user input before proceeding (if not empty).
|
|||||||
|
|
||||||
- Use absolute paths
|
- Use absolute paths
|
||||||
- ERROR on gate failures or unresolved clarifications
|
- ERROR on gate failures or unresolved clarifications
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,32 @@ The text the user typed after `/speckit.specify` in the triggering message **is*
|
|||||||
|
|
||||||
Given that feature description, do this:
|
Given that feature description, do this:
|
||||||
|
|
||||||
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.
|
1. **Generate a concise short name** (2-4 words) for the branch:
|
||||||
**IMPORTANT** You must only ever run this script once. The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for. For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot").
|
- Analyze the feature description and extract the most meaningful keywords
|
||||||
2. Load `templates/spec-template.md` to understand required sections.
|
- Create a 2-4 word short name that captures the essence of the feature
|
||||||
|
- Use action-noun format when possible (e.g., "add-user-auth", "fix-payment-bug")
|
||||||
|
- Preserve technical terms and acronyms (OAuth2, API, JWT, etc.)
|
||||||
|
- Keep it concise but descriptive enough to understand the feature at a glance
|
||||||
|
- Examples:
|
||||||
|
- "I want to add user authentication" → "user-auth"
|
||||||
|
- "Implement OAuth2 integration for the API" → "oauth2-api-integration"
|
||||||
|
- "Create a dashboard for analytics" → "analytics-dashboard"
|
||||||
|
- "Fix payment processing timeout bug" → "fix-payment-timeout"
|
||||||
|
|
||||||
3. Follow this execution flow:
|
2. Run the script `{SCRIPT}` from repo root **with the short-name argument** and parse its JSON output for BRANCH_NAME and SPEC_FILE. All file paths must be absolute.
|
||||||
|
|
||||||
|
**IMPORTANT**:
|
||||||
|
|
||||||
|
- Append the short-name argument to the `{SCRIPT}` command with the 2-4 word short name you created in step 1
|
||||||
|
- Bash: `--short-name "your-generated-short-name"`
|
||||||
|
- PowerShell: `-ShortName "your-generated-short-name"`
|
||||||
|
- For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\''m Groot' (or double-quote if possible: "I'm Groot")
|
||||||
|
- You must only ever run this script once
|
||||||
|
- The JSON is provided in the terminal as output - always refer to it to get the actual content you're looking for
|
||||||
|
|
||||||
|
3. Load `templates/spec-template.md` to understand required sections.
|
||||||
|
|
||||||
|
4. Follow this execution flow:
|
||||||
|
|
||||||
1. Parse user description from Input
|
1. Parse user description from Input
|
||||||
If empty: ERROR "No feature description provided"
|
If empty: ERROR "No feature description provided"
|
||||||
@@ -49,9 +70,9 @@ Given that feature description, do this:
|
|||||||
7. Identify Key Entities (if data involved)
|
7. Identify Key Entities (if data involved)
|
||||||
8. Return: SUCCESS (spec ready for planning)
|
8. Return: SUCCESS (spec ready for planning)
|
||||||
|
|
||||||
4. 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.
|
5. 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.
|
||||||
|
|
||||||
5. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria:
|
6. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria:
|
||||||
|
|
||||||
a. **Create Spec Quality Checklist**: Generate a checklist file at `FEATURE_DIR/checklists/requirements.md` using the checklist template structure with these validation items:
|
a. **Create Spec Quality Checklist**: Generate a checklist file at `FEATURE_DIR/checklists/requirements.md` using the checklist template structure with these validation items:
|
||||||
|
|
||||||
@@ -143,7 +164,7 @@ Given that feature description, do this:
|
|||||||
|
|
||||||
d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status
|
d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status
|
||||||
|
|
||||||
6. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
|
7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`).
|
||||||
|
|
||||||
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
|
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
|
||||||
|
|
||||||
@@ -209,3 +230,4 @@ Success criteria must be:
|
|||||||
- "Database can handle 1000 TPS" (implementation detail, use user-facing metric)
|
- "Database can handle 1000 TPS" (implementation detail, use user-facing metric)
|
||||||
- "React components render efficiently" (framework-specific)
|
- "React components render efficiently" (framework-specific)
|
||||||
- "Redis cache hit rate above 80%" (technology-specific)
|
- "Redis cache hit rate above 80%" (technology-specific)
|
||||||
|
|
||||||
|
|||||||
@@ -130,3 +130,4 @@ Every task MUST strictly follow this format:
|
|||||||
- Each phase should be a complete, independently testable increment
|
- Each phase should be a complete, independently testable increment
|
||||||
- **Final Phase**: Polish & Cross-Cutting Concerns
|
- **Final Phase**: Polish & Cross-Cutting Concerns
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -102,3 +102,4 @@ directories captured above]
|
|||||||
|-----------|------------|-------------------------------------|
|
|-----------|------------|-------------------------------------|
|
||||||
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
||||||
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|
||||||
|
|
||||||
|
|||||||
@@ -113,3 +113,4 @@
|
|||||||
- **SC-002**: [Measurable metric, e.g., "System handles 1000 concurrent users without degradation"]
|
- **SC-002**: [Measurable metric, e.g., "System handles 1000 concurrent users without degradation"]
|
||||||
- **SC-003**: [User satisfaction metric, e.g., "90% of users successfully complete primary task on first attempt"]
|
- **SC-003**: [User satisfaction metric, e.g., "90% of users successfully complete primary task on first attempt"]
|
||||||
- **SC-004**: [Business metric, e.g., "Reduce support tickets related to [X] by 50%"]
|
- **SC-004**: [Business metric, e.g., "Reduce support tickets related to [X] by 50%"]
|
||||||
|
|
||||||
|
|||||||
@@ -248,3 +248,4 @@ With multiple developers:
|
|||||||
- Avoid: vague tasks, same file conflicts, cross-story dependencies that break independence
|
- Avoid: vague tasks, same file conflicts, cross-story dependencies that break independence
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,3 +11,4 @@
|
|||||||
".specify/scripts/powershell/": true
|
".specify/scripts/powershell/": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user