specpulse 1.0.6__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- specpulse/__init__.py +1 -1
- specpulse/cli/main.py +809 -617
- specpulse/core/specpulse.py +1140 -1105
- specpulse/resources/commands/claude/sp-continue.md +203 -0
- specpulse/resources/commands/claude/sp-decompose.md +227 -0
- specpulse/resources/commands/claude/sp-plan.md +220 -0
- 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/sp-task.md +315 -0
- specpulse/resources/commands/gemini/sp-continue.toml +56 -0
- specpulse/resources/commands/gemini/sp-decompose.toml +54 -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 +53 -1
- specpulse/resources/scripts/sp-pulse-decompose.py +66 -0
- specpulse/resources/scripts/sp-pulse-decompose.sh +56 -0
- 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/decomposition/api-contract.yaml +22 -0
- specpulse/resources/templates/decomposition/integration-plan.md +134 -0
- specpulse/resources/templates/decomposition/interface.ts +20 -0
- specpulse/resources/templates/decomposition/microservices.md +34 -0
- specpulse/resources/templates/decomposition/service-plan.md +168 -0
- 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.2.0.dist-info}/METADATA +95 -36
- specpulse-1.2.0.dist-info/RECORD +50 -0
- specpulse/resources/commands/claude/plan.md +0 -184
- specpulse/resources/commands/claude/pulse.md +0 -91
- specpulse/resources/commands/claude/task.md +0 -237
- 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.2.0.dist-info}/WHEEL +0 -0
- {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/entry_points.txt +0 -0
- {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {specpulse-1.0.6.dist-info → specpulse-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,127 +1,137 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
# Generate implementation plan
|
3
|
-
|
4
|
-
set -euo pipefail # Exit on error, unset vars, pipe failures
|
5
|
-
|
6
|
-
# Configuration
|
7
|
-
SCRIPT_NAME="$(basename "$0")"
|
8
|
-
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
9
|
-
|
10
|
-
# Function to log messages
|
11
|
-
log() {
|
12
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $SCRIPT_NAME: $1" >&2
|
13
|
-
}
|
14
|
-
|
15
|
-
# Function to handle errors
|
16
|
-
error_exit() {
|
17
|
-
log "ERROR: $1"
|
18
|
-
exit 1
|
19
|
-
}
|
20
|
-
|
21
|
-
# Validate arguments
|
22
|
-
if [ $# -eq 0 ]; then
|
23
|
-
error_exit "Usage: $SCRIPT_NAME <feature-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
|
36
|
-
if [ -z "$FEATURE_DIR" ]; then
|
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
|
47
|
-
fi
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# Ensure plans directory exists
|
54
|
-
mkdir -p "$
|
55
|
-
|
56
|
-
#
|
57
|
-
if [
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
error_exit "
|
64
|
-
fi
|
65
|
-
|
66
|
-
#
|
67
|
-
if [
|
68
|
-
|
69
|
-
|
70
|
-
else
|
71
|
-
|
72
|
-
fi
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
"
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
log "
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
1
|
+
#!/bin/bash
|
2
|
+
# Generate implementation plan
|
3
|
+
|
4
|
+
set -euo pipefail # Exit on error, unset vars, pipe failures
|
5
|
+
|
6
|
+
# Configuration
|
7
|
+
SCRIPT_NAME="$(basename "$0")"
|
8
|
+
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
9
|
+
|
10
|
+
# Function to log messages
|
11
|
+
log() {
|
12
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $SCRIPT_NAME: $1" >&2
|
13
|
+
}
|
14
|
+
|
15
|
+
# Function to handle errors
|
16
|
+
error_exit() {
|
17
|
+
log "ERROR: $1"
|
18
|
+
exit 1
|
19
|
+
}
|
20
|
+
|
21
|
+
# Validate arguments
|
22
|
+
if [ $# -eq 0 ]; then
|
23
|
+
error_exit "Usage: $SCRIPT_NAME <feature-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
|
36
|
+
if [ -z "$FEATURE_DIR" ]; then
|
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
|
47
|
+
fi
|
48
|
+
|
49
|
+
PLAN_DIR="$PROJECT_ROOT/plans/${FEATURE_DIR}"
|
50
|
+
SPEC_DIR="$PROJECT_ROOT/specs/${FEATURE_DIR}"
|
51
|
+
TEMPLATE_FILE="$PROJECT_ROOT/templates/plan-001.md"
|
52
|
+
|
53
|
+
# Ensure plans directory exists
|
54
|
+
mkdir -p "$PLAN_DIR"
|
55
|
+
|
56
|
+
# Find latest spec file
|
57
|
+
if [ -d "$SPEC_DIR" ]; then
|
58
|
+
SPEC_FILE=$(find "$SPEC_DIR" -name "spec-*.md" -printf "%T@ %p\n" | sort -n | tail -1 | cut -d' ' -f2-)
|
59
|
+
if [ -z "$SPEC_FILE" ]; then
|
60
|
+
error_exit "No specification files found in $SPEC_DIR. Please create specification first."
|
61
|
+
fi
|
62
|
+
else
|
63
|
+
error_exit "Specifications directory not found: $SPEC_DIR. Please create specification first."
|
64
|
+
fi
|
65
|
+
|
66
|
+
# Find next available plan number or create new one
|
67
|
+
if [ -d "$PLAN_DIR" ]; then
|
68
|
+
existing_plans=$(find "$PLAN_DIR" -name "plan-*.md" | wc -l)
|
69
|
+
plan_number=$((existing_plans + 1))
|
70
|
+
else
|
71
|
+
plan_number=1
|
72
|
+
fi
|
73
|
+
PLAN_FILE="$PLAN_DIR/plan-$(printf "%03d" $plan_number).md"
|
74
|
+
|
75
|
+
# Ensure plan template exists
|
76
|
+
if [ ! -f "$TEMPLATE_FILE" ]; then
|
77
|
+
error_exit "Template not found: $TEMPLATE_FILE"
|
78
|
+
fi
|
79
|
+
|
80
|
+
# Create plan
|
81
|
+
log "Creating implementation plan from template: $PLAN_FILE"
|
82
|
+
cp "$TEMPLATE_FILE" "$PLAN_FILE" || error_exit "Failed to copy plan template"
|
83
|
+
|
84
|
+
# Validate plan structure
|
85
|
+
log "Validating implementation plan..."
|
86
|
+
|
87
|
+
# Check for required sections
|
88
|
+
REQUIRED_SECTIONS=("## Implementation Plan:" "## Specification Reference" "## Phase -1: Pre-Implementation Gates" "## Implementation Phases")
|
89
|
+
MISSING_SECTIONS=()
|
90
|
+
|
91
|
+
for section in "${REQUIRED_SECTIONS[@]}"; do
|
92
|
+
if ! grep -q "$section" "$PLAN_FILE"; then
|
93
|
+
MISSING_SECTIONS+=("$section")
|
94
|
+
fi
|
95
|
+
done
|
96
|
+
|
97
|
+
if [ ${#MISSING_SECTIONS[@]} -gt 0 ]; then
|
98
|
+
log "WARNING: Missing required sections: ${MISSING_SECTIONS[*]}"
|
99
|
+
fi
|
100
|
+
|
101
|
+
# Check Constitutional Gates
|
102
|
+
log "Checking Constitutional Gates..."
|
103
|
+
|
104
|
+
CONSTITUTIONAL_GATES=(
|
105
|
+
"Simplicity Gate"
|
106
|
+
"Anti-Abstraction Gate"
|
107
|
+
"Test-First Gate"
|
108
|
+
"Integration-First Gate"
|
109
|
+
"Research Gate"
|
110
|
+
)
|
111
|
+
|
112
|
+
for gate in "${CONSTITUTIONAL_GATES[@]}"; do
|
113
|
+
if ! grep -q "$gate" "$PLAN_FILE"; then
|
114
|
+
log "WARNING: Missing constitutional gate: $gate"
|
115
|
+
fi
|
116
|
+
done
|
117
|
+
|
118
|
+
# Check if specification has clarifications needed
|
119
|
+
if grep -q "NEEDS CLARIFICATION" "$SPEC_FILE"; then
|
120
|
+
CLARIFICATION_COUNT=$(grep -c "NEEDS CLARIFICATION" "$SPEC_FILE")
|
121
|
+
log "WARNING: Specification has $CLARIFICATION_COUNT clarifications needed - resolve before proceeding"
|
122
|
+
fi
|
123
|
+
|
124
|
+
# Validate gate compliance
|
125
|
+
GATE_STATUS=$(grep -A5 "Gate Status:" "$PLAN_FILE" | tail -1 | sed 's/.*\[\(.*\)\].*/\1/' || echo "PENDING")
|
126
|
+
|
127
|
+
if [ "$GATE_STATUS" != "COMPLETED" ]; then
|
128
|
+
log "WARNING: Constitutional gates not completed. Status: $GATE_STATUS"
|
129
|
+
fi
|
130
|
+
|
131
|
+
log "Implementation plan processing completed successfully"
|
132
|
+
|
133
|
+
echo "PLAN_FILE=$PLAN_FILE"
|
134
|
+
echo "SPEC_FILE=$SPEC_FILE"
|
135
|
+
echo "MISSING_SECTIONS=${#MISSING_SECTIONS[@]}"
|
136
|
+
echo "CONSTITUTIONAL_GATES_STATUS=$GATE_STATUS"
|
127
137
|
echo "STATUS=ready"
|
@@ -13,10 +13,10 @@ import datetime
|
|
13
13
|
class SpecPulseSpec:
|
14
14
|
def __init__(self):
|
15
15
|
self.script_name = Path(__file__).name
|
16
|
-
self.project_root = Path(__file__).parent.parent
|
16
|
+
self.project_root = Path(__file__).parent.parent
|
17
17
|
self.memory_dir = self.project_root / "memory"
|
18
18
|
self.context_file = self.memory_dir / "context.md"
|
19
|
-
self.templates_dir = self.project_root / "
|
19
|
+
self.templates_dir = self.project_root / "templates"
|
20
20
|
|
21
21
|
def log(self, message):
|
22
22
|
"""Log messages with timestamp"""
|
@@ -101,7 +101,7 @@ class SpecPulseSpec:
|
|
101
101
|
def main(self, args):
|
102
102
|
"""Main execution function"""
|
103
103
|
if len(args) < 1:
|
104
|
-
self.error_exit("Usage: python pulse-spec.py <feature-dir> [spec-content]")
|
104
|
+
self.error_exit("Usage: python sp-pulse-spec.py <feature-dir> [spec-content]")
|
105
105
|
|
106
106
|
feature_dir = args[0]
|
107
107
|
spec_content = args[1] if len(args) > 1 else None
|
@@ -112,8 +112,24 @@ class SpecPulseSpec:
|
|
112
112
|
sanitized_dir = self.get_current_feature_dir(feature_dir)
|
113
113
|
|
114
114
|
# Set file paths
|
115
|
-
|
116
|
-
|
115
|
+
specs_dir = self.project_root / "specs" / sanitized_dir
|
116
|
+
spec_file = specs_dir / "spec-001.md"
|
117
|
+
|
118
|
+
# Find next available spec number
|
119
|
+
if specs_dir.exists():
|
120
|
+
existing_specs = list(specs_dir.glob("spec-*.md"))
|
121
|
+
if existing_specs:
|
122
|
+
spec_numbers = [int(f.stem.split('-')[1]) for f in existing_specs if f.stem.split('-')[1].isdigit()]
|
123
|
+
next_number = max(spec_numbers) + 1 if spec_numbers else 1
|
124
|
+
else:
|
125
|
+
next_number = 1
|
126
|
+
else:
|
127
|
+
next_number = 1
|
128
|
+
specs_dir.mkdir(parents=True, exist_ok=True)
|
129
|
+
|
130
|
+
# Update spec_file to use versioned naming
|
131
|
+
spec_file = specs_dir / f"spec-{next_number:03d}.md"
|
132
|
+
template_file = self.templates_dir / "spec-001.md"
|
117
133
|
|
118
134
|
# Ensure specs directory exists
|
119
135
|
spec_file.parent.mkdir(parents=True, exist_ok=True)
|
@@ -133,7 +149,11 @@ class SpecPulseSpec:
|
|
133
149
|
|
134
150
|
self.log(f"Creating specification from template: {spec_file}")
|
135
151
|
try:
|
136
|
-
|
152
|
+
template_content = template_file.read_text(encoding='utf-8')
|
153
|
+
# Update template content to use sp- prefixed commands
|
154
|
+
template_content = template_content.replace("/plan ", "/sp-plan ")
|
155
|
+
template_content = template_content.replace("/task ", "/sp-task ")
|
156
|
+
spec_file.write_text(template_content)
|
137
157
|
except Exception as e:
|
138
158
|
self.error_exit(f"Failed to copy specification template: {e}")
|
139
159
|
else:
|
@@ -1,104 +1,127 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
# Generate or update specification
|
3
|
-
|
4
|
-
set -euo pipefail # Exit on error, unset vars, pipe failures
|
5
|
-
|
6
|
-
# Configuration
|
7
|
-
SCRIPT_NAME="$(basename "$0")"
|
8
|
-
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
9
|
-
|
10
|
-
# Function to log messages
|
11
|
-
log() {
|
12
|
-
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $SCRIPT_NAME: $1" >&2
|
13
|
-
}
|
14
|
-
|
15
|
-
# Function to handle errors
|
16
|
-
error_exit() {
|
17
|
-
log "ERROR: $1"
|
18
|
-
exit 1
|
19
|
-
}
|
20
|
-
|
21
|
-
# Validate arguments
|
22
|
-
if [ $# -eq 0 ]; then
|
23
|
-
error_exit "Usage: $SCRIPT_NAME <feature-dir> [spec-content]"
|
24
|
-
fi
|
25
|
-
|
26
|
-
FEATURE_DIR="$1"
|
27
|
-
SPEC_CONTENT="${2:-}"
|
28
|
-
|
29
|
-
# Sanitize feature directory
|
30
|
-
SANITIZED_DIR=$(echo "$FEATURE_DIR" | sed 's/[^a-zA-Z0-9_-]//g')
|
31
|
-
|
32
|
-
if [ -z "$SANITIZED_DIR" ]; then
|
33
|
-
error_exit "Invalid feature directory: '$FEATURE_DIR'"
|
34
|
-
fi
|
35
|
-
|
36
|
-
# Find feature directory if not provided
|
37
|
-
if [ -z "$FEATURE_DIR" ]; then
|
38
|
-
CONTEXT_FILE="$PROJECT_ROOT/memory/context.md"
|
39
|
-
if [ -f "$CONTEXT_FILE" ]; then
|
40
|
-
FEATURE_DIR=$(grep -A1 "Active Feature" "$CONTEXT_FILE" | tail -1 | cut -d: -f2 | xargs)
|
41
|
-
if [ -z "$FEATURE_DIR" ]; then
|
42
|
-
error_exit "No active feature found in context file"
|
43
|
-
fi
|
44
|
-
log "Using active feature from context: $FEATURE_DIR"
|
45
|
-
else
|
46
|
-
error_exit "No feature directory provided and no context file found"
|
47
|
-
fi
|
48
|
-
fi
|
49
|
-
|
50
|
-
|
51
|
-
TEMPLATE_FILE="$PROJECT_ROOT/templates/spec.md"
|
52
|
-
|
53
|
-
# Ensure specs directory exists
|
54
|
-
mkdir -p "$
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
1
|
+
#!/bin/bash
|
2
|
+
# Generate or update specification
|
3
|
+
|
4
|
+
set -euo pipefail # Exit on error, unset vars, pipe failures
|
5
|
+
|
6
|
+
# Configuration
|
7
|
+
SCRIPT_NAME="$(basename "$0")"
|
8
|
+
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
9
|
+
|
10
|
+
# Function to log messages
|
11
|
+
log() {
|
12
|
+
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $SCRIPT_NAME: $1" >&2
|
13
|
+
}
|
14
|
+
|
15
|
+
# Function to handle errors
|
16
|
+
error_exit() {
|
17
|
+
log "ERROR: $1"
|
18
|
+
exit 1
|
19
|
+
}
|
20
|
+
|
21
|
+
# Validate arguments
|
22
|
+
if [ $# -eq 0 ]; then
|
23
|
+
error_exit "Usage: $SCRIPT_NAME <feature-dir> [spec-content]"
|
24
|
+
fi
|
25
|
+
|
26
|
+
FEATURE_DIR="$1"
|
27
|
+
SPEC_CONTENT="${2:-}"
|
28
|
+
|
29
|
+
# Sanitize feature directory
|
30
|
+
SANITIZED_DIR=$(echo "$FEATURE_DIR" | sed 's/[^a-zA-Z0-9_-]//g')
|
31
|
+
|
32
|
+
if [ -z "$SANITIZED_DIR" ]; then
|
33
|
+
error_exit "Invalid feature directory: '$FEATURE_DIR'"
|
34
|
+
fi
|
35
|
+
|
36
|
+
# Find feature directory if not provided
|
37
|
+
if [ -z "$FEATURE_DIR" ]; then
|
38
|
+
CONTEXT_FILE="$PROJECT_ROOT/memory/context.md"
|
39
|
+
if [ -f "$CONTEXT_FILE" ]; then
|
40
|
+
FEATURE_DIR=$(grep -A1 "Active Feature" "$CONTEXT_FILE" | tail -1 | cut -d: -f2 | xargs)
|
41
|
+
if [ -z "$FEATURE_DIR" ]; then
|
42
|
+
error_exit "No active feature found in context file"
|
43
|
+
fi
|
44
|
+
log "Using active feature from context: $FEATURE_DIR"
|
45
|
+
else
|
46
|
+
error_exit "No feature directory provided and no context file found"
|
47
|
+
fi
|
48
|
+
fi
|
49
|
+
|
50
|
+
SPEC_DIR="$PROJECT_ROOT/specs/${FEATURE_DIR}"
|
51
|
+
TEMPLATE_FILE="$PROJECT_ROOT/templates/spec-001.md"
|
52
|
+
|
53
|
+
# Ensure specs directory exists
|
54
|
+
mkdir -p "$SPEC_DIR"
|
55
|
+
|
56
|
+
# Find latest spec file or create new one
|
57
|
+
if [ -n "$SPEC_CONTENT" ]; then
|
58
|
+
# Find next available spec number
|
59
|
+
if [ -d "$SPEC_DIR" ]; then
|
60
|
+
existing_specs=$(find "$SPEC_DIR" -name "spec-*.md" | wc -l)
|
61
|
+
spec_number=$((existing_specs + 1))
|
62
|
+
else
|
63
|
+
spec_number=1
|
64
|
+
fi
|
65
|
+
SPEC_FILE="$SPEC_DIR/spec-$(printf "%03d" $spec_number).md"
|
66
|
+
|
67
|
+
# Update specification with provided content
|
68
|
+
log "Creating specification: $SPEC_FILE"
|
69
|
+
echo "$SPEC_CONTENT" > "$SPEC_FILE" || error_exit "Failed to write specification content"
|
70
|
+
else
|
71
|
+
# Find latest spec file
|
72
|
+
if [ -d "$SPEC_DIR" ]; then
|
73
|
+
SPEC_FILE=$(find "$SPEC_DIR" -name "spec-*.md" -printf "%T@ %p\n" | sort -n | tail -1 | cut -d' ' -f2-)
|
74
|
+
if [ -z "$SPEC_FILE" ]; then
|
75
|
+
# No spec files found, create first one
|
76
|
+
SPEC_FILE="$SPEC_DIR/spec-001.md"
|
77
|
+
if [ ! -f "$TEMPLATE_FILE" ]; then
|
78
|
+
error_exit "Template not found: $TEMPLATE_FILE"
|
79
|
+
fi
|
80
|
+
log "Creating specification from template: $SPEC_FILE"
|
81
|
+
cp "$TEMPLATE_FILE" "$SPEC_FILE" || error_exit "Failed to copy specification template"
|
82
|
+
else
|
83
|
+
log "Using latest specification: $SPEC_FILE"
|
84
|
+
fi
|
85
|
+
else
|
86
|
+
# Create directory and first spec
|
87
|
+
SPEC_FILE="$SPEC_DIR/spec-001.md"
|
88
|
+
if [ ! -f "$TEMPLATE_FILE" ]; then
|
89
|
+
error_exit "Template not found: $TEMPLATE_FILE"
|
90
|
+
fi
|
91
|
+
log "Creating specification from template: $SPEC_FILE"
|
92
|
+
cp "$TEMPLATE_FILE" "$SPEC_FILE" || error_exit "Failed to copy specification template"
|
93
|
+
fi
|
94
|
+
fi
|
95
|
+
|
96
|
+
# Validate specification
|
97
|
+
log "Validating specification..."
|
98
|
+
if [ ! -f "$SPEC_FILE" ]; then
|
99
|
+
error_exit "Specification file does not exist: $SPEC_FILE"
|
100
|
+
fi
|
101
|
+
|
102
|
+
# Check for required sections
|
103
|
+
REQUIRED_SECTIONS=("## Specification:" "## Metadata" "## Functional Requirements" "## Acceptance Scenarios")
|
104
|
+
MISSING_SECTIONS=()
|
105
|
+
|
106
|
+
for section in "${REQUIRED_SECTIONS[@]}"; do
|
107
|
+
if ! grep -q "$section" "$SPEC_FILE"; then
|
108
|
+
MISSING_SECTIONS+=("$section")
|
109
|
+
fi
|
110
|
+
done
|
111
|
+
|
112
|
+
if [ ${#MISSING_SECTIONS[@]} -gt 0 ]; then
|
113
|
+
log "WARNING: Missing required sections: ${MISSING_SECTIONS[*]}"
|
114
|
+
fi
|
115
|
+
|
116
|
+
# Check for clarifications needed
|
117
|
+
if grep -q "NEEDS CLARIFICATION" "$SPEC_FILE"; then
|
118
|
+
CLARIFICATION_COUNT=$(grep -c "NEEDS CLARIFICATION" "$SPEC_FILE")
|
119
|
+
log "WARNING: Specification has $CLARIFICATION_COUNT clarifications needed"
|
120
|
+
fi
|
121
|
+
|
122
|
+
log "Specification processing completed successfully"
|
123
|
+
|
124
|
+
echo "SPEC_FILE=$SPEC_FILE"
|
125
|
+
echo "CLARIFICATIONS_NEEDED=${CLARIFICATION_COUNT:-0}"
|
126
|
+
echo "MISSING_SECTIONS=${#MISSING_SECTIONS[@]}"
|
104
127
|
echo "STATUS=updated"
|
@@ -13,10 +13,10 @@ import datetime
|
|
13
13
|
class SpecPulseTask:
|
14
14
|
def __init__(self):
|
15
15
|
self.script_name = Path(__file__).name
|
16
|
-
self.project_root = Path(__file__).parent.parent
|
16
|
+
self.project_root = Path(__file__).parent.parent
|
17
17
|
self.memory_dir = self.project_root / "memory"
|
18
18
|
self.context_file = self.memory_dir / "context.md"
|
19
|
-
self.templates_dir = self.project_root / "
|
19
|
+
self.templates_dir = self.project_root / "templates"
|
20
20
|
|
21
21
|
def log(self, message):
|
22
22
|
"""Log messages with timestamp"""
|
@@ -155,7 +155,7 @@ class SpecPulseTask:
|
|
155
155
|
def main(self, args):
|
156
156
|
"""Main execution function"""
|
157
157
|
if len(args) < 1:
|
158
|
-
self.error_exit("Usage: python pulse-task.py <feature-dir>")
|
158
|
+
self.error_exit("Usage: python sp-pulse-task.py <feature-dir>")
|
159
159
|
|
160
160
|
feature_dir = args[0]
|
161
161
|
|
@@ -165,10 +165,46 @@ class SpecPulseTask:
|
|
165
165
|
sanitized_dir = self.get_current_feature_dir(feature_dir)
|
166
166
|
|
167
167
|
# Set file paths
|
168
|
-
|
168
|
+
plans_dir = self.project_root / "plans" / sanitized_dir
|
169
|
+
tasks_dir = self.project_root / "tasks" / sanitized_dir
|
170
|
+
|
171
|
+
# Find latest spec and plan files
|
172
|
+
specs_dir = self.project_root / "specs" / sanitized_dir
|
173
|
+
|
174
|
+
if specs_dir.exists():
|
175
|
+
spec_files = list(specs_dir.glob("spec-*.md"))
|
176
|
+
if spec_files:
|
177
|
+
latest_spec = max(spec_files, key=lambda f: f.stat().st_mtime)
|
178
|
+
else:
|
179
|
+
self.error_exit(f"No spec files found in {specs_dir}")
|
180
|
+
else:
|
181
|
+
self.error_exit(f"Specs directory not found: {specs_dir}")
|
182
|
+
|
183
|
+
if plans_dir.exists():
|
184
|
+
plan_files = list(plans_dir.glob("plan-*.md"))
|
185
|
+
if plan_files:
|
186
|
+
latest_plan = max(plan_files, key=lambda f: f.stat().st_mtime)
|
187
|
+
else:
|
188
|
+
self.error_exit(f"No plan files found in {plans_dir}")
|
189
|
+
else:
|
190
|
+
self.error_exit(f"Plans directory not found: {plans_dir}")
|
191
|
+
|
192
|
+
# Find next available task number
|
193
|
+
if tasks_dir.exists():
|
194
|
+
existing_tasks = list(tasks_dir.glob("task-*.md"))
|
195
|
+
if existing_tasks:
|
196
|
+
task_numbers = [int(f.stem.split('-')[1]) for f in existing_tasks if f.stem.split('-')[1].isdigit()]
|
197
|
+
next_number = max(task_numbers) + 1 if task_numbers else 1
|
198
|
+
else:
|
199
|
+
next_number = 1
|
200
|
+
else:
|
201
|
+
next_number = 1
|
202
|
+
tasks_dir.mkdir(parents=True, exist_ok=True)
|
203
|
+
|
204
|
+
task_file = tasks_dir / f"task-{next_number:03d}.md"
|
169
205
|
template_file = self.templates_dir / "task.md"
|
170
|
-
plan_file =
|
171
|
-
spec_file =
|
206
|
+
plan_file = latest_plan
|
207
|
+
spec_file = latest_spec
|
172
208
|
|
173
209
|
# Ensure tasks directory exists
|
174
210
|
task_file.parent.mkdir(parents=True, exist_ok=True)
|