Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 09:01:58 +08:00
commit aa33fffdfe
25 changed files with 4562 additions and 0 deletions

View File

@@ -0,0 +1,161 @@
---
name: "specimin-spec"
description: "Create or update the feature specification from a natural language feature description. Only invoke when user explicitly requests to create a specification, spec, or feature specification."
allowed-tools:
- run_terminal_cmd
- write
- read_file
---
**ALWAYS WAIT for user input before generating spec.**
Ask the user for:
1. A brief description of the spec they want to create
2. (Optional) A GitHub issue link associated with this feature
BEFORE generating the spec.
# Interactive Specification Generator
## Role
Senior product requirements analyst translating feature requests into clear, actionable specifications. Define WHAT and WHY, not HOW.
## Process Flow
### Stage 0: Branch Name (FIRST)
If the user provided a **GitHub issue link**, extract the issue number from it:
- Parse URLs like: `https://github.com/owner/repo/issues/123` → Extract `123`
- Store as `$ISSUE_NUMBER` for Stage 4
Otherwise, generate a **2-3 word, kebab-case branch name** from the user's requirement:
- **Good**: `user-auth`, `pdf-export`, `real-time-sync`
- **Bad**: `authentication-system-with-jwt`, `feature`, `new-feature`
Store as `$BRANCH_NAME` for Stage 4.
### Stage 1: Analyze & Clarify
1. Identify critical ambiguities: scope > security/privacy > UX > technical
2. Ask 2-5 focused questions with concrete options
3. Show impact of each option
4. Wait for responses
**Question Template:**
```
## Q[N]: [Topic]
**Need to know**: [Specific question]
**Options**:
- A: [Description] → Impact: [Consequence]
- B: [Description] → Impact: [Consequence]
- Custom: [Your preference]
```
### Stage 2: Generate Draft
Create specification using Output Format (below) based on user answers.
### Stage 3: Iterate
Ask: "Does this capture what you need? What should I adjust?"
Refine until approved.
### Stage 4: Finalize
**ONLY after user approval:**
1. Write approved spec to temporary file `/tmp/spec-draft.md`
2. Execute save script:
- **If GitHub issue link was provided:**
```bash
bash ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/skills/specimin-spec/scripts/save-spec.sh "$USER_REQUIREMENT" "$BRANCH_NAME" /tmp/spec-draft.md "$ISSUE_NUMBER"
```
- **Otherwise:**
```bash
bash ${CLAUDE_PLUGIN_ROOT}/.claude-plugin/skills/specimin-spec/scripts/save-spec.sh "$USER_REQUIREMENT" "$BRANCH_NAME" /tmp/spec-draft.md
```
3. Parse JSON output and confirm to user:
"✓ Specification saved to `[spec_path]` on branch `[branch_name]`"
## Output Format
**Objective**: [What needs accomplishing]
**Context**: [Why needed, business impact]
**Assumptions**: [Reasonable defaults]
**Constraints**: [Technical and business limitations]
**Acceptance Criteria**: [Verifiable, testable conditions]
**User Scenarios**: [Step-by-step flows with expected outcomes]
**Edge Cases**: [Boundary conditions]
**Dependencies** *(if applicable)*: [External requirements]
**Out of Scope**: [Explicitly excluded]
## Requirements
**Include:**
- Clear objectives and constraints
- Testable acceptance criteria (measurable, technology-agnostic)
- Realistic user scenarios
- Explicit scope boundaries
- Documented assumptions
**Exclude:**
- Technology choices (databases, frameworks, languages)
- API designs or code structure
- Implementation algorithms
**Good**: "Users complete checkout in under 3 minutes"
**Bad**: "API response time under 200ms" (too technical)
## Example
**User**: "Users should stay logged in when they close and reopen the browser"
**Objective**
Implement persistent user authentication across browser sessions.
**Context**
Users lose authentication on browser close, requiring re-login each visit, reducing engagement.
**Assumptions**
- Standard web security practices apply
- Session duration configurable by administrators
- Users expect multi-day persistence unless explicitly logging out
- Browser storage mechanisms available
**Constraints**
- Must integrate with existing authentication system
- Must follow security best practices for credential storage
- Session duration must be configurable
- Must handle expiration gracefully
**Acceptance Criteria**
- User remains authenticated after browser close/reopen
- User prompted to re-authenticate after session expires
- User can explicitly log out to end session
- Works across major browsers (Chrome, Firefox, Safari, Edge)
**User Scenarios**
1. Returning user: Login → Close browser → Reopen → Still authenticated
2. Session expiration: Login → Wait past duration → Prompted to re-login
3. Explicit logout: Authenticated → Logout → Close/reopen → Must login
**Edge Cases**
- Multiple simultaneous sessions (different devices/windows)
- Session expiration during active use
- Browser storage unavailable or cleared
- User switches between devices
**Dependencies**
- Existing authentication system must expose session management APIs
**Out of Scope**
- Cross-device session synchronization
- "Remember this device" functionality
- Biometric authentication

