specpulse 1.0.4__py3-none-any.whl → 1.0.6__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 (34) hide show
  1. specpulse/__init__.py +1 -1
  2. specpulse/cli/main.py +617 -631
  3. specpulse/core/specpulse.py +8 -2
  4. specpulse/resources/commands/claude/plan.md +156 -45
  5. specpulse/resources/commands/claude/pulse.md +63 -24
  6. specpulse/resources/commands/claude/spec.md +110 -31
  7. specpulse/resources/commands/claude/task.md +202 -43
  8. specpulse/resources/commands/gemini/plan.toml +13 -5
  9. specpulse/resources/commands/gemini/pulse.toml +4 -1
  10. specpulse/resources/commands/gemini/spec.toml +12 -4
  11. specpulse/resources/commands/gemini/task.toml +17 -5
  12. specpulse/resources/memory/constitution.md +2 -2
  13. specpulse/resources/scripts/pulse-init.ps1 +186 -0
  14. specpulse/resources/scripts/pulse-init.py +171 -0
  15. specpulse/resources/scripts/pulse-init.sh +80 -21
  16. specpulse/resources/scripts/pulse-plan.ps1 +251 -0
  17. specpulse/resources/scripts/pulse-plan.py +191 -0
  18. specpulse/resources/scripts/pulse-plan.sh +113 -12
  19. specpulse/resources/scripts/pulse-spec.ps1 +185 -0
  20. specpulse/resources/scripts/pulse-spec.py +167 -0
  21. specpulse/resources/scripts/pulse-spec.sh +86 -11
  22. specpulse/resources/scripts/pulse-task.ps1 +263 -0
  23. specpulse/resources/scripts/pulse-task.py +237 -0
  24. specpulse/resources/scripts/pulse-task.sh +123 -9
  25. specpulse/resources/templates/plan.md +142 -287
  26. specpulse/resources/templates/spec.md +80 -246
  27. specpulse/resources/templates/task.md +114 -93
  28. {specpulse-1.0.4.dist-info → specpulse-1.0.6.dist-info}/METADATA +29 -19
  29. specpulse-1.0.6.dist-info/RECORD +41 -0
  30. specpulse-1.0.4.dist-info/RECORD +0 -33
  31. {specpulse-1.0.4.dist-info → specpulse-1.0.6.dist-info}/WHEEL +0 -0
  32. {specpulse-1.0.4.dist-info → specpulse-1.0.6.dist-info}/entry_points.txt +0 -0
  33. {specpulse-1.0.4.dist-info → specpulse-1.0.6.dist-info}/licenses/LICENSE +0 -0
  34. {specpulse-1.0.4.dist-info → specpulse-1.0.6.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SpecPulse Feature Initialization Script
4
+ Cross-platform Python equivalent of pulse-init.sh
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import subprocess
10
+ import re
11
+ import datetime
12
+ from pathlib import Path
13
+
14
+ class SpecPulseInit:
15
+ def __init__(self):
16
+ self.script_name = Path(__file__).name
17
+ self.project_root = Path(__file__).parent.parent.parent
18
+ self.memory_dir = self.project_root / "memory"
19
+ self.context_file = self.memory_dir / "context.md"
20
+ self.templates_dir = self.project_root / "resources" / "templates"
21
+
22
+ def log(self, message):
23
+ """Log messages with timestamp"""
24
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
25
+ print(f"[{timestamp}] {self.script_name}: {message}", file=sys.stderr)
26
+
27
+ def error_exit(self, message):
28
+ """Exit with error message"""
29
+ self.log(f"ERROR: {message}")
30
+ sys.exit(1)
31
+
32
+ def sanitize_feature_name(self, feature_name):
33
+ """Sanitize feature name for safe directory naming"""
34
+ if not feature_name:
35
+ self.error_exit("Feature name cannot be empty")
36
+
37
+ # Convert to lowercase, replace spaces and special chars with hyphens
38
+ sanitized = re.sub(r'[^a-z0-9-]', '-', feature_name.lower())
39
+ sanitized = re.sub(r'-+', '-', sanitized) # Remove consecutive hyphens
40
+ sanitized = sanitized.strip('-') # Remove leading/trailing hyphens
41
+
42
+ if not sanitized:
43
+ self.error_exit(f"Invalid feature name: '{feature_name}'")
44
+
45
+ return sanitized
46
+
47
+ def get_feature_id(self, custom_id=None):
48
+ """Get next feature ID"""
49
+ if custom_id:
50
+ return f"{int(custom_id):03d}"
51
+
52
+ # Find existing feature directories
53
+ specs_dir = self.project_root / "specs"
54
+ if specs_dir.exists():
55
+ existing = [d for d in specs_dir.iterdir() if d.is_dir() and d.name.isdigit()]
56
+ next_id = len(existing) + 1
57
+ else:
58
+ next_id = 1
59
+
60
+ return f"{next_id:03d}"
61
+
62
+ def create_directories(self, branch_name):
63
+ """Create feature directories"""
64
+ dirs_to_create = [
65
+ self.project_root / "specs" / branch_name,
66
+ self.project_root / "plans" / branch_name,
67
+ self.project_root / "tasks" / branch_name
68
+ ]
69
+
70
+ for directory in dirs_to_create:
71
+ try:
72
+ directory.mkdir(parents=True, exist_ok=True)
73
+ self.log(f"Created directory: {directory}")
74
+ except Exception as e:
75
+ self.error_exit(f"Failed to create directory {directory}: {e}")
76
+
77
+ def copy_templates(self, branch_name):
78
+ """Copy templates to feature directories"""
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"
83
+ }
84
+
85
+ for template_name, target_path in templates.items():
86
+ template_path = self.templates_dir / template_name
87
+ if not template_path.exists():
88
+ self.error_exit(f"Template not found: {template_path}")
89
+
90
+ try:
91
+ target_path.write_text(template_path.read_text())
92
+ self.log(f"Copied template to: {target_path}")
93
+ except Exception as e:
94
+ self.error_exit(f"Failed to copy template {template_path}: {e}")
95
+
96
+ def update_context(self, feature_name, feature_id, branch_name):
97
+ """Update context file"""
98
+ try:
99
+ self.memory_dir.mkdir(parents=True, exist_ok=True)
100
+
101
+ context_entry = f"""
102
+
103
+ ## Active Feature: {feature_name}
104
+ - Feature ID: {feature_id}
105
+ - Branch: {branch_name}
106
+ - Started: {datetime.datetime.now().isoformat()}
107
+ """
108
+
109
+ with open(self.context_file, 'a', encoding='utf-8') as f:
110
+ f.write(context_entry)
111
+
112
+ self.log(f"Updated context file: {self.context_file}")
113
+ except Exception as e:
114
+ self.error_exit(f"Failed to update context file: {e}")
115
+
116
+ def create_git_branch(self, branch_name):
117
+ """Create git branch if in git repository"""
118
+ git_dir = self.project_root / ".git"
119
+ if not git_dir.exists():
120
+ return
121
+
122
+ try:
123
+ # Check if branch already exists
124
+ result = subprocess.run(
125
+ ["git", "branch", "--list", branch_name],
126
+ cwd=self.project_root,
127
+ capture_output=True,
128
+ text=True
129
+ )
130
+
131
+ if branch_name in result.stdout:
132
+ self.log(f"Git branch '{branch_name}' already exists, checking out")
133
+ subprocess.run(["git", "checkout", branch_name], cwd=self.project_root, check=True)
134
+ else:
135
+ self.log(f"Creating new git branch '{branch_name}'")
136
+ subprocess.run(["git", "checkout", "-b", branch_name], cwd=self.project_root, check=True)
137
+ except subprocess.CalledProcessError as e:
138
+ self.error_exit(f"Git operation failed: {e}")
139
+
140
+ def main(self, args):
141
+ """Main execution function"""
142
+ if len(args) < 1:
143
+ self.error_exit("Usage: python pulse-init.py <feature-name> [feature-id]")
144
+
145
+ feature_name = args[0]
146
+ custom_id = args[1] if len(args) > 1 else None
147
+
148
+ self.log(f"Initializing feature: {feature_name}")
149
+
150
+ # Sanitize and generate identifiers
151
+ sanitized_name = self.sanitize_feature_name(feature_name)
152
+ feature_id = self.get_feature_id(custom_id)
153
+ branch_name = f"{feature_id}-{sanitized_name}"
154
+
155
+ # Create structure
156
+ self.create_directories(branch_name)
157
+ self.copy_templates(branch_name)
158
+ self.update_context(feature_name, feature_id, branch_name)
159
+ self.create_git_branch(branch_name)
160
+
161
+ # Output results
162
+ print(f"BRANCH_NAME={branch_name}")
163
+ print(f"SPEC_DIR=specs/{branch_name}")
164
+ print(f"FEATURE_ID={feature_id}")
165
+ print("STATUS=initialized")
166
+
167
+ self.log(f"Successfully initialized feature '{feature_name}' with ID {feature_id}")
168
+
169
+ if __name__ == "__main__":
170
+ init = SpecPulseInit()
171
+ init.main(sys.argv[1:])
@@ -1,37 +1,96 @@
1
1
  #!/bin/bash
2
2
  # Initialize a new feature with SpecPulse
3
3
 
4
- FEATURE_NAME="${1:-feature}"
4
+ set -euo pipefail # Exit on error, unset vars, pipe failures
5
5
 
6
- # Get next feature ID
7
- FEATURE_ID=$(printf "%03d" $(ls -d specs/[0-9]* 2>/dev/null | wc -l | xargs expr 1 +))
6
+ # Configuration
7
+ SCRIPT_NAME="$(basename "$0")"
8
+ PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
8
9
 
9
- # Create branch name
10
- BRANCH_NAME="${FEATURE_ID}-${FEATURE_NAME}"
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}"
11
44
 
12
45
  # Create directories
13
- mkdir -p "specs/${BRANCH_NAME}"
14
- mkdir -p "plans/${BRANCH_NAME}"
15
- mkdir -p "tasks/${BRANCH_NAME}"
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"
16
55
 
17
56
  # Create initial files from templates
18
- cp templates/spec.md "specs/${BRANCH_NAME}/spec.md"
19
- cp templates/plan.md "plans/${BRANCH_NAME}/plan.md"
20
- cp templates/task.md "tasks/${BRANCH_NAME}/tasks.md"
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"
21
66
 
22
67
  # Update context
23
- echo "" >> memory/context.md
24
- echo "## Active Feature: ${FEATURE_NAME}" >> memory/context.md
25
- echo "- Feature ID: ${FEATURE_ID}" >> memory/context.md
26
- echo "- Branch: ${BRANCH_NAME}" >> memory/context.md
27
- echo "- Started: $(date -Iseconds)" >> memory/context.md
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"
28
78
 
29
79
  # Create git branch if in git repo
30
- if [ -d .git ]; then
31
- git checkout -b "${BRANCH_NAME}" 2>/dev/null || git checkout "${BRANCH_NAME}"
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
32
89
  fi
33
90
 
34
- echo "BRANCH_NAME=${BRANCH_NAME}"
35
- echo "SPEC_DIR=specs/${BRANCH_NAME}"
36
- echo "FEATURE_ID=${FEATURE_ID}"
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"
37
96
  echo "STATUS=initialized"
@@ -0,0 +1,251 @@
1
+ # SpecPulse Plan Generation Script
2
+ # Cross-platform PowerShell equivalent of pulse-plan.sh
3
+
4
+ param(
5
+ [Parameter(Mandatory=$true)]
6
+ [string]$FeatureDir,
7
+
8
+ [string]$PlanContent = ""
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-PlanStructure {
81
+ param([string]$PlanFile)
82
+
83
+ if (-not (Test-Path $PlanFile)) {
84
+ Exit-WithError "Plan file does not exist: $PlanFile"
85
+ }
86
+
87
+ # Required sections
88
+ $RequiredSections = @(
89
+ "# Implementation Plan:",
90
+ "## Technology Stack",
91
+ "## Architecture Overview",
92
+ "## Implementation Phases"
93
+ )
94
+
95
+ $Content = Get-Content $PlanFile -Raw -Encoding UTF8
96
+ $MissingSections = @()
97
+
98
+ foreach ($Section in $RequiredSections) {
99
+ if ($Content -notmatch [regex]::Escape($Section)) {
100
+ $MissingSections += $Section
101
+ }
102
+ }
103
+
104
+ if ($MissingSections.Count -gt 0) {
105
+ Write-Log "WARNING: Missing required sections: $($MissingSections -join ', ')"
106
+ }
107
+
108
+ return $MissingSections.Count
109
+ }
110
+
111
+ function Check-ConstitutionalGates {
112
+ param([string]$PlanFile)
113
+
114
+ if (-not (Test-Path $PlanFile)) {
115
+ Exit-WithError "Plan file does not exist: $PlanFile"
116
+ }
117
+
118
+ $Content = Get-Content $PlanFile -Raw -Encoding UTF8
119
+ $GatesStatus = @{}
120
+
121
+ # Check for constitutional gates compliance
122
+ $Gates = @{
123
+ "Simplicity Gate" = "Article VII: Simplicity Gate"
124
+ "Anti-Abstraction Gate" = "Article VII: Anti-Abstraction Gate"
125
+ "Test-First Gate" = "Article III: Test-First Gate"
126
+ "Integration-First Gate" = "Article VIII: Integration-First Gate"
127
+ "Research Gate" = "Article VI: Research Gate"
128
+ }
129
+
130
+ foreach ($Gate in $Gates.GetEnumerator()) {
131
+ if ($Content -match $Gate.Value) {
132
+ $GatesStatus[$Gate.Key] = "COMPLETED"
133
+ } else {
134
+ $GatesStatus[$Gate.Key] = "PENDING"
135
+ }
136
+ }
137
+
138
+ return $GatesStatus
139
+ }
140
+
141
+ function Analyze-Complexity {
142
+ param([string]$PlanFile)
143
+
144
+ if (-not (Test-Path $PlanFile)) {
145
+ Exit-WithError "Plan file does not exist: $PlanFile"
146
+ }
147
+
148
+ $Content = Get-Content $PlanFile -Raw -Encoding UTF8
149
+
150
+ # Count complexity exceptions
151
+ $ExceptionMatches = [regex]::Matches($Content, "Complexity Exception")
152
+ $ExceptionCount = $ExceptionMatches.Count
153
+
154
+ # Check for complexity tracking
155
+ $HasComplexityTracking = $Content -match "Complexity Tracking"
156
+
157
+ # Count modules/projects mentioned
158
+ $ModuleMatches = [regex]::Matches($Content, "(?i)(module|project)s?\s*[:\-]?\s*\d+")
159
+ $ModuleCount = 0
160
+ if ($ModuleMatches.Count -gt 0) {
161
+ foreach ($Match in $ModuleMatches) {
162
+ if ($Match.Value -match "\d+") {
163
+ $ModuleCount = [int]$matches[0]
164
+ break
165
+ }
166
+ }
167
+ }
168
+
169
+ return @{
170
+ ExceptionCount = $ExceptionCount
171
+ HasComplexityTracking = $HasComplexityTracking
172
+ ModuleCount = $ModuleCount
173
+ }
174
+ }
175
+
176
+ # Main execution
177
+ Write-Log "Processing plan..."
178
+
179
+ # Get and sanitize feature directory
180
+ $SanitizedDir = Get-CurrentFeatureDir -ProvidedDir $FeatureDir
181
+
182
+ # Set file paths
183
+ $PlanFile = Join-Path $ProjectRoot "plans" $SanitizedDir "plan.md"
184
+ $SpecFile = Join-Path $ProjectRoot "specs" $SanitizedDir "spec.md"
185
+ $TemplateFile = Join-Path $TemplatesDir "plan.md"
186
+
187
+ # Ensure plans directory exists
188
+ $PlanDir = Split-Path $PlanFile -Parent
189
+ New-Item -ItemType Directory -Path $PlanDir -Force | Out-Null
190
+
191
+ # Handle plan content
192
+ if ($PlanContent) {
193
+ Write-Log "Updating plan file: $PlanFile"
194
+ try {
195
+ Set-Content -Path $PlanFile -Value $PlanContent -Encoding UTF8
196
+ } catch {
197
+ Exit-WithError "Failed to write plan content: $_"
198
+ }
199
+ } else {
200
+ # Ensure plan file exists from template
201
+ if (-not (Test-Path $PlanFile)) {
202
+ if (-not (Test-Path $TemplateFile)) {
203
+ Exit-WithError "Template not found: $TemplateFile"
204
+ }
205
+
206
+ Write-Log "Creating plan file from template: $PlanFile"
207
+ try {
208
+ Copy-Item $TemplateFile $PlanFile -Force
209
+ } catch {
210
+ Exit-WithError "Failed to copy plan template: $_"
211
+ }
212
+ } else {
213
+ Write-Log "Plan file already exists: $PlanFile"
214
+ }
215
+ }
216
+
217
+ # Validate plan structure
218
+ Write-Log "Validating plan structure..."
219
+ $MissingSections = Validate-PlanStructure -PlanFile $PlanFile
220
+
221
+ # Check constitutional gates
222
+ Write-Log "Checking constitutional gates..."
223
+ $GatesStatus = Check-ConstitutionalGates -PlanFile $PlanFile
224
+
225
+ # Analyze complexity
226
+ Write-Log "Analyzing complexity..."
227
+ $ComplexityAnalysis = Analyze-Complexity -PlanFile $PlanFile
228
+
229
+ # Count completed constitutional gates
230
+ $CompletedGates = 0
231
+ foreach ($Status in $GatesStatus.Values) {
232
+ if ($Status -eq "COMPLETED") {
233
+ $CompletedGates++
234
+ }
235
+ }
236
+
237
+ # Check if specification exists
238
+ $SpecExists = Test-Path $SpecFile
239
+
240
+ Write-Log "Plan processing completed successfully"
241
+
242
+ # Output results
243
+ Write-Host "PLAN_FILE=$PlanFile"
244
+ Write-Host "SPEC_EXISTS=$SpecExists"
245
+ Write-Host "MISSING_SECTIONS=$MissingSections"
246
+ Write-Host "COMPLETED_GATES=$CompletedGates"
247
+ Write-Host "TOTAL_GATES=$($GatesStatus.Count)"
248
+ Write-Host "COMPLEXITY_EXCEPTIONS=$($ComplexityAnalysis.ExceptionCount)"
249
+ Write-Host "HAS_COMPLEXITY_TRACKING=$($ComplexityAnalysis.HasComplexityTracking)"
250
+ Write-Host "MODULE_COUNT=$($ComplexityAnalysis.ModuleCount)"
251
+ Write-Host "STATUS=processed"