Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:45:31 +08:00
commit ca9b85ccda
35 changed files with 10784 additions and 0 deletions

View File

@@ -0,0 +1,197 @@
#!/bin/bash
# Check Spec Completeness Script
# Shows detailed TODO items and missing sections
set -o pipefail
SPEC_FILE="${1:-.}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Color codes
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
GRAY='\033[0;37m'
NC='\033[0m'
if [ ! -f "$SPEC_FILE" ]; then
echo -e "${RED}✗ Error: File not found: $SPEC_FILE${NC}"
exit 1
fi
# Extract and display all TODO items
show_todos() {
echo -e "${BLUE}📝 TODO ITEMS (Incomplete):${NC}"
echo "================================"
echo ""
local todo_count=0
local line_num=0
while IFS= read -r line; do
((line_num++))
if [[ $line =~ TODO|FIXME ]]; then
((todo_count++))
# Extract TODO content
if [[ $line =~ \[TODO:(.*)\] ]]; then
echo -e "${YELLOW}[$todo_count]${NC} Line $line_num: ${BASH_REMATCH[1]}"
elif [[ $line =~ TODO:(.*) ]]; then
echo -e "${YELLOW}[$todo_count]${NC} Line $line_num: ${BASH_REMATCH[1]}"
else
# Clean up the line for display
clean_line=$(echo "$line" | sed 's/^[[:space:]]*//' | sed 's/^<!--[[:space:]]*//' | sed 's/[[:space:]]*-->$//')
echo -e "${YELLOW}[$todo_count]${NC} Line $line_num: $clean_line"
fi
fi
done < "$SPEC_FILE"
if [ $todo_count -eq 0 ]; then
echo -e "${GREEN}✓ No TODO items found!${NC}"
else
echo ""
echo -e "Total TODOs: ${RED}$todo_count${NC}"
fi
echo ""
}
# Show missing sections
show_missing_sections() {
echo -e "${BLUE}📋 SECTION ANALYSIS:${NC}"
echo "================================"
echo ""
local found_sections=()
local all_possible_sections=(
"Description"
"Overview"
"Executive Summary"
"Problem Statement"
"Goals & Success Criteria"
"Business Value"
"Stakeholders"
"User Stories"
"Acceptance Criteria"
"Proposed Solution"
"Architecture"
"Implementation Plan"
"Risk & Mitigation"
"Security Considerations"
"Testing Strategy"
"Rollout & Deployment Strategy"
"Dependencies & Assumptions"
"References"
"Related Resources"
)
for section in "${all_possible_sections[@]}"; do
if grep -qE "^## $section" "$SPEC_FILE"; then
found_sections+=("$section")
fi
done
echo -e "${GREEN}✓ Found Sections (${#found_sections[@]}):${NC}"
for section in "${found_sections[@]}"; do
echo "$section"
done
echo ""
}
# Show file statistics
show_statistics() {
echo -e "${BLUE}📊 FILE STATISTICS:${NC}"
echo "================================"
echo ""
local total_lines=$(wc -l < "$SPEC_FILE")
local blank_lines
blank_lines=$(grep -c '^[[:space:]]*$' "$SPEC_FILE" 2>/dev/null)
blank_lines=${blank_lines:-0}
local code_lines
code_lines=$(grep -c '```' "$SPEC_FILE" 2>/dev/null)
code_lines=${code_lines:-0}
local comment_lines
comment_lines=$(grep -c '<!--' "$SPEC_FILE" 2>/dev/null)
comment_lines=${comment_lines:-0}
local has_frontmatter
has_frontmatter=$(grep -c '^---$' "$SPEC_FILE" 2>/dev/null)
has_frontmatter=${has_frontmatter:-0}
echo "Total Lines: $total_lines"
echo "Blank Lines: $blank_lines"
echo "Code Blocks: $((code_lines / 2))"
echo "Comments: $((comment_lines / 2))"
frontmatter_status="No"
[ "$has_frontmatter" -gt 0 ] && frontmatter_status="Yes"
echo "Has YAML Frontmatter: $frontmatter_status"
echo ""
}
# Show completion percentage
show_completion() {
local completion=$(grep -c "^##" "$SPEC_FILE" || echo 0)
completion=$((completion * 100 / 15)) # Assume ~15 sections for full spec
[ $completion -gt 100 ] && completion=100
echo -e "${BLUE}📈 COMPLETION:${NC}"
echo "================================"
echo ""
# Simple progress bar
local filled=$((completion / 5))
local empty=$((20 - filled))
local bar=""
for i in $(seq 1 $filled); do bar+="█"; done
for i in $(seq 1 $empty); do bar+="░"; done
echo -n "Progress: [$bar] "
if [ $completion -lt 50 ]; then
echo -e "${RED}$completion%${NC}"
elif [ $completion -lt 80 ]; then
echo -e "${YELLOW}$completion%${NC}"
else
echo -e "${GREEN}$completion%${NC}"
fi
echo ""
}
# Show references/links
show_references() {
echo -e "${BLUE}🔗 EXTERNAL REFERENCES:${NC}"
echo "================================"
echo ""
local refs=$(grep -oE '\[([^\]]+)\]\(([^)]+)\)' "$SPEC_FILE" | cut -d'(' -f2 | tr -d ')' | sort -u || echo "")
if [ -z "$refs" ]; then
echo "No external references found"
else
echo "$refs" | while read -r ref; do
# Check if link is broken (relative and file doesn't exist)
if [[ ! "$ref" =~ ^http ]] && [ ! -f "$ref" ]; then
echo -e " ${YELLOW}${NC} $ref (file not found)"
else
echo " ${GREEN}${NC} $ref"
fi
done
fi
echo ""
}
# Main
echo ""
echo -e "${BLUE}📋 SPEC COMPLETENESS ANALYSIS${NC}"
echo "========================================"
echo "File: $(basename "$SPEC_FILE")"
echo ""
show_todos
show_missing_sections
show_statistics
show_completion
show_references
echo -e "${GRAY}---${NC}"
echo "Run 'validate-spec.sh' for full validation"

View File

@@ -0,0 +1,186 @@
#!/usr/bin/env bash
# Generate Spec from Template
# Creates a new spec file in docs/specs/ from a template
set -o pipefail
TEMPLATE_NAME="${1:-.}"
SPEC_ID="${2:-.}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Find git repo root for reliable path resolution
# This allows scripts to work when called from any directory in the repo
if git -C "$SCRIPT_DIR" rev-parse --git-dir > /dev/null 2>&1; then
PROJECT_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)"
else
# Fallback to relative path traversal (4 levels up from scripts/)
# From: project-root/project-basics/skills/spec-author/scripts/
# To: project-root/
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
fi
# Specs are always stored in the git repo root under docs/specs/
SPECS_DIR="$PROJECT_ROOT/docs/specs"
# Function to get folder name for spec type (same as template name)
get_spec_folder() {
local template=$1
case "$template" in
business-requirement|technical-requirement|design-document|api-contract|data-model|component|plan|milestone|flow-schematic|deployment-procedure|configuration-schema)
echo "$template"
;;
*)
echo ""
;;
esac
}
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Print usage
usage() {
echo "Usage: generate-spec.sh <template-type> [<spec-id> | --next <slug>]"
echo ""
echo "Template types:"
echo " - business-requirement (brd-XXX-slug)"
echo " - technical-requirement (prd-XXX-slug)"
echo " - design-document (des-XXX-slug)"
echo " - api-contract (api-XXX-slug)"
echo " - data-model (data-XXX-slug)"
echo " - component (cmp-XXX-slug)"
echo " - plan (pln-XXX-slug)"
echo " - milestone (mls-XXX-slug)"
echo " - flow-schematic (flow-XXX-slug)"
echo " - deployment-procedure (deploy-XXX-slug)"
echo " - configuration-schema (config-XXX-slug)"
echo ""
echo "Spec ID: Use the full ID (e.g., brd-001-user-export)"
echo " OR use --next <slug> to auto-generate next ID"
echo ""
echo "Examples:"
echo " generate-spec.sh business-requirement brd-001-user-export"
echo " Creates: docs/specs/business-requirement/brd-001-user-export.md"
echo ""
echo " generate-spec.sh business-requirement --next user-export"
echo " Auto-determines next ID (e.g., brd-002-user-export)"
echo ""
echo " generate-spec.sh api-contract --next"
echo " Auto-determines next ID without slug (e.g., api-001)"
exit 1
}
if [ "$TEMPLATE_NAME" = "-h" ] || [ "$TEMPLATE_NAME" = "--help" ] || [ -z "$TEMPLATE_NAME" ]; then
usage
fi
# Handle --next flag for auto-generating next ID
if [ "$SPEC_ID" = "--next" ]; then
# Check if slug is provided as third argument
SLUG="${3:-}"
if [ -n "$SLUG" ]; then
SPEC_ID=$("$SCRIPT_DIR/next-id.sh" "$TEMPLATE_NAME" --with-slug "$SLUG" --quiet)
else
SPEC_ID=$("$SCRIPT_DIR/next-id.sh" "$TEMPLATE_NAME" --quiet)
fi
if [ $? -ne 0 ]; then
echo -e "${RED}✗ Error: Failed to determine next ID${NC}"
exit 1
fi
echo -e "${BLUE}Auto-generated ID: $SPEC_ID${NC}"
echo ""
elif [ -z "$SPEC_ID" ]; then
usage
fi
# Check if template exists (try multiple locations in order of preference)
# 1. project-basics/skills/spec-author/templates/ (plugin subdirectory in project)
# 2. skills/spec-author/templates/ (plugin in root of project)
# 3. templates/ (root-level templates directory)
# 4. Current script directory/../templates (relative to script)
TEMPLATE_FILE=""
TEMPLATE_SEARCH_PATHS=(
"$PROJECT_ROOT/project-basics/skills/spec-author/templates/$TEMPLATE_NAME.md"
"$PROJECT_ROOT/skills/spec-author/templates/$TEMPLATE_NAME.md"
"$PROJECT_ROOT/templates/$TEMPLATE_NAME.md"
"$SCRIPT_DIR/../templates/$TEMPLATE_NAME.md"
)
for path in "${TEMPLATE_SEARCH_PATHS[@]}"; do
if [ -f "$path" ]; then
TEMPLATE_FILE="$path"
break
fi
done
if [ -z "$TEMPLATE_FILE" ] || [ ! -f "$TEMPLATE_FILE" ]; then
echo -e "${RED}✗ Error: Template not found${NC}"
echo "Looked in:"
for path in "${TEMPLATE_SEARCH_PATHS[@]}"; do
echo " - $path"
done
echo ""
echo "Run 'list-templates.sh' to see available templates"
exit 1
fi
# Get the folder name for this spec type
SPEC_FOLDER=$(get_spec_folder "$TEMPLATE_NAME")
if [ -z "$SPEC_FOLDER" ]; then
echo -e "${RED}✗ Error: Unknown template type: $TEMPLATE_NAME${NC}"
echo "Run 'list-templates.sh' to see available templates"
exit 1
fi
# Ensure specs type directory exists
SPEC_TYPE_DIR="$SPECS_DIR/$SPEC_FOLDER"
mkdir -p "$SPEC_TYPE_DIR"
# Build output path
OUTPUT_PATH="$SPEC_TYPE_DIR/$SPEC_ID.md"
# Check if output file already exists
if [ -f "$OUTPUT_PATH" ]; then
echo -e "${YELLOW}⚠ File already exists: $OUTPUT_PATH${NC}"
read -p "Overwrite? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Cancelled"
exit 0
fi
fi
# Copy template to output location
cp "$TEMPLATE_FILE" "$OUTPUT_PATH"
# Get current date
CURRENT_DATE=$(date +%Y-%m-%d)
# Fill in some default values in the new spec
sed -i '' "s/\[Date\]/$CURRENT_DATE/g" "$OUTPUT_PATH" 2>/dev/null || sed -i "s/\[Date\]/$CURRENT_DATE/g" "$OUTPUT_PATH"
echo -e "${GREEN}✓ Spec created successfully!${NC}"
echo ""
echo "File: $OUTPUT_PATH"
echo "Template: $(basename "$TEMPLATE_FILE")"
echo "ID: $SPEC_ID"
echo ""
echo "Next steps:"
echo "1. Open the file: $OUTPUT_PATH"
echo "2. Fill in the ID and descriptive information"
echo "3. Complete all TODO sections (marked with TODO)"
echo "4. Remove placeholder text in [brackets]"
echo ""
echo "To check what needs to be completed:"
echo " ./scripts/check-completeness.sh $OUTPUT_PATH"
echo ""
echo "To validate the spec:"
echo " ./scripts/validate-spec.sh $OUTPUT_PATH"

View File

@@ -0,0 +1,114 @@
#!/bin/bash
# List Available Spec Templates
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATES_DIR="$SCRIPT_DIR/../templates"
# Color codes
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo ""
echo -e "${BLUE}📚 AVAILABLE SPECIFICATION TEMPLATES${NC}"
echo "====================================="
echo ""
if [ ! -d "$TEMPLATES_DIR" ]; then
echo "Note: Templates will be synced from project templates directory"
echo ""
fi
# Display templates by category
echo -e "${BLUE}📋 BY CATEGORY${NC}"
echo ""
echo -e "${YELLOW}Requirements & Business:${NC}"
echo " • business-requirement"
echo " Document business needs and customer problems"
echo ""
echo " • technical-requirement"
echo " How to build what business requires"
echo ""
echo -e "${YELLOW}Architecture & Design:${NC}"
echo " • design-document"
echo " Architecture and technical design decisions"
echo ""
echo " • api-contract"
echo " API endpoint specifications and contracts"
echo ""
echo " • data-model"
echo " Entity definitions and database schemas"
echo ""
echo -e "${YELLOW}Implementation & Operations:${NC}"
echo " • component"
echo " System component specifications"
echo ""
echo " • plan"
echo " Implementation roadmap and phases"
echo ""
echo " • milestone"
echo " Delivery milestones and checkpoints"
echo ""
echo -e "${YELLOW}Process & Deployment:${NC}"
echo " • flow-schematic"
echo " Business process flows and workflows"
echo ""
echo " • deployment-procedure"
echo " Production deployment steps"
echo ""
echo " • configuration-schema"
echo " Configuration specifications"
echo ""
echo -e "${BLUE}📝 USAGE${NC}"
echo "--------"
echo ""
echo "To create a new spec from a template, use:"
echo -e " ${GREEN}./scripts/generate-spec.sh <template-name> <output-path>${NC}"
echo ""
echo "Example:"
echo -e " ${GREEN}./scripts/generate-spec.sh business-requirement specs/user-export.md${NC}"
echo ""
echo -e "${BLUE}🎯 ID CONVENTIONS${NC}"
echo "------------------"
echo ""
echo "• Business Requirement: brd-XXX-descriptive-slug"
echo " Example: brd-001-user-export-pdf"
echo ""
echo "• Technical Requirement: prd-XXX-descriptive-slug"
echo " Example: prd-001-export-service"
echo ""
echo "• Design Document: des-XXX-descriptive-slug"
echo " Example: des-001-microservices-architecture"
echo ""
echo "• API Contract: api-XXX-descriptive-slug"
echo " Example: api-001-user-service"
echo ""
echo "• Data Model: data-XXX-descriptive-slug"
echo " Example: data-001-user-schema"
echo ""
echo "• Component: cmp-XXX-descriptive-slug"
echo " Example: cmp-001-auth-service"
echo ""
echo "• Plan: pln-XXX-descriptive-slug"
echo " Example: pln-001-migration-roadmap"
echo ""
echo "• Milestone: mls-XXX-descriptive-slug"
echo " Example: mls-001-phase-1-delivery"
echo ""
echo "• Flow Schematic: flow-XXX-descriptive-slug"
echo " Example: flow-001-user-signup"
echo ""
echo "• Deployment Procedure: deploy-XXX-descriptive-slug"
echo " Example: deploy-001-staging-release"
echo ""
echo "• Configuration Schema: config-XXX-descriptive-slug"
echo " Example: config-001-app-settings"
echo ""

View File

@@ -0,0 +1,195 @@
#!/usr/bin/env bash
# Next Spec ID Script
# Determines the next available ID number for a spec type
set -o pipefail
SPEC_TYPE="${1:-.}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Find git repo root for reliable path resolution
# This allows scripts to work when called from any directory in the repo
if git -C "$SCRIPT_DIR" rev-parse --git-dir > /dev/null 2>&1; then
PROJECT_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)"
else
# Fallback to relative path traversal (4 levels up from scripts/)
# From: project-root/project-basics/skills/spec-author/scripts/
# To: project-root/
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
fi
# Specs are always stored in the git repo root under docs/specs/
SPECS_DIR="$PROJECT_ROOT/docs/specs"
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Map spec types to their prefixes and folder names
get_spec_info() {
local type=$1
case "$type" in
business-requirement)
echo "brd:business-requirement"
;;
technical-requirement)
echo "prd:technical-requirement"
;;
design-document)
echo "des:design-document"
;;
api-contract)
echo "api:api-contract"
;;
data-model)
echo "data:data-model"
;;
component)
echo "cmp:component"
;;
plan)
echo "pln:plan"
;;
milestone)
echo "mls:milestone"
;;
flow-schematic)
echo "flow:flow-schematic"
;;
deployment-procedure)
echo "deploy:deployment-procedure"
;;
configuration-schema)
echo "config:configuration-schema"
;;
*)
echo ""
;;
esac
}
# Print usage
usage() {
echo "Usage: next-id.sh <spec-type> [--with-slug <slug>]"
echo ""
echo "Determines the next available ID number for a spec type."
echo ""
echo "Spec types:"
echo " - business-requirement"
echo " - technical-requirement"
echo " - design-document"
echo " - api-contract"
echo " - data-model"
echo " - component"
echo " - plan"
echo " - milestone"
echo " - flow-schematic"
echo " - deployment-procedure"
echo " - configuration-schema"
echo ""
echo "Options:"
echo " --with-slug <slug> Include slug in output (e.g., brd-001-my-feature)"
echo " --quiet Only output the ID, no formatting"
echo ""
echo "Examples:"
echo " next-id.sh business-requirement"
echo " Output: brd-001"
echo ""
echo " next-id.sh business-requirement --with-slug user-export"
echo " Output: brd-001-user-export"
echo ""
echo " next-id.sh api-contract --quiet"
echo " Output: api-001"
exit 1
}
# Parse arguments
SLUG=""
QUIET=false
if [ "$SPEC_TYPE" = "-h" ] || [ "$SPEC_TYPE" = "--help" ] || [ -z "$SPEC_TYPE" ]; then
usage
fi
shift
while [[ $# -gt 0 ]]; do
case $1 in
--with-slug)
SLUG="$2"
shift 2
;;
--quiet)
QUIET=true
shift
;;
*)
echo -e "${RED}✗ Unknown option: $1${NC}" >&2
usage
;;
esac
done
# Get spec info
SPEC_INFO=$(get_spec_info "$SPEC_TYPE")
if [ -z "$SPEC_INFO" ]; then
echo -e "${RED}✗ Error: Unknown spec type: $SPEC_TYPE${NC}" >&2
echo "Run 'next-id.sh --help' to see available types" >&2
exit 1
fi
PREFIX=$(echo "$SPEC_INFO" | cut -d: -f1)
FOLDER=$(echo "$SPEC_INFO" | cut -d: -f2)
SPEC_TYPE_DIR="$SPECS_DIR/$FOLDER"
# Find highest existing ID number
MAX_ID=0
if [ -d "$SPEC_TYPE_DIR" ]; then
# Look for files matching the pattern: prefix-NNN-*.md
for file in "$SPEC_TYPE_DIR/$PREFIX"-*.md; do
if [ -f "$file" ]; then
# Extract the number part (NNN)
filename=$(basename "$file" .md)
# Remove prefix and leading dash
rest="${filename#$PREFIX-}"
# Extract just the number (first field before next dash)
num=$(echo "$rest" | cut -d- -f1)
# Check if it's a valid number
if [[ "$num" =~ ^[0-9]+$ ]]; then
if [ "$num" -gt "$MAX_ID" ]; then
MAX_ID=$num
fi
fi
fi
done
fi
# Calculate next ID
NEXT_NUM=$((MAX_ID + 1))
NEXT_ID=$(printf "%s-%03d" "$PREFIX" "$NEXT_NUM")
# Add slug if provided
if [ -n "$SLUG" ]; then
FULL_ID="$NEXT_ID-$SLUG"
else
FULL_ID="$NEXT_ID"
fi
# Output
if [ "$QUIET" = true ]; then
echo "$FULL_ID"
else
echo -e "${GREEN}Next available ID for $SPEC_TYPE:${NC}"
echo -e "${BLUE}$FULL_ID${NC}"
echo ""
if [ "$MAX_ID" -gt 0 ]; then
echo "Last ID: $PREFIX-$(printf "%03d" "$MAX_ID")"
else
echo "No existing specs found (this will be the first)"
fi
fi