View File

@@ -0,0 +1,58 @@
#!/bin/bash
# save-spec.sh - Saves approved specification and commits to feature branch
# Usage: save-spec.sh <user_requirement> <branch_name> <spec_file_path> [issue_number]
set -e
USER_REQUIREMENT="$1"
BRANCH_NAME="$2"
SPEC_FILE_PATH="$3"
ISSUE_NUMBER="${4:-}"
if [ -z "$USER_REQUIREMENT" ] || [ -z "$BRANCH_NAME" ] || [ -z "$SPEC_FILE_PATH" ]; then
echo "Error: Missing required arguments"
echo "Usage: save-spec.sh <user_requirement> <branch_name> <spec_file_path> [issue_number]"
exit 1
fi
# Verify initialization
if [ ! -d ".specimin/plans/" ]; then
echo "Error: Specimin not initialized. Please run /init first."
exit 1
fi
# Verify spec file exists
if [ ! -f "$SPEC_FILE_PATH" ]; then
echo "Error: Spec file not found: $SPEC_FILE_PATH"
exit 1
fi
# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Run setup script
if [ -n "$ISSUE_NUMBER" ]; then
SETUP_OUTPUT=$(bash "$SCRIPT_DIR/setup.feature.sh" "$USER_REQUIREMENT" --json --no-commit --branch-name "$BRANCH_NAME" --issue-number "$ISSUE_NUMBER")
else
SETUP_OUTPUT=$(bash "$SCRIPT_DIR/setup.feature.sh" "$USER_REQUIREMENT" --json --no-commit --branch-name "$BRANCH_NAME")
fi
# Parse output
FEATURE_DIR=$(echo "$SETUP_OUTPUT" | jq -r '.feature_dir')
BRANCH_NAME=$(echo "$SETUP_OUTPUT" | jq -r '.branch_name')
if [ -z "$FEATURE_DIR" ] || [ "$FEATURE_DIR" = "null" ]; then
echo "Error: Failed to create feature directory"
exit 1
fi
# Copy spec file to feature directory
SPEC_DEST="$FEATURE_DIR/spec.md"
cp "$SPEC_FILE_PATH" "$SPEC_DEST"
# Commit
git add "$SPEC_DEST"
git commit -m "Add specification: $USER_REQUIREMENT"
# Output success message with JSON
echo "{\"feature_dir\": \"$FEATURE_DIR\", \"branch_name\": \"$BRANCH_NAME\", \"spec_path\": \"$SPEC_DEST\"}"

View File

