specpulse 1.0.3__py3-none-any.whl → 1.0.5__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 (32) hide show
  1. specpulse/__init__.py +1 -1
  2. specpulse/resources/commands/claude/plan.md +180 -50
  3. specpulse/resources/commands/claude/pulse.md +79 -24
  4. specpulse/resources/commands/claude/spec.md +156 -43
  5. specpulse/resources/commands/claude/task.md +229 -48
  6. specpulse/resources/commands/gemini/plan.toml +59 -48
  7. specpulse/resources/commands/gemini/pulse.toml +21 -39
  8. specpulse/resources/commands/gemini/spec.toml +40 -28
  9. specpulse/resources/commands/gemini/task.toml +69 -51
  10. specpulse/resources/memory/constitution.md +2 -2
  11. specpulse/resources/scripts/pulse-init.ps1 +186 -0
  12. specpulse/resources/scripts/pulse-init.py +171 -0
  13. specpulse/resources/scripts/pulse-init.sh +80 -21
  14. specpulse/resources/scripts/pulse-plan.ps1 +251 -0
  15. specpulse/resources/scripts/pulse-plan.py +191 -0
  16. specpulse/resources/scripts/pulse-plan.sh +113 -12
  17. specpulse/resources/scripts/pulse-spec.ps1 +185 -0
  18. specpulse/resources/scripts/pulse-spec.py +167 -0
  19. specpulse/resources/scripts/pulse-spec.sh +86 -11
  20. specpulse/resources/scripts/pulse-task.ps1 +263 -0
  21. specpulse/resources/scripts/pulse-task.py +237 -0
  22. specpulse/resources/scripts/pulse-task.sh +123 -9
  23. specpulse/resources/templates/plan.md +142 -287
  24. specpulse/resources/templates/spec.md +80 -246
  25. specpulse/resources/templates/task.md +114 -93
  26. {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/METADATA +67 -34
  27. specpulse-1.0.5.dist-info/RECORD +41 -0
  28. specpulse-1.0.3.dist-info/RECORD +0 -33
  29. {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/WHEEL +0 -0
  30. {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/entry_points.txt +0 -0
  31. {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/licenses/LICENSE +0 -0
  32. {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,263 @@
1
+ # SpecPulse Task Generation Script
2
+ # Cross-platform PowerShell equivalent of pulse-task.sh
3
+
4
+ param(
5
+ [Parameter(Mandatory=$true)]
6
+ [string]$FeatureDir,
7
+
8
+ [string]$TaskContent = ""
9
+ )
10
+
11
+ $ErrorActionPreference = "Stop"
12
+ $ProgressPreference = "SilentlyContinue"
13
+
14
+ # Configuration
15
+ $ScriptName = $MyInvocation.MyCommand.Name
16
+ $ProjectRoot = $PSScriptRoot | Split-Path -Parent | Split-Path -Parent
17
+ $MemoryDir = Join-Path $ProjectRoot "memory"
18
+ $ContextFile = Join-Path $MemoryDir "context.md"
19
+ $TemplatesDir = Join-Path $ProjectRoot "resources" "templates"
20
+
21
+ function Write-Log {
22
+ param([string]$Message)
23
+ $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
24
+ Write-Host "[$Timestamp] $ScriptName : $Message" -ForegroundColor Yellow
25
+ }
26
+
27
+ function Exit-WithError {
28
+ param([string]$Message)
29
+ Write-Log "ERROR: $Message"
30
+ exit 1
31
+ }
32
+
33
+ function Sanitize-FeatureDir {
34
+ param([string]$Dir)
35
+
36
+ if ([string]::IsNullOrWhiteSpace($Dir)) {
37
+ Exit-WithError "Feature directory cannot be empty"
38
+ }
39
+
40
+ # Remove non-alphanumeric characters except hyphens and underscores
41
+ $Sanitized = $Dir -replace '[^a-zA-Z0-9_-]', ''
42
+
43
+ if ([string]::IsNullOrWhiteSpace($Sanitized)) {
44
+ Exit-WithError "Invalid feature directory: '$Dir'"
45
+ }
46
+
47
+ return $Sanitized
48
+ }
49
+
50
+ function Find-FeatureDirectory {
51
+ if (-not (Test-Path $ContextFile)) {
52
+ Exit-WithError "No context file found and no feature directory provided"
53
+ }
54
+
55
+ try {
56
+ $Content = Get-Content $ContextFile -Raw -Encoding UTF8
57
+ # Look for "Active Feature" section
58
+ if ($Content -match 'Active Feature:\s*(.+)') {
59
+ $FeatureDir = $matches[1].Trim()
60
+ Write-Log "Using active feature from context: $FeatureDir"
61
+ return $FeatureDir
62
+ } else {
63
+ Exit-WithError "No active feature found in context file"
64
+ }
65
+ } catch {
66
+ Exit-WithError "Failed to read context file: $_"
67
+ }
68
+ }
69
+
70
+ function Get-CurrentFeatureDir {
71
+ param([string]$ProvidedDir)
72
+
73
+ if ($ProvidedDir) {
74
+ return Sanitize-FeatureDir -Dir $ProvidedDir
75
+ } else {
76
+ return Find-FeatureDirectory
77
+ }
78
+ }
79
+
80
+ function Validate-TaskStructure {
81
+ param([string]$TaskFile)
82
+
83
+ if (-not (Test-Path $TaskFile)) {
84
+ Exit-WithError "Task file does not exist: $TaskFile"
85
+ }
86
+
87
+ # Required sections
88
+ $RequiredSections = @(
89
+ "# Task List:",
90
+ "## Progress Tracking",
91
+ "### Parallel Group"
92
+ )
93
+
94
+ $Content = Get-Content $TaskFile -Raw -Encoding UTF8
95
+ $MissingSections = @()
96
+
97
+ foreach ($Section in $RequiredSections) {
98
+ if ($Content -notmatch [regex]::Escape($Section)) {
99
+ $MissingSections += $Section
100
+ }
101
+ }
102
+
103
+ if ($MissingSections.Count -gt 0) {
104
+ Write-Log "WARNING: Missing required sections: $($MissingSections -join ', ')"
105
+ }
106
+
107
+ return $MissingSections.Count
108
+ }
109
+
110
+ function Check-ConstitutionalGates {
111
+ param([string]$TaskFile)
112
+
113
+ if (-not (Test-Path $TaskFile)) {
114
+ Exit-WithError "Task file does not exist: $TaskFile"
115
+ }
116
+
117
+ $Content = Get-Content $TaskFile -Raw -Encoding UTF8
118
+ $GatesStatus = @{}
119
+
120
+ # Check for constitutional gates compliance
121
+ $Gates = @{
122
+ "Simplicity Gate" = "Simplicity Gate"
123
+ "Test-First Gate" = "Test-First Gate"
124
+ "Integration-First Gate" = "Integration-First Gate"
125
+ "Research Gate" = "Research Gate"
126
+ }
127
+
128
+ foreach ($Gate in $Gates.GetEnumerator()) {
129
+ if ($Content -match $Gate.Value) {
130
+ $GatesStatus[$Gate.Key] = "COMPLETED"
131
+ } else {
132
+ $GatesStatus[$Gate.Key] = "PENDING"
133
+ }
134
+ }
135
+
136
+ return $GatesStatus
137
+ }
138
+
139
+ function Analyze-Tasks {
140
+ param([string]$TaskFile)
141
+
142
+ if (-not (Test-Path $TaskFile)) {
143
+ Exit-WithError "Task file does not exist: $TaskFile"
144
+ }
145
+
146
+ $Content = Get-Content $TaskFile -Raw -Encoding UTF8
147
+
148
+ # Count tasks
149
+ $TaskMatches = [regex]::Matches($Content, '### T\d{3}:')
150
+ $TaskCount = $TaskMatches.Count
151
+
152
+ # Count parallel tasks
153
+ $ParallelMatches = [regex]::Matches($Content, '\[P\]')
154
+ $ParallelCount = $ParallelMatches.Count
155
+
156
+ # Check for completion status
157
+ $CompletionSection = $Content -match "## Progress Tracking"
158
+ $CompletedTasks = 0
159
+ $InProgressTasks = 0
160
+ $BlockedTasks = 0
161
+
162
+ if ($CompletionSection) {
163
+ # Look for progress tracking YAML section
164
+ if ($Content -match "completed:\s*(\d+)") {
165
+ $CompletedTasks = [int]$matches[1]
166
+ }
167
+ if ($Content -match "in_progress:\s*(\d+)") {
168
+ $InProgressTasks = [int]$matches[1]
169
+ }
170
+ if ($Content -match "blocked:\s*(\d+)") {
171
+ $BlockedTasks = [int]$matches[1]
172
+ }
173
+ }
174
+
175
+ return @{
176
+ TotalTasks = $TaskCount
177
+ ParallelTasks = $ParallelCount
178
+ CompletedTasks = $CompletedTasks
179
+ InProgressTasks = $InProgressTasks
180
+ BlockedTasks = $BlockedTasks
181
+ }
182
+ }
183
+
184
+ # Main execution
185
+ Write-Log "Processing tasks..."
186
+
187
+ # Get and sanitize feature directory
188
+ $SanitizedDir = Get-CurrentFeatureDir -ProvidedDir $FeatureDir
189
+
190
+ # Set file paths
191
+ $TaskFile = Join-Path $ProjectRoot "tasks" $SanitizedDir "tasks.md"
192
+ $PlanFile = Join-Path $ProjectRoot "plans" $SanitizedDir "plan.md"
193
+ $TemplateFile = Join-Path $TemplatesDir "task.md"
194
+
195
+ # Ensure tasks directory exists
196
+ $TaskDir = Split-Path $TaskFile -Parent
197
+ New-Item -ItemType Directory -Path $TaskDir -Force | Out-Null
198
+
199
+ # Handle task content
200
+ if ($TaskContent) {
201
+ Write-Log "Updating task file: $TaskFile"
202
+ try {
203
+ Set-Content -Path $TaskFile -Value $TaskContent -Encoding UTF8
204
+ } catch {
205
+ Exit-WithError "Failed to write task content: $_"
206
+ }
207
+ } else {
208
+ # Ensure task file exists from template
209
+ if (-not (Test-Path $TaskFile)) {
210
+ if (-not (Test-Path $TemplateFile)) {
211
+ Exit-WithError "Template not found: $TemplateFile"
212
+ }
213
+
214
+ Write-Log "Creating task file from template: $TaskFile"
215
+ try {
216
+ Copy-Item $TemplateFile $TaskFile -Force
217
+ } catch {
218
+ Exit-WithError "Failed to copy task template: $_"
219
+ }
220
+ } else {
221
+ Write-Log "Task file already exists: $TaskFile"
222
+ }
223
+ }
224
+
225
+ # Validate task structure
226
+ Write-Log "Validating task structure..."
227
+ $MissingSections = Validate-TaskStructure -TaskFile $TaskFile
228
+
229
+ # Check constitutional gates
230
+ Write-Log "Checking constitutional gates..."
231
+ $GatesStatus = Check-ConstitutionalGates -TaskFile $TaskFile
232
+
233
+ # Analyze tasks
234
+ Write-Log "Analyzing tasks..."
235
+ $TaskAnalysis = Analyze-Tasks -TaskFile $TaskFile
236
+
237
+ # Calculate completion percentage
238
+ $CompletionPercentage = 0
239
+ if ($TaskAnalysis.TotalTasks -gt 0) {
240
+ $CompletionPercentage = [math]::Round(($TaskAnalysis.CompletedTasks / $TaskAnalysis.TotalTasks) * 100, 2)
241
+ }
242
+
243
+ # Count pending constitutional gates
244
+ $PendingGates = 0
245
+ foreach ($Status in $GatesStatus.Values) {
246
+ if ($Status -eq "PENDING") {
247
+ $PendingGates++
248
+ }
249
+ }
250
+
251
+ Write-Log "Task processing completed successfully"
252
+
253
+ # Output results
254
+ Write-Host "TASK_FILE=$TaskFile"
255
+ Write-Host "TOTAL_TASKS=$($TaskAnalysis.TotalTasks)"
256
+ Write-Host "COMPLETED_TASKS=$($TaskAnalysis.CompletedTasks)"
257
+ Write-Host "IN_PROGRESS_TASKS=$($TaskAnalysis.InProgressTasks)"
258
+ Write-Host "BLOCKED_TASKS=$($TaskAnalysis.BlockedTasks)"
259
+ Write-Host "PARALLEL_TASKS=$($TaskAnalysis.ParallelTasks)"
260
+ Write-Host "COMPLETION_PERCENTAGE=$CompletionPercentage"
261
+ Write-Host "MISSING_SECTIONS=$MissingSections"
262
+ Write-Host "CONSTITUTIONAL_GATES_PENDING=$PendingGates"
263
+ Write-Host "STATUS=processed"
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SpecPulse Task Generation Script
4
+ Cross-platform Python equivalent of pulse-task.sh
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import re
10
+ from pathlib import Path
11
+ import datetime
12
+
13
+ class SpecPulseTask:
14
+ def __init__(self):
15
+ self.script_name = Path(__file__).name
16
+ self.project_root = Path(__file__).parent.parent.parent
17
+ self.memory_dir = self.project_root / "memory"
18
+ self.context_file = self.memory_dir / "context.md"
19
+ self.templates_dir = self.project_root / "resources" / "templates"
20
+
21
+ def log(self, message):
22
+ """Log messages with timestamp"""
23
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
24
+ try:
25
+ print(f"[{timestamp}] {self.script_name}: {message}", file=sys.stderr)
26
+ except UnicodeEncodeError:
27
+ # Fallback for Windows console encoding issues
28
+ message = message.encode('cp1252', errors='replace').decode('cp1252')
29
+ print(f"[{timestamp}] {self.script_name}: {message}", file=sys.stderr)
30
+
31
+ def error_exit(self, message):
32
+ """Exit with error message"""
33
+ self.log(f"ERROR: {message}")
34
+ sys.exit(1)
35
+
36
+ def sanitize_feature_dir(self, feature_dir):
37
+ """Sanitize feature directory name"""
38
+ if not feature_dir:
39
+ self.error_exit("Feature directory cannot be empty")
40
+
41
+ sanitized = re.sub(r'[^a-zA-Z0-9_-]', '', feature_dir)
42
+
43
+ if not sanitized:
44
+ self.error_exit(f"Invalid feature directory: '{feature_dir}'")
45
+
46
+ return sanitized
47
+
48
+ def find_feature_directory(self):
49
+ """Find feature directory from context file"""
50
+ if not self.context_file.exists():
51
+ self.error_exit("No context file found and no feature directory provided")
52
+
53
+ try:
54
+ content = self.context_file.read_text(encoding='utf-8')
55
+ match = re.search(r'Active Feature:\s*(.+)', content)
56
+ if match:
57
+ feature_dir = match.group(1).strip()
58
+ self.log(f"Using active feature from context: {feature_dir}")
59
+ return feature_dir
60
+ else:
61
+ self.error_exit("No active feature found in context file")
62
+ except Exception as e:
63
+ self.error_exit(f"Failed to read context file: {e}")
64
+
65
+ def get_current_feature_dir(self, provided_dir):
66
+ """Get current feature directory"""
67
+ if provided_dir:
68
+ return self.sanitize_feature_dir(provided_dir)
69
+ else:
70
+ return self.find_feature_directory()
71
+
72
+ def analyze_tasks(self, task_file):
73
+ """Analyze task structure and count statuses"""
74
+ if not task_file.exists():
75
+ return {
76
+ 'total': 0,
77
+ 'completed': 0,
78
+ 'pending': 0,
79
+ 'blocked': 0,
80
+ 'parallel': 0
81
+ }
82
+
83
+ content = task_file.read_text(encoding='utf-8')
84
+
85
+ # Count different task states
86
+ total_tasks = len(re.findall(r'^- \[.\]', content, re.MULTILINE))
87
+ completed_tasks = len(re.findall(r'^- \[x\]', content, re.MULTILINE))
88
+ pending_tasks = len(re.findall(r'^- \[ \]', content, re.MULTILINE))
89
+ blocked_tasks = len(re.findall(r'^- \[!\]', content, re.MULTILINE))
90
+ parallel_tasks = len(re.findall(r'\[P\]', content))
91
+
92
+ return {
93
+ 'total': total_tasks,
94
+ 'completed': completed_tasks,
95
+ 'pending': pending_tasks,
96
+ 'blocked': blocked_tasks,
97
+ 'parallel': parallel_tasks
98
+ }
99
+
100
+ def validate_task_structure(self, task_file):
101
+ """Validate task structure"""
102
+ required_sections = [
103
+ "## Task List:",
104
+ "## Task Organization",
105
+ "## Critical Path",
106
+ "## Execution Schedule"
107
+ ]
108
+
109
+ missing_sections = []
110
+ content = task_file.read_text(encoding='utf-8')
111
+
112
+ for section in required_sections:
113
+ if section not in content:
114
+ missing_sections.append(section)
115
+
116
+ if missing_sections:
117
+ self.log(f"WARNING: Missing required sections: {', '.join(missing_sections)}")
118
+
119
+ return len(missing_sections)
120
+
121
+ def check_constitutional_gates(self, task_file):
122
+ """Check constitutional gates compliance"""
123
+ if not task_file.exists():
124
+ return 0
125
+
126
+ content = task_file.read_text(encoding='utf-8')
127
+
128
+ # Find constitutional gates section
129
+ constitutional_section = re.search(
130
+ r'Constitutional Gates Compliance.*?(?=\n##|\Z)',
131
+ content,
132
+ re.DOTALL
133
+ )
134
+
135
+ if constitutional_section:
136
+ gates_text = constitutional_section.group(0)
137
+ pending_gates = len(re.findall(r'\[ \]', gates_text))
138
+ return pending_gates
139
+
140
+ return 0
141
+
142
+ def check_plan_gates_status(self, plan_file):
143
+ """Check if plan has constitutional gates completed"""
144
+ if not plan_file.exists():
145
+ return "UNKNOWN"
146
+
147
+ content = plan_file.read_text(encoding='utf-8')
148
+ gate_status_match = re.search(r'Gate Status:\s*\[([^\]]+)\]', content)
149
+
150
+ if gate_status_match:
151
+ return gate_status_match.group(1)
152
+ else:
153
+ return "UNKNOWN"
154
+
155
+ def main(self, args):
156
+ """Main execution function"""
157
+ if len(args) < 1:
158
+ self.error_exit("Usage: python pulse-task.py <feature-dir>")
159
+
160
+ feature_dir = args[0]
161
+
162
+ self.log("Processing task breakdown...")
163
+
164
+ # Get and sanitize feature directory
165
+ sanitized_dir = self.get_current_feature_dir(feature_dir)
166
+
167
+ # Set file paths
168
+ task_file = self.project_root / "tasks" / sanitized_dir / "tasks.md"
169
+ template_file = self.templates_dir / "task.md"
170
+ plan_file = self.project_root / "plans" / sanitized_dir / "plan.md"
171
+ spec_file = self.project_root / "specs" / sanitized_dir / "spec.md"
172
+
173
+ # Ensure tasks directory exists
174
+ task_file.parent.mkdir(parents=True, exist_ok=True)
175
+
176
+ # Check if specification and plan exist first
177
+ if not spec_file.exists():
178
+ self.error_exit(f"Specification file not found: {spec_file}. Please create specification first.")
179
+
180
+ if not plan_file.exists():
181
+ self.error_exit(f"Implementation plan not found: {plan_file}. Please create plan first.")
182
+
183
+ # Ensure task template exists
184
+ if not template_file.exists():
185
+ self.error_exit(f"Template not found: {template_file}")
186
+
187
+ # Create task file if it doesn't exist
188
+ if not task_file.exists():
189
+ self.log(f"Creating task breakdown from template: {task_file}")
190
+ try:
191
+ task_file.write_text(template_file.read_text(encoding='utf-8'))
192
+ except Exception as e:
193
+ self.error_exit(f"Failed to copy task template: {e}")
194
+ else:
195
+ self.log(f"Task breakdown already exists: {task_file}")
196
+
197
+ # Validate task structure
198
+ self.log("Validating task breakdown...")
199
+ missing_sections = self.validate_task_structure(task_file)
200
+
201
+ # Analyze tasks
202
+ task_stats = self.analyze_tasks(task_file)
203
+
204
+ # Check constitutional gates compliance
205
+ pending_gates = self.check_constitutional_gates(task_file)
206
+
207
+ # Check if plan has constitutional gates completed
208
+ plan_gate_status = self.check_plan_gates_status(plan_file)
209
+
210
+ if plan_gate_status != "COMPLETED":
211
+ self.log(f"WARNING: Implementation plan constitutional gates not completed. Status: {plan_gate_status}")
212
+
213
+ # Calculate completion percentage
214
+ if task_stats['total'] > 0:
215
+ completion_percentage = int((task_stats['completed'] / task_stats['total']) * 100)
216
+ else:
217
+ completion_percentage = 0
218
+
219
+ self.log(f"Task analysis completed - Total: {task_stats['total']}, Completed: {task_stats['completed']} ({completion_percentage}%), Parallel: {task_stats['parallel']}")
220
+
221
+ # Output comprehensive status
222
+ print(f"TASK_FILE={task_file}")
223
+ print(f"SPEC_FILE={spec_file}")
224
+ print(f"PLAN_FILE={plan_file}")
225
+ print(f"TOTAL_TASKS={task_stats['total']}")
226
+ print(f"COMPLETED_TASKS={task_stats['completed']}")
227
+ print(f"PENDING_TASKS={task_stats['pending']}")
228
+ print(f"BLOCKED_TASKS={task_stats['blocked']}")
229
+ print(f"PARALLEL_TASKS={task_stats['parallel']}")
230
+ print(f"CONSTITUTIONAL_GATES_PENDING={pending_gates}")
231
+ print(f"COMPLETION_PERCENTAGE={completion_percentage}")
232
+ print(f"MISSING_SECTIONS={missing_sections}")
233
+ print("STATUS=generated")
234
+
235
+ if __name__ == "__main__":
236
+ task = SpecPulseTask()
237
+ task.main(sys.argv[1:])
@@ -1,23 +1,137 @@
1
1
  #!/bin/bash
2
2
  # Generate task breakdown
3
3
 
4
- FEATURE_DIR="${1}"
4
+ set -euo pipefail # Exit on error, unset vars, pipe failures
5
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-dir>"
24
+ fi
25
+
26
+ FEATURE_DIR="$1"
27
+
28
+ # Sanitize feature directory
29
+ SANITIZED_DIR=$(echo "$FEATURE_DIR" | sed 's/[^a-zA-Z0-9_-]//g')
30
+
31
+ if [ -z "$SANITIZED_DIR" ]; then
32
+ error_exit "Invalid feature directory: '$FEATURE_DIR'"
33
+ fi
34
+
35
+ # Find feature directory if not provided
6
36
  if [ -z "$FEATURE_DIR" ]; then
7
- # Find current feature from context
8
- FEATURE_DIR=$(grep -A1 "Active Feature" memory/context.md | tail -1 | cut -d: -f2 | xargs)
37
+ CONTEXT_FILE="$PROJECT_ROOT/memory/context.md"
38
+ if [ -f "$CONTEXT_FILE" ]; then
39
+ FEATURE_DIR=$(grep -A1 "Active Feature" "$CONTEXT_FILE" | tail -1 | cut -d: -f2 | xargs)
40
+ if [ -z "$FEATURE_DIR" ]; then
41
+ error_exit "No active feature found in context file"
42
+ fi
43
+ log "Using active feature from context: $FEATURE_DIR"
44
+ else
45
+ error_exit "No feature directory provided and no context file found"
46
+ fi
9
47
  fi
10
48
 
11
- TASK_FILE="tasks/${FEATURE_DIR}/tasks.md"
49
+ TASK_FILE="$PROJECT_ROOT/tasks/${FEATURE_DIR}/tasks.md"
50
+ TEMPLATE_FILE="$PROJECT_ROOT/templates/task.md"
51
+ PLAN_FILE="$PROJECT_ROOT/plans/${FEATURE_DIR}/plan.md"
52
+ SPEC_FILE="$PROJECT_ROOT/specs/${FEATURE_DIR}/spec.md"
53
+
54
+ # Ensure tasks directory exists
55
+ mkdir -p "$(dirname "$TASK_FILE")"
56
+
57
+ # Check if specification and plan exist first
58
+ if [ ! -f "$SPEC_FILE" ]; then
59
+ error_exit "Specification file not found: $SPEC_FILE. Please create specification first."
60
+ fi
12
61
 
13
- # Ensure template exists
62
+ if [ ! -f "$PLAN_FILE" ]; then
63
+ error_exit "Implementation plan not found: $PLAN_FILE. Please create plan first."
64
+ fi
65
+
66
+ # Ensure task template exists
67
+ if [ ! -f "$TEMPLATE_FILE" ]; then
68
+ error_exit "Template not found: $TEMPLATE_FILE"
69
+ fi
70
+
71
+ # Create task file if it doesn't exist
14
72
  if [ ! -f "$TASK_FILE" ]; then
15
- cp templates/task.md "$TASK_FILE"
73
+ log "Creating task breakdown from template: $TASK_FILE"
74
+ cp "$TEMPLATE_FILE" "$TASK_FILE" || error_exit "Failed to copy task template"
75
+ else
76
+ log "Task breakdown already exists: $TASK_FILE"
77
+ fi
78
+
79
+ # Validate task structure
80
+ log "Validating task breakdown..."
81
+
82
+ # Check for required sections
83
+ REQUIRED_SECTIONS=("## Task List:" "## Task Organization" "## Critical Path" "## Execution Schedule")
84
+ MISSING_SECTIONS=()
85
+
86
+ for section in "${REQUIRED_SECTIONS[@]}"; do
87
+ if ! grep -q "$section" "$TASK_FILE"; then
88
+ MISSING_SECTIONS+=("$section")
89
+ fi
90
+ done
91
+
92
+ if [ ${#MISSING_SECTIONS[@]} -gt 0 ]; then
93
+ log "WARNING: Missing required sections: ${MISSING_SECTIONS[*]}"
94
+ fi
95
+
96
+ # Count tasks and analyze structure
97
+ TOTAL_TASKS=$(grep -c "^- \[.\]" "$TASK_FILE" 2>/dev/null || echo "0")
98
+ COMPLETED_TASKS=$(grep -c "^- \[x\]" "$TASK_FILE" 2>/dev/null || echo "0")
99
+ PENDING_TASKS=$(grep -c "^- \[ \]" "$TASK_FILE" 2>/dev/null || echo "0")
100
+ BLOCKED_TASKS=$(grep -c "^- \[!\]" "$TASK_FILE" 2>/dev/null || echo "0")
101
+
102
+ # Check for parallel tasks
103
+ PARALLEL_TASKS=$(grep -c "\[P\]" "$TASK_FILE" 2>/dev/null || echo "0")
104
+
105
+ # Check constitutional gates compliance
106
+ CONSTITUTIONAL_SECTION=$(grep -A 20 "Constitutional Gates Compliance" "$TASK_FILE" 2>/dev/null || echo "")
107
+ GATES_COUNT=$(echo "$CONSTITUTIONAL_SECTION" | grep -c "\[ \]" 2>/dev/null || echo "0")
108
+
109
+ # Check if plan has constitutional gates completed
110
+ PLAN_GATE_STATUS=$(grep -A5 "Gate Status:" "$PLAN_FILE" | tail -1 | sed 's/.*\[\(.*\)\].*/\1/' || echo "PENDING")
111
+
112
+ if [ "$PLAN_GATE_STATUS" != "COMPLETED" ]; then
113
+ log "WARNING: Implementation plan constitutional gates not completed. Task generation may be premature."
114
+ fi
115
+
116
+ # Calculate completion percentage
117
+ if [ "$TOTAL_TASKS" -gt 0 ]; then
118
+ COMPLETION_PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
119
+ else
120
+ COMPLETION_PERCENTAGE=0
16
121
  fi
17
122
 
18
- # Count tasks
19
- TASK_COUNT=$(grep -c "^- \[" "$TASK_FILE" 2>/dev/null || echo "0")
123
+ log "Task analysis completed - Total: $TOTAL_TASKS, Completed: $COMPLETED_TASKS ($COMPLETION_PERCENTAGE%), Parallel: $PARALLEL_TASKS"
20
124
 
125
+ # Output comprehensive status
21
126
  echo "TASK_FILE=$TASK_FILE"
22
- echo "TASK_COUNT=$TASK_COUNT"
127
+ echo "SPEC_FILE=$SPEC_FILE"
128
+ echo "PLAN_FILE=$PLAN_FILE"
129
+ echo "TOTAL_TASKS=$TOTAL_TASKS"
130
+ echo "COMPLETED_TASKS=$COMPLETED_TASKS"
131
+ echo "PENDING_TASKS=$PENDING_TASKS"
132
+ echo "BLOCKED_TASKS=$BLOCKED_TASKS"
133
+ echo "PARALLEL_TASKS=$PARALLEL_TASKS"
134
+ echo "CONSTITUTIONAL_GATES_PENDING=$GATES_COUNT"
135
+ echo "COMPLETION_PERCENTAGE=$COMPLETION_PERCENTAGE"
136
+ echo "MISSING_SECTIONS=${#MISSING_SECTIONS[@]}"
23
137
  echo "STATUS=generated"