View File

@@ -0,0 +1,182 @@
#!/bin/bash
# Task Manager - Main CLI Orchestrator
# Compose smaller scripts to create a unified task management system
set -o pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="$SCRIPT_DIR/lib"
source "$LIB_DIR/task-common.sh"
# Check if jq is available
check_jq() {
if ! command -v jq &> /dev/null; then
log_warn "jq not found - some features will have limited functionality"
fi
}
# Print help
show_help() {
cat <<'EOF'
Task Manager - Unified Task Tracking System
USAGE:
task.sh <command> [options]
COMMANDS:
parse <file|dir> Extract tasks from Markdown files as JSON
Options:
--json Output as JSON (default)
--table Output as formatted table
query <criteria> Query and filter tasks from JSON input (via pipe)
Options:
--status <status> Filter by status (Draft|Planning|In Progress|Completed|Blocked)
--priority <priority> Filter by priority (critical|high|medium|low)
--owner <owner> Filter by owner
--id <id> Find specific task by ID
--search <text> Search in title/description
--unassigned Find unassigned tasks
--blockers Find tasks with dependencies
--dependents <id> Find tasks that depend on this task
--count <field> Count tasks by field
--sort <field> Sort by field
--table Format as table
update <file> <id> [options] Update task properties in place
Options:
--status <status> Set status
--priority <priority> Set priority
--owner <owner> Set owner
--effort <effort> Set effort estimate
report [type] Generate reports from task JSON (via pipe)
Types:
--summary Summary statistics
--board Kanban board view
--priority Tasks grouped by priority
--owner Tasks grouped by owner
--dependencies Dependency graph
--burndown Progress/burndown chart
--csv Export as CSV
--all All reports
validate [type] Validate task consistency (via pipe)
Types:
--schema Validate schema compliance
--dependencies Validate dependencies exist
--uniqueness Check for duplicate IDs
--all Full validation (default)
list Quick task listing with filtering
Options:
(same as query)
EXAMPLES:
# Parse all tasks from a plan file
./task.sh parse pln-001-roadmap.md
# Parse all tasks in a directory
./task.sh parse ./specs/
# Find all high-priority tasks
./task.sh parse . | ./task.sh query --priority high
# Find tasks assigned to Alice
./task.sh parse . | ./task.sh query --owner "Alice" --table
# Find unassigned tasks across all plans
./task.sh parse specs/ | ./task.sh query --unassigned --table
# Generate a kanban board
./task.sh parse . | ./task.sh report --board
# Export tasks as CSV
./task.sh parse . | ./task.sh report --csv
# Validate all tasks
./task.sh parse . | ./task.sh validate --all
# Update task status
./task.sh update pln-001-roadmap.md tsk-001 --status "In Progress"
# Update multiple fields
./task.sh update pln-001-roadmap.md tsk-001 --status "Completed" --priority "high"
PIPING EXAMPLES:
# Find completed tasks by priority
./task.sh parse . | ./task.sh query --status "Completed" | ./task.sh query --priority "high" | ./task.sh report --summary
# Find all blockers assigned to a person
./task.sh parse . | ./task.sh query --owner "Bob" | ./task.sh query --blockers | ./task.sh report --board
# Generate CSV of all in-progress tasks
./task.sh parse . | ./task.sh query --status "In Progress" | ./task.sh report --csv > tasks.csv
NOTES:
- JSON piping allows composing commands together
- Update operations modify files in place with automatic backups
- All text output uses colors (disable with NO_COLOR env var)
- Requires jq for full functionality (graceful degradation without it)
EOF
}
# Main command routing
main() {
check_jq
if [[ $# -eq 0 ]] || [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then
show_help
return 0
fi
local command="$1"
shift
case "$command" in
parse)
bash "$LIB_DIR/task-parse.sh" "$@"
;;
query)
bash "$LIB_DIR/task-query.sh" "$@"
;;
update)
bash "$LIB_DIR/task-update.sh" "$@"
;;
report)
bash "$LIB_DIR/task-report.sh" "$@"
;;
validate)
bash "$LIB_DIR/task-validate.sh" "$@"
;;
list)
# Convenience command: parse + query
if [[ $# -eq 0 ]]; then
log_error "Usage: task.sh list <file|dir> [query options]"
return 1
fi
local target="$1"
shift
bash "$LIB_DIR/task-parse.sh" "$target" | bash "$LIB_DIR/task-query.sh" "$@"
;;
--version)
echo "Task Manager v0.1.0"
;;
*)
log_error "Unknown command: $command"
echo ""
show_help
return 1
;;
esac
}
# Execute main
main "$@"

View File

@@ -0,0 +1,385 @@
#!/bin/bash
# Spec Author Validation Script
# Validates specification documents against required templates and standards
set -o pipefail
SPEC_FILE="${1:-.}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Find git repo root for reliable path resolution
# This allows scripts to work when called from any directory in the repo
if git -C "$SCRIPT_DIR" rev-parse --git-dir > /dev/null 2>&1; then
PROJECT_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)"
else
# Fallback to relative path traversal (4 levels up from scripts/)
# From: project-root/project-basics/skills/spec-author/scripts/
# To: project-root/
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
fi
# Templates are located in the spec-author plugin directory
TEMPLATES_DIR="$SCRIPT_DIR/../templates"
# Check if file is a template file
is_template_file() {
local basename=$(basename "$SPEC_FILE")
# Check if file matches any template filename
[[ "$basename" == "api-contract.md" ]] ||
[[ "$basename" == "business-requirement.md" ]] ||
[[ "$basename" == "component.md" ]] ||
[[ "$basename" == "configuration-schema.md" ]] ||
[[ "$basename" == "constitution.md" ]] ||
[[ "$basename" == "data-model.md" ]] ||
[[ "$basename" == "deployment-procedure.md" ]] ||
[[ "$basename" == "design-document.md" ]] ||
[[ "$basename" == "flow-schematic.md" ]] ||
[[ "$basename" == "milestone.md" ]] ||
[[ "$basename" == "plan.md" ]] ||
[[ "$basename" == "technical-requirement.md" ]]
}
# Color codes
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Check if file exists
if [ ! -f "$SPEC_FILE" ]; then
echo -e "${RED}✗ Error: Spec file not found: $SPEC_FILE${NC}"
exit 1
fi
# Skip validation for template files themselves
if is_template_file; then
echo -e "${BLUE}📋 SPEC VALIDATION REPORT${NC}"
echo "===================="
echo ""
echo "Spec: $(basename "$SPEC_FILE")"
echo "Status: ${GREEN}✓ TEMPLATE${NC} (templates are not validated, they are used as references)"
echo ""
exit 0
fi
# Determine spec type from filename or spec ID
determine_spec_type() {
local filename=$(basename "$SPEC_FILE" .md)
# First check by spec ID prefix in filename
case "$filename" in
brd-*) echo "business"; return ;;
prd-*) echo "technical"; return ;;
des-*) echo "design"; return ;;
api-*) echo "api"; return ;;
data-*) echo "data"; return ;;
cmp-*) echo "component"; return ;;
pln-*) echo "plan"; return ;;
mls-*) echo "milestone"; return ;;
flow-*) echo "flow"; return ;;
deploy-*) echo "deployment"; return ;;
config-*) echo "config"; return ;;
esac
# Then check by full template name
if [[ "$filename" == "design-document" ]] || [[ "$SPEC_FILE" =~ design-document ]]; then
echo "design"
elif [[ "$filename" == "technical-requirement" ]] || [[ "$SPEC_FILE" =~ technical-requirement ]]; then
echo "technical"
elif [[ "$filename" == "business-requirement" ]] || [[ "$SPEC_FILE" =~ business-requirement ]]; then
echo "business"
elif [[ "$filename" == "api-contract" ]] || [[ "$SPEC_FILE" =~ api-contract ]]; then
echo "api"
elif [[ "$filename" == "data-model" ]] || [[ "$SPEC_FILE" =~ data-model ]]; then
echo "data"
elif [[ "$filename" == "component" ]] || [[ "$SPEC_FILE" =~ component ]]; then
echo "component"
elif [[ "$filename" == "plan" ]] || [[ "$SPEC_FILE" =~ plan ]]; then
echo "plan"
elif [[ "$filename" == "milestone" ]] || [[ "$SPEC_FILE" =~ milestone ]]; then
echo "milestone"
elif [[ "$filename" == "flow-schematic" ]] || [[ "$SPEC_FILE" =~ flow-schematic ]]; then
echo "flow"
elif [[ "$filename" == "deployment-procedure" ]] || [[ "$SPEC_FILE" =~ deployment-procedure ]]; then
echo "deployment"
elif [[ "$filename" == "configuration-schema" ]] || [[ "$SPEC_FILE" =~ configuration-schema ]]; then
echo "config"
else
echo "unknown"
fi
}
# Get required sections for spec type
get_required_sections() {
local spec_type=$1
case $spec_type in
design)
echo "Executive Summary|Problem Statement|Goals & Success Criteria|Proposed Solution|Implementation Plan|Risk & Mitigation"
;;
technical)
echo "Overview|Requirements|Constraints|Success Criteria"
;;
business)
echo "Description|Business Value|Stakeholders|User Stories|Acceptance Criteria"
;;
api)
echo "Endpoints|Request/Response|Authentication|Error Handling"
;;
data)
echo "Entities|Fields|Relationships|Constraints"
;;
component)
echo "Description|Responsibilities|Interfaces|Configuration"
;;
plan)
echo "Overview|Phases|Deliverables|Success Metrics"
;;
milestone)
echo "Milestone|Deliverables|Success Criteria"
;;
flow)
echo "Overview|Process Flow|Steps|Decision Points"
;;
deployment)
echo "Prerequisites|Instructions|Rollback|Monitoring"
;;
config)
echo "Schema|Fields|Validation|Examples"
;;
*)
echo "Title"
;;
esac
}
# Count lines and TODOs
count_todos() {
grep -o "TODO" "$SPEC_FILE" | wc -l
}
# Count TODO items more accurately
count_todo_items() {
local count
count=$(grep -cE "(TODO|<!-- TODO|<!-- \[TODO)" "$SPEC_FILE" 2>/dev/null)
count=${count:-0}
echo "$count"
}
# Check for sections with only template guidance (not filled in by user)
check_template_only_sections() {
local spec_type=$(determine_spec_type)
local required_sections=$(get_required_sections "$spec_type")
local template_only_sections=()
while IFS= read -r section; do
if [ -z "$section" ]; then
continue
fi
# Escape special regex characters in section name
local escaped_section=$(echo "$section" | sed 's/[[\.*^$/]/\\&/g')
# Find section content between the section header and the next section
local section_content=$(awk "/^## $escaped_section/{found=1; next} /^## /{found=0} found" "$SPEC_FILE")
# Remove only leading/trailing whitespace, keep all content
local trimmed_content=$(echo "$section_content" | sed 's/^[[:space:]]*//g' | sed 's/[[:space:]]*$//g')
# Check if section is completely empty or ONLY contains HTML comments
# (i.e., user hasn't written any actual content)
local non_comment_content=$(echo "$trimmed_content" | grep -v '^<!--' | grep -v '^-->' | grep -v '^$' | head -1)
if [ -z "$non_comment_content" ]; then
template_only_sections+=("$section")
fi
done < <(echo "$required_sections" | tr '|' '\n')
printf '%s\n' "${template_only_sections[@]}"
}
# Calculate completion percentage
calculate_completion() {
local spec_type=$(determine_spec_type)
local required_sections=$(get_required_sections "$spec_type")
local total_required=$(echo "$required_sections" | tr '|' '\n' | wc -l)
local found_sections=0
while IFS= read -r section; do
if grep -qE "^## $section" "$SPEC_FILE"; then
((found_sections++))
fi
done < <(echo "$required_sections" | tr '|' '\n')
# Check for TODO items
local todo_count
todo_count=$(count_todo_items)
local completed_percentage=$((found_sections * 100 / total_required))
# Reduce percentage based on TODO count (max reduction of 25%)
if [ "$todo_count" -gt 0 ] 2>/dev/null; then
local todo_penalty=$((todo_count * 2))
[ $todo_penalty -gt 25 ] && todo_penalty=25
completed_percentage=$((completed_percentage - todo_penalty))
fi
[ $completed_percentage -lt 0 ] && completed_percentage=0
echo "$completed_percentage"
}
# Validate spec structure
validate_spec() {
local spec_type=$(determine_spec_type)
local required_sections=$(get_required_sections "$spec_type")
local content=$(cat "$SPEC_FILE")
local pass_count=0
local warn_count=0
local error_count=0
local pass_items=()
local warn_items=()
local error_items=()
# Check for proper formatting
if grep -qE "^# " "$SPEC_FILE"; then
pass_items+=("Title present")
((pass_count++))
else
error_items+=("Missing title (# Title)")
((error_count++))
fi
# Check required sections
while IFS= read -r section; do
if [ -z "$section" ]; then
continue
fi
if grep -qE "^## $section" "$SPEC_FILE"; then
pass_items+=("Section '$section' present")
((pass_count++))
else
error_items+=("Missing required section: '$section'")
((error_count++))
fi
done < <(echo "$required_sections" | tr '|' '\n')
# Check for TODO items - only flag non-comment TODOs
# TODOs inside HTML comments <!-- ... --> are just guidance and are acceptable
local todo_count
todo_count=$(grep -v "^<!--" "$SPEC_FILE" | grep -cE "TODO" 2>/dev/null || echo 0)
if [ "$todo_count" -gt 0 ] 2>/dev/null; then
error_items+=("Contains $todo_count TODO items that must be resolved")
((error_count++))
else
pass_items+=("No TODO items remaining")
((pass_count++))
fi
# Check for sections that still contain only template guidance (not user-written content)
local template_only_list=$(check_template_only_sections)
if [ -n "$template_only_list" ]; then
while IFS= read -r section; do
if [ -n "$section" ]; then
error_items+=("Section '$section' still contains only template guidance - must write your own content")
((error_count++))
fi
done < <(echo "$template_only_list")
else
pass_items+=("All required sections contain user-written content (not template placeholders)")
((pass_count++))
fi
# Check for placeholder text - only flag outside of HTML comments
# Brackets inside <!-- ... --> comments are just guidance templates and are acceptable
local placeholder_count
placeholder_count=$(grep -v "^<!--" "$SPEC_FILE" | grep -cE "FIXME|XXX" 2>/dev/null || echo 0)
if [ "$placeholder_count" -gt 0 ] 2>/dev/null; then
warn_items+=("Contains $placeholder_count placeholder markers (FIXME/XXX)")
((warn_count++))
else
pass_items+=("No placeholder text detected")
((pass_count++))
fi
# Check for empty code blocks
if grep -qE "^\`\`\`\s*$" "$SPEC_FILE"; then
warn_items+=("Contains empty code blocks")
((warn_count++))
fi
# Check for broken links
local broken_links=$(grep -o '\[[^]]*\]([^)]*)' "$SPEC_FILE" 2>/dev/null | grep -c '()$' 2>/dev/null || echo 0)
broken_links=${broken_links:-0}
if [ "$broken_links" -gt 0 ] 2>/dev/null; then
warn_items+=("Found $broken_links potentially broken links")
((warn_count++))
fi
# Output results
local completion=$(calculate_completion)
local status_emoji="✓"
local status_text="PASS"
if [ "$error_count" -gt 0 ]; then
status_emoji="✗"
status_text="INVALID"
elif [ "$warn_count" -gt 0 ]; then
status_emoji="⚠"
status_text="INCOMPLETE"
fi
echo ""
echo -e "${BLUE}📋 SPEC VALIDATION REPORT${NC}"
echo "===================="
echo ""
echo "Spec: $(basename "$SPEC_FILE")"
echo "Type: $spec_type"
echo "Status: $status_emoji $status_text ($completion% complete)"
echo ""
if [ "$pass_count" -gt 0 ]; then
echo -e "${GREEN}✓ PASS ($pass_count items)${NC}"
for item in "${pass_items[@]}"; do
echo " - $item"
done
echo ""
fi
if [ "$warn_count" -gt 0 ]; then
echo -e "${YELLOW}⚠ WARNINGS ($warn_count items)${NC}"
for item in "${warn_items[@]}"; do
echo " - $item"
done
echo ""
fi
if [ "$error_count" -gt 0 ]; then
echo -e "${RED}✗ ERRORS ($error_count items)${NC}"
for item in "${error_items[@]}"; do
echo " - $item"
done
echo ""
fi
# Return appropriate exit code
if [ "$error_count" -gt 0 ]; then
return 1
elif [ "$warn_count" -gt 0 ]; then
return 2
else
return 0
fi
}
# Main execution
validate_spec
exit_code=$?
# Return proper exit codes
case $exit_code in
0) exit 0 ;; # All pass
2) exit 0 ;; # Warnings (still acceptable)
1) exit 1 ;; # Errors
esac