@@ -0,0 +1,172 @@
#!/usr/bin/env bash
set -euo pipefail
# Script: setup.feature.sh
# Description: Create feature branch and planning directory
# Usage: ./setup.feature.sh "feature description" [--json] [--no-commit] [--branch-name "custom-name"] [--issue-number "123"]
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
print_error() { echo -e "${RED}ERROR: $1${NC}" >&2; }
print_success() { echo -e "${GREEN}SUCCESS: $1${NC}"; }
print_info() { echo -e "${YELLOW}INFO: $1${NC}"; }
get_next_branch_number() {
# Get all branches matching the pattern and extract the highest number
local max_num=$(git branch -a | \
grep -oE '[0-9]{3}-' | \
grep -oE '[0-9]{3}' | \
sort -n | \
tail -1)
if [[ -z "$max_num" ]]; then
echo "001"
else
printf "%03d" $((10#$max_num + 1))
fi
}
generate_branch_name() {
local description="$1"
# Extract first two words and clean them
local two_words=$(echo "$description" | \
tr '[:upper:]' '[:lower:]' | \
sed 's/[^a-z0-9 -]//g' | \
tr -s ' ' | \
awk '{print $1"-"$2}' | \
sed 's/^-//;s/-$//')
# Get next branch number
local branch_num=$(get_next_branch_number)
echo "${branch_num}-${two_words}"
}
check_git_repo() {
if ! git rev-parse --git-dir > /dev/null 2>&1; then
print_error "Not a git repository"
exit 1
fi
}
check_working_tree() {
if ! git diff-index --quiet HEAD -- 2>/dev/null; then
print_error "Uncommitted changes exist. Commit or stash first."
exit 1
fi
}
check_branch_exists() {
local branch_name="$1"
if git rev-parse --verify "$branch_name" > /dev/null 2>&1; then
print_error "Branch '$branch_name' already exists"
exit 1
fi
}
main() {
local feature_description="${1:-}"
local json_output=false
local no_commit=false
local custom_branch_name=""
local issue_number=""
# Parse flags
local i=2
while [[ $i -le $# ]]; do
case "${!i}" in
--json) json_output=true ;;
--no-commit) no_commit=true ;;
--branch-name)
((i++))
custom_branch_name="${!i}"
;;
--issue-number)
((i++))
issue_number="${!i}"
;;
esac
((i++))
done
if [[ -z "$feature_description" ]]; then
print_error "Feature description required"
echo "Usage: $0 \"feature description\" [--json] [--no-commit] [--branch-name \"custom-name\"] [--issue-number \"123\"]"
exit 1
fi
check_git_repo
check_working_tree
local branch_name
local branch_num
# Determine branch number
if [[ -n "$issue_number" ]]; then
# Use issue number (padded to 3 digits)
branch_num=$(printf "%03d" "$issue_number")
else
# Auto-increment (backward compatible)
branch_num=$(get_next_branch_number)
fi
if [[ -n "$custom_branch_name" ]]; then
# Use custom branch name with branch number
branch_name="${branch_num}-${custom_branch_name}"
else
# Generate from description (backward compatible)
# Extract first two words and clean them
local two_words=$(echo "$feature_description" | \
tr '[:upper:]' '[:lower:]' | \
sed 's/[^a-z0-9 -]//g' | \
tr -s ' ' | \
awk '{print $1"-"$2}' | \
sed 's/^-//;s/-$//')
branch_name="${branch_num}-${two_words}"
fi
check_branch_exists "$branch_name"
local feature_dir=".specimin/plans/${branch_name}"
# Create directory
mkdir -p "$feature_dir"
# Create and checkout branch
git checkout -b "$branch_name" --quiet
if [[ "$no_commit" == false ]]; then
# Commit empty directory structure (backward compatible behavior)
touch "$feature_dir/.gitkeep"
git add "$feature_dir"
git commit -m "Initialize feature: $feature_description" --quiet
fi
if [[ "$json_output" == true ]]; then
cat << EOF
{
"branch_name": "$branch_name",
"feature_dir": "$feature_dir",
"absolute_path": "$(pwd)/$feature_dir",
"status": "success"
}
EOF
else
print_success "Feature setup complete!"
echo ""
echo "Branch: $branch_name"
echo "Directory: $feature_dir"
if [[ "$no_commit" == false ]]; then
echo ""
echo "Next: Use spec template to create $feature_dir/spec.md"
fi
fi
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi