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.
- specpulse/__init__.py +1 -1
- specpulse/resources/commands/claude/plan.md +180 -50
- specpulse/resources/commands/claude/pulse.md +79 -24
- specpulse/resources/commands/claude/spec.md +156 -43
- specpulse/resources/commands/claude/task.md +229 -48
- specpulse/resources/commands/gemini/plan.toml +59 -48
- specpulse/resources/commands/gemini/pulse.toml +21 -39
- specpulse/resources/commands/gemini/spec.toml +40 -28
- specpulse/resources/commands/gemini/task.toml +69 -51
- specpulse/resources/memory/constitution.md +2 -2
- specpulse/resources/scripts/pulse-init.ps1 +186 -0
- specpulse/resources/scripts/pulse-init.py +171 -0
- specpulse/resources/scripts/pulse-init.sh +80 -21
- specpulse/resources/scripts/pulse-plan.ps1 +251 -0
- specpulse/resources/scripts/pulse-plan.py +191 -0
- specpulse/resources/scripts/pulse-plan.sh +113 -12
- specpulse/resources/scripts/pulse-spec.ps1 +185 -0
- specpulse/resources/scripts/pulse-spec.py +167 -0
- specpulse/resources/scripts/pulse-spec.sh +86 -11
- specpulse/resources/scripts/pulse-task.ps1 +263 -0
- specpulse/resources/scripts/pulse-task.py +237 -0
- specpulse/resources/scripts/pulse-task.sh +123 -9
- specpulse/resources/templates/plan.md +142 -287
- specpulse/resources/templates/spec.md +80 -246
- specpulse/resources/templates/task.md +114 -93
- {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/METADATA +67 -34
- specpulse-1.0.5.dist-info/RECORD +41 -0
- specpulse-1.0.3.dist-info/RECORD +0 -33
- {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/WHEEL +0 -0
- {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/entry_points.txt +0 -0
- {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {specpulse-1.0.3.dist-info → specpulse-1.0.5.dist-info}/top_level.txt +0 -0
@@ -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"
|
@@ -0,0 +1,191 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
SpecPulse Plan Generation Script
|
4
|
+
Cross-platform Python equivalent of pulse-plan.sh
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
import re
|
10
|
+
from pathlib import Path
|
11
|
+
import datetime
|
12
|
+
|
13
|
+
class SpecPulsePlan:
|
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 validate_plan_structure(self, plan_file):
|
73
|
+
"""Validate plan structure"""
|
74
|
+
required_sections = [
|
75
|
+
"## Implementation Plan:",
|
76
|
+
"## Specification Reference",
|
77
|
+
"## Phase -1: Pre-Implementation Gates",
|
78
|
+
"## Implementation Phases"
|
79
|
+
]
|
80
|
+
|
81
|
+
missing_sections = []
|
82
|
+
content = plan_file.read_text(encoding='utf-8')
|
83
|
+
|
84
|
+
for section in required_sections:
|
85
|
+
if section not in content:
|
86
|
+
missing_sections.append(section)
|
87
|
+
|
88
|
+
if missing_sections:
|
89
|
+
self.log(f"WARNING: Missing required sections: {', '.join(missing_sections)}")
|
90
|
+
|
91
|
+
return len(missing_sections)
|
92
|
+
|
93
|
+
def check_constitutional_gates(self, plan_file):
|
94
|
+
"""Check constitutional gates"""
|
95
|
+
constitutional_gates = [
|
96
|
+
"Simplicity Gate",
|
97
|
+
"Anti-Abstraction Gate",
|
98
|
+
"Test-First Gate",
|
99
|
+
"Integration-First Gate",
|
100
|
+
"Research Gate"
|
101
|
+
]
|
102
|
+
|
103
|
+
missing_gates = []
|
104
|
+
content = plan_file.read_text(encoding='utf-8')
|
105
|
+
|
106
|
+
for gate in constitutional_gates:
|
107
|
+
if gate not in content:
|
108
|
+
missing_gates.append(gate)
|
109
|
+
|
110
|
+
if missing_gates:
|
111
|
+
self.log(f"WARNING: Missing constitutional gates: {', '.join(missing_gates)}")
|
112
|
+
|
113
|
+
# Check gate status
|
114
|
+
gate_status_match = re.search(r'Gate Status:\s*\[([^\]]+)\]', content)
|
115
|
+
if gate_status_match:
|
116
|
+
gate_status = gate_status_match.group(1)
|
117
|
+
if gate_status.upper() != "COMPLETED":
|
118
|
+
self.log(f"WARNING: Constitutional gates not completed. Status: {gate_status}")
|
119
|
+
return gate_status
|
120
|
+
else:
|
121
|
+
self.log("WARNING: Constitutional gates status not found")
|
122
|
+
return "UNKNOWN"
|
123
|
+
|
124
|
+
return "COMPLETED"
|
125
|
+
|
126
|
+
def main(self, args):
|
127
|
+
"""Main execution function"""
|
128
|
+
if len(args) < 1:
|
129
|
+
self.error_exit("Usage: python pulse-plan.py <feature-dir>")
|
130
|
+
|
131
|
+
feature_dir = args[0]
|
132
|
+
|
133
|
+
self.log("Processing implementation plan...")
|
134
|
+
|
135
|
+
# Get and sanitize feature directory
|
136
|
+
sanitized_dir = self.get_current_feature_dir(feature_dir)
|
137
|
+
|
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"
|
142
|
+
|
143
|
+
# Ensure plans directory exists
|
144
|
+
plan_file.parent.mkdir(parents=True, exist_ok=True)
|
145
|
+
|
146
|
+
# Check if specification exists first
|
147
|
+
if not spec_file.exists():
|
148
|
+
self.error_exit(f"Specification file not found: {spec_file}. Please create specification first.")
|
149
|
+
|
150
|
+
# Ensure plan template exists
|
151
|
+
if not template_file.exists():
|
152
|
+
self.error_exit(f"Template not found: {template_file}")
|
153
|
+
|
154
|
+
# Create plan if it doesn't exist
|
155
|
+
if not plan_file.exists():
|
156
|
+
self.log(f"Creating implementation plan from template: {plan_file}")
|
157
|
+
try:
|
158
|
+
plan_file.write_text(template_file.read_text(encoding='utf-8'))
|
159
|
+
except Exception as e:
|
160
|
+
self.error_exit(f"Failed to copy plan template: {e}")
|
161
|
+
else:
|
162
|
+
self.log(f"Implementation plan already exists: {plan_file}")
|
163
|
+
|
164
|
+
# Validate plan structure
|
165
|
+
self.log("Validating implementation plan...")
|
166
|
+
missing_sections = self.validate_plan_structure(plan_file)
|
167
|
+
|
168
|
+
# Check Constitutional Gates
|
169
|
+
self.log("Checking Constitutional Gates...")
|
170
|
+
gate_status = self.check_constitutional_gates(plan_file)
|
171
|
+
|
172
|
+
# Check if specification has clarifications needed
|
173
|
+
if spec_file.exists():
|
174
|
+
spec_content = spec_file.read_text(encoding='utf-8')
|
175
|
+
clarification_matches = re.findall(r'NEEDS CLARIFICATION', spec_content)
|
176
|
+
if clarification_matches:
|
177
|
+
clarification_count = len(clarification_matches)
|
178
|
+
self.log(f"WARNING: Specification has {clarification_count} clarifications needed - resolve before proceeding")
|
179
|
+
|
180
|
+
self.log("Implementation plan processing completed successfully")
|
181
|
+
|
182
|
+
# Output results
|
183
|
+
print(f"PLAN_FILE={plan_file}")
|
184
|
+
print(f"SPEC_FILE={spec_file}")
|
185
|
+
print(f"MISSING_SECTIONS={missing_sections}")
|
186
|
+
print(f"CONSTITUTIONAL_GATES_STATUS={gate_status}")
|
187
|
+
print("STATUS=ready")
|
188
|
+
|
189
|
+
if __name__ == "__main__":
|
190
|
+
plan = SpecPulsePlan()
|
191
|
+
plan.main(sys.argv[1:])
|
@@ -1,26 +1,127 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
# Generate implementation plan
|
3
3
|
|
4
|
-
|
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
|
-
|
8
|
-
|
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
|
-
PLAN_FILE="plans/${FEATURE_DIR}/plan.md"
|
49
|
+
PLAN_FILE="$PROJECT_ROOT/plans/${FEATURE_DIR}/plan.md"
|
50
|
+
TEMPLATE_FILE="$PROJECT_ROOT/templates/plan.md"
|
51
|
+
SPEC_FILE="$PROJECT_ROOT/specs/${FEATURE_DIR}/spec.md"
|
52
|
+
|
53
|
+
# Ensure plans directory exists
|
54
|
+
mkdir -p "$(dirname "$PLAN_FILE")"
|
55
|
+
|
56
|
+
# Check if specification exists first
|
57
|
+
if [ ! -f "$SPEC_FILE" ]; then
|
58
|
+
error_exit "Specification file not found: $SPEC_FILE. Please create specification first."
|
59
|
+
fi
|
60
|
+
|
61
|
+
# Ensure plan template exists
|
62
|
+
if [ ! -f "$TEMPLATE_FILE" ]; then
|
63
|
+
error_exit "Template not found: $TEMPLATE_FILE"
|
64
|
+
fi
|
12
65
|
|
13
|
-
#
|
66
|
+
# Create plan if it doesn't exist
|
14
67
|
if [ ! -f "$PLAN_FILE" ]; then
|
15
|
-
|
68
|
+
log "Creating implementation plan from template: $PLAN_FILE"
|
69
|
+
cp "$TEMPLATE_FILE" "$PLAN_FILE" || error_exit "Failed to copy plan template"
|
70
|
+
else
|
71
|
+
log "Implementation plan already exists: $PLAN_FILE"
|
72
|
+
fi
|
73
|
+
|
74
|
+
# Validate plan structure
|
75
|
+
log "Validating implementation plan..."
|
76
|
+
|
77
|
+
# Check for required sections
|
78
|
+
REQUIRED_SECTIONS=("## Implementation Plan:" "## Specification Reference" "## Phase -1: Pre-Implementation Gates" "## Implementation Phases")
|
79
|
+
MISSING_SECTIONS=()
|
80
|
+
|
81
|
+
for section in "${REQUIRED_SECTIONS[@]}"; do
|
82
|
+
if ! grep -q "$section" "$PLAN_FILE"; then
|
83
|
+
MISSING_SECTIONS+=("$section")
|
84
|
+
fi
|
85
|
+
done
|
86
|
+
|
87
|
+
if [ ${#MISSING_SECTIONS[@]} -gt 0 ]; then
|
88
|
+
log "WARNING: Missing required sections: ${MISSING_SECTIONS[*]}"
|
89
|
+
fi
|
90
|
+
|
91
|
+
# Check Constitutional Gates
|
92
|
+
log "Checking Constitutional Gates..."
|
93
|
+
|
94
|
+
CONSTITUTIONAL_GATES=(
|
95
|
+
"Simplicity Gate"
|
96
|
+
"Anti-Abstraction Gate"
|
97
|
+
"Test-First Gate"
|
98
|
+
"Integration-First Gate"
|
99
|
+
"Research Gate"
|
100
|
+
)
|
101
|
+
|
102
|
+
for gate in "${CONSTITUTIONAL_GATES[@]}"; do
|
103
|
+
if ! grep -q "$gate" "$PLAN_FILE"; then
|
104
|
+
log "WARNING: Missing constitutional gate: $gate"
|
105
|
+
fi
|
106
|
+
done
|
107
|
+
|
108
|
+
# Check if specification has clarifications needed
|
109
|
+
if grep -q "NEEDS CLARIFICATION" "$SPEC_FILE"; then
|
110
|
+
CLARIFICATION_COUNT=$(grep -c "NEEDS CLARIFICATION" "$SPEC_FILE")
|
111
|
+
log "WARNING: Specification has $CLARIFICATION_COUNT clarifications needed - resolve before proceeding"
|
112
|
+
fi
|
113
|
+
|
114
|
+
# Validate gate compliance
|
115
|
+
GATE_STATUS=$(grep -A5 "Gate Status:" "$PLAN_FILE" | tail -1 | sed 's/.*\[\(.*\)\].*/\1/' || echo "PENDING")
|
116
|
+
|
117
|
+
if [ "$GATE_STATUS" != "COMPLETED" ]; then
|
118
|
+
log "WARNING: Constitutional gates not completed. Status: $GATE_STATUS"
|
16
119
|
fi
|
17
120
|
|
18
|
-
|
19
|
-
echo "Checking Phase Gates..."
|
20
|
-
echo "- Constitutional compliance"
|
21
|
-
echo "- Simplicity check (≤3 modules)"
|
22
|
-
echo "- Test-first strategy"
|
23
|
-
echo "- Framework selection"
|
121
|
+
log "Implementation plan processing completed successfully"
|
24
122
|
|
25
123
|
echo "PLAN_FILE=$PLAN_FILE"
|
124
|
+
echo "SPEC_FILE=$SPEC_FILE"
|
125
|
+
echo "MISSING_SECTIONS=${#MISSING_SECTIONS[@]}"
|
126
|
+
echo "CONSTITUTIONAL_GATES_STATUS=$GATE_STATUS"
|
26
127
|
echo "STATUS=ready"
|