ruby_reactor 0.5.3 → 0.6.0
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.
- checksums.yaml +4 -4
- data/.claude/skills/speckit-agent-context-update/SKILL.md +32 -0
- data/.claude/skills/speckit-analyze/SKILL.md +262 -0
- data/.claude/skills/speckit-checklist/SKILL.md +374 -0
- data/.claude/skills/speckit-clarify/SKILL.md +286 -0
- data/.claude/skills/speckit-constitution/SKILL.md +157 -0
- data/.claude/skills/speckit-converge/SKILL.md +277 -0
- data/.claude/skills/speckit-implement/SKILL.md +224 -0
- data/.claude/skills/speckit-plan/SKILL.md +171 -0
- data/.claude/skills/speckit-specify/SKILL.md +346 -0
- data/.claude/skills/speckit-tasks/SKILL.md +215 -0
- data/.claude/skills/speckit-taskstoissues/SKILL.md +110 -0
- data/.release-please-manifest.json +1 -1
- data/.specify/extensions/.registry +19 -0
- data/.specify/extensions/agent-context/README.md +66 -0
- data/.specify/extensions/agent-context/agent-context-config.yml +5 -0
- data/.specify/extensions/agent-context/commands/speckit.agent-context.update.md +27 -0
- data/.specify/extensions/agent-context/extension.yml +34 -0
- data/.specify/extensions/agent-context/scripts/bash/update-agent-context.sh +282 -0
- data/.specify/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +353 -0
- data/.specify/extensions.yml +23 -0
- data/.specify/feature.json +3 -0
- data/.specify/init-options.json +9 -0
- data/.specify/integration.json +15 -0
- data/.specify/integrations/claude.manifest.json +17 -0
- data/.specify/integrations/speckit.manifest.json +17 -0
- data/.specify/memory/constitution.md +134 -0
- data/.specify/scripts/bash/check-prerequisites.sh +189 -0
- data/.specify/scripts/bash/common.sh +619 -0
- data/.specify/scripts/bash/create-new-feature.sh +299 -0
- data/.specify/scripts/bash/setup-plan.sh +84 -0
- data/.specify/scripts/bash/setup-tasks.sh +91 -0
- data/.specify/templates/checklist-template.md +40 -0
- data/.specify/templates/constitution-template.md +50 -0
- data/.specify/templates/plan-template.md +113 -0
- data/.specify/templates/spec-template.md +131 -0
- data/.specify/templates/tasks-template.md +252 -0
- data/.specify/workflows/speckit/workflow.yml +77 -0
- data/.specify/workflows/workflow-registry.json +13 -0
- data/CHANGELOG.md +14 -0
- data/README.md +146 -65
- data/lib/ruby_reactor/adapters/active_job/compat.rb +24 -0
- data/lib/ruby_reactor/adapters/active_job/map_collector_worker.rb +19 -0
- data/lib/ruby_reactor/adapters/active_job/map_element_worker.rb +19 -0
- data/lib/ruby_reactor/adapters/active_job/router.rb +91 -0
- data/lib/ruby_reactor/adapters/active_job/sweeper_worker.rb +16 -0
- data/lib/ruby_reactor/adapters/active_job/worker.rb +24 -0
- data/lib/ruby_reactor/adapters/sidekiq/map_collector_worker.rb +15 -0
- data/lib/ruby_reactor/adapters/sidekiq/map_element_worker.rb +15 -0
- data/lib/ruby_reactor/adapters/sidekiq/router.rb +91 -0
- data/lib/ruby_reactor/adapters/sidekiq/sweeper_worker.rb +19 -0
- data/lib/ruby_reactor/adapters/sidekiq/worker.rb +25 -0
- data/lib/ruby_reactor/configuration.rb +24 -4
- data/lib/ruby_reactor/dsl/template_helpers.rb +7 -1
- data/lib/ruby_reactor/map/element_executor.rb +1 -1
- data/lib/ruby_reactor/rspec/active_job_helpers.rb +52 -0
- data/lib/ruby_reactor/rspec/async_test_helpers.rb +41 -0
- data/lib/ruby_reactor/rspec/sidekiq_helpers.rb +3 -3
- data/lib/ruby_reactor/rspec/test_subject.rb +11 -7
- data/lib/ruby_reactor/rspec.rb +4 -0
- data/lib/ruby_reactor/step.rb +4 -0
- data/lib/ruby_reactor/sweeper_job.rb +70 -0
- data/lib/ruby_reactor/version.rb +1 -1
- data/lib/ruby_reactor/worker.rb +226 -0
- data/lib/ruby_reactor.rb +40 -1
- data/specs/active_job.md +259 -0
- metadata +54 -6
- data/lib/ruby_reactor/sidekiq_adapter.rb +0 -87
- data/lib/ruby_reactor/sidekiq_workers/map_collector_worker.rb +0 -13
- data/lib/ruby_reactor/sidekiq_workers/map_element_worker.rb +0 -13
- data/lib/ruby_reactor/sidekiq_workers/sweeper_worker.rb +0 -73
- data/lib/ruby_reactor/sidekiq_workers/worker.rb +0 -222
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
JSON_MODE=false
|
|
6
|
+
DRY_RUN=false
|
|
7
|
+
ALLOW_EXISTING=false
|
|
8
|
+
SHORT_NAME=""
|
|
9
|
+
BRANCH_NUMBER=""
|
|
10
|
+
USE_TIMESTAMP=false
|
|
11
|
+
ARGS=()
|
|
12
|
+
i=1
|
|
13
|
+
while [ $i -le $# ]; do
|
|
14
|
+
arg="${!i}"
|
|
15
|
+
case "$arg" in
|
|
16
|
+
--json)
|
|
17
|
+
JSON_MODE=true
|
|
18
|
+
;;
|
|
19
|
+
--dry-run)
|
|
20
|
+
DRY_RUN=true
|
|
21
|
+
;;
|
|
22
|
+
--allow-existing-branch)
|
|
23
|
+
ALLOW_EXISTING=true
|
|
24
|
+
;;
|
|
25
|
+
--short-name)
|
|
26
|
+
if [ $((i + 1)) -gt $# ]; then
|
|
27
|
+
echo 'Error: --short-name requires a value' >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
i=$((i + 1))
|
|
31
|
+
next_arg="${!i}"
|
|
32
|
+
# Check if the next argument is another option (starts with --)
|
|
33
|
+
if [[ "$next_arg" == --* ]]; then
|
|
34
|
+
echo 'Error: --short-name requires a value' >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
SHORT_NAME="$next_arg"
|
|
38
|
+
;;
|
|
39
|
+
--number)
|
|
40
|
+
if [ $((i + 1)) -gt $# ]; then
|
|
41
|
+
echo 'Error: --number requires a value' >&2
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
i=$((i + 1))
|
|
45
|
+
next_arg="${!i}"
|
|
46
|
+
if [[ "$next_arg" == --* ]]; then
|
|
47
|
+
echo 'Error: --number requires a value' >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
50
|
+
BRANCH_NUMBER="$next_arg"
|
|
51
|
+
;;
|
|
52
|
+
--timestamp)
|
|
53
|
+
USE_TIMESTAMP=true
|
|
54
|
+
;;
|
|
55
|
+
--help|-h)
|
|
56
|
+
echo "Usage: $0 [--json] [--dry-run] [--allow-existing-branch] [--short-name <name>] [--number N] [--timestamp] <feature_description>"
|
|
57
|
+
echo ""
|
|
58
|
+
echo "Options:"
|
|
59
|
+
echo " --json Output in JSON format"
|
|
60
|
+
echo " --dry-run Compute feature name and paths without creating directories or files"
|
|
61
|
+
echo " --allow-existing-branch Reuse an existing feature directory if it already exists"
|
|
62
|
+
echo " --short-name <name> Provide a custom short name (2-4 words) for the feature"
|
|
63
|
+
echo " --number N Specify branch number manually (overrides auto-detection)"
|
|
64
|
+
echo " --timestamp Use timestamp prefix (YYYYMMDD-HHMMSS) instead of sequential numbering"
|
|
65
|
+
echo " --help, -h Show this help message"
|
|
66
|
+
echo ""
|
|
67
|
+
echo "Examples:"
|
|
68
|
+
echo " $0 'Add user authentication system' --short-name 'user-auth'"
|
|
69
|
+
echo " $0 'Implement OAuth2 integration for API' --number 5"
|
|
70
|
+
echo " $0 --timestamp --short-name 'user-auth' 'Add user authentication'"
|
|
71
|
+
exit 0
|
|
72
|
+
;;
|
|
73
|
+
*)
|
|
74
|
+
ARGS+=("$arg")
|
|
75
|
+
;;
|
|
76
|
+
esac
|
|
77
|
+
i=$((i + 1))
|
|
78
|
+
done
|
|
79
|
+
|
|
80
|
+
FEATURE_DESCRIPTION="${ARGS[*]}"
|
|
81
|
+
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
|
82
|
+
echo "Usage: $0 [--json] [--dry-run] [--allow-existing-branch] [--short-name <name>] [--number N] [--timestamp] <feature_description>" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Trim whitespace and validate description is not empty (e.g., user passed only whitespace)
|
|
87
|
+
FEATURE_DESCRIPTION=$(echo "$FEATURE_DESCRIPTION" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')
|
|
88
|
+
if [ -z "$FEATURE_DESCRIPTION" ]; then
|
|
89
|
+
echo "Error: Feature description cannot be empty or contain only whitespace" >&2
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Function to get highest number from specs directory
|
|
94
|
+
get_highest_from_specs() {
|
|
95
|
+
local specs_dir="$1"
|
|
96
|
+
local highest=0
|
|
97
|
+
|
|
98
|
+
if [ -d "$specs_dir" ]; then
|
|
99
|
+
for dir in "$specs_dir"/*; do
|
|
100
|
+
[ -d "$dir" ] || continue
|
|
101
|
+
dirname=$(basename "$dir")
|
|
102
|
+
# Match sequential prefixes (>=3 digits), but skip timestamp dirs.
|
|
103
|
+
if echo "$dirname" | grep -Eq '^[0-9]{3,}-' && ! echo "$dirname" | grep -Eq '^[0-9]{8}-[0-9]{6}-'; then
|
|
104
|
+
number=$(echo "$dirname" | grep -Eo '^[0-9]+')
|
|
105
|
+
number=$((10#$number))
|
|
106
|
+
if [ "$number" -gt "$highest" ]; then
|
|
107
|
+
highest=$number
|
|
108
|
+
fi
|
|
109
|
+
fi
|
|
110
|
+
done
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
echo "$highest"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Function to clean and format a branch name
|
|
117
|
+
clean_branch_name() {
|
|
118
|
+
local name="$1"
|
|
119
|
+
echo "$name" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//'
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Resolve repository root using common.sh functions which prioritize .specify
|
|
123
|
+
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
124
|
+
source "$SCRIPT_DIR/common.sh"
|
|
125
|
+
|
|
126
|
+
REPO_ROOT=$(get_repo_root) || exit 1
|
|
127
|
+
|
|
128
|
+
cd "$REPO_ROOT"
|
|
129
|
+
|
|
130
|
+
SPECS_DIR="$REPO_ROOT/specs"
|
|
131
|
+
if [ "$DRY_RUN" != true ]; then
|
|
132
|
+
mkdir -p "$SPECS_DIR"
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# Function to generate branch name with stop word filtering and length filtering
|
|
136
|
+
generate_branch_name() {
|
|
137
|
+
local description="$1"
|
|
138
|
+
|
|
139
|
+
# Common stop words to filter out
|
|
140
|
+
local stop_words="^(i|a|an|the|to|for|of|in|on|at|by|with|from|is|are|was|were|be|been|being|have|has|had|do|does|did|will|would|should|could|can|may|might|must|shall|this|that|these|those|my|your|our|their|want|need|add|get|set)$"
|
|
141
|
+
|
|
142
|
+
# Convert to lowercase and split into words
|
|
143
|
+
local clean_name=$(echo "$description" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/ /g')
|
|
144
|
+
|
|
145
|
+
# Filter words: remove stop words and words shorter than 3 chars (unless they're uppercase acronyms in original)
|
|
146
|
+
local meaningful_words=()
|
|
147
|
+
for word in $clean_name; do
|
|
148
|
+
# Skip empty words
|
|
149
|
+
[ -z "$word" ] && continue
|
|
150
|
+
|
|
151
|
+
# Keep words that are NOT stop words AND (length >= 3 OR are potential acronyms)
|
|
152
|
+
if ! echo "$word" | grep -qiE "$stop_words"; then
|
|
153
|
+
if [ ${#word} -ge 3 ]; then
|
|
154
|
+
meaningful_words+=("$word")
|
|
155
|
+
elif echo "$description" | grep -q "\b${word^^}\b"; then
|
|
156
|
+
# Keep short words if they appear as uppercase in original (likely acronyms)
|
|
157
|
+
meaningful_words+=("$word")
|
|
158
|
+
fi
|
|
159
|
+
fi
|
|
160
|
+
done
|
|
161
|
+
|
|
162
|
+
# If we have meaningful words, use first 3-4 of them
|
|
163
|
+
if [ ${#meaningful_words[@]} -gt 0 ]; then
|
|
164
|
+
local max_words=3
|
|
165
|
+
if [ ${#meaningful_words[@]} -eq 4 ]; then max_words=4; fi
|
|
166
|
+
|
|
167
|
+
local result=""
|
|
168
|
+
local count=0
|
|
169
|
+
for word in "${meaningful_words[@]}"; do
|
|
170
|
+
if [ $count -ge $max_words ]; then break; fi
|
|
171
|
+
if [ -n "$result" ]; then result="$result-"; fi
|
|
172
|
+
result="$result$word"
|
|
173
|
+
count=$((count + 1))
|
|
174
|
+
done
|
|
175
|
+
echo "$result"
|
|
176
|
+
else
|
|
177
|
+
# Fallback to original logic if no meaningful words found
|
|
178
|
+
local cleaned=$(clean_branch_name "$description")
|
|
179
|
+
echo "$cleaned" | tr '-' '\n' | grep -v '^$' | head -3 | tr '\n' '-' | sed 's/-$//'
|
|
180
|
+
fi
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Generate branch name
|
|
184
|
+
if [ -n "$SHORT_NAME" ]; then
|
|
185
|
+
# Use provided short name, just clean it up
|
|
186
|
+
BRANCH_SUFFIX=$(clean_branch_name "$SHORT_NAME")
|
|
187
|
+
else
|
|
188
|
+
# Generate from description with smart filtering
|
|
189
|
+
BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION")
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# Warn if --number and --timestamp are both specified
|
|
193
|
+
if [ "$USE_TIMESTAMP" = true ] && [ -n "$BRANCH_NUMBER" ]; then
|
|
194
|
+
>&2 echo "[specify] Warning: --number is ignored when --timestamp is used"
|
|
195
|
+
BRANCH_NUMBER=""
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# Determine branch prefix
|
|
199
|
+
if [ "$USE_TIMESTAMP" = true ]; then
|
|
200
|
+
FEATURE_NUM=$(date +%Y%m%d-%H%M%S)
|
|
201
|
+
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
|
202
|
+
else
|
|
203
|
+
# Determine branch number from existing feature directories
|
|
204
|
+
if [ -z "$BRANCH_NUMBER" ]; then
|
|
205
|
+
HIGHEST=$(get_highest_from_specs "$SPECS_DIR")
|
|
206
|
+
BRANCH_NUMBER=$((HIGHEST + 1))
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
# Force base-10 interpretation to prevent octal conversion (e.g., 010 → 8 in octal, but should be 10 in decimal)
|
|
210
|
+
FEATURE_NUM=$(printf "%03d" "$((10#$BRANCH_NUMBER))")
|
|
211
|
+
BRANCH_NAME="${FEATURE_NUM}-${BRANCH_SUFFIX}"
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# GitHub enforces a 244-byte limit on branch names
|
|
215
|
+
# Validate and truncate if necessary
|
|
216
|
+
MAX_BRANCH_LENGTH=244
|
|
217
|
+
if [ ${#BRANCH_NAME} -gt $MAX_BRANCH_LENGTH ]; then
|
|
218
|
+
# Calculate how much we need to trim from suffix
|
|
219
|
+
# Account for prefix length: timestamp (15) + hyphen (1) = 16, or sequential (3) + hyphen (1) = 4
|
|
220
|
+
PREFIX_LENGTH=$(( ${#FEATURE_NUM} + 1 ))
|
|
221
|
+
MAX_SUFFIX_LENGTH=$((MAX_BRANCH_LENGTH - PREFIX_LENGTH))
|
|
222
|
+
|
|
223
|
+
# Truncate suffix at word boundary if possible
|
|
224
|
+
TRUNCATED_SUFFIX=$(echo "$BRANCH_SUFFIX" | cut -c1-$MAX_SUFFIX_LENGTH)
|
|
225
|
+
# Remove trailing hyphen if truncation created one
|
|
226
|
+
TRUNCATED_SUFFIX=$(echo "$TRUNCATED_SUFFIX" | sed 's/-$//')
|
|
227
|
+
|
|
228
|
+
ORIGINAL_BRANCH_NAME="$BRANCH_NAME"
|
|
229
|
+
BRANCH_NAME="${FEATURE_NUM}-${TRUNCATED_SUFFIX}"
|
|
230
|
+
|
|
231
|
+
>&2 echo "[specify] Warning: Branch name exceeded GitHub's 244-byte limit"
|
|
232
|
+
>&2 echo "[specify] Original: $ORIGINAL_BRANCH_NAME (${#ORIGINAL_BRANCH_NAME} bytes)"
|
|
233
|
+
>&2 echo "[specify] Truncated to: $BRANCH_NAME (${#BRANCH_NAME} bytes)"
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
FEATURE_DIR="$SPECS_DIR/$BRANCH_NAME"
|
|
237
|
+
SPEC_FILE="$FEATURE_DIR/spec.md"
|
|
238
|
+
|
|
239
|
+
if [ "$DRY_RUN" != true ]; then
|
|
240
|
+
if [ -d "$FEATURE_DIR" ] && [ "$ALLOW_EXISTING" != true ]; then
|
|
241
|
+
if [ "$USE_TIMESTAMP" = true ]; then
|
|
242
|
+
>&2 echo "Error: Feature directory '$FEATURE_DIR' already exists. Rerun to get a new timestamp or use a different --short-name."
|
|
243
|
+
else
|
|
244
|
+
>&2 echo "Error: Feature directory '$FEATURE_DIR' already exists. Please use a different feature name or specify a different number with --number."
|
|
245
|
+
fi
|
|
246
|
+
exit 1
|
|
247
|
+
fi
|
|
248
|
+
|
|
249
|
+
mkdir -p "$FEATURE_DIR"
|
|
250
|
+
|
|
251
|
+
if [ ! -f "$SPEC_FILE" ]; then
|
|
252
|
+
TEMPLATE=$(resolve_template "spec-template" "$REPO_ROOT") || true
|
|
253
|
+
if [ -n "$TEMPLATE" ] && [ -f "$TEMPLATE" ]; then
|
|
254
|
+
cp "$TEMPLATE" "$SPEC_FILE"
|
|
255
|
+
else
|
|
256
|
+
echo "Warning: Spec template not found; created empty spec file" >&2
|
|
257
|
+
touch "$SPEC_FILE"
|
|
258
|
+
fi
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# Persist to .specify/feature.json so downstream commands can find the feature
|
|
262
|
+
_persist_feature_json "$REPO_ROOT" "$FEATURE_DIR"
|
|
263
|
+
|
|
264
|
+
# Inform the user how to set feature state in their own shell
|
|
265
|
+
printf '# To persist: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME" >&2
|
|
266
|
+
printf '# export SPECIFY_FEATURE_DIRECTORY=%q\n' "$FEATURE_DIR" >&2
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
if $JSON_MODE; then
|
|
270
|
+
if command -v jq >/dev/null 2>&1; then
|
|
271
|
+
if [ "$DRY_RUN" = true ]; then
|
|
272
|
+
jq -cn \
|
|
273
|
+
--arg branch_name "$BRANCH_NAME" \
|
|
274
|
+
--arg spec_file "$SPEC_FILE" \
|
|
275
|
+
--arg feature_num "$FEATURE_NUM" \
|
|
276
|
+
'{BRANCH_NAME:$branch_name,SPEC_FILE:$spec_file,FEATURE_NUM:$feature_num,DRY_RUN:true}'
|
|
277
|
+
else
|
|
278
|
+
jq -cn \
|
|
279
|
+
--arg branch_name "$BRANCH_NAME" \
|
|
280
|
+
--arg spec_file "$SPEC_FILE" \
|
|
281
|
+
--arg feature_num "$FEATURE_NUM" \
|
|
282
|
+
'{BRANCH_NAME:$branch_name,SPEC_FILE:$spec_file,FEATURE_NUM:$feature_num}'
|
|
283
|
+
fi
|
|
284
|
+
else
|
|
285
|
+
if [ "$DRY_RUN" = true ]; then
|
|
286
|
+
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s","DRY_RUN":true}\n' "$(json_escape "$BRANCH_NAME")" "$(json_escape "$SPEC_FILE")" "$(json_escape "$FEATURE_NUM")"
|
|
287
|
+
else
|
|
288
|
+
printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$(json_escape "$BRANCH_NAME")" "$(json_escape "$SPEC_FILE")" "$(json_escape "$FEATURE_NUM")"
|
|
289
|
+
fi
|
|
290
|
+
fi
|
|
291
|
+
else
|
|
292
|
+
echo "BRANCH_NAME: $BRANCH_NAME"
|
|
293
|
+
echo "SPEC_FILE: $SPEC_FILE"
|
|
294
|
+
echo "FEATURE_NUM: $FEATURE_NUM"
|
|
295
|
+
if [ "$DRY_RUN" != true ]; then
|
|
296
|
+
printf '# To persist in your shell: export SPECIFY_FEATURE=%q\n' "$BRANCH_NAME"
|
|
297
|
+
printf '# export SPECIFY_FEATURE_DIRECTORY=%q\n' "$FEATURE_DIR"
|
|
298
|
+
fi
|
|
299
|
+
fi
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Parse command line arguments
|
|
6
|
+
JSON_MODE=false
|
|
7
|
+
ARGS=()
|
|
8
|
+
|
|
9
|
+
for arg in "$@"; do
|
|
10
|
+
case "$arg" in
|
|
11
|
+
--json)
|
|
12
|
+
JSON_MODE=true
|
|
13
|
+
;;
|
|
14
|
+
--help|-h)
|
|
15
|
+
echo "Usage: $0 [--json]"
|
|
16
|
+
echo " --json Output results in JSON format"
|
|
17
|
+
echo " --help Show this help message"
|
|
18
|
+
exit 0
|
|
19
|
+
;;
|
|
20
|
+
*)
|
|
21
|
+
ARGS+=("$arg")
|
|
22
|
+
;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
# Get script directory and load common functions
|
|
27
|
+
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
28
|
+
source "$SCRIPT_DIR/common.sh"
|
|
29
|
+
|
|
30
|
+
# Get all paths and variables from common functions
|
|
31
|
+
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
|
|
32
|
+
eval "$_paths_output"
|
|
33
|
+
unset _paths_output
|
|
34
|
+
|
|
35
|
+
# Ensure the feature directory exists
|
|
36
|
+
mkdir -p "$FEATURE_DIR"
|
|
37
|
+
|
|
38
|
+
# Copy plan template if plan doesn't already exist
|
|
39
|
+
if [[ -f "$IMPL_PLAN" ]]; then
|
|
40
|
+
if $JSON_MODE; then
|
|
41
|
+
echo "Plan already exists at $IMPL_PLAN, skipping template copy" >&2
|
|
42
|
+
else
|
|
43
|
+
echo "Plan already exists at $IMPL_PLAN, skipping template copy"
|
|
44
|
+
fi
|
|
45
|
+
else
|
|
46
|
+
TEMPLATE=$(resolve_template "plan-template" "$REPO_ROOT") || true
|
|
47
|
+
if [[ -n "$TEMPLATE" ]] && [[ -f "$TEMPLATE" ]]; then
|
|
48
|
+
cp "$TEMPLATE" "$IMPL_PLAN"
|
|
49
|
+
if $JSON_MODE; then
|
|
50
|
+
echo "Copied plan template to $IMPL_PLAN" >&2
|
|
51
|
+
else
|
|
52
|
+
echo "Copied plan template to $IMPL_PLAN"
|
|
53
|
+
fi
|
|
54
|
+
else
|
|
55
|
+
if $JSON_MODE; then
|
|
56
|
+
echo "Warning: Plan template not found" >&2
|
|
57
|
+
else
|
|
58
|
+
echo "Warning: Plan template not found"
|
|
59
|
+
fi
|
|
60
|
+
# Create a basic plan file if template doesn't exist
|
|
61
|
+
touch "$IMPL_PLAN"
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
# Output results
|
|
66
|
+
if $JSON_MODE; then
|
|
67
|
+
if has_jq; then
|
|
68
|
+
jq -cn \
|
|
69
|
+
--arg feature_spec "$FEATURE_SPEC" \
|
|
70
|
+
--arg impl_plan "$IMPL_PLAN" \
|
|
71
|
+
--arg specs_dir "$FEATURE_DIR" \
|
|
72
|
+
--arg branch "$CURRENT_BRANCH" \
|
|
73
|
+
'{FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,SPECS_DIR:$specs_dir,BRANCH:$branch}'
|
|
74
|
+
else
|
|
75
|
+
printf '{"FEATURE_SPEC":"%s","IMPL_PLAN":"%s","SPECS_DIR":"%s","BRANCH":"%s"}\n' \
|
|
76
|
+
"$(json_escape "$FEATURE_SPEC")" "$(json_escape "$IMPL_PLAN")" "$(json_escape "$FEATURE_DIR")" "$(json_escape "$CURRENT_BRANCH")"
|
|
77
|
+
fi
|
|
78
|
+
else
|
|
79
|
+
echo "FEATURE_SPEC: $FEATURE_SPEC"
|
|
80
|
+
echo "IMPL_PLAN: $IMPL_PLAN"
|
|
81
|
+
echo "SPECS_DIR: $FEATURE_DIR"
|
|
82
|
+
echo "BRANCH: $CURRENT_BRANCH"
|
|
83
|
+
fi
|
|
84
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -e
|
|
4
|
+
|
|
5
|
+
# Parse command line arguments
|
|
6
|
+
JSON_MODE=false
|
|
7
|
+
|
|
8
|
+
for arg in "$@"; do
|
|
9
|
+
case "$arg" in
|
|
10
|
+
--json) JSON_MODE=true ;;
|
|
11
|
+
--help|-h)
|
|
12
|
+
echo "Usage: $0 [--json]"
|
|
13
|
+
echo " --json Output results in JSON format"
|
|
14
|
+
echo " --help Show this help message"
|
|
15
|
+
exit 0
|
|
16
|
+
;;
|
|
17
|
+
*) echo "ERROR: Unknown option '$arg'" >&2; exit 1 ;;
|
|
18
|
+
esac
|
|
19
|
+
done
|
|
20
|
+
|
|
21
|
+
# Source common functions
|
|
22
|
+
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
source "$SCRIPT_DIR/common.sh"
|
|
24
|
+
|
|
25
|
+
# Get feature paths
|
|
26
|
+
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
|
|
27
|
+
eval "$_paths_output"
|
|
28
|
+
unset _paths_output
|
|
29
|
+
|
|
30
|
+
# Validate required files
|
|
31
|
+
if [[ ! -f "$IMPL_PLAN" ]]; then
|
|
32
|
+
echo "ERROR: plan.md not found in $FEATURE_DIR" >&2
|
|
33
|
+
echo "Run /speckit-plan first to create the implementation plan." >&2
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [[ ! -f "$FEATURE_SPEC" ]]; then
|
|
38
|
+
echo "ERROR: spec.md not found in $FEATURE_DIR" >&2
|
|
39
|
+
echo "Run /speckit-specify first to create the feature structure." >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# Build available docs list
|
|
44
|
+
docs=()
|
|
45
|
+
[[ -f "$RESEARCH" ]] && docs+=("research.md")
|
|
46
|
+
[[ -f "$DATA_MODEL" ]] && docs+=("data-model.md")
|
|
47
|
+
if [[ -d "$CONTRACTS_DIR" ]] && [[ -n "$(ls -A "$CONTRACTS_DIR" 2>/dev/null)" ]]; then
|
|
48
|
+
docs+=("contracts/")
|
|
49
|
+
fi
|
|
50
|
+
[[ -f "$QUICKSTART" ]] && docs+=("quickstart.md")
|
|
51
|
+
|
|
52
|
+
# Resolve tasks template through override stack
|
|
53
|
+
TASKS_TEMPLATE=$(resolve_template "tasks-template" "$REPO_ROOT") || true
|
|
54
|
+
if [[ -z "$TASKS_TEMPLATE" ]] || [[ ! -f "$TASKS_TEMPLATE" ]]; then
|
|
55
|
+
echo "ERROR: Could not resolve required tasks-template from the template override stack for $REPO_ROOT" >&2
|
|
56
|
+
echo "Template 'tasks-template' was not found in any supported location (overrides, presets, extensions, or shared core). Add an override at .specify/templates/overrides/tasks-template.md, or run 'specify init' / reinstall shared infra to restore the core .specify/templates/tasks-template.md template." >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Output results
|
|
61
|
+
if $JSON_MODE; then
|
|
62
|
+
if has_jq; then
|
|
63
|
+
if [[ ${#docs[@]} -eq 0 ]]; then
|
|
64
|
+
json_docs="[]"
|
|
65
|
+
else
|
|
66
|
+
json_docs=$(printf '%s\n' "${docs[@]}" | jq -R . | jq -s .)
|
|
67
|
+
fi
|
|
68
|
+
jq -cn \
|
|
69
|
+
--arg feature_dir "$FEATURE_DIR" \
|
|
70
|
+
--argjson docs "$json_docs" \
|
|
71
|
+
--arg tasks_template "${TASKS_TEMPLATE:-}" \
|
|
72
|
+
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs,TASKS_TEMPLATE:$tasks_template}'
|
|
73
|
+
else
|
|
74
|
+
if [[ ${#docs[@]} -eq 0 ]]; then
|
|
75
|
+
json_docs="[]"
|
|
76
|
+
else
|
|
77
|
+
json_docs=$(for d in "${docs[@]}"; do printf '"%s",' "$(json_escape "$d")"; done)
|
|
78
|
+
json_docs="[${json_docs%,}]"
|
|
79
|
+
fi
|
|
80
|
+
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s,"TASKS_TEMPLATE":"%s"}\n' \
|
|
81
|
+
"$(json_escape "$FEATURE_DIR")" "$json_docs" "$(json_escape "${TASKS_TEMPLATE:-}")"
|
|
82
|
+
fi
|
|
83
|
+
else
|
|
84
|
+
echo "FEATURE_DIR: $FEATURE_DIR"
|
|
85
|
+
echo "TASKS_TEMPLATE: ${TASKS_TEMPLATE:-not found}"
|
|
86
|
+
echo "AVAILABLE_DOCS:"
|
|
87
|
+
check_file "$RESEARCH" "research.md"
|
|
88
|
+
check_file "$DATA_MODEL" "data-model.md"
|
|
89
|
+
check_dir "$CONTRACTS_DIR" "contracts/"
|
|
90
|
+
check_file "$QUICKSTART" "quickstart.md"
|
|
91
|
+
fi
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# [CHECKLIST TYPE] Checklist: [FEATURE NAME]
|
|
2
|
+
|
|
3
|
+
**Purpose**: [Brief description of what this checklist covers]
|
|
4
|
+
**Created**: [DATE]
|
|
5
|
+
**Feature**: [Link to spec.md or relevant documentation]
|
|
6
|
+
|
|
7
|
+
**Note**: This checklist is generated by the `/speckit-checklist` command based on feature context and requirements.
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
============================================================================
|
|
11
|
+
IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only.
|
|
12
|
+
|
|
13
|
+
The /speckit-checklist command MUST replace these with actual items based on:
|
|
14
|
+
- User's specific checklist request
|
|
15
|
+
- Feature requirements from spec.md
|
|
16
|
+
- Technical context from plan.md
|
|
17
|
+
- Implementation details from tasks.md
|
|
18
|
+
|
|
19
|
+
DO NOT keep these sample items in the generated checklist file.
|
|
20
|
+
============================================================================
|
|
21
|
+
-->
|
|
22
|
+
|
|
23
|
+
## [Category 1]
|
|
24
|
+
|
|
25
|
+
- [ ] CHK001 First checklist item with clear action
|
|
26
|
+
- [ ] CHK002 Second checklist item
|
|
27
|
+
- [ ] CHK003 Third checklist item
|
|
28
|
+
|
|
29
|
+
## [Category 2]
|
|
30
|
+
|
|
31
|
+
- [ ] CHK004 Another category item
|
|
32
|
+
- [ ] CHK005 Item with specific criteria
|
|
33
|
+
- [ ] CHK006 Final item in this category
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- Check items off as completed: `[x]`
|
|
38
|
+
- Add comments or findings inline
|
|
39
|
+
- Link to relevant resources or documentation
|
|
40
|
+
- Items are numbered sequentially for easy reference
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# [PROJECT_NAME] Constitution
|
|
2
|
+
<!-- Example: Spec Constitution, TaskFlow Constitution, etc. -->
|
|
3
|
+
|
|
4
|
+
## Core Principles
|
|
5
|
+
|
|
6
|
+
### [PRINCIPLE_1_NAME]
|
|
7
|
+
<!-- Example: I. Library-First -->
|
|
8
|
+
[PRINCIPLE_1_DESCRIPTION]
|
|
9
|
+
<!-- Example: Every feature starts as a standalone library; Libraries must be self-contained, independently testable, documented; Clear purpose required - no organizational-only libraries -->
|
|
10
|
+
|
|
11
|
+
### [PRINCIPLE_2_NAME]
|
|
12
|
+
<!-- Example: II. CLI Interface -->
|
|
13
|
+
[PRINCIPLE_2_DESCRIPTION]
|
|
14
|
+
<!-- Example: Every library exposes functionality via CLI; Text in/out protocol: stdin/args → stdout, errors → stderr; Support JSON + human-readable formats -->
|
|
15
|
+
|
|
16
|
+
### [PRINCIPLE_3_NAME]
|
|
17
|
+
<!-- Example: III. Test-First (NON-NEGOTIABLE) -->
|
|
18
|
+
[PRINCIPLE_3_DESCRIPTION]
|
|
19
|
+
<!-- Example: TDD mandatory: Tests written → User approved → Tests fail → Then implement; Red-Green-Refactor cycle strictly enforced -->
|
|
20
|
+
|
|
21
|
+
### [PRINCIPLE_4_NAME]
|
|
22
|
+
<!-- Example: IV. Integration Testing -->
|
|
23
|
+
[PRINCIPLE_4_DESCRIPTION]
|
|
24
|
+
<!-- Example: Focus areas requiring integration tests: New library contract tests, Contract changes, Inter-service communication, Shared schemas -->
|
|
25
|
+
|
|
26
|
+
### [PRINCIPLE_5_NAME]
|
|
27
|
+
<!-- Example: V. Observability, VI. Versioning & Breaking Changes, VII. Simplicity -->
|
|
28
|
+
[PRINCIPLE_5_DESCRIPTION]
|
|
29
|
+
<!-- Example: Text I/O ensures debuggability; Structured logging required; Or: MAJOR.MINOR.BUILD format; Or: Start simple, YAGNI principles -->
|
|
30
|
+
|
|
31
|
+
## [SECTION_2_NAME]
|
|
32
|
+
<!-- Example: Additional Constraints, Security Requirements, Performance Standards, etc. -->
|
|
33
|
+
|
|
34
|
+
[SECTION_2_CONTENT]
|
|
35
|
+
<!-- Example: Technology stack requirements, compliance standards, deployment policies, etc. -->
|
|
36
|
+
|
|
37
|
+
## [SECTION_3_NAME]
|
|
38
|
+
<!-- Example: Development Workflow, Review Process, Quality Gates, etc. -->
|
|
39
|
+
|
|
40
|
+
[SECTION_3_CONTENT]
|
|
41
|
+
<!-- Example: Code review requirements, testing gates, deployment approval process, etc. -->
|
|
42
|
+
|
|
43
|
+
## Governance
|
|
44
|
+
<!-- Example: Constitution supersedes all other practices; Amendments require documentation, approval, migration plan -->
|
|
45
|
+
|
|
46
|
+
[GOVERNANCE_RULES]
|
|
47
|
+
<!-- Example: All PRs/reviews must verify compliance; Complexity must be justified; Use [GUIDANCE_FILE] for runtime development guidance -->
|
|
48
|
+
|
|
49
|
+
**Version**: [CONSTITUTION_VERSION] | **Ratified**: [RATIFICATION_DATE] | **Last Amended**: [LAST_AMENDED_DATE]
|
|
50
|
+
<!-- Example: Version: 2.1.1 | Ratified: 2025-06-13 | Last Amended: 2025-07-16 -->
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Implementation Plan: [FEATURE]
|
|
2
|
+
|
|
3
|
+
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
|
|
4
|
+
|
|
5
|
+
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
|
|
6
|
+
|
|
7
|
+
**Note**: This template is filled in by the `/speckit-plan` command. See `.specify/templates/plan-template.md` for the execution workflow.
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
[Extract from feature spec: primary requirement + technical approach from research]
|
|
12
|
+
|
|
13
|
+
## Technical Context
|
|
14
|
+
|
|
15
|
+
<!--
|
|
16
|
+
ACTION REQUIRED: Replace the content in this section with the technical details
|
|
17
|
+
for the project. The structure here is presented in advisory capacity to guide
|
|
18
|
+
the iteration process.
|
|
19
|
+
-->
|
|
20
|
+
|
|
21
|
+
**Language/Version**: [e.g., Python 3.11, Swift 5.9, Rust 1.75 or NEEDS CLARIFICATION]
|
|
22
|
+
|
|
23
|
+
**Primary Dependencies**: [e.g., FastAPI, UIKit, LLVM or NEEDS CLARIFICATION]
|
|
24
|
+
|
|
25
|
+
**Storage**: [if applicable, e.g., PostgreSQL, CoreData, files or N/A]
|
|
26
|
+
|
|
27
|
+
**Testing**: [e.g., pytest, XCTest, cargo test or NEEDS CLARIFICATION]
|
|
28
|
+
|
|
29
|
+
**Target Platform**: [e.g., Linux server, iOS 15+, WASM or NEEDS CLARIFICATION]
|
|
30
|
+
|
|
31
|
+
**Project Type**: [e.g., library/cli/web-service/mobile-app/compiler/desktop-app or NEEDS CLARIFICATION]
|
|
32
|
+
|
|
33
|
+
**Performance Goals**: [domain-specific, e.g., 1000 req/s, 10k lines/sec, 60 fps or NEEDS CLARIFICATION]
|
|
34
|
+
|
|
35
|
+
**Constraints**: [domain-specific, e.g., <200ms p95, <100MB memory, offline-capable or NEEDS CLARIFICATION]
|
|
36
|
+
|
|
37
|
+
**Scale/Scope**: [domain-specific, e.g., 10k users, 1M LOC, 50 screens or NEEDS CLARIFICATION]
|
|
38
|
+
|
|
39
|
+
## Constitution Check
|
|
40
|
+
|
|
41
|
+
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
|
42
|
+
|
|
43
|
+
[Gates determined based on constitution file]
|
|
44
|
+
|
|
45
|
+
## Project Structure
|
|
46
|
+
|
|
47
|
+
### Documentation (this feature)
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
specs/[###-feature]/
|
|
51
|
+
├── plan.md # This file (/speckit-plan command output)
|
|
52
|
+
├── research.md # Phase 0 output (/speckit-plan command)
|
|
53
|
+
├── data-model.md # Phase 1 output (/speckit-plan command)
|
|
54
|
+
├── quickstart.md # Phase 1 output (/speckit-plan command)
|
|
55
|
+
├── contracts/ # Phase 1 output (/speckit-plan command)
|
|
56
|
+
└── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Source Code (repository root)
|
|
60
|
+
<!--
|
|
61
|
+
ACTION REQUIRED: Replace the placeholder tree below with the concrete layout
|
|
62
|
+
for this feature. Delete unused options and expand the chosen structure with
|
|
63
|
+
real paths (e.g., apps/admin, packages/something). The delivered plan must
|
|
64
|
+
not include Option labels.
|
|
65
|
+
-->
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
# [REMOVE IF UNUSED] Option 1: Single project (DEFAULT)
|
|
69
|
+
src/
|
|
70
|
+
├── models/
|
|
71
|
+
├── services/
|
|
72
|
+
├── cli/
|
|
73
|
+
└── lib/
|
|
74
|
+
|
|
75
|
+
tests/
|
|
76
|
+
├── contract/
|
|
77
|
+
├── integration/
|
|
78
|
+
└── unit/
|
|
79
|
+
|
|
80
|
+
# [REMOVE IF UNUSED] Option 2: Web application (when "frontend" + "backend" detected)
|
|
81
|
+
backend/
|
|
82
|
+
├── src/
|
|
83
|
+
│ ├── models/
|
|
84
|
+
│ ├── services/
|
|
85
|
+
│ └── api/
|
|
86
|
+
└── tests/
|
|
87
|
+
|
|
88
|
+
frontend/
|
|
89
|
+
├── src/
|
|
90
|
+
│ ├── components/
|
|
91
|
+
│ ├── pages/
|
|
92
|
+
│ └── services/
|
|
93
|
+
└── tests/
|
|
94
|
+
|
|
95
|
+
# [REMOVE IF UNUSED] Option 3: Mobile + API (when "iOS/Android" detected)
|
|
96
|
+
api/
|
|
97
|
+
└── [same as backend above]
|
|
98
|
+
|
|
99
|
+
ios/ or android/
|
|
100
|
+
└── [platform-specific structure: feature modules, UI flows, platform tests]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Structure Decision**: [Document the selected structure and reference the real
|
|
104
|
+
directories captured above]
|
|
105
|
+
|
|
106
|
+
## Complexity Tracking
|
|
107
|
+
|
|
108
|
+
> **Fill ONLY if Constitution Check has violations that must be justified**
|
|
109
|
+
|
|
110
|
+
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
|
111
|
+
|-----------|------------|-------------------------------------|
|
|
112
|
+
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
|
113
|
+
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|