specpulse 1.0.6__py3-none-any.whl → 1.1.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.
- specpulse/__init__.py +1 -1
- specpulse/cli/main.py +598 -617
- specpulse/core/specpulse.py +1110 -1105
- specpulse/resources/commands/claude/sp-continue.md +203 -0
- specpulse/resources/commands/claude/{plan.md → sp-plan.md} +53 -38
- specpulse/resources/commands/claude/sp-pulse.md +142 -0
- specpulse/resources/commands/claude/{spec.md → sp-spec.md} +36 -23
- specpulse/resources/commands/claude/sp-status.md +170 -0
- specpulse/resources/commands/claude/{task.md → sp-task.md} +68 -48
- specpulse/resources/commands/gemini/sp-continue.toml +56 -0
- specpulse/resources/commands/gemini/sp-plan.toml +68 -0
- specpulse/resources/commands/gemini/{pulse.toml → sp-pulse.toml} +12 -6
- specpulse/resources/commands/gemini/sp-spec.toml +54 -0
- specpulse/resources/commands/gemini/sp-status.toml +61 -0
- specpulse/resources/commands/gemini/sp-task.toml +79 -0
- specpulse/resources/memory/constitution.md +5 -5
- specpulse/resources/memory/context.md +12 -1
- specpulse/resources/scripts/{pulse-init.py → sp-pulse-init.py} +6 -6
- specpulse/resources/scripts/{pulse-init.sh → sp-pulse-init.sh} +95 -95
- specpulse/resources/scripts/{pulse-plan.py → sp-pulse-plan.py} +32 -7
- specpulse/resources/scripts/{pulse-plan.sh → sp-pulse-plan.sh} +136 -126
- specpulse/resources/scripts/{pulse-spec.py → sp-pulse-spec.py} +26 -6
- specpulse/resources/scripts/{pulse-spec.sh → sp-pulse-spec.sh} +126 -103
- specpulse/resources/scripts/{pulse-task.py → sp-pulse-task.py} +42 -6
- specpulse/resources/scripts/{pulse-task.sh → sp-pulse-task.sh} +32 -16
- specpulse/resources/templates/plan.md +206 -206
- specpulse/resources/templates/spec.md +125 -125
- specpulse/resources/templates/task.md +164 -163
- {specpulse-1.0.6.dist-info → specpulse-1.1.0.dist-info}/METADATA +35 -35
- specpulse-1.1.0.dist-info/RECORD +41 -0
- specpulse/resources/commands/claude/pulse.md +0 -91
- specpulse/resources/commands/gemini/plan.toml +0 -59
- specpulse/resources/commands/gemini/spec.toml +0 -45
- specpulse/resources/commands/gemini/task.toml +0 -69
- specpulse/resources/scripts/pulse-init.ps1 +0 -186
- specpulse/resources/scripts/pulse-plan.ps1 +0 -251
- specpulse/resources/scripts/pulse-spec.ps1 +0 -185
- specpulse/resources/scripts/pulse-task.ps1 +0 -263
- specpulse-1.0.6.dist-info/RECORD +0 -41
- {specpulse-1.0.6.dist-info → specpulse-1.1.0.dist-info}/WHEEL +0 -0
- {specpulse-1.0.6.dist-info → specpulse-1.1.0.dist-info}/entry_points.txt +0 -0
- {specpulse-1.0.6.dist-info → specpulse-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {specpulse-1.0.6.dist-info → specpulse-1.1.0.dist-info}/top_level.txt +0 -0
@@ -1,251 +0,0 @@
|
|
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"
|
@@ -1,185 +0,0 @@
|
|
1
|
-
# SpecPulse Specification Generation Script
|
2
|
-
# Cross-platform PowerShell equivalent of pulse-spec.sh
|
3
|
-
|
4
|
-
param(
|
5
|
-
[Parameter(Mandatory=$true)]
|
6
|
-
[string]$FeatureDir,
|
7
|
-
|
8
|
-
[string]$SpecContent = ""
|
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-Specification {
|
81
|
-
param([string]$SpecFile)
|
82
|
-
|
83
|
-
if (-not (Test-Path $SpecFile)) {
|
84
|
-
Exit-WithError "Specification file does not exist: $SpecFile"
|
85
|
-
}
|
86
|
-
|
87
|
-
# Required sections
|
88
|
-
$RequiredSections = @(
|
89
|
-
"## Specification:",
|
90
|
-
"## Metadata",
|
91
|
-
"## Functional Requirements",
|
92
|
-
"## Acceptance Scenarios"
|
93
|
-
)
|
94
|
-
|
95
|
-
$Content = Get-Content $SpecFile -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
|
-
# Check for clarifications needed
|
109
|
-
$ClarificationMatches = [regex]::Matches($Content, 'NEEDS CLARIFICATION')
|
110
|
-
if ($ClarificationMatches.Count -gt 0) {
|
111
|
-
$ClarificationCount = $ClarificationMatches.Count
|
112
|
-
Write-Log "WARNING: Specification has $ClarificationCount clarifications needed"
|
113
|
-
return $ClarificationCount
|
114
|
-
}
|
115
|
-
|
116
|
-
return 0
|
117
|
-
}
|
118
|
-
|
119
|
-
# Main execution
|
120
|
-
Write-Log "Processing specification..."
|
121
|
-
|
122
|
-
# Get and sanitize feature directory
|
123
|
-
$SanitizedDir = Get-CurrentFeatureDir -ProvidedDir $FeatureDir
|
124
|
-
|
125
|
-
# Set file paths
|
126
|
-
$SpecFile = Join-Path $ProjectRoot "specs" $SanitizedDir "spec.md"
|
127
|
-
$TemplateFile = Join-Path $TemplatesDir "spec.md"
|
128
|
-
|
129
|
-
# Ensure specs directory exists
|
130
|
-
$SpecDir = Split-Path $SpecFile -Parent
|
131
|
-
New-Item -ItemType Directory -Path $SpecDir -Force | Out-Null
|
132
|
-
|
133
|
-
# Handle specification content
|
134
|
-
if ($SpecContent) {
|
135
|
-
Write-Log "Updating specification: $SpecFile"
|
136
|
-
try {
|
137
|
-
Set-Content -Path $SpecFile -Value $SpecContent -Encoding UTF8
|
138
|
-
} catch {
|
139
|
-
Exit-WithError "Failed to write specification content: $_"
|
140
|
-
}
|
141
|
-
} else {
|
142
|
-
# Ensure specification exists from template
|
143
|
-
if (-not (Test-Path $SpecFile)) {
|
144
|
-
if (-not (Test-Path $TemplateFile)) {
|
145
|
-
Exit-WithError "Template not found: $TemplateFile"
|
146
|
-
}
|
147
|
-
|
148
|
-
Write-Log "Creating specification from template: $SpecFile"
|
149
|
-
try {
|
150
|
-
Copy-Item $TemplateFile $SpecFile -Force
|
151
|
-
} catch {
|
152
|
-
Exit-WithError "Failed to copy specification template: $_"
|
153
|
-
}
|
154
|
-
} else {
|
155
|
-
Write-Log "Specification already exists: $SpecFile"
|
156
|
-
}
|
157
|
-
}
|
158
|
-
|
159
|
-
# Validate specification
|
160
|
-
Write-Log "Validating specification..."
|
161
|
-
$ClarificationCount = Validate-Specification -SpecFile $SpecFile
|
162
|
-
|
163
|
-
# Check for missing sections
|
164
|
-
$Content = Get-Content $SpecFile -Raw -Encoding UTF8
|
165
|
-
$RequiredSections = @(
|
166
|
-
"## Specification:",
|
167
|
-
"## Metadata",
|
168
|
-
"## Functional Requirements",
|
169
|
-
"## Acceptance Scenarios"
|
170
|
-
)
|
171
|
-
|
172
|
-
$MissingSections = @()
|
173
|
-
foreach ($Section in $RequiredSections) {
|
174
|
-
if ($Content -notmatch [regex]::Escape($Section)) {
|
175
|
-
$MissingSections += $Section
|
176
|
-
}
|
177
|
-
}
|
178
|
-
|
179
|
-
Write-Log "Specification processing completed successfully"
|
180
|
-
|
181
|
-
# Output results
|
182
|
-
Write-Host "SPEC_FILE=$SpecFile"
|
183
|
-
Write-Host "CLARIFICATIONS_NEEDED=$ClarificationCount"
|
184
|
-
Write-Host "MISSING_SECTIONS=$($MissingSections.Count)"
|
185
|
-
Write-Host "STATUS=updated"
|
@@ -1,263 +0,0 @@
|
|
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"
|