specpulse 1.0.6__py3-none-any.whl → 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. specpulse/__init__.py +1 -1
  2. specpulse/cli/main.py +809 -617
  3. specpulse/core/specpulse.py +1140 -1105
  4. specpulse/resources/commands/claude/sp-continue.md +203 -0
  5. specpulse/resources/commands/claude/sp-decompose.md +227 -0
  6. specpulse/resources/commands/claude/sp-plan.md +220 -0
  7. specpulse/resources/commands/claude/sp-pulse.md +142 -0
  8. specpulse/resources/commands/claude/{spec.md → sp-spec.md} +36 -23
  9. specpulse/resources/commands/claude/sp-status.md +170 -0
  10. specpulse/resources/commands/claude/sp-task.md +315 -0
  11. specpulse/resources/commands/gemini/sp-continue.toml +56 -0
  12. specpulse/resources/commands/gemini/sp-decompose.toml +54 -0
  13. specpulse/resources/commands/gemini/sp-plan.toml +68 -0
  14. specpulse/resources/commands/gemini/{pulse.toml → sp-pulse.toml} +12 -6
  15. specpulse/resources/commands/gemini/sp-spec.toml +54 -0
  16. specpulse/resources/commands/gemini/sp-status.toml +61 -0
  17. specpulse/resources/commands/gemini/sp-task.toml +79 -0
  18. specpulse/resources/memory/constitution.md +5 -5
  19. specpulse/resources/memory/context.md +53 -1
  20. specpulse/resources/scripts/sp-pulse-decompose.py +66 -0
  21. specpulse/resources/scripts/sp-pulse-decompose.sh +56 -0
  22. specpulse/resources/scripts/{pulse-init.py → sp-pulse-init.py} +6 -6
  23. specpulse/resources/scripts/{pulse-init.sh → sp-pulse-init.sh} +95 -95
  24. specpulse/resources/scripts/{pulse-plan.py → sp-pulse-plan.py} +32 -7
  25. specpulse/resources/scripts/{pulse-plan.sh → sp-pulse-plan.sh} +136 -126
  26. specpulse/resources/scripts/{pulse-spec.py → sp-pulse-spec.py} +26 -6
  27. specpulse/resources/scripts/{pulse-spec.sh → sp-pulse-spec.sh} +126 -103
  28. specpulse/resources/scripts/{pulse-task.py → sp-pulse-task.py} +42 -6
  29. specpulse/resources/scripts/{pulse-task.sh → sp-pulse-task.sh} +32 -16
  30. specpulse/resources/templates/decomposition/api-contract.yaml +22 -0
  31. specpulse/resources/templates/decomposition/integration-plan.md +134 -0
  32. specpulse/resources/templates/decomposition/interface.ts +20 -0
  33. specpulse/resources/templates/decomposition/microservices.md +34 -0
  34. specpulse/resources/templates/decomposition/service-plan.md +168 -0
  35. specpulse/resources/templates/plan.md +206 -206
  36. specpulse/resources/templates/spec.md +125 -125
  37. specpulse/resources/templates/task.md +164 -163
  38. {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/METADATA +95 -36
  39. specpulse-1.2.0.dist-info/RECORD +50 -0
  40. specpulse/resources/commands/claude/plan.md +0 -184
  41. specpulse/resources/commands/claude/pulse.md +0 -91
  42. specpulse/resources/commands/claude/task.md +0 -237
  43. specpulse/resources/commands/gemini/plan.toml +0 -59
  44. specpulse/resources/commands/gemini/spec.toml +0 -45
  45. specpulse/resources/commands/gemini/task.toml +0 -69
  46. specpulse/resources/scripts/pulse-init.ps1 +0 -186
  47. specpulse/resources/scripts/pulse-plan.ps1 +0 -251
  48. specpulse/resources/scripts/pulse-spec.ps1 +0 -185
  49. specpulse/resources/scripts/pulse-task.ps1 +0 -263
  50. specpulse-1.0.6.dist-info/RECORD +0 -41
  51. {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/WHEEL +0 -0
  52. {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/entry_points.txt +0 -0
  53. {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/licenses/LICENSE +0 -0
  54. {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,79 @@
1
+ description = "Generate and manage task breakdowns with versioning"
2
+ prompt = """
3
+ Handle the /sp-task command with arguments: {{args}}
4
+
5
+ First, detect current feature context:
6
+ - Read memory/context.md for current feature metadata
7
+ - Use git branch name if available (e.g., 001-user-authentication)
8
+ - Fall back to most recently created feature directory
9
+ - If no context found, ask user to specify feature or run /sp-pulse first
10
+
11
+ Parse arguments to determine action:
12
+ - If "update": Update task status
13
+ - If "status": Show progress
14
+ - Otherwise: Generate task breakdown
15
+
16
+ ## For /sp-task breakdown or /sp-task:
17
+ 1. Show list of existing plan files in current feature directory
18
+ 2. Ask user which plan file to base tasks on
19
+ 3. Read selected implementation plan from @{plans/*/plan-XXX.md}
20
+
21
+ 4. Generate tasks from plan:
22
+ - Create task categories:
23
+ • Critical Path (Phase 0)
24
+ • Phase 1: Foundation
25
+ • Phase 2: Core Features
26
+ • Phase 3: Polish
27
+ • Phase 4: Testing
28
+
29
+ - For each task:
30
+ • Use T[XXX] format (T001, T002)
31
+ • Include clear description
32
+ • Mark dependencies
33
+ • Estimate complexity (S/M/L/XL)
34
+ • Assign priority
35
+
36
+ 5. Check existing task files and create next version (task-001.md, task-002.md, etc.)
37
+ 6. Write tasks to tasks/XXX-feature/task-XXX.md
38
+ 7. Run cross-platform validation:
39
+ - Linux/macOS: !{bash scripts/sp-pulse-task.sh "XXX-feature"}
40
+ - Fallback: !{python scripts/sp-pulse-task.py "XXX-feature"}
41
+
42
+ ## For /sp-task update:
43
+ 1. Show list of existing task files in current feature directory
44
+ 2. Ask user which task file to update
45
+ 3. Read selected current tasks from @{tasks/*/task-XXX.md}
46
+ 4. Ask which tasks to update
47
+ 5. Mark tasks as completed/in-progress
48
+ 6. Add newly discovered tasks
49
+ 7. Update dependencies and blockers
50
+ 8. Save updated task list
51
+ 9. Run cross-platform validation:
52
+ - Linux/macOS: !{bash scripts/sp-pulse-task.sh "XXX-feature"}
53
+ - Fallback: !{python scripts/sp-pulse-task.py "XXX-feature"}
54
+
55
+ ## For /sp-task status:
56
+ 1. Show list of existing task files in current feature directory
57
+ 2. Ask user which task file to show status for
58
+ 3. Read selected current tasks
59
+ 4. Run cross-platform analysis:
60
+ - Linux/macOS: !{bash scripts/sp-pulse-task.sh "XXX-feature"}
61
+ - Fallback: !{python scripts/sp-pulse-task.py "XXX-feature"}
62
+ 5. Count completed vs total
63
+ 6. Show current phase progress
64
+ 7. List any blockers
65
+ 8. Estimate remaining work
66
+ 9. Display progress summary
67
+
68
+ Task Format:
69
+ ```markdown
70
+ - [ ] T001: [S] Set up project structure
71
+ - [ ] T002: [M] Create database schema
72
+ - [x] T003: [L] Implement authentication
73
+ ```
74
+
75
+ Examples:
76
+ - /sp-task breakdown
77
+ - /sp-task update
78
+ - /sp-task status
79
+ """
@@ -110,7 +110,7 @@ This includes:
110
110
  Every implementation plan MUST pass through constitutional gates:
111
111
 
112
112
  #### Phase -1: Pre-Implementation Gates
113
- - [ ] Simplicity Gate (Article VII): Using 3 projects? No future-proofing?
113
+ - [ ] Simplicity Gate (Article VII): Using <=3 projects? No future-proofing?
114
114
  - [ ] Anti-Abstraction Gate (Article VII): Using framework directly? Single model?
115
115
  - [ ] Test-First Gate (Article III): Tests written? Tests reviewed? Tests failing?
116
116
  - [ ] Integration-First Gate (Article VIII): Contracts defined? Contract tests written?
@@ -188,11 +188,11 @@ While principles are immutable, their application can evolve:
188
188
 
189
189
  ### SpecPulse Development Process
190
190
  1. `specpulse init` - Initialize feature with proper structure
191
- 2. Create specification following template guidelines
192
- 3. Generate implementation plan with Phase Gates
193
- 4. Break down into executable tasks
191
+ 2. `/sp-spec` - Create specification following template guidelines
192
+ 3. `/sp-plan` - Generate implementation plan with Phase Gates
193
+ 4. `/sp-task` - Break down into executable tasks
194
194
  5. Execute with Test-First Development
195
- 6. Validate against constitution and specification
195
+ 6. `/sp-validate` - Validate against constitution and specification
196
196
  7. Update specifications based on learnings
197
197
 
198
198
  ### Version Control
@@ -4,6 +4,27 @@
4
4
  - **Active Feature**: None
5
5
  - **Last Updated**: [AUTO-GENERATED]
6
6
  - **Phase**: Initialization
7
+ - **Architecture**: [Monolithic|Decomposed]
8
+
9
+ ## Active Features
10
+ <!-- Format:
11
+ 1. **[feature-name]** (SPEC-XXX)
12
+ - Status: [Specification|Planning|Implementation|Testing|Deployed]
13
+ - Branch: [branch-name]
14
+ - Architecture: [Monolithic|Decomposed]
15
+ - Services: [None|Service list]
16
+ - Blockers: [None|List]
17
+ -->
18
+
19
+ ## Decomposition Status
20
+ <!-- Updated when /sp-decompose is run -->
21
+ - **Feature**: [feature-name]
22
+ - **Decomposed**: [Yes|No]
23
+ - **Services Identified**:
24
+ - [Service Name]: [Responsibility]
25
+ - **Integration Points**: [Count]
26
+ - **Data Boundaries**: [Defined|Pending]
27
+ - **Decomposition Date**: [Date]
7
28
 
8
29
  ## Recent Decisions
9
30
  <!-- Updated by AI during development -->
@@ -11,11 +32,29 @@
11
32
  ## Active Specifications
12
33
  <!-- List of in-progress specifications -->
13
34
 
35
+ ## Implementation Plans
36
+ <!-- Track plan generation -->
37
+ - **Monolithic Plans**: [List]
38
+ - **Service Plans**: [List]
39
+ - **Integration Plans**: [List]
40
+
41
+ ## Task Breakdown
42
+ <!-- Track task creation -->
43
+ - **Total Tasks**: [Count]
44
+ - **Service Tasks**: [Count per service]
45
+ - **Integration Tasks**: [Count]
46
+ - **Completed**: [Percentage]
47
+
14
48
  ## Completed Features
15
49
  <!-- List of completed features with links -->
16
50
 
17
51
  ## Technical Stack
18
52
  <!-- Current technology choices -->
53
+ - **Primary Language**: [Language]
54
+ - **Framework**: [Framework]
55
+ - **Database**: [Database]
56
+ - **Message Queue**: [If decomposed]
57
+ - **Service Mesh**: [If decomposed]
19
58
 
20
59
  ## Known Issues
21
60
  <!-- Active bugs or technical debt -->
@@ -24,4 +63,17 @@
24
63
  <!-- Key performance indicators -->
25
64
 
26
65
  ## Team Notes
27
- <!-- Important reminders or observations -->
66
+ <!-- Important reminders or observations -->
67
+
68
+ ## Workflow History
69
+ <!-- Auto-generated history -->
70
+
71
+ ### Active Feature: test-feature
72
+ - Feature ID: 001
73
+ - Branch: 001-test-feature
74
+ - Started: 2025-09-12T00:06:47.683447
75
+
76
+ ### Active Feature: test-feature-2
77
+ - Feature ID: 001
78
+ - Branch: 001-test-feature-2
79
+ - Started: 2025-09-12T00:09:08.283919
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SpecPulse Decomposition Orchestrator
4
+ Validates decomposition request and returns status for AI processing
5
+ """
6
+
7
+ import sys
8
+ import os
9
+ from pathlib import Path
10
+
11
+ def main():
12
+ spec_id = sys.argv[1] if len(sys.argv) > 1 else None
13
+ action = sys.argv[2] if len(sys.argv) > 2 else "validate"
14
+
15
+ print(f"[SpecPulse Decompose] Processing spec: {spec_id or 'current'}")
16
+
17
+ # Find specification
18
+ specs_dir = Path("specs")
19
+ if not specs_dir.exists():
20
+ print("ERROR: No specifications directory")
21
+ print("SUGGESTION: Run /sp-spec create first")
22
+ sys.exit(1)
23
+
24
+ if spec_id:
25
+ spec_dirs = list(specs_dir.glob(f"{spec_id}*"))
26
+ spec_dir = spec_dirs[0] if spec_dirs else None
27
+ else:
28
+ spec_dirs = sorted(specs_dir.glob("*"), reverse=True)
29
+ spec_dir = spec_dirs[0] if spec_dirs else None
30
+
31
+ if not spec_dir:
32
+ print("ERROR: No specification found")
33
+ print("SUGGESTION: Run /sp-spec create first")
34
+ sys.exit(1)
35
+
36
+ # Check complexity
37
+ spec_files = list(spec_dir.glob("spec-*.md"))
38
+ if spec_files:
39
+ spec_file = sorted(spec_files)[-1]
40
+ line_count = len(spec_file.read_text(encoding='utf-8').splitlines())
41
+
42
+ print(f"SPEC_FILE={spec_file}")
43
+ print(f"COMPLEXITY={line_count} lines")
44
+
45
+ if line_count > 100:
46
+ print("RECOMMENDATION=Decomposition advised (complex spec)")
47
+ else:
48
+ print("RECOMMENDATION=Single service may suffice")
49
+
50
+ # Check for existing decomposition
51
+ decomp_dir = spec_dir / "decomposition"
52
+ if decomp_dir.exists():
53
+ print("STATUS=Decomposition exists")
54
+ print(f"PATH={decomp_dir}")
55
+ else:
56
+ print("STATUS=Ready for decomposition")
57
+ print(f"PATH={spec_dir}")
58
+
59
+ # Return template paths for AI
60
+ print("TEMPLATES_DIR=templates/decomposition")
61
+ print("MEMORY_FILE=memory/context.md")
62
+
63
+ return 0
64
+
65
+ if __name__ == "__main__":
66
+ sys.exit(main())
@@ -0,0 +1,56 @@
1
+ #!/bin/bash
2
+
3
+ # SpecPulse Decomposition Orchestrator
4
+ # Validates decomposition request and returns status for AI processing
5
+
6
+ SPEC_ID="$1"
7
+ ACTION="${2:-validate}"
8
+
9
+ # Colors
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[1;33m'
12
+ NC='\033[0m'
13
+
14
+ echo -e "${GREEN}[SpecPulse Decompose]${NC} Processing spec: ${SPEC_ID:-current}"
15
+
16
+ # Find specification
17
+ if [ -z "$SPEC_ID" ]; then
18
+ SPEC_DIR=$(ls -d specs/* 2>/dev/null | sort -r | head -1)
19
+ else
20
+ SPEC_DIR=$(ls -d specs/${SPEC_ID}* 2>/dev/null | head -1)
21
+ fi
22
+
23
+ if [ -z "$SPEC_DIR" ] || [ ! -d "$SPEC_DIR" ]; then
24
+ echo "ERROR: No specification found"
25
+ echo "SUGGESTION: Run /sp-spec create first"
26
+ exit 1
27
+ fi
28
+
29
+ # Check complexity (simple heuristic)
30
+ SPEC_FILE=$(ls "$SPEC_DIR"/spec-*.md 2>/dev/null | sort | tail -1)
31
+ if [ -f "$SPEC_FILE" ]; then
32
+ LINE_COUNT=$(wc -l < "$SPEC_FILE")
33
+ echo "SPEC_FILE=$SPEC_FILE"
34
+ echo "COMPLEXITY=$LINE_COUNT lines"
35
+
36
+ if [ "$LINE_COUNT" -gt 100 ]; then
37
+ echo "RECOMMENDATION=Decomposition advised (complex spec)"
38
+ else
39
+ echo "RECOMMENDATION=Single service may suffice"
40
+ fi
41
+ fi
42
+
43
+ # Check for existing decomposition
44
+ if [ -d "$SPEC_DIR/decomposition" ]; then
45
+ echo "STATUS=Decomposition exists"
46
+ echo "PATH=$SPEC_DIR/decomposition"
47
+ else
48
+ echo "STATUS=Ready for decomposition"
49
+ echo "PATH=$SPEC_DIR"
50
+ fi
51
+
52
+ # Return template paths for AI
53
+ echo "TEMPLATES_DIR=templates/decomposition"
54
+ echo "MEMORY_FILE=memory/context.md"
55
+
56
+ exit 0
@@ -14,10 +14,10 @@ from pathlib import Path
14
14
  class SpecPulseInit:
15
15
  def __init__(self):
16
16
  self.script_name = Path(__file__).name
17
- self.project_root = Path(__file__).parent.parent.parent
17
+ self.project_root = Path(__file__).parent.parent
18
18
  self.memory_dir = self.project_root / "memory"
19
19
  self.context_file = self.memory_dir / "context.md"
20
- self.templates_dir = self.project_root / "resources" / "templates"
20
+ self.templates_dir = self.project_root / "templates"
21
21
 
22
22
  def log(self, message):
23
23
  """Log messages with timestamp"""
@@ -77,9 +77,9 @@ class SpecPulseInit:
77
77
  def copy_templates(self, branch_name):
78
78
  """Copy templates to feature directories"""
79
79
  templates = {
80
- "spec.md": self.project_root / "specs" / branch_name / "spec.md",
81
- "plan.md": self.project_root / "plans" / branch_name / "plan.md",
82
- "task.md": self.project_root / "tasks" / branch_name / "tasks.md"
80
+ "spec.md": self.project_root / "specs" / branch_name / "spec-001.md",
81
+ "plan.md": self.project_root / "plans" / branch_name / "plan-001.md",
82
+ "task.md": self.project_root / "tasks" / branch_name / "task-001.md"
83
83
  }
84
84
 
85
85
  for template_name, target_path in templates.items():
@@ -140,7 +140,7 @@ class SpecPulseInit:
140
140
  def main(self, args):
141
141
  """Main execution function"""
142
142
  if len(args) < 1:
143
- self.error_exit("Usage: python pulse-init.py <feature-name> [feature-id]")
143
+ self.error_exit("Usage: python sp-pulse-init.py <feature-name> [feature-id]")
144
144
 
145
145
  feature_name = args[0]
146
146
  custom_id = args[1] if len(args) > 1 else None
@@ -1,96 +1,96 @@
1
- #!/bin/bash
2
- # Initialize a new feature with SpecPulse
3
-
4
- set -euo pipefail # Exit on error, unset vars, pipe failures
5
-
6
- # Configuration
7
- SCRIPT_NAME="$(basename "$0")"
8
- PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
9
-
10
- # Function to log messages
11
- log() {
12
- echo "[$(date '+%Y-%m-%d %H:%M:%S')] $SCRIPT_NAME: $1" >&2
13
- }
14
-
15
- # Function to handle errors
16
- error_exit() {
17
- log "ERROR: $1"
18
- exit 1
19
- }
20
-
21
- # Validate arguments
22
- if [ $# -eq 0 ]; then
23
- error_exit "Usage: $SCRIPT_NAME <feature-name> [feature-id]"
24
- fi
25
-
26
- FEATURE_NAME="$1"
27
- CUSTOM_ID="${2:-}"
28
-
29
- # Sanitize feature name
30
- BRANCH_SAFE_NAME=$(echo "$FEATURE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
31
-
32
- if [ -z "$BRANCH_SAFE_NAME" ]; then
33
- error_exit "Invalid feature name: '$FEATURE_NAME'"
34
- fi
35
-
36
- # Get feature ID
37
- if [ -n "$CUSTOM_ID" ]; then
38
- FEATURE_ID=$(printf "%03d" "$CUSTOM_ID")
39
- else
40
- FEATURE_ID=$(printf "%03d" $(find "$PROJECT_ROOT/specs" -maxdepth 1 -type d -name '[0-9]*' 2>/dev/null | wc -l | awk '{print $1 + 1}'))
41
- fi
42
-
43
- BRANCH_NAME="${FEATURE_ID}-${BRANCH_SAFE_NAME}"
44
-
45
- # Create directories
46
- SPECS_DIR="$PROJECT_ROOT/specs/${BRANCH_NAME}"
47
- PLANS_DIR="$PROJECT_ROOT/plans/${BRANCH_NAME}"
48
- TASKS_DIR="$PROJECT_ROOT/tasks/${BRANCH_NAME}"
49
-
50
- log "Creating feature directories for '$FEATURE_NAME'"
51
-
52
- mkdir -p "$SPECS_DIR" || error_exit "Failed to create specs directory: $SPECS_DIR"
53
- mkdir -p "$PLANS_DIR" || error_exit "Failed to create plans directory: $PLANS_DIR"
54
- mkdir -p "$TASKS_DIR" || error_exit "Failed to create tasks directory: $TASKS_DIR"
55
-
56
- # Create initial files from templates
57
- TEMPLATE_DIR="$PROJECT_ROOT/templates"
58
-
59
- if [ ! -f "$TEMPLATE_DIR/spec.md" ]; then
60
- error_exit "Template not found: $TEMPLATE_DIR/spec.md"
61
- fi
62
-
63
- cp "$TEMPLATE_DIR/spec.md" "$SPECS_DIR/spec.md" || error_exit "Failed to copy spec template"
64
- cp "$TEMPLATE_DIR/plan.md" "$PLANS_DIR/plan.md" || error_exit "Failed to copy plan template"
65
- cp "$TEMPLATE_DIR/task.md" "$TASKS_DIR/tasks.md" || error_exit "Failed to copy task template"
66
-
67
- # Update context
68
- CONTEXT_FILE="$PROJECT_ROOT/memory/context.md"
69
- mkdir -p "$(dirname "$CONTEXT_FILE")"
70
-
71
- {
72
- echo ""
73
- echo "## Active Feature: $FEATURE_NAME"
74
- echo "- Feature ID: $FEATURE_ID"
75
- echo "- Branch: $BRANCH_NAME"
76
- echo "- Started: $(date -Iseconds)"
77
- } >> "$CONTEXT_FILE" || error_exit "Failed to update context file"
78
-
79
- # Create git branch if in git repo
80
- if [ -d "$PROJECT_ROOT/.git" ]; then
81
- cd "$PROJECT_ROOT"
82
- if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then
83
- log "Git branch '$BRANCH_NAME' already exists, checking out"
84
- git checkout "$BRANCH_NAME" || error_exit "Failed to checkout existing branch"
85
- else
86
- log "Creating new git branch '$BRANCH_NAME'"
87
- git checkout -b "$BRANCH_NAME" || error_exit "Failed to create new branch"
88
- fi
89
- fi
90
-
91
- log "Successfully initialized feature '$FEATURE_NAME' with ID $FEATURE_ID"
92
-
93
- echo "BRANCH_NAME=$BRANCH_NAME"
94
- echo "SPEC_DIR=$SPECS_DIR"
95
- echo "FEATURE_ID=$FEATURE_ID"
1
+ #!/bin/bash
2
+ # Initialize a new feature with SpecPulse
3
+
4
+ set -euo pipefail # Exit on error, unset vars, pipe failures
5
+
6
+ # Configuration
7
+ SCRIPT_NAME="$(basename "$0")"
8
+ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
9
+
10
+ # Function to log messages
11
+ log() {
12
+ echo "[$(date '+%Y-%m-%d %H:%M:%S')] $SCRIPT_NAME: $1" >&2
13
+ }
14
+
15
+ # Function to handle errors
16
+ error_exit() {
17
+ log "ERROR: $1"
18
+ exit 1
19
+ }
20
+
21
+ # Validate arguments
22
+ if [ $# -eq 0 ]; then
23
+ error_exit "Usage: $SCRIPT_NAME <feature-name> [feature-id]"
24
+ fi
25
+
26
+ FEATURE_NAME="$1"
27
+ CUSTOM_ID="${2:-}"
28
+
29
+ # Sanitize feature name
30
+ BRANCH_SAFE_NAME=$(echo "$FEATURE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
31
+
32
+ if [ -z "$BRANCH_SAFE_NAME" ]; then
33
+ error_exit "Invalid feature name: '$FEATURE_NAME'"
34
+ fi
35
+
36
+ # Get feature ID
37
+ if [ -n "$CUSTOM_ID" ]; then
38
+ FEATURE_ID=$(printf "%03d" "$CUSTOM_ID")
39
+ else
40
+ FEATURE_ID=$(printf "%03d" $(find "$PROJECT_ROOT/specs" -maxdepth 1 -type d -name '[0-9]*' 2>/dev/null | wc -l | awk '{print $1 + 1}'))
41
+ fi
42
+
43
+ BRANCH_NAME="${FEATURE_ID}-${BRANCH_SAFE_NAME}"
44
+
45
+ # Create directories
46
+ SPECS_DIR="$PROJECT_ROOT/specs/${BRANCH_NAME}"
47
+ PLANS_DIR="$PROJECT_ROOT/plans/${BRANCH_NAME}"
48
+ TASKS_DIR="$PROJECT_ROOT/tasks/${BRANCH_NAME}"
49
+
50
+ log "Creating feature directories for '$FEATURE_NAME'"
51
+
52
+ mkdir -p "$SPECS_DIR" || error_exit "Failed to create specs directory: $SPECS_DIR"
53
+ mkdir -p "$PLANS_DIR" || error_exit "Failed to create plans directory: $PLANS_DIR"
54
+ mkdir -p "$TASKS_DIR" || error_exit "Failed to create tasks directory: $TASKS_DIR"
55
+
56
+ # Create initial files from templates
57
+ TEMPLATE_DIR="$PROJECT_ROOT/templates"
58
+
59
+ if [ ! -f "$TEMPLATE_DIR/spec-001.md" ]; then
60
+ error_exit "Template not found: $TEMPLATE_DIR/spec-001.md"
61
+ fi
62
+
63
+ cp "$TEMPLATE_DIR/spec-001.md" "$SPECS_DIR/spec-001.md" || error_exit "Failed to copy spec template"
64
+ cp "$TEMPLATE_DIR/plan-001.md" "$PLANS_DIR/plan-001.md" || error_exit "Failed to copy plan template"
65
+ cp "$TEMPLATE_DIR/task.md" "$TASKS_DIR/task-001.md" || error_exit "Failed to copy task template"
66
+
67
+ # Update context
68
+ CONTEXT_FILE="$PROJECT_ROOT/memory/context.md"
69
+ mkdir -p "$(dirname "$CONTEXT_FILE")"
70
+
71
+ {
72
+ echo ""
73
+ echo "## Active Feature: $FEATURE_NAME"
74
+ echo "- Feature ID: $FEATURE_ID"
75
+ echo "- Branch: $BRANCH_NAME"
76
+ echo "- Started: $(date -Iseconds)"
77
+ } >> "$CONTEXT_FILE" || error_exit "Failed to update context file"
78
+
79
+ # Create git branch if in git repo
80
+ if [ -d "$PROJECT_ROOT/.git" ]; then
81
+ cd "$PROJECT_ROOT"
82
+ if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then
83
+ log "Git branch '$BRANCH_NAME' already exists, checking out"
84
+ git checkout "$BRANCH_NAME" || error_exit "Failed to checkout existing branch"
85
+ else
86
+ log "Creating new git branch '$BRANCH_NAME'"
87
+ git checkout -b "$BRANCH_NAME" || error_exit "Failed to create new branch"
88
+ fi
89
+ fi
90
+
91
+ log "Successfully initialized feature '$FEATURE_NAME' with ID $FEATURE_ID"
92
+
93
+ echo "BRANCH_NAME=$BRANCH_NAME"
94
+ echo "SPEC_DIR=$SPECS_DIR"
95
+ echo "FEATURE_ID=$FEATURE_ID"
96
96
  echo "STATUS=initialized"
@@ -13,10 +13,10 @@ import datetime
13
13
  class SpecPulsePlan:
14
14
  def __init__(self):
15
15
  self.script_name = Path(__file__).name
16
- self.project_root = Path(__file__).parent.parent.parent
16
+ self.project_root = Path(__file__).parent.parent
17
17
  self.memory_dir = self.project_root / "memory"
18
18
  self.context_file = self.memory_dir / "context.md"
19
- self.templates_dir = self.project_root / "resources" / "templates"
19
+ self.templates_dir = self.project_root / "templates"
20
20
 
21
21
  def log(self, message):
22
22
  """Log messages with timestamp"""
@@ -126,7 +126,7 @@ class SpecPulsePlan:
126
126
  def main(self, args):
127
127
  """Main execution function"""
128
128
  if len(args) < 1:
129
- self.error_exit("Usage: python pulse-plan.py <feature-dir>")
129
+ self.error_exit("Usage: python sp-pulse-plan.py <feature-dir>")
130
130
 
131
131
  feature_dir = args[0]
132
132
 
@@ -136,9 +136,34 @@ class SpecPulsePlan:
136
136
  sanitized_dir = self.get_current_feature_dir(feature_dir)
137
137
 
138
138
  # Set file paths
139
- plan_file = self.project_root / "plans" / sanitized_dir / "plan.md"
140
- template_file = self.templates_dir / "plan.md"
141
- spec_file = self.project_root / "specs" / sanitized_dir / "spec.md"
139
+ specs_dir = self.project_root / "specs" / sanitized_dir
140
+ plans_dir = self.project_root / "plans" / sanitized_dir
141
+
142
+ # Find latest spec file
143
+ if specs_dir.exists():
144
+ spec_files = list(specs_dir.glob("spec-*.md"))
145
+ if spec_files:
146
+ latest_spec = max(spec_files, key=lambda f: f.stat().st_mtime)
147
+ else:
148
+ self.error_exit(f"No specification files found in {specs_dir}")
149
+ else:
150
+ self.error_exit(f"Specifications directory not found: {specs_dir}")
151
+
152
+ # Find next available plan number
153
+ if plans_dir.exists():
154
+ existing_plans = list(plans_dir.glob("plan-*.md"))
155
+ if existing_plans:
156
+ plan_numbers = [int(f.stem.split('-')[1]) for f in existing_plans if f.stem.split('-')[1].isdigit()]
157
+ next_number = max(plan_numbers) + 1 if plan_numbers else 1
158
+ else:
159
+ next_number = 1
160
+ else:
161
+ next_number = 1
162
+ plans_dir.mkdir(parents=True, exist_ok=True)
163
+
164
+ plan_file = plans_dir / f"plan-{next_number:03d}.md"
165
+ template_file = self.templates_dir / "plan-001.md"
166
+ spec_file = latest_spec
142
167
 
143
168
  # Ensure plans directory exists
144
169
  plan_file.parent.mkdir(parents=True, exist_ok=True)
@@ -155,7 +180,7 @@ class SpecPulsePlan:
155
180
  if not plan_file.exists():
156
181
  self.log(f"Creating implementation plan from template: {plan_file}")
157
182
  try:
158
- plan_file.write_text(template_file.read_text(encoding='utf-8'))
183
+ plan_file.write_text(template_file.read_text(encoding='utf-8'), encoding='utf-8')
159
184
  except Exception as e:
160
185
  self.error_exit(f"Failed to copy plan template: {e}")
161
186
  